/* * Add to send map a PREPARED statement */ void pool_add_prep_where(char *name, bool *map) { int i; if (!session_context) { pool_error("pool_add_prep_where: session context is not initialized"); return; } for (i=0;i<POOL_MAX_PREPARED_STATEMENTS;i++) { if (*session_context->prep_where.name[i] == '\0') { strncpy(session_context->prep_where.name[i], name, POOL_MAX_PREPARED_NAME); pool_copy_prep_where(map, session_context->prep_where.where_to_send[i]); return; } } pool_error("pool_add_prep_where: no empty slot found"); }
/* * 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; }