/* * Returns true if the DB node is needed to send query. * Intended to be called from VALID_BACKEND */ bool pool_is_node_to_be_sent_in_current_query(int node_id) { POOL_SESSION_CONTEXT *sc; if (RAW_MODE) return node_id == REAL_MASTER_NODE_ID; sc = pool_get_session_context(); if (!sc) return true; if (pool_is_query_in_progress() && sc->query_context) { return pool_is_node_to_be_sent(sc->query_context, node_id); } return true; }
POOL_STATUS ErrorResponse(POOL_CONNECTION *frontend, POOL_CONNECTION_POOL *backend) { char *string = NULL; int len; int i; POOL_STATUS ret = POOL_CONTINUE; for (i=0;i<NUM_BACKENDS;i++) { if (VALID_BACKEND(i)) { /* read error message */ string = pool_read_string(CONNECTION(backend, i), &len, 0); if (string == NULL) return POOL_END; } } /* forward to the frontend */ pool_write(frontend, "E", 1); if (pool_write_and_flush(frontend, string, len) < 0) return POOL_END; /* * check session context, because this function is called * by pool_do_auth too. */ if (pool_get_session_context()) ret = raise_intentional_error_if_need(backend); /* change transaction state */ for (i=0;i<NUM_BACKENDS;i++) { if (VALID_BACKEND(i)) { if (TSTATE(backend, i) == 'T') TSTATE(backend, i) = 'E'; } } return ret; }
/* * Create "ERROR" etc., timestamp, pid, user name string and return * it. The returned string is in a static buff and subsequent calls * will overwrite it. */ static char *optstring(int kind) { static char *kindstr[] = {"ERROR:", "DEBUG:", "LOG: "}; char timebuf[MAXSTRFTIME]; time_t now; static char optbuf[MAXSTRFTIME+7+8+NAMEDATALEN]; #ifndef POOL_TOOLS char username[NAMEDATALEN]; #endif char buf[128]; #ifndef POOL_TOOLS POOL_SESSION_CONTEXT *c; #endif optbuf[0] = '\0'; if (pool_config->print_timestamp) { now = time(NULL); strftime(timebuf, MAXSTRFTIME, "%Y-%m-%d %H:%M:%S ", localtime(&now)); strcat(optbuf, timebuf); } snprintf(buf, sizeof(buf), "%s pid: %d", kindstr[kind], (int)getpid()); strcat(optbuf, buf); #ifndef POOL_TOOLS if (pool_config->print_user) { if ((c = pool_get_session_context())) if (MASTER_CONNECTION(c->backend) && MASTER_CONNECTION(c->backend)->sp && MASTER_CONNECTION(c->backend)->sp->user) { strlcpy(username, MASTER_CONNECTION(c->backend)->sp->user, sizeof(username)); strcat(optbuf, " user: "); strcat(optbuf, username); } } #endif return optbuf; }
/* * Returns virtual master DB node id, */ int pool_virtual_master_db_node_id(void) { POOL_SESSION_CONTEXT *sc; sc = pool_get_session_context(); if (!sc) { return REAL_MASTER_NODE_ID; } if (sc->query_context) { return sc->query_context->virtual_master_node_id; } /* * No query context exists. Returns master node id in private buffer. */ return my_master_node_id; }
/* * Start query */ void pool_start_query(POOL_QUERY_CONTEXT *query_context, char *query, int len, Node *node) { POOL_SESSION_CONTEXT *session_context; if (query_context) { session_context = pool_get_session_context(); query_context->original_length = len; query_context->rewritten_length = -1; query_context->original_query = pstrdup(query); query_context->rewritten_query = NULL; query_context->parse_tree = node; query_context->virtual_master_node_id = my_master_node_id; query_context->is_cache_safe = false; if (pool_config->memory_cache_enabled) query_context->temp_cache = pool_create_temp_query_cache(query); pool_set_query_in_progress(); session_context->query_context = query_context; } }
/* * Check if the function is stable. */ static bool is_immutable_function(char *fname) { /* * Query to know if the function is IMMUTABLE */ #define IS_STABLE_FUNCTION_QUERY "SELECT count(*) FROM pg_catalog.pg_proc AS p WHERE p.proname = '%s' AND p.provolatile = 'i'" bool result; static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; backend = pool_get_session_context(false)->backend; if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, IS_STABLE_FUNCTION_QUERY, int_register_func, int_unregister_func, false); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking if the function is immutable"))); return false; } ereport(DEBUG1, (errmsg("checking if the function is IMMUTABLE"), errdetail("relcache created"))); } result = (pool_search_relcache(relcache, backend, fname) == 0) ? 0 : 1; ereport(DEBUG1, (errmsg("checking if the function is IMMUTABLE"), errdetail("search result = %d", result))); return result; }
/* * Send extended query and wait for response * send_type: * -1: do not send this node_id * 0: send to all nodes * >0: send to this node_id */ POOL_STATUS pool_extended_send_and_wait(POOL_QUERY_CONTEXT *query_context, char *kind, int len, char *contents, int send_type, int node_id) { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; bool is_commit; bool is_begin_read_write; int i; int str_len; int rewritten_len; char *str; char *rewritten_begin; session_context = pool_get_session_context(); frontend = session_context->frontend; backend = session_context->backend; is_commit = is_commit_or_rollback_query(query_context->parse_tree); is_begin_read_write = false; str_len = 0; rewritten_len = 0; str = NULL; rewritten_begin = NULL; /* * If the query is BEGIN READ WRITE or * BEGIN ... SERIALIZABLE in master/slave mode, * we send BEGIN to slaves/standbys instead. * original_query which is BEGIN READ WRITE is sent to primary. * rewritten_query which is BEGIN is sent to standbys. */ if (pool_need_to_treat_as_if_default_transaction(query_context)) { is_begin_read_write = true; if (*kind == 'P') { rewritten_begin = remove_read_write(len, contents, &rewritten_len); if (rewritten_begin == NULL) return POOL_END; } } if (!rewritten_begin) { str_len = len; str = contents; } /* Send query */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in reset context, we send COMMIT/ABORT to nodes those * are not in I(idle) state. This will ensure that * transactions are closed. */ if (is_commit && session_context->reset_context && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } if (rewritten_begin) { if (REAL_PRIMARY_NODE_ID == i) { str = contents; str_len = len; } else { str = rewritten_begin; str_len = rewritten_len; } } if (pool_config->log_per_node_statement) { char msgbuf[QUERY_STRING_BUFFER_LEN]; char *stmt; if (*kind == 'P' || *kind == 'E') { if (query_context->rewritten_query) { if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) stmt = query_context->original_query; else stmt = query_context->rewritten_query; } else { stmt = query_context->rewritten_query; } } else { stmt = query_context->original_query; } if (*kind == 'P') snprintf(msgbuf, sizeof(msgbuf), "Parse: %s", stmt); else snprintf(msgbuf, sizeof(msgbuf), "Execute: %s", stmt); } else { snprintf(msgbuf, sizeof(msgbuf), "%c message", *kind); } per_node_statement_log(backend, i, msgbuf); } if (send_extended_protocol_message(backend, i, kind, str_len, str) != POOL_CONTINUE) { free(rewritten_begin); return POOL_END; } } if (!is_begin_read_write) { if (query_context->rewritten_query) str = query_context->rewritten_query; else str = query_context->original_query; } /* Wait for response */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { continue; } if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) str = query_context->original_query; else str = query_context->rewritten_query; } if (wait_for_query_response(frontend, CONNECTION(backend, i), MAJOR(backend)) != POOL_CONTINUE) { /* Cancel current transaction */ CancelPacket cancel_packet; cancel_packet.protoVersion = htonl(PROTO_CANCEL); cancel_packet.pid = MASTER_CONNECTION(backend)->pid; cancel_packet.key= MASTER_CONNECTION(backend)->key; cancel_request(&cancel_packet); free(rewritten_begin); return POOL_END; } /* * Check if some error detected. If so, emit * log. This is usefull when invalid encoding error * occurs. In this case, PostgreSQL does not report * what statement caused that error and make users * confused. */ per_node_error_log(backend, i, str, "pool_send_and_wait: Error or notice message from backend: ", true); } free(rewritten_begin); return POOL_CONTINUE; }
/* * Send simple query and wait for response * send_type: * -1: do not send this node_id * 0: send to all nodes * >0: send to this node_id */ POOL_STATUS pool_send_and_wait(POOL_QUERY_CONTEXT *query_context, int send_type, int node_id) { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION *frontend; POOL_CONNECTION_POOL *backend; bool is_commit; bool is_begin_read_write; int i; int len; char *string; session_context = pool_get_session_context(); frontend = session_context->frontend; backend = session_context->backend; is_commit = is_commit_or_rollback_query(query_context->parse_tree); is_begin_read_write = false; len = 0; string = NULL; /* * If the query is BEGIN READ WRITE or * BEGIN ... SERIALIZABLE in master/slave mode, * we send BEGIN to slaves/standbys instead. * original_query which is BEGIN READ WRITE is sent to primary. * rewritten_query which is BEGIN is sent to standbys. */ if (pool_need_to_treat_as_if_default_transaction(query_context)) { is_begin_read_write = true; } else { if (query_context->rewritten_query) { len = query_context->rewritten_length; string = query_context->rewritten_query; } else { len = query_context->original_length; string = query_context->original_query; } } /* Send query */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } /* * If in reset context, we send COMMIT/ABORT to nodes those * are not in I(idle) state. This will ensure that * transactions are closed. */ if (is_commit && session_context->reset_context && TSTATE(backend, i) == 'I') { pool_unset_node_to_be_sent(query_context, i); continue; } if (is_begin_read_write) { if (REAL_PRIMARY_NODE_ID == i) { len = query_context->original_length; string = query_context->original_query; } else { len = query_context->rewritten_length; string = query_context->rewritten_query; } } per_node_statement_log(backend, i, string); if (send_simplequery_message(CONNECTION(backend, i), len, string, MAJOR(backend)) != POOL_CONTINUE) { return POOL_END; } } /* Wait for response */ for (i=0;i<NUM_BACKENDS;i++) { if (!VALID_BACKEND(i)) continue; else if (send_type < 0 && i == node_id) continue; else if (send_type > 0 && i != node_id) continue; #ifdef NOT_USED /* * If in master/slave mode, we do not send COMMIT/ABORT to * slaves/standbys if it's in I(idle) state. */ if (is_commit && MASTER_SLAVE && !IS_MASTER_NODE_ID(i) && TSTATE(backend, i) == 'I') { continue; } #endif if (is_begin_read_write) { if(REAL_PRIMARY_NODE_ID == i) string = query_context->original_query; else string = query_context->rewritten_query; } if (wait_for_query_response(frontend, CONNECTION(backend, i), MAJOR(backend)) != POOL_CONTINUE) { /* Cancel current transaction */ CancelPacket cancel_packet; cancel_packet.protoVersion = htonl(PROTO_CANCEL); cancel_packet.pid = MASTER_CONNECTION(backend)->pid; cancel_packet.key= MASTER_CONNECTION(backend)->key; cancel_request(&cancel_packet); return POOL_END; } /* * Check if some error detected. If so, emit * log. This is usefull when invalid encoding error * occurs. In this case, PostgreSQL does not report * what statement caused that error and make users * confused. */ per_node_error_log(backend, i, string, "pool_send_and_wait: Error or notice message from backend: ", true); } return POOL_CONTINUE; }
/* * Decide where to send queries(thus expecting response) */ void pool_where_to_send(POOL_QUERY_CONTEXT *query_context, char *query, Node *node) { POOL_SESSION_CONTEXT *session_context; POOL_CONNECTION_POOL *backend; int i; if (!query_context) { pool_error("pool_where_to_send: no query context"); return; } session_context = pool_get_session_context(); backend = session_context->backend; /* * Zap out DB node map */ pool_clear_node_to_be_sent(query_context); /* * If there is "NO LOAD BALANCE" comment, we send only to master node. */ if (!strncasecmp(query, NO_LOAD_BALANCE, NO_LOAD_BALANCE_COMMENT_SZ)) { pool_set_node_to_be_sent(query_context, MASTER_SLAVE ? PRIMARY_NODE_ID : REAL_MASTER_NODE_ID); for (i=0;i<NUM_BACKENDS;i++) { if (query_context->where_to_send[i]) { query_context->virtual_master_node_id = i; break; } } return; } /* * In raw mode, we send only to master node. Simple enough. */ if (RAW_MODE) { pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); } else if (MASTER_SLAVE && query_context->is_multi_statement) { /* * If we are in master/slave mode and we have multi stametemt * query, we should send it to primary server only. Otherwise * it is possible to send a write query to standby servers * because we only use the first element of the multi * statement query and don't care about the rest. Typical * situation where we are bugged by this is, "BEGIN;DELETE * FROM table;END". Note that from pgpool-II 3.1.0 * transactional statements such as "BEGIN" is unconditionaly * sent to all nodes(see send_to_where() for more details). * Someday we might be able to understand all part of multi * statement queries, but until that day we need this band * aid. */ if (query_context->is_multi_statement) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } } else if (MASTER_SLAVE) { POOL_DEST dest; POOL_MEMORY_POOL *old_context; old_context = pool_memory_context_switch_to(query_context->memory_context); dest = send_to_where(node, query); pool_memory_context_switch_to(old_context); pool_debug("send_to_where: %d query: %s", dest, query); /* Should be sent to primary only? */ if (dest == POOL_PRIMARY) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* Should be sent to both primary and standby? */ else if (dest == POOL_BOTH) { pool_setall_node_to_be_sent(query_context); } /* * Ok, we might be able to load balance the SELECT query. */ else { if (pool_config->load_balance_mode && is_select_query(node, query) && MAJOR(backend) == PROTO_MAJOR_V3) { /* * If (we are outside of an explicit transaction) OR * (the transaction has not issued a write query yet, AND * transaction isolation level is not SERIALIZABLE) * we might be able to load balance. */ if (TSTATE(backend, PRIMARY_NODE_ID) == 'I' || (!pool_is_writing_transaction() && !pool_is_failed_transaction() && pool_get_transaction_isolation() != POOL_SERIALIZABLE)) { BackendInfo *bkinfo = pool_get_node_info(session_context->load_balance_node_id); /* * Load balance if possible */ /* * If replication delay is too much, we prefer to send to the primary. */ if (!strcmp(pool_config->master_slave_sub_mode, MODE_STREAMREP) && pool_config->delay_threshold && bkinfo->standby_delay > pool_config->delay_threshold) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* * If a writing function call is used, * we prefer to send to the primary. */ else if (pool_has_function_call(node)) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* * If system catalog is used in the SELECT, we * prefer to send to the primary. Example: SELECT * * FROM pg_class WHERE relname = 't1'; Because * 't1' is a constant, it's hard to recognize as * table name. Most use case such query is * against system catalog, and the table name can * be a temporary table, it's best to query * against primary system catalog. * Please note that this test must be done *before* * test using pool_has_temp_table. */ else if (pool_has_system_catalog(node)) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* * If temporary table is used in the SELECT, * we prefer to send to the primary. */ else if (pool_config->check_temp_table && pool_has_temp_table(node)) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } /* * If unlogged table is used in the SELECT, * we prefer to send to the primary. */ else if (pool_has_unlogged_table(node)) { pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } else { pool_set_node_to_be_sent(query_context, session_context->load_balance_node_id); } } else { /* Send to the primary only */ pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } } else { /* Send to the primary only */ pool_set_node_to_be_sent(query_context, PRIMARY_NODE_ID); } } } else if (REPLICATION || PARALLEL_MODE) { if (is_select_query(node, query)) { /* * If a writing function call is used or replicate_select is true, * we prefer to send to all nodes. */ if ((pool_has_function_call(node) || pool_config->replicate_select)) { pool_setall_node_to_be_sent(query_context); } else if (pool_config->load_balance_mode && MAJOR(backend) == PROTO_MAJOR_V3 && TSTATE(backend, MASTER_NODE_ID) == 'I') { /* load balance */ pool_set_node_to_be_sent(query_context, session_context->load_balance_node_id); } else { /* only send to master node */ pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); } } else if (IsA(node, DeclareCursorStmt) || IsA(node, ClosePortalStmt) || IsA(node, FetchStmt)) { if (query_context->loadbalance_cursor) { if (pool_config->load_balance_mode && MAJOR(backend) == PROTO_MAJOR_V3 && TSTATE(backend, MASTER_NODE_ID) == 'I') { /* load balance */ pool_set_node_to_be_sent(query_context, session_context->load_balance_node_id); } else { /* only send to master node */ pool_set_node_to_be_sent(query_context, REAL_MASTER_NODE_ID); } } else { /* send to all nodes */ pool_setall_node_to_be_sent(query_context); } } else { /* send to all nodes */ pool_setall_node_to_be_sent(query_context); } } else { pool_error("pool_where_to_send: unknown mode"); return; } /* * EXECUTE? */ if (IsA(node, ExecuteStmt)) { POOL_SENT_MESSAGE *msg; msg = pool_get_sent_message('Q', ((ExecuteStmt *)node)->name); if (!msg) msg = pool_get_sent_message('P', ((ExecuteStmt *)node)->name); if (msg) pool_copy_prep_where(msg->query_context->where_to_send, query_context->where_to_send); } /* * DEALLOCATE? */ else if (IsA(node, DeallocateStmt)) { where_to_send_deallocate(query_context, node); } for (i=0;i<NUM_BACKENDS;i++) { if (query_context->where_to_send[i]) { query_context->virtual_master_node_id = i; break; } } return; }
/* * Judge the table used in a query is a view or not. */ bool is_view(char *table_name) { /* * Query to know if the target table is a view (including a materialized view). */ #define ISVIEWQUERY "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.relname = '%s' AND (c.relkind = 'v' OR c.relkind = 'm')" #define ISVIEWQUERY2 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pgpool_regclass('%s') AND (c.relkind = 'v' OR c.relkind = 'm')" #define ISVIEWQUERY3 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pg_catalog.to_regclass('%s') AND (c.relkind = 'v' OR c.relkind = 'm')" static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; bool result; char *query; if (table_name == NULL) { return false; } if (!pool_has_to_regclass() && !pool_has_pgpool_regclass()) table_name = remove_quotes_and_schema_from_relname(table_name); backend = pool_get_session_context(false)->backend; /* PostgreSQL 9.4 or later has to_regclass() */ if (pool_has_to_regclass()) { query = ISVIEWQUERY3; } /* pgpool_regclass has been installed */ else if (pool_has_pgpool_regclass()) { query = ISVIEWQUERY2; } else { query = ISVIEWQUERY; } if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, false); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for view"))); return false; } } /* * Search relcache. */ result = pool_search_relcache(relcache, backend, table_name) == 0 ? false : true; return result; }
/* * Judge the table used in a query represented by node is a unlogged * table or not. */ bool is_unlogged_table(char *table_name) { /* * Query to know if pg_class has relpersistence column or not. * PostgreSQL 9.1 or later has this. */ #define HASRELPERSISTENCEQUERY "SELECT count(*) FROM pg_catalog.pg_class AS c, pg_catalog.pg_attribute AS a WHERE c.relname = 'pg_class' AND a.attrelid = c.oid AND a.attname = 'relpersistence'" /* * Query to know if the target table is a unlogged one. This query * is valid in PostgreSQL 9.1 or later. */ #define ISUNLOGGEDQUERY "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.relname = '%s' AND c.relpersistence = 'u'" #define ISUNLOGGEDQUERY2 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pgpool_regclass('%s') AND c.relpersistence = 'u'" #define ISUNLOGGEDQUERY3 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.oid = pg_catalog.to_regclass('%s') AND c.relpersistence = 'u'" int hasrelpersistence; static POOL_RELCACHE * hasrelpersistence_cache; static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; if (table_name == NULL) { return false; } if (!pool_has_to_regclass() && !pool_has_pgpool_regclass()) table_name = remove_quotes_and_schema_from_relname(table_name); backend = pool_get_session_context(false)->backend; /* * Check backend version */ if (!hasrelpersistence_cache) { hasrelpersistence_cache = pool_create_relcache(pool_config->relcache_size, HASRELPERSISTENCEQUERY, int_register_func, int_unregister_func, false); if (hasrelpersistence_cache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for unlogged table"))); return false; } } hasrelpersistence = pool_search_relcache(hasrelpersistence_cache, backend, "pg_class") == 0 ? 0 : 1; if (hasrelpersistence) { bool result; char *query; /* PostgreSQL 9.4 or later has to_regclass() */ if (pool_has_to_regclass()) { query = ISUNLOGGEDQUERY3; } /* pgpool_regclass has been installed */ else if (pool_has_pgpool_regclass()) { query = ISUNLOGGEDQUERY2; } else { query = ISUNLOGGEDQUERY; } /* * If relcache does not exist, create it. */ if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, false); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for unlogged table"))); return false; } } /* * Search relcache. */ result = pool_search_relcache(relcache, backend, table_name) == 0 ? false : true; return result; } else { return false; } }
static bool is_temp_table(char *table_name) { /* * Query to know if pg_class has relistemp column or not. * PostgreSQL 8.4 and 9.0 have this. */ #define HASRELITEMPPQUERY "SELECT count(*) FROM pg_catalog.pg_class AS c, pg_attribute AS a WHERE c.relname = 'pg_class' AND a.attrelid = c.oid AND a.attname = 'relistemp'" /* * Query to know if the target table is a temporary one. This query * is valid in PostgreSQL 7.3 to 8.3 and 9.1 or later. We do not use * regclass (or its variant) here, because temporary tables never have * schema qualified name. */ #define ISTEMPQUERY83 "SELECT count(*) FROM pg_catalog.pg_class AS c, pg_namespace AS n WHERE c.relname = '%s' AND c.relnamespace = n.oid AND n.nspname ~ '^pg_temp_'" /* * Query to know if the target table is a temporary one. This query * is valid in PostgreSQL 8.4 and 9.0. We do not use regclass (or its * variant) here, because temporary tables never have schema qualified * name. */ #define ISTEMPQUERY84 "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.relname = '%s' AND c.relistemp" int hasrelistemp; bool result; static POOL_RELCACHE * hasrelistemp_cache; char *query; POOL_CONNECTION_POOL *backend; if (table_name == NULL) { return false; } backend = pool_get_session_context(false)->backend; /* * Check backend version */ if (!hasrelistemp_cache) { hasrelistemp_cache = pool_create_relcache(pool_config->relcache_size, HASRELITEMPPQUERY, int_register_func, int_unregister_func, false); if (hasrelistemp_cache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for temporary table"))); return false; } } hasrelistemp = pool_search_relcache(hasrelistemp_cache, backend, "pg_class") == 0 ? 0 : 1; if (hasrelistemp) query = ISTEMPQUERY84; else query = ISTEMPQUERY83; /* * If relcache does not exist, create it. */ if (!is_temp_table_relcache) { is_temp_table_relcache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, true); if (is_temp_table_relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for temporary table"))); return false; } } /* * Search relcache. */ result = pool_search_relcache(is_temp_table_relcache, backend, table_name) == 0 ? false : true; return result; }
/* * Judge the table used in a query represented by node is a system * catalog or not. */ static bool is_system_catalog(char *table_name) { /* * Query to know if pg_namespace exists. PostgreSQL 7.2 or before doesn't have. */ #define HASPGNAMESPACEQUERY "SELECT count(*) FROM pg_catalog.pg_class AS c WHERE c.relname = '%s'" /* * Query to know if the target table belongs pg_catalog schema. */ #define ISBELONGTOPGCATALOGQUERY "SELECT count(*) FROM pg_class AS c, pg_namespace AS n WHERE c.relname = '%s' AND c.relnamespace = n.oid AND n.nspname = 'pg_catalog'" #define ISBELONGTOPGCATALOGQUERY2 "SELECT count(*) FROM pg_class AS c, pg_namespace AS n WHERE c.oid = pgpool_regclass('\"%s\"') AND c.relnamespace = n.oid AND n.nspname = 'pg_catalog'" #define ISBELONGTOPGCATALOGQUERY3 "SELECT count(*) FROM pg_class AS c, pg_namespace AS n WHERE c.oid = pg_catalog.to_regclass('\"%s\"') AND c.relnamespace = n.oid AND n.nspname = 'pg_catalog'" int hasreliscatalog; bool result; static POOL_RELCACHE * hasreliscatalog_cache; static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; if (table_name == NULL) { return false; } if (!pool_has_to_regclass() && !pool_has_pgpool_regclass()) table_name = remove_quotes_and_schema_from_relname(table_name); backend = pool_get_session_context(false)->backend; /* * Check if pg_namespace exists */ if (!hasreliscatalog_cache) { char *query; query = HASPGNAMESPACEQUERY; hasreliscatalog_cache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, false); if (hasreliscatalog_cache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for system catalog"))); return false; } } hasreliscatalog = pool_search_relcache(hasreliscatalog_cache, backend, "pg_namespace") == 0 ? 0 : 1; if (hasreliscatalog) { /* * If relcache does not exist, create it. */ if (!relcache) { char *query; /* PostgreSQL 9.4 or later has to_regclass() */ if (pool_has_to_regclass()) { query = ISBELONGTOPGCATALOGQUERY3; } /* pgpool_regclass has been installed */ else if (pool_has_pgpool_regclass()) { query = ISBELONGTOPGCATALOGQUERY2; } else { query = ISBELONGTOPGCATALOGQUERY; } relcache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, false); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, while checking for system catalog"))); return false; } } /* * Search relcache. */ result = pool_search_relcache(relcache, backend, table_name) == 0 ? false : true; return result; } /* * Pre 7.3. Just check whether the table starts with "pg_". */ return (strcasecmp(table_name, "pg_") == 0); }
/* * Convert table_name(possibly including schema name) to oid */ int pool_table_name_to_oid(char *table_name) { /* * Query to convert table name to oid */ #define TABLE_TO_OID_QUERY "SELECT pgpool_regclass('%s')" #define TABLE_TO_OID_QUERY2 "SELECT oid FROM pg_class WHERE relname = '%s'" #define TABLE_TO_OID_QUERY3 "SELECT COALESCE(pg_catalog.to_regclass('%s')::oid, 0)" int oid = 0; static POOL_RELCACHE * relcache; POOL_CONNECTION_POOL *backend; char *query; if (table_name == NULL) { return oid; } if (!pool_has_to_regclass() && !pool_has_pgpool_regclass()) table_name = remove_quotes_and_schema_from_relname(table_name); backend = pool_get_session_context(false)->backend; if (pool_has_to_regclass()) { query = TABLE_TO_OID_QUERY3; } else if (pool_has_pgpool_regclass()) { query = TABLE_TO_OID_QUERY; } else { query = TABLE_TO_OID_QUERY2; } /* * If relcache does not exist, create it. */ if (!relcache) { relcache = pool_create_relcache(pool_config->relcache_size, query, int_register_func, int_unregister_func, true); if (relcache == NULL) { ereport(WARNING, (errmsg("unable to create relcache, getting OID from table name"))); return oid; } /* * Se do not cache if pgpool_regclass() returns 0, which indicates * there's no such a table. In this case we do not want to cache the * state because the table might be created later in this session. */ relcache->no_cache_if_zero = true; } /* * Search relcache. */ oid = (int) (intptr_t) pool_search_relcache(relcache, backend, table_name); return oid; }