/* * Do house keeping works when pgpool child process exits */ void child_exit(int code) { if (getpid() == mypid) { pool_log("child_exit: called from pgpool main. ignored."); return; } /* count down global connection counter */ if (accepted) connection_count_down(); /* prepare to shutdown connections to system db */ if(pool_config->parallel_mode || pool_config->enable_query_cache) { if (system_db_info->pgconn) pool_close_libpq_connection(); if (pool_system_db_connection()) pool_close(pool_system_db_connection()->con); } if (pool_config->memory_cache_enabled && !pool_is_shmem_cache()) { memcached_disconnect(); } /* let backend know now we are exiting */ if (pool_connection_pool) send_frontend_exits(); exit(code); }
/* -------------------------------- * pool_query_cache_table_exists - checks if query_cache table exists in the SystemDB * * This function is called once and only once from the pgpool parent process. * return 1 if query_cache table exists, 0 otherwise. * -------------------------------- */ int pool_query_cache_table_exists(void) { PGresult *pg_result = NULL; char *sql = NULL; int sql_len = strlen(pool_config->system_db_schema) + strlen(QUERY_CACHE_TABLE_NAME) + 64; if (! system_db_connection_exists()) return 0; sql = (char *)malloc(sql_len); if (malloc_failed(sql)) return 0; snprintf(sql, sql_len, "SELECT hash, query, value, dbname, create_time FROM %s.%s LIMIT 1", pool_config->system_db_schema, QUERY_CACHE_TABLE_NAME); pg_result = PQexec(system_db_info->pgconn, sql); if (!pg_result || PQresultStatus(pg_result) != PGRES_TUPLES_OK) { pool_error("pool_query_cache_table_exists: PQexec() failed. reason: %s", PQerrorMessage(system_db_info->pgconn)); PQclear(pg_result); free(sql); return 0; } PQclear(pg_result); pool_close_libpq_connection(); free(sql); return 1; }
void pool_backend_timer(void) { #define TMINTMAX 0x7fffffff POOL_CONNECTION_POOL *p = pool_connection_pool; int i, j; time_t now; time_t nearest = TMINTMAX; ConnectionInfo *info; POOL_SETMASK(&BlockSig); now = time(NULL); pool_debug("pool_backend_timer_handler called at %ld", now); for (i=0;i<pool_config->max_pool;i++, p++) { if (!MASTER_CONNECTION(p)) continue; if (!MASTER_CONNECTION(p)->sp) continue; if (MASTER_CONNECTION(p)->sp->user == NULL) continue; /* timer expire? */ if (MASTER_CONNECTION(p)->closetime) { int freed = 0; pool_debug("pool_backend_timer_handler: expire time: %ld", MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time); if (now >= (MASTER_CONNECTION(p)->closetime+pool_config->connection_life_time)) { /* discard expired connection */ pool_debug("pool_backend_timer_handler: expires user %s database %s", MASTER_CONNECTION(p)->sp->user, MASTER_CONNECTION(p)->sp->database); pool_send_frontend_exits(p); for (j=0;j<NUM_BACKENDS;j++) { if (!VALID_BACKEND(j)) continue; if (!freed) { pool_free_startup_packet(CONNECTION_SLOT(p, j)->sp); freed = 1; } pool_close(CONNECTION(p, j)); free(CONNECTION_SLOT(p, j)); } info = p->info; memset(p, 0, sizeof(POOL_CONNECTION_POOL)); p->info = info; memset(p->info, 0, sizeof(ConnectionInfo) * MAX_NUM_BACKENDS); /* prepare to shutdown connections to system db */ if(pool_config->system_db_dynamic_connection && (pool_config->parallel_mode || pool_config->enable_query_cache)) { if (system_db_info->pgconn) pool_close_libpq_connection(); if (pool_system_db_connection() && pool_system_db_connection()->con) { pool_send_frontend_exit(pool_system_db_connection()->con); pool_close(pool_system_db_connection()->con); } if( system_db_info->connection ) { free( system_db_info->connection ); memset(system_db_info->connection, 0, sizeof(POOL_CONNECTION_POOL_SLOT)); system_db_info->connection = NULL; } } } else { /* look for nearest timer */ if (MASTER_CONNECTION(p)->closetime < nearest) nearest = MASTER_CONNECTION(p)->closetime; } } } /* any remaining timer */ if (nearest != TMINTMAX) { nearest = pool_config->connection_life_time - (now - nearest); if (nearest <= 0) nearest = 1; pool_signal(SIGALRM, pool_backend_timer_handler); alarm(nearest); } POOL_SETMASK(&UnBlockSig); }
/* * pool_memset_system_db_info: * Initializes distribution rules. Distribution rules are stored in * System DB. So we have to execute query, and expand results on * memory. */ int pool_memset_system_db_info (SystemDBInfo *info) { int i; static char sql[1024],sql2[1024]; PGresult *result; DistDefInfo *dist_info = NULL; RepliDefInfo *repli_info = NULL; if (!system_db_info->pgconn || (PQstatus(system_db_info->pgconn) != CONNECTION_OK)) { if (system_db_connect()) return -1; } /* get distribution rules */ snprintf(sql, sizeof(sql), "SELECT dbname, schema_name, table_name,col_name,array_upper(col_list,1),col_list,type_list, dist_def_func FROM %s.dist_def", pool_config->system_db_schema); result = PQexec(system_db_info->pgconn, sql); if (!result || PQresultStatus(result) != PGRES_TUPLES_OK) { ereport(WARNING, (errmsg("error while initializing distribution rules, PQexec failed: \"%s\"", PQerrorMessage(system_db_info->pgconn)))); return -1; } else { info->dist_def_num = PQntuples(result); if (PQntuples(result) > 0) { dist_info = palloc(sizeof(DistDefInfo) * info->dist_def_num); } info->dist_def_slot = dist_info; for (i = 0; i < PQntuples(result); ++i) { char *t_dbname; char *t_schema_name; char *t_table_name; char *t_dist_key_col_name; char *t_dist_def_func; int num; int len; num = atol(PQgetvalue(result, i ,4)); t_dbname = palloc(strlen(PQgetvalue(result,i,0)) + 1); strcpy(t_dbname, PQgetvalue(result,i,0)); dist_info[i].dbname = t_dbname; t_schema_name = palloc(strlen(PQgetvalue(result,i,1)) + 1); strcpy(t_schema_name, PQgetvalue(result,i,1)); dist_info[i].schema_name = t_schema_name; t_table_name = palloc(strlen(PQgetvalue(result,i,2)) + 1); strcpy(t_table_name, PQgetvalue(result,i,2)); dist_info[i].table_name = t_table_name; t_dist_key_col_name = palloc(strlen(PQgetvalue(result,i,3)) + 1); strcpy(t_dist_key_col_name, PQgetvalue(result,i,3)); dist_info[i].dist_key_col_name = t_dist_key_col_name; t_dist_def_func = palloc(strlen(PQgetvalue(result,i,7)) + 1); strcpy(t_dist_def_func, PQgetvalue(result,i,7)); dist_info[i].dist_def_func = t_dist_def_func; dist_info[i].col_num = num; dist_info[i].col_list = palloc0(num * sizeof(char *)); dist_info[i].type_list = palloc0(num * sizeof(char *)); if (get_col_list(&dist_info[i]) < 0) { ereport(WARNING, (errmsg("error while initializing distribution rules, failed to get column list"))); PQclear(result); pool_close_libpq_connection(); return -1; } /* create PREPARE statement */ len = strlen(t_dbname) + strlen(t_schema_name) + strlen(t_table_name) + strlen("pgpool_"); dist_info[i].prepare_name = palloc(len + 1); snprintf(dist_info[i].prepare_name, len+1, "pgpool_%s%s%s", t_dbname, t_schema_name, t_table_name); dist_info[i].prepare_name[len] = '\0'; } } PQclear(result); /* get replication rules */ snprintf(sql2, sizeof(sql2), "SELECT dbname, schema_name, table_name, array_upper(col_list,1),col_list,type_list FROM %s.replicate_def", pool_config->system_db_schema); result = PQexec(system_db_info->pgconn, sql2); if (!result) { ereport(WARNING, (errmsg("error while initializing distribution rules, PQexec failed: \"%s\"", PQerrorMessage(system_db_info->pgconn)))); return -1; } else if (PQresultStatus(result) != PGRES_TUPLES_OK) { info->repli_def_num = 0; info->repli_def_slot = NULL; } else { info->repli_def_num = PQntuples(result); if (PQntuples(result) > 0) { repli_info = palloc(sizeof(RepliDefInfo) * info->repli_def_num); } info->repli_def_slot = repli_info; for (i = 0; i < PQntuples(result); ++i) { char *t_dbname; char *t_schema_name; char *t_table_name; int num; int len; num = atol(PQgetvalue(result, i ,3)); t_dbname = palloc(strlen(PQgetvalue(result,i,0)) + 1); strcpy(t_dbname, PQgetvalue(result,i,0)); repli_info[i].dbname = t_dbname; t_schema_name = palloc(strlen(PQgetvalue(result,i,1)) + 1); strcpy(t_schema_name, PQgetvalue(result,i,1)); repli_info[i].schema_name = t_schema_name; t_table_name = palloc(strlen(PQgetvalue(result,i,2)) + 1); strcpy(t_table_name, PQgetvalue(result,i,2)); repli_info[i].table_name = t_table_name; repli_info[i].col_num = num; repli_info[i].col_list = palloc0(num * sizeof(char *)); repli_info[i].type_list = palloc0(num * sizeof(char *)); if (get_col_list2(&repli_info[i]) < 0) { ereport(WARNING, (errmsg("error while initializing distribution rules, failed to get column list"))); PQclear(result); pool_close_libpq_connection(); return -1; } /* create PREPARE statement */ len = strlen(t_dbname) + strlen(t_schema_name) + strlen(t_table_name) + strlen("pgpool_"); repli_info[i].prepare_name = palloc(len + 1); snprintf(repli_info[i].prepare_name, len+1, "pgpool_%s%s%s", t_dbname, t_schema_name, t_table_name); repli_info[i].prepare_name[len] = '\0'; } } PQclear(result); pool_close_libpq_connection(); return i; }
/* -------------------------------- * pool_query_cache_register() - register query cache to the SystemDB * * returns 0 on sucess, -1 otherwise * -------------------------------- */ int pool_query_cache_register(char kind, POOL_CONNECTION *frontend, char *database, char *data, int data_len, char *query) { int ret; int send_len; if (! system_db_connection_exists()) return -1; if (! CACHE_TABLE_INFO.has_prepared_statement) define_prepared_statements(); switch (kind) { case 'T': /* RowDescription */ { /* for all SELECT result data from the backend, 'T' must come first */ if (query_cache_info != NULL) { pool_error("pool_query_cache_register: received RowDescription in the wrong order"); free_query_cache_info(); return -1; } pool_debug("pool_query_cache_register: saving cache for query: \"%s\"", query); /* initialize query_cache_info and save the query */ ret = init_query_cache_info(frontend, database, query); if (ret) return ret; /* store data into the cache */ write_cache(&kind, 1); send_len = htonl(data_len + sizeof(int)); write_cache(&send_len, sizeof(int)); write_cache(data, data_len); break; } case 'D': /* DataRow */ { /* for all SELECT result data from the backend, 'T' must come first */ if (query_cache_info == NULL) { pool_error("pool_query_cache_register: received DataRow in the wrong order"); return -1; } write_cache(&kind, 1); send_len = htonl(data_len + sizeof(int)); write_cache(&send_len, sizeof(int)); write_cache(data, data_len); break; } case 'C': /* CommandComplete */ { PGresult *pg_result = NULL; char *escaped_query = NULL; size_t escaped_query_len; time_t now = time(NULL); char *values[5]; int values_len[5]; int values_format[5]; int i; /* for all SELECT result data from the backend, 'T' must come first */ if (query_cache_info == NULL) { pool_error("pool_query_cache_register: received CommandComplete in the wrong order"); return -1; } /* pack CommandComplete data into the cache */ write_cache(&kind, 1); send_len = htonl(data_len + sizeof(int)); write_cache(&send_len, sizeof(int)); write_cache(data, data_len); query_cache_info->create_time = pq_time_to_str(now); if (malloc_failed(query_cache_info->create_time)) { free_query_cache_info(); return -1; } escaped_query = (char *)malloc(strlen(query_cache_info->query) * 2 + 1); if (malloc_failed(escaped_query)) { free_query_cache_info(); return -1; } /* escaped_query_len = PQescapeStringConn(system_db_info->pgconn, */ /* escaped_query, */ /* query_cache_info->query, */ /* strlen(query_cache_info->query))); */ escaped_query_len = PQescapeString(escaped_query, query_cache_info->query, strlen(query_cache_info->query)); /* all the result data have been received. store into the SystemDB */ values[0] = strdup(query_cache_info->md5_query); values[1] = strdup(escaped_query); values[2] = (char *)malloc(query_cache_info->cache_offset); memcpy(values[2], query_cache_info->cache, query_cache_info->cache_offset); values[3] = strdup(query_cache_info->db_name); values[4] = strdup(query_cache_info->create_time); for (i = 0; i < 5; i++) { if (malloc_failed(values[i])) { pool_error("pool_query_cache_register: malloc() failed"); free_query_cache_info(); { int j; for (j = 0; j < i; j++) free(values[j]); } return -1; } values_len[i] = strlen(values[i]); } values_format[0] = values_format[1] = values_format[3] = values_format[4] = 0; values_format[2] = 1; values_len[2] = query_cache_info->cache_offset; pg_result = PQexecPrepared(system_db_info->pgconn, CACHE_TABLE_INFO.register_prepared_statement, 5, (const char * const *)values, values_len, values_format, 0); if (!pg_result || PQresultStatus(pg_result) != PGRES_COMMAND_OK) { pool_error("pool_query_cache_register: PQexecPrepared() failed. reason: %s", PQerrorMessage(system_db_info->pgconn)); PQclear(pg_result); PQfreemem(escaped_query); free_query_cache_info(); for (i = 0; i < 5; i++) free(values[i]); return -1; } PQclear(pg_result); PQfreemem(escaped_query); for (i = 0; i < 5; i++) free(values[i]); free_query_cache_info(); break; } case 'E': { pool_debug("pool_query_cache_register: received 'E': free query cache buffer"); pool_close_libpq_connection(); free_query_cache_info(); break; } } return 0; }
/* -------------------------------- * pool_query_cache_lookup - retrieve query cache from the SystemDB * * creates a SQL query string for searching a cache from the SystemDB. * * returns POOL_CONTINUE if cache is found. returns POOL_END if cache was * not found. returns POOL_ERROR if an error has been encountered while * searching. * * Note that POOL_END and POOL_ERROR are treated the same by the caller * (pool_process_query.c). * POOL_END and POOL_ERROR both indicates to the caller that the search * query must be forwarded to the backends in order to retrieve data and * the result be cached. * Only difference is that POOL_ERROR indicates that some fatal error has * occured; query cache function, however, should be seemless to the user * whether cache was not found or error has occured during cache retrieve. * -------------------------------- */ POOL_STATUS pool_query_cache_lookup(POOL_CONNECTION *frontend, char *query, char *database, char tstate) { char *sql = NULL; int sql_len; char md5_query[33]; struct timeval timeout; int status; if (! system_db_connection_exists()) return POOL_ERROR; /* same as POOL_END ... at least for now */ sql_len = strlen(pool_config->system_db_schema) + strlen(QUERY_CACHE_TABLE_NAME) + sizeof(md5_query) + strlen(database) + 64; sql = (char *)malloc(sql_len); if (malloc_failed(sql)) return POOL_ERROR; /* should I exit here rather than returning an error? */ /* cached data lookup */ pool_md5_hash(query, strlen(query), md5_query); snprintf(sql, sql_len, "SELECT value FROM %s.%s WHERE hash = '%s' AND dbname = '%s'", pool_config->system_db_schema, QUERY_CACHE_TABLE_NAME, md5_query, database); /* set timeout value for select */ timeout.tv_sec = pool_config->child_life_time; timeout.tv_usec = 0; pool_debug("pool_query_cache_lookup: searching cache for query: \"%s\"", query); status = search_system_db_for_cache(frontend, sql, strlen(sql)+1, &timeout, tstate); /* make sure that the remaining data is discarded */ SYSDB_CON->po = 0; SYSDB_CON->len = 0; free(sql); /* cache found, and no backend communication needed */ if (status == CACHE_FOUND) { return POOL_CONTINUE; } /* cache not found */ if (status == CACHE_ERROR) { pool_error("pool_query_cache_lookup: query cache lookup failed"); /* reset the SystemDB connection */ if (system_db_info->pgconn) pool_close_libpq_connection(); return POOL_ERROR; /* same as POOL_END ... at least for now */ } pool_debug("pool_query_cache_lookup: query cache not found"); return POOL_END; }