From a38e1440c6154a8d64333b3c87538d1e29738cb6 Mon Sep 17 00:00:00 2001 From: Tatsuo Ishii Date: Fri, 31 Mar 2006 02:13:21 +0000 Subject: [PATCH] Automatically DEALLOCATE prepared statememt when client disconnects. Patch contributed by Yoshiyuki Asaba. --- child.c | 2 + pool.h | 2 + pool_process_query.c | 185 ++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 188 insertions(+), 1 deletion(-) diff --git a/child.c b/child.c index 4fb8dbb..4bf85b6 100644 --- a/child.c +++ b/child.c @@ -121,6 +121,8 @@ void do_child(int unix_fd, int inet_fd) timeout.tv_sec = pool_config.child_life_time; timeout.tv_usec = 0; + init_prepared_list(); + for (;;) { int connection_reuse = 1; diff --git a/pool.h b/pool.h index 5304653..1af8acd 100644 --- a/pool.h +++ b/pool.h @@ -333,4 +333,6 @@ extern void pool_send_error_message(POOL_CONNECTION *frontend, int protoMajor, extern void pool_free_startup_packet(StartupPacket *sp); extern int health_check(void); +extern void init_prepared_list(void); + #endif /* POOL_H */ diff --git a/pool_process_query.c b/pool_process_query.c index e3afb10..71c5b41 100644 --- a/pool_process_query.c +++ b/pool_process_query.c @@ -39,6 +39,17 @@ #include "pool.h" +#define INIT_STATEMENT_LIST_SIZE 8 + +/* + * prepared statement list + */ +typedef struct { + int size; + int cnt; + char **stmt_list; +} PreparedStatementList; + static POOL_STATUS NotificationResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend); @@ -107,6 +118,11 @@ static POOL_STATUS insert_lock(POOL_CONNECTION_POOL *backend, char *query); static char *get_insert_command_table_name(char *query); static char *skip_comment(char *query); +static void add_prepared_list(PreparedStatementList *p, char *name); +static void del_prepared_list(PreparedStatementList *p, char *name); +static void reset_prepared_list(PreparedStatementList *p); +static int send_deallocate(POOL_CONNECTION_POOL *backend, PreparedStatementList *p, int n); + static POOL_CONNECTION_POOL_SLOT *slots[MAX_CONNECTION_SLOTS]; static int in_load_balance; /* non 0 if in load balance mode */ @@ -115,6 +131,10 @@ static int replication_was_enabled; /* replication mode was enabled */ static int master_slave_was_enabled; /* master/slave mode was enabled */ static int internal_transaction_started; /* to issue table lock command a transaction has been started internally */ +static void (*pending_function)(PreparedStatementList *p, char *name) = NULL; +static char *pending_prepared_name = NULL; + +static PreparedStatementList prepared_list; /* prepared statement name list */ static int is_drop_database(char *query); /* returns non 0 if this is a DROP DATABASE command */ POOL_STATUS pool_process_query(POOL_CONNECTION *frontend, @@ -584,6 +604,37 @@ static POOL_STATUS Query(POOL_CONNECTION *frontend, return POOL_CONTINUE; } + if (frontend && + (strncasecmp("prepare", string, 7) == 0 || + strncasecmp("deallocate", string, 10) == 0)) + { + char *query = string; + char *buf, *name; + query = skip_comment(query); + + /* skip "prepare" or "deallocate" */ + while (*query && !isspace(*query)) + query++; + + /* skip spaces */ + while (*query && isspace(*query)) + query++; + + buf = strdup(query); + name = strtok(buf, "\t\r\n (;"); + if (name && (*string == 'p' || *string == 'P')) + { + pending_function = add_prepared_list; + pending_prepared_name = strdup(name); + } + else if (name && (*string == 'd' || *string == 'D')) + { + pending_function = del_prepared_list; + pending_prepared_name = strdup(name); + } + free(buf); + } + /* load balance trick */ if (load_balance_enabled(backend, string)) start_load_balance(backend); @@ -2231,6 +2282,15 @@ POOL_STATUS SimpleForwardToFrontend(char kind, POOL_CONNECTION *frontend, POOL_C pool_write(frontend, &kind, 1); + if ((kind == 'C' || kind == '1' || kind == '3') && + pending_function && pending_prepared_name) + { + pending_function(&prepared_list, pending_prepared_name); + } + free(pending_prepared_name); + pending_function = NULL; + pending_prepared_name = NULL; + status = pool_read(MASTER(backend), &len, sizeof(len)); if (status < 0) { @@ -2277,6 +2337,8 @@ POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CO int len; int sendlen; char *p; + int name_len; + char *name; if (pool_write(MASTER(backend), &kind, 1)) return POOL_END; @@ -2310,6 +2372,33 @@ POOL_STATUS SimpleForwardToBackend(char kind, POOL_CONNECTION *frontend, POOL_CO if (pool_write(SECONDARY(backend), p, len)) return POOL_END; + if (kind == 'P' && *p) + { + name_len = strlen(p) + 3; + name = malloc(name_len); + if (name == NULL) + { + pool_error("SimpleForwardToBackend: malloc failed: %s", strerror(errno)); + return POOL_END; + } + sprintf(name, "\"%s\"", p); + pending_function = add_prepared_list; + pending_prepared_name = name; + } + else if (kind == 'C' && *p == 'S' && *(p + 1)) + { + name_len = strlen(p + 1) + 3; + name = malloc(name_len); + if (name == NULL) + { + pool_error("SimpleForwardToBackend: malloc failed: %s", strerror(errno)); + return POOL_END; + } + sprintf(name, "\"%s\"", p + 1); + pending_function = del_prepared_list; + pending_prepared_name = name; + } + return POOL_CONTINUE; } @@ -2381,7 +2470,16 @@ static int reset_backend(POOL_CONNECTION_POOL *backend, int qcnt) qn = pool_config.num_reset_queries; if (qcnt >= qn) - return 2; + { + if (qcnt >= qn + prepared_list.cnt) + { + reset_prepared_list(&prepared_list); + return 2; + } + + send_deallocate(backend, &prepared_list, qcnt - qn); + return 1; + } query = pool_config.reset_query_list[qcnt]; @@ -2848,3 +2946,88 @@ static char *skip_comment(char *query) } return query; } + +void init_prepared_list(void) +{ + prepared_list.cnt = 0; + prepared_list.size = INIT_STATEMENT_LIST_SIZE; + prepared_list.stmt_list = malloc(sizeof(char *) * prepared_list.size); + if (prepared_list.stmt_list == NULL) + { + pool_error("init_prepared_list: malloc failed: %s", strerror(errno)); + exit(1); + } +} + +static void add_prepared_list(PreparedStatementList *p, char *name) +{ + if (p->cnt == p->size) + { + p->size *= 2; + p->stmt_list = realloc(p->stmt_list, sizeof(char *) * p->size); + if (p->stmt_list == NULL) + { + pool_error("add_prepared_list: realloc failed: %s", strerror(errno)); + exit(1); + } + } + + p->stmt_list[p->cnt++] = strdup(name); +} + +static void del_prepared_list(PreparedStatementList *p, char *name) +{ + int i; + + for (i = 0; i < p->cnt; i++) + { + if (strcmp(p->stmt_list[i], name) == 0) + break; + } + + if (i == p->cnt) + return; + else if (i == p->cnt - 1) + p->cnt--; + else + { + memmove(p->stmt_list[i], p->stmt_list[i+1], sizeof(char *) * (p->cnt - i - 1)); + p->cnt--; + } +} + +static void reset_prepared_list(PreparedStatementList *p) +{ + int i; + + for (i = 0; i < p->cnt; i++) + free(p->stmt_list[i]); + p->cnt = 0; +} + +static int send_deallocate(POOL_CONNECTION_POOL *backend, PreparedStatementList *p, + int n) +{ + char *query; + int len; + + if (p->cnt <= n) + return 1; + + len = strlen(p->stmt_list[n]) + 12; /* "DEALLOCATE " + '\0' */ + query = malloc(len); + if (query == NULL) + { + pool_error("send_deallocate: malloc failed: %s", strerror(errno)); + exit(1); + } + sprintf(query, "DEALLOCATE %s", p->stmt_list[n]); + if (Query(NULL, backend, query) != POOL_CONTINUE) + { + free(query); + return 1; + } + free(query); + + return 0; +} -- 2.39.5