/* ------------------------------------------------------------------------- * * postgres.cpp * POSTGRES C Backend Interface * * Portions Copyright (c) 2020 Huawei Technologies Co.,Ltd. * Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group * Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 2010-2012 Postgres-XC Development Group * Portions Copyright (c) 2021, openGauss Contributors * * IDENTIFICATION * src/gausskernel/process/tcop/postgres.cpp * * NOTES * this is the "main" module of the postgres backend and * hence the main module of the "traffic cop". * * ------------------------------------------------------------------------- */ #include "postgres.h" #include "knl/knl_variable.h" #include "catalog/pg_user_status.h" #include #include #ifdef HAVE_SYS_RESOURCE_H #include #include #endif #ifndef HAVE_GETRUSAGE #include "rusagestub.h" #endif #ifdef HAVE_POLL_H #include #endif #include "access/printtup.h" #include "access/xact.h" #include "access/dfs/dfs_am.h" #include "access/ustore/undo/knl_uundoapi.h" #include "access/double_write.h" #include "catalog/namespace.h" #include "catalog/pg_authid.h" #include "catalog/pg_database.h" #include "catalog/pg_proc.h" #include "catalog/pg_type.h" #include "catalog/pg_extension.h" #include "commands/async.h" #include "commands/extension.h" #include "commands/matview.h" #include "commands/prepare.h" #include "commands/user.h" #include "commands/vacuum.h" #ifdef PGXC #include "commands/trigger.h" #endif #include "executor/spi.h" #include "executor/node/nodeRecursiveunion.h" #include "libpq/libpq.h" #include "libpq/pqformat.h" #include "libpq/pqsignal.h" #include "libpq/crypt.h" #include "miscadmin.h" #include "nodes/print.h" #include "optimizer/planner.h" #include "optimizer/bucketpruning.h" #include "pgstat.h" #include "pg_trace.h" #include "parser/analyze.h" #include "parser/parse_hint.h" #include "parser/parser.h" #ifdef PGXC #include "parser/parse_type.h" #endif /* PGXC */ #include "postmaster/autovacuum.h" #include "postmaster/postmaster.h" #include "postmaster/snapcapturer.h" #include "replication/logicallauncher.h" #include "replication/logicalworker.h" #include "replication/dataqueue.h" #include "replication/datasender.h" #include "replication/walsender.h" #include "replication/slot.h" #include "replication/syncrep.h" #include "rewrite/rewriteHandler.h" #include "storage/buf/bufmgr.h" #include "storage/ipc.h" #include "storage/lmgr.h" #include "storage/proc.h" #include "storage/procsignal.h" #include "storage/xlog_share_storage/xlog_share_storage.h" #include "storage/sinval.h" #include "tcop/fastpath.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" #include "utils/be_module.h" #include "utils/hotkey.h" #include "utils/inval.h" #include "utils/lsyscache.h" #include "utils/memutils.h" #include "utils/postinit.h" #include "utils/ps_status.h" #include "utils/plog.h" #include "utils/selfuncs.h" #include "utils/snapmgr.h" #include "utils/timestamp.h" #include "utils/fmgroids.h" #include "mb/pg_wchar.h" #include "pgaudit.h" #include "auditfuncs.h" #include "funcapi.h" #ifdef PGXC #include "distributelayer/streamMain.h" #include "storage/procarray.h" #include "pgxc/groupmgr.h" #include "pgxc/pgxc.h" #include "access/gtm.h" /* PGXC_COORD */ #include "pgxc/execRemote.h" #include "pgxc/barrier.h" #include "optimizer/pgxcplan.h" #include "nodes/nodes.h" #include "pgxc/poolmgr.h" #include "pgxc/poolutils.h" #include "pgxc/pgxcnode.h" #include "pgxc/locator.h" #include "commands/copy.h" #include "commands/matview.h" #include "workload/cpwlm.h" #include "workload/workload.h" /* PGXC_DATANODE */ #include "access/transam.h" #include "catalog/namespace.h" #endif #include "gssignal/gs_signal.h" #include "optimizer/streamplan.h" #include "optimizer/randomplan.h" #ifdef HAVE_INT_OPTRESET extern int optreset; /* might not be declared by system headers */ #endif #include "access/xact.h" #include "catalog/pgxc_node.h" #include "executor/exec/execStream.h" #include "executor/lightProxy.h" #include "executor/node/nodeIndexscan.h" #include "gstrace/gstrace_infra.h" #include "gstrace/tcop_gstrace.h" #include "gs_policy/policy_common.h" #include "instruments/instr_unique_sql.h" #include "instruments/snapshot.h" #include "instruments/instr_slow_query.h" #include "instruments/unique_query.h" #include "instruments/instr_handle_mgr.h" #include "instruments/instr_statement.h" #include "nodes/parsenodes.h" #include "opfusion/opfusion.h" #include "parser/parser.h" #include "parser/parsetree.h" #include "pgxc/route.h" #include "tcop/stmt_retry.h" #include "threadpool/threadpool.h" #ifdef ENABLE_MULTIPLE_NODES #include "pgxc/pgFdwRemote.h" #include "tsdb/utils/ts_redis.h" #endif #include "utils/acl.h" #include "utils/distribute_test.h" #include "utils/elog.h" #include "utils/guc_storage.h" #include "utils/guc_tables.h" #include "utils/memprot.h" #include "utils/memtrack.h" #include "utils/plpgsql.h" #include "utils/syscache.h" #include "access/heapam.h" #include "utils/plancache.h" #include "commands/tablecmds.h" #include "catalog/gs_matview.h" #include "streaming/dictcache.h" #include "parser/parse_target.h" #include "streaming/streaming_catalog.h" #include "postmaster/bgworker.h" #ifdef ENABLE_MOT #include "storage/mot/jit_exec.h" #endif #include "commands/sqladvisor.h" THR_LOCAL VerifyCopyCommandIsReparsed copy_need_to_be_reparse = NULL; #define GSCGROUP_ATTACH_TASK() \ { \ if (u_sess->attr.attr_resource.use_workload_manager && g_instance.wlm_cxt->gscgroup_init_done && \ !IsAbortedTransactionBlockState() && u_sess->wlm_cxt->cgroup_state == CG_USERSET && \ CGroupIsValid(u_sess->wlm_cxt->control_group) && !CGroupIsDefault(u_sess->wlm_cxt->control_group)) { \ gscgroup_attach_task(t_thrd.wlm_cxt.thread_node_group, u_sess->wlm_cxt->control_group); \ } \ } #define IS_CLIENT_CONN_VALID(port) \ (((port) == NULL) \ ? false \ : (((port)->is_logic_conn) ? ((port)->gs_sock.type != GSOCK_INVALID) : ((port)->sock != NO_SOCKET))) typedef struct AttachInfoContext { char* info_query_string; Node* info_node; int info_index; } AttachInfoContext; #define PARAMS_LEN 4096 #define PRINFT_DST_MAX_DOUBLE 64 #define MEMCPY_DST_NUM 4 #define MAXSTRLEN ((1 << 11) - 1) extern PgBackendStatus* GetMyBEEntry(void); extern THR_LOCAL bool g_pq_interrupt_happened; /* * global node definition, process-wise global variable, mainly for debug purpose * when using view pgxc_thread_wait_status. It will be updated only if cluster size * changes. */ GlobalNodeDefinition* global_node_definition = NULL; pthread_mutex_t nodeDefCopyLock; Id64Gen gt_queryId = {0, 0, false}; IdGen gt_tempId = {0, 0, false}; /* * On IA64 we also have to remember the register stack base. */ #if defined(__ia64__) || defined(__ia64) char* register_stack_base_ptr = NULL; #endif extern THR_LOCAL DistInsertSelectState* distInsertSelectState; extern void InitQueryHashTable(void); static THR_LOCAL void (*pre_receiveSlot_func)(TupleTableSlot*, DestReceiver*); static void get_query_result(TupleTableSlot* slot, DestReceiver* self); /* * @hdfs * Define different mesage type used for exec_simple_query */ typedef enum { QUERY_MESSAGE = 0, HYBRID_MESSAGE } MessageType; /* For shared storage mode */ typedef enum { XLOG_COPY_NOT = 0, XLOG_COPY_FROM_LOCAL, XLOG_FORCE_COPY_FROM_LOCAL, XLOG_COPY_FROM_SHARE } XLogCopyMode; static XLogCopyMode xlogCopyMode = XLOG_COPY_NOT; /* ---------------------------------------------------------------- * decls for routines only used in this file * ---------------------------------------------------------------- */ static int InteractiveBackend(StringInfo inBuf); static int interactive_getc(void); static int SocketBackend(StringInfo inBuf); static int ReadCommand(StringInfo inBuf); static List* pg_rewrite_query(Query* query); static bool check_log_statement(List* stmt_list); static int errdetail_execute(List* raw_parsetree_list); static int errdetail_params(ParamListInfo params); static int errdetail_recovery_conflict(void); static bool IsTransactionExitStmt(Node* parsetree); static bool IsTransactionExitStmtList(List* parseTrees); static bool IsTransactionStmtList(List* parseTrees); #ifdef ENABLE_MOT static bool IsTransactionPrepareStmt(const Node* parsetree); #endif static void drop_unnamed_stmt(void); static void SigHupHandler(SIGNAL_ARGS); static void ForceModifyInitialPwd(const char* query_string, List* parsetree_list); static void ForceModifyExpiredPwd(const char* queryString, const List* parsetreeList); #ifdef ENABLE_MULTIPLE_NODES static void InitGlobalNodeDefinition(PlannedStmt* planstmt); #endif static int getSingleNodeIdx_internal(ExecNodes* exec_nodes, ParamListInfo params); extern void CancelAutoAnalyze(); void EnableDoingCommandRead() { t_thrd.postgres_cxt.DoingCommandRead = true; } void DisableDoingCommandRead() { t_thrd.postgres_cxt.DoingCommandRead = false; } extern void CodeGenThreadInitialize(); extern CmdType set_cmd_type(const char* commandTag); static void exec_batch_bind_execute(StringInfo input_message); /* * MPP with recursive support */ void InitRecursiveCTEGlobalVariables(const PlannedStmt* planstmt) { producer_top_plannode_str = NULL; is_syncup_producer = false; if (!IS_PGXC_DATANODE) { return; } if (StreamTopConsumerAmI() && !u_sess->attr.attr_sql.enable_stream_recursive) { return; } Plan* top_plan = planstmt->planTree; if (!EXEC_IN_RECURSIVE_MODE(top_plan)) { return; } /* build the top plan node string */ /* set is_sync_producer flag */ is_syncup_producer = IsSyncUpProducerThread(); /* set top producer plannode string */ StringInfoData si; initStringInfo(&si); appendStringInfo(&si, "TopPlanNode:%s[%d]", nodeTagToString(nodeTag(top_plan)), top_plan->plan_node_id); if (is_syncup_producer) { appendStringInfo(&si, " <>"); } producer_top_plannode_str = (char*)pstrdup(si.data); pfree_ext(si.data); } #ifdef ENABLE_MULTIPLE_NODES /* ---------------------------------------------------------------- * PG-XC routines * ---------------------------------------------------------------- */ /* * Called when the backend is ending. */ static void DataNodeShutdown(int code, Datum arg) { /* Close connection with GTM, if active */ CloseGTM(); /* Free remote xact state */ free_RemoteXactState(); /* Free gxip */ UnsetGlobalSnapshotData(); } /* * Check if the SIGUSR2 is trying to cancel GTM connection. */ static bool SignalCancelGTMConnection() { if (t_thrd.proc != NULL) { uint32 gtmhost_flag = pg_atomic_fetch_add_u32(&t_thrd.proc->signal_cancel_gtm_conn_flag, 0); (void)pg_atomic_exchange_u32(&t_thrd.proc->signal_cancel_gtm_conn_flag, 0); if (gtmhost_flag > 0) { SetGTMInterruptFlag(); t_thrd.proc->suggested_gtmhost = FLAG2HOST(gtmhost_flag); return true; } } return false; } #endif /* ---------------------------------------------------------------- * routines to obtain user input * ---------------------------------------------------------------- */ /* ---------------- * InteractiveBackend() is called for user interactive connections * * the string entered by the user is placed in its parameter inBuf, * and we act like a Q message was received. * * EOF is returned if end-of-file input is seen; time to shut down. * ---------------- */ static int InteractiveBackend(StringInfo inBuf) { int c; /* character read from getc() */ bool end = false; /* end-of-input flag */ bool backslashSeen = false; /* have we seen a \ ? */ /* * display a prompt and obtain input from the user */ printf("backend> "); fflush(stdout); resetStringInfo(inBuf); if (t_thrd.postgres_cxt.UseNewLine) { /* * if we are using \n as a delimiter, then read characters until the * \n. */ while ((c = interactive_getc()) != EOF) { if (c == '\n') { if (backslashSeen) { /* discard backslash from inBuf */ if (inBuf->len > 0) { inBuf->data[--inBuf->len] = '\0'; } backslashSeen = false; continue; } else { /* keep the newline character */ appendStringInfoChar(inBuf, '\n'); break; } } else if (c == '\\') backslashSeen = true; else backslashSeen = false; appendStringInfoChar(inBuf, (char)c); } if (c == EOF) end = true; } else { /* * otherwise read characters until EOF. */ while ((c = interactive_getc()) != EOF) appendStringInfoChar(inBuf, (char)c); /* No input before EOF signal means time to quit. */ if (inBuf->len == 0) end = true; } if (end) return EOF; /* * otherwise we have a user query so process it. */ /* Add '\0' to make it look the same as message case. */ appendStringInfoChar(inBuf, (char)'\0'); /* * if the query echo flag was given, print the query.. */ if (t_thrd.postgres_cxt.EchoQuery) printf("statement: %s\n", inBuf->data); fflush(stdout); return 'Q'; } /* * interactive_getc -- collect one character from stdin * * Even though we are not reading from a "client" process, we still want to * respond to signals, particularly SIGTERM/SIGQUIT. Hence we must use * prepare_for_client_read and client_read_ended. */ static int interactive_getc(void) { int c; prepare_for_client_read(); c = getc(stdin); client_read_ended(); return c; } /* ---------------- * SocketBackend() Is called for frontend-backend connections * * Returns the message type code, and loads message body data into inBuf. * * EOF is returned if the connection is lost. * ---------------- */ static int SocketBackend(StringInfo inBuf) { int qtype; #ifdef ENABLE_MULTIPLE_NODES int aCount = 0; #endif /* * Get message type code from the frontend. */ qtype = pq_getbyte(); while (1) { if (qtype == EOF) { /* frontend disconnected */ if (IsTransactionState()) { ereport(COMMERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("unexpected EOF on client connection with an open transaction"))); } else { /* * Can't send DEBUG log messages to client at this point. Since * we're disconnecting right away, we don't need to restore * whereToSendOutput. */ t_thrd.postgres_cxt.whereToSendOutput = DestNone; ereport(DEBUG1, (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), errmsg("unexpected EOF on client connection"))); } u_sess->tri_cxt.exec_row_trigger_on_datanode = false; return qtype; } else if (qtype == 'a') { /* on DN only */ #ifdef ENABLE_MULTIPLE_NODES if (aCount > MSG_A_REPEAT_NUM_MAX) { ereport(DEBUG1, (errmsg("the character is repeat : %c", qtype))); break; } aCount++; u_sess->tri_cxt.exec_row_trigger_on_datanode = true; ereport(DEBUG1, (errmsg("received trigger shipping message : %c", qtype))); qtype = pq_getbyte(); #else ereport(ERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("a_type message is invalid"))); #endif } else { break; } } /* reset doing_extended_query_message */ u_sess->postgres_cxt.doing_extended_query_message = false; /* * Validate message type code before trying to read body; if we have lost * sync, better to say "command unknown" than to run out of memory because * we used garbage as a length word. * * This also gives us a place to set the doing_extended_query_message flag * as soon as possible. */ switch (qtype) { case 'u': /* AUTONOMOUS_TRANSACTION simple query */ case 'Q': /* simple query */ case 'O': /* to reset openGauss thread in pooler stateless reuse mode */ case 'Z': /* simple plan */ case 'h': /* hybrid message query */ case 'Y': /* plan with params */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) { /* old style without length word; convert */ if (pq_getstring(inBuf)) { if (IsTransactionState()) ereport(COMMERROR, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("unexpected EOF on client connection with an open transaction"))); else { /* * Can't send DEBUG log messages to client at this * point.Since we're disconnecting right away, we * don't need to restore whereToSendOutput. */ t_thrd.postgres_cxt.whereToSendOutput = DestNone; ereport(DEBUG1, (errcode(ERRCODE_CONNECTION_DOES_NOT_EXIST), errmsg("unexpected EOF on client connection"))); } return EOF; } } break; case 'F': /* fastpath function call */ case 'I': /* Push, Pop schema name */ case 'L': /* Link gc_fdw */ case 'J': /* Trace ID */ break; case 'X': /* terminate */ u_sess->postgres_cxt.doing_extended_query_message = false; u_sess->postgres_cxt.ignore_till_sync = false; break; case 'U': /* batch bind-execute */ case 'B': /* bind */ case 'C': /* close */ case 'D': /* describe */ case 'E': /* execute */ case 'H': /* flush */ case 'P': /* parse */ u_sess->postgres_cxt.doing_extended_query_message = true; /* these are only legal in protocol 3 */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) ereport( FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %d", qtype))); break; case 'S': /* sync */ /* stop any active skip-till-Sync */ u_sess->postgres_cxt.ignore_till_sync = false; /* mark not-extended, so that a new error doesn't begin skip */ u_sess->postgres_cxt.doing_extended_query_message = false; /* only legal in protocol 3 */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) ereport( FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %d", qtype))); break; case 'd': /* copy data */ case 'c': /* copy done */ case 'f': /* copy fail */ u_sess->postgres_cxt.doing_extended_query_message = false; /* these are only legal in protocol 3 */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) < 3) ereport( FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %d", qtype))); break; #ifdef ENABLE_MULTIPLE_NODES /* PGXC_DATANODE */ case 'q': /* Query ID */ case 'r': /* Plan ID with sync */ case 'M': /* Command ID */ case 'g': /* GXID */ case 's': /* Snapshot */ case 't': /* Timestamp */ case 'p': /* Process Pid */ case 'b': /* Barrier */ case 'W': /* WLM Control Group */ case 'i': /* Instrumentation */ case 'A': /* AC WLM */ case 'e': /* Thread ID */ case 'w': /* dynamic WLM */ case 'R': /* Reply collect info */ case 'n': /* Committing */ case 'N': /* Commit csn */ case 'l': /* get and handle csn for csnminsync */ case 'G': /* PGXCBucketMap and PGXCNodeId */ case 'j': /* Check gtm mode */ case 'a': /* Not reach : Trigger shipped to DN */ case 'k': /* Global session ID */ case 'z': /* PBE for DDL */ case 'y': /* sequence from cn 2 dn */ case 'T': /* consistency point */ break; #endif default: /* * Otherwise we got garbage from the frontend. We treat this as * fatal because we have probably lost message boundary sync, and * there's no good way to recover. */ ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %c", qtype))); break; } /* * In protocol version 3, all frontend messages have a length word next * after the type code; we can read the message contents independently of * the type. */ if (PG_PROTOCOL_MAJOR(FrontendProtocol) >= 3) { if (pq_getmessage(inBuf, 0)) return EOF; /* suitable message already logged */ } return qtype; } /* if online sqladvsior collect workload is running, check query->rtable */ static bool checkCollectSimpleQuery(bool isCollect, List* querytreeList) { if (isCollect && querytreeList != NULL) { TableConstraint tableConstraint; initTableConstraint(&tableConstraint); checkQuery(querytreeList, &tableConstraint); if (tableConstraint.isHasTable && !tableConstraint.isHasTempTable && !tableConstraint.isHasFunction) { return true; } } return false; } static void collectSimpleQuery(const char* queryString, bool isCollect) { if (isCollect && checkAdivsorState()) { collectDynWithArgs(queryString, NULL, 0); } } /* ---------------- * ReadCommand reads a command from either the frontend or * standard input, places it in inBuf, and returns the * message type code (first byte of the message). * EOF is returned if end of file. * ---------------- */ static int ReadCommand(StringInfo inBuf) { int result; /* * At begin of ReadCommand, reset extended-query-message flag, so that any * errors encountered in "idle" state don't provoke skip. */ u_sess->postgres_cxt.doing_extended_query_message = false; /* Start a timer for session timeout. */ if (!enable_session_sig_alarm(u_sess->attr.attr_common.SessionTimeout * 1000)) ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not set timer for session timeout"))); if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) result = SocketBackend(inBuf); else if (t_thrd.postgres_cxt.whereToSendOutput == DestDebug) result = InteractiveBackend(inBuf); else result = EOF; /* Disable a timer for session timeout. */ if (!disable_session_sig_alarm()) ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("could not disable timer for session timeout"))); u_sess->proc_cxt.firstChar = (char)result; return result; } /* * prepare_for_client_read -- set up to possibly block on client input * * This must be called immediately before any low-level read from the * client connection. It is necessary to do it at a sufficiently low level * that there won't be any other operations except the read kernel call * itself between this call and the subsequent client_read_ended() call. * In particular there mustn't be use of malloc() or other potentially * non-reentrant libc functions. This restriction makes it safe for us * to allow interrupt service routines to execute nontrivial code while * we are waiting for input. */ void prepare_for_client_read(void) { if (t_thrd.postgres_cxt.DoingCommandRead) { /* Allow cancel/die interrupts to be processed while waiting */ t_thrd.int_cxt.ImmediateInterruptOK = true; /* And don't forget to detect one that already arrived */ CHECK_FOR_INTERRUPTS(); } } /* * prepare_for_logic_conn_read */ void prepare_for_logic_conn_read(void) { if (t_thrd.postgres_cxt.DoingCommandRead) { /* Allow cancel/die interrupts to be processed while waiting */ t_thrd.int_cxt.ImmediateInterruptOK = true; /* And don't forget to detect one that already arrived */ CHECK_FOR_INTERRUPTS(); } } /* * logic_conn_read_ended */ void logic_conn_read_check_ended(void) { if (t_thrd.postgres_cxt.DoingCommandRead) { int save_errno = errno; /* Process sinval catchup interrupts that happened while reading */ if (catchupInterruptPending) ProcessCatchupInterrupt(); /* Process sinval catchup interrupts that happened while reading */ if (notifyInterruptPending) ProcessNotifyInterrupt(); t_thrd.int_cxt.ImmediateInterruptOK = false; errno = save_errno; } } /* * client_read_ended -- get out of the client-input state */ void client_read_ended(void) { if (t_thrd.postgres_cxt.DoingCommandRead) { int save_errno = errno; /* Should reset ImmediateInterruptOK before proccessing catchup interrupts */ t_thrd.int_cxt.ImmediateInterruptOK = false; /* Process sinval catchup interrupts that happened while reading */ if (catchupInterruptPending) ProcessCatchupInterrupt(); /* Process sinval catchup interrupts that happened while reading */ if (notifyInterruptPending) ProcessNotifyInterrupt(); errno = save_errno; } } #ifndef ENABLE_MULTIPLE_NODES void ExecuteFunctionIfExisted(const char *filename, char *funcname) { CFunInfo tmpCF; tmpCF = load_external_function(filename, funcname, false, false); if (tmpCF.user_fn != NULL) { ((void* (*)(void))(tmpCF.user_fn))(); } } bool IsFileExisted(const char *filename) { char* fullname = expand_dynamic_library_name(filename); return file_exists(fullname); } #define INIT_PLUGIN_OBJECT "init_plugin_object" #define WHALE "whale" #define DOLPHIN "dolphin" void InitASqlPluginHookIfNeeded() { ExecuteFunctionIfExisted(WHALE, INIT_PLUGIN_OBJECT); } void InitBSqlPluginHookIfNeeded() { ExecuteFunctionIfExisted(DOLPHIN, INIT_PLUGIN_OBJECT); } #define LOAD_DOLPHIN "create_dolphin_extension" void LoadDolphinIfNeeded() { if (IsFileExisted(DOLPHIN)) { ExecuteFunctionIfExisted(DOLPHIN, LOAD_DOLPHIN); } } #endif /* * Do raw parsing (only). * * A list of parsetrees is returned, since there might be multiple * commands in the given string. * * NOTE: for interactive queries, it is important to keep this routine * separate from the analysis & rewrite stages. Analysis and rewriting * cannot be done in an aborted transaction, since they require access to * database tables. So, we rely on the raw parser to determine whether * we've seen a COMMIT or ABORT command; when we are in abort state, other * commands are not processed any further than the raw parse stage. */ List* pg_parse_query(const char* query_string, List** query_string_locationlist) { List* raw_parsetree_list = NULL; PGSTAT_INIT_TIME_RECORD(); TRACE_POSTGRESQL_QUERY_PARSE_START(query_string); if (u_sess->attr.attr_common.log_parser_stats) ResetUsage(); PGSTAT_START_TIME_RECORD(); List* (*parser_hook)(const char*, List**) = raw_parser; #ifndef ENABLE_MULTIPLE_NODES if (u_sess->attr.attr_sql.whale || u_sess->attr.attr_sql.dolphin) { int id = GetCustomParserId(); if (id >= 0 && g_instance.raw_parser_hook[id] != NULL) { parser_hook = (List* (*)(const char*, List**))g_instance.raw_parser_hook[id]; } } #endif raw_parsetree_list = parser_hook(query_string, query_string_locationlist); PGSTAT_END_TIME_RECORD(PARSE_TIME); if (u_sess->attr.attr_common.log_parser_stats) ShowUsage("PARSER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass raw parsetrees through copyObject() */ { List* new_list = (List*)copyObject(raw_parsetree_list); /* This checks both copyObject() and the equal() routines... */ if (!equal(new_list, raw_parsetree_list)) { ereport(WARNING, (errmsg("copyObject() failed to produce an equal raw parse tree"))); } else { raw_parsetree_list = new_list; } } #endif TRACE_POSTGRESQL_QUERY_PARSE_DONE(query_string); return raw_parsetree_list; } /* * Given a raw parsetree (gram.y output), and optionally information about * types of parameter symbols ($n), perform parse analysis and rule rewriting. * * A list of Query nodes is returned, since either the analyzer or the * rewriter might expand one query to several. * * NOTE: for reasons mentioned above, this must be separate from raw parsing. */ List* pg_analyze_and_rewrite(Node* parsetree, const char* query_string, Oid* paramTypes, int numParams) { Query* query = NULL; List* querytree_list = NULL; TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string); /* * (1) Perform parse analysis. */ if (u_sess->attr.attr_common.log_parser_stats) ResetUsage(); #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && IsA(parsetree, SelectStmt) && !is_streaming_thread() && !streaming_context_is_ddl() && is_contquery_with_dict((Node *)((SelectStmt *) parsetree)->fromClause)) { transform_contquery_selectstmt_with_dict((SelectStmt *) parsetree); } #endif query = parse_analyze(parsetree, query_string, paramTypes, numParams); if (u_sess->attr.attr_common.log_parser_stats) ShowUsage("PARSE ANALYSIS STATISTICS"); /* * (2) Rewrite the queries, as necessary */ querytree_list = pg_rewrite_query(query); #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { ListCell* lc = NULL; foreach (lc, querytree_list) { Query* query_tmp = (Query*)lfirst(lc); if (query_tmp->sql_statement == NULL) query_tmp->sql_statement = (char*)query_string; } } #endif TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string); return querytree_list; } /* * Do parse analysis and rewriting. This is the same as pg_analyze_and_rewrite * except that external-parameter resolution is determined by parser callback * hooks instead of a fixed list of parameter datatypes. */ List* pg_analyze_and_rewrite_params( Node* parsetree, const char* query_string, ParserSetupHook parserSetup, void* parserSetupArg) { ParseState* pstate = NULL; Query* query = NULL; List* querytree_list = NULL; Assert(query_string != NULL); /* required as of 8.4 */ TRACE_POSTGRESQL_QUERY_REWRITE_START(query_string); /* * (1) Perform parse analysis. */ if (u_sess->attr.attr_common.log_parser_stats) ResetUsage(); pstate = make_parsestate(NULL); pstate->p_sourcetext = query_string; (*parserSetup)(pstate, parserSetupArg); query = transformTopLevelStmt(pstate, parsetree); /* it's unsafe to deal with plugins hooks as dynamic lib may be released */ if (post_parse_analyze_hook && !(g_instance.status > NoShutdown)) { (*post_parse_analyze_hook)(pstate, query); } free_parsestate(pstate); if (u_sess->attr.attr_common.log_parser_stats) ShowUsage("PARSE ANALYSIS STATISTICS"); #ifdef PGXC if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, CreateTableAsStmt)) { CreateTableAsStmt* cts = (CreateTableAsStmt*)query->utilityStmt; cts->parserSetup = (void*)parserSetup; cts->parserSetupArg = parserSetupArg; } #endif /* * (2) Rewrite the queries, as necessary */ querytree_list = pg_rewrite_query(query); TRACE_POSTGRESQL_QUERY_REWRITE_DONE(query_string); return querytree_list; } /* * Perform rewriting of a query produced by parse analysis. * * Note: query must just have come from the parser, because we do not do * AcquireRewriteLocks() on it. */ static List* pg_rewrite_query(Query* query) { List* querytree_list = NIL; PGSTAT_INIT_TIME_RECORD(); if (u_sess->attr.attr_sql.Debug_print_parse) elog_node_display(LOG, "parse tree", query, u_sess->attr.attr_sql.Debug_pretty_print); if (u_sess->attr.attr_common.log_parser_stats) ResetUsage(); PGSTAT_START_TIME_RECORD(); if (query->commandType == CMD_UTILITY) { #ifdef ENABLE_MULTIPLE_NODES if (IsA(query->utilityStmt, CreateTableAsStmt)) { /* * CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the * target table is created first. The SELECT query is then transformed * into an INSERT INTO statement */ /* handle materilized view. */ CreateTableAsStmt *stmt = (CreateTableAsStmt *)query->utilityStmt; if (IS_PGXC_COORDINATOR && stmt->relkind == OBJECT_MATVIEW) { /* Check if OK to create matview */ check_basetable((Query *)stmt->query, true, stmt->into->ivm); /* Check for select privilege */ (void)ExecCheckRTPerms(query->rtable, true); if (stmt->into) { if (stmt->into->ivm) { check_matview_op_supported(stmt); stmt->into->distributeby = infer_incmatview_distkey(stmt); } } } if (stmt->relkind == OBJECT_MATVIEW && IS_PGXC_DATANODE) { querytree_list = list_make1(query); } else { /* * CREATE TABLE AS SELECT and SELECT INTO are rewritten so that the * target table is created first. The SELECT query is then transformed * into an INSERT INTO statement */ querytree_list = QueryRewriteCTAS(query); } } else if (IsA(query->utilityStmt, CopyStmt)) { querytree_list = query_rewrite_copy(query); } else if(IsA(query->utilityStmt, RefreshMatViewStmt)) { RefreshMatViewStmt *stmt = (RefreshMatViewStmt *)query->utilityStmt; if (!stmt->incremental && !isIncMatView(stmt->relation)) { querytree_list = QueryRewriteRefresh(query); } else { querytree_list = list_make1(query); } } else if (IsA(query->utilityStmt, AlterTableStmt)) { querytree_list = query_rewrite_alter_table(query); } else { /* don't rewrite utilities, just dump them into result list */ querytree_list = list_make1(query); } #else if (IsA(query->utilityStmt, CreateTableAsStmt)) { CreateTableAsStmt *stmt = (CreateTableAsStmt *)query->utilityStmt; if (stmt->relkind == OBJECT_MATVIEW) { /* Check if OK to create matview */ check_basetable((Query *)stmt->query, true, stmt->into->ivm); /* Check for select privilege */ (void)ExecCheckRTPerms(query->rtable, true); if (stmt->into && stmt->into->ivm) { check_matview_op_supported(stmt); } querytree_list = list_make1(query); } else { querytree_list = QueryRewriteCTAS(query); } } else { querytree_list = list_make1(query); } #endif } else { /* rewrite regular queries */ querytree_list = QueryRewrite(query); } PGSTAT_END_TIME_RECORD(REWRITE_TIME); if (u_sess->attr.attr_common.log_parser_stats) ShowUsage("REWRITER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass querytree output through copyObject() */ { List* new_list = NIL; new_list = (List*)copyObject(querytree_list); /* This checks both copyObject() and the equal() routines... */ if (!equal(new_list, querytree_list)) ereport(WARNING, (errmsg("copyObject() failed to produce equal parse tree"))); else querytree_list = new_list; } #endif if (u_sess->attr.attr_sql.Debug_print_rewritten) elog_node_display(LOG, "rewritten parse tree", querytree_list, u_sess->attr.attr_sql.Debug_pretty_print); return querytree_list; } /* * check compute privilieges for expected_computing_modegroup . */ static void check_query_acl(Query* query) { if (containing_ordinary_table((Node*)query)) { ComputingNodeGroupMode cng_mode = ng_get_computing_nodegroup_mode(); if (cng_mode == CNG_MODE_COSTBASED_EXPECT || cng_mode == CNG_MODE_FORCE) { Distribution* distribution = ng_get_group_distribution(u_sess->attr.attr_sql.expected_computing_nodegroup); if (InvalidOid != distribution->group_oid) { /* Check current user has privilige to this group */ AclResult aclresult = pg_nodegroup_aclcheck(distribution->group_oid, GetUserId(), ACL_COMPUTE | ACL_USAGE); if (aclresult != ACLCHECK_OK) { aclcheck_error(aclresult, ACL_KIND_NODEGROUP, u_sess->attr.attr_sql.expected_computing_nodegroup); } } } } } /* * Generate a plan for a single already-rewritten query. * This is a thin wrapper around planner() and takes the same parameters. */ PlannedStmt* pg_plan_query(Query* querytree, int cursorOptions, ParamListInfo boundParams, bool underExplain) { PlannedStmt* plan = NULL; PGSTAT_INIT_TIME_RECORD(); bool multi_node_hint = false; /* Utility commands have no plans. */ if (querytree->commandType == CMD_UTILITY) return NULL; if (querytree->hintState != NULL) { multi_node_hint = querytree->hintState->multi_node_hint; } /* Planner must have a snapshot in case it calls user-defined functions. */ if (!GTM_LITE_MODE) { /* Skip if GTMLite */ #ifndef ENABLE_MOT /* To support skipping snapshot in case of MOT tables. */ Assert(ActiveSnapshotSet()); #endif } TRACE_POSTGRESQL_QUERY_PLAN_START(); if (u_sess->attr.attr_common.log_planner_stats) ResetUsage(); /* Update hard parse counter for Unique SQL */ UniqueSQLStatCountHardParse(1); PGSTAT_START_TIME_RECORD(); /* check perssion for expect_computing_nodegroup */ if (!OidIsValid(lc_replan_nodegroup)) check_query_acl(querytree); /* call the optimizer */ plan = planner(querytree, cursorOptions, boundParams); PGSTAT_END_TIME_RECORD(PLAN_TIME); if (u_sess->attr.attr_common.log_planner_stats) ShowUsage("PLANNER STATISTICS"); #ifdef COPY_PARSE_PLAN_TREES /* Optional debugging check: pass plan output through copyObject() */ { PlannedStmt* new_plan = (PlannedStmt*)copyObject(plan); /* * equal() currently does not have routines to compare Plan nodes, so * don't try to test equality here. Perhaps fix someday? */ #ifdef NOT_USED /* This checks both copyObject() and the equal() routines... */ if (!equal(new_plan, plan)) ereport(WARNING, (errmsg("copyObject() failed to produce an equal plan tree"))); else #endif plan = new_plan; } #endif /* * Print plan if debugging. */ if (u_sess->attr.attr_sql.Debug_print_plan) elog_node_display(LOG, "plan", plan, u_sess->attr.attr_sql.Debug_pretty_print); if (!underExplain) output_hint_warning(plan->plan_hint_warning, DEBUG1); TRACE_POSTGRESQL_QUERY_PLAN_DONE(); plan->is_stream_plan = u_sess->opt_cxt.is_stream; plan->multi_node_hint = multi_node_hint; return plan; } /* * Check whether the query is insert multiple values query. * In the insert query, only one RTE_VALUES in the ratble is a multi-values query.. */ static bool is_insert_multiple_values_query_in_gtmfree(Query* query) { if (query->commandType != CMD_INSERT || !g_instance.attr.attr_storage.enable_gtm_free) { return false; } List* rtables = query->rtable; ListCell* cell = NULL; RangeTblEntry* rte = NULL; RangeTblEntry* values_rte = NULL; foreach(cell, rtables) { rte = (RangeTblEntry*)lfirst(cell); if (rte->rtekind == RTE_VALUES) { if (values_rte != NULL) { return false; } values_rte = rte; } } if (values_rte != NULL) { return true; } else { return false; } } /* * Generate plans for a list of already-rewritten queries. * * Normal optimizable statements generate PlannedStmt entries in the result * list. Utility statements are simply represented by their statement nodes. */ List* pg_plan_queries(List* querytrees, int cursorOptions, ParamListInfo boundParams) { List* stmt_list = NIL; ListCell* query_list = NULL; /* for abstimeout, transfer time to str in insert has some problem, * so distinguish insert and select situation for ABSTIMEOUTFUN to avoid problem.*/ t_thrd.time_cxt.is_abstimeout_in = true; /* Set initalized plan seed according to guc plan_mode_seed for random plan testing function */ set_inital_plan_seed(); #ifndef ENABLE_MULTIPLE_NODES if (unlikely(list_length(querytrees) != 1)) { u_sess->opt_cxt.query_dop = 1; } #endif foreach (query_list, querytrees) { Query* query = castNode(Query, lfirst(query_list)); Node* stmt = NULL; PlannedStmt* ps = NULL; if (query->commandType == CMD_UTILITY) { /* Utility commands have no plans. */ stmt = query->utilityStmt; /* output grammer error of hint */ output_utility_hint_warning((Node*)query, DEBUG1); } else { query->boundParamsQ = boundParams; bool is_insert_multiple_values = is_insert_multiple_values_query_in_gtmfree(query); /* Temporarily apply SET hint using PG_TRY for later recovery */ int nest_level = apply_set_hint(query); PG_TRY(); { stmt = (Node*)pg_plan_query(query, cursorOptions, boundParams); } PG_CATCH(); { recover_set_hint(nest_level); PG_RE_THROW(); } PG_END_TRY(); recover_set_hint(nest_level); /* When insert multiple values query is a generate plan, * we don't consider whether it can be executed on a single dn. * Because it can be executed on a single DN only in custom plan. * u_sess->pcache_cxt.query_has_params: checks whether the plan is in cachedplan. * boundParams: checks whether the plan is a generate plan. */ if (!(is_insert_multiple_values && boundParams == NULL && u_sess->pcache_cxt.query_has_params)) { check_gtm_free_plan((PlannedStmt *)stmt, u_sess->attr.attr_sql.explain_allow_multinode ? WARNING : ERROR); } ps = (PlannedStmt*)stmt; check_plan_mergeinto_replicate(ps, ERROR); if (ps != NULL) u_sess->wlm_cxt->wlm_num_streams += ps->num_streams; } stmt_list = lappend(stmt_list, stmt); } t_thrd.time_cxt.is_abstimeout_in = false; /* Set warning that no-analyzed relation name to log. */ output_noanalyze_rellist_to_log(LOG); return stmt_list; } /* Get the tag automatically for plan shipping. * For now plan shipping is used only for SELECT\INSERT\DELETE\UPDATA\MERGE. */ const char* CreateCommandTagForPlan(CmdType commandType) { const char* tag = NULL; switch (commandType) { case CMD_SELECT: tag = "SELECT"; break; case CMD_INSERT: tag = "INSERT"; break; case CMD_DELETE: tag = "DELETE"; break; case CMD_UPDATE: tag = "UPDATE"; break; case CMD_MERGE: tag = "MERGE"; break; default: ereport(WARNING, (errmsg("unrecognized command type: %d", (int)commandType))); tag = "?\?\?"; break; } return tag; } /* * exec_simple_plan * * Execute a "simple Plan" received from coordinator */ void exec_simple_plan(PlannedStmt* plan) { CommandDest dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; MemoryContext oldcontext; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; bool isTopLevel = false; char msec_str[PRINTF_DST_MAX]; if (plan == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("Invaild parameter."))); } plpgsql_estate = NULL; /* Initialize the global variables for recursive */ InitRecursiveCTEGlobalVariables(plan); /* * The "debug_query_string" may still be used after transaction is done, * say in ShowUsage. To avoid the "debug_query_string" point to a freed * space, premalloc a space for "debug_query_string". */ if (save_log_statement_stats) t_thrd.postgres_cxt.debug_query_string = MemoryContextStrdup(t_thrd.mem_cxt.msg_mem_cxt, plan->query_string); else t_thrd.postgres_cxt.debug_query_string = plan->query_string; instr_stmt_report_start_time(); pgstat_report_activity(STATE_RUNNING, t_thrd.postgres_cxt.debug_query_string); /* * We use save_log_statement_stats so ShowUsage doesn't report incorrect * results because ResetUsage wasn't called. */ if (save_log_statement_stats) ResetUsage(); if (ThreadIsDummy(plan->planTree)) { u_sess->stream_cxt.dummy_thread = true; u_sess->exec_cxt.executorStopFlag = true; } /* "IS_PGXC_DATANODE && StreamTopConsumerAmI()" means on the CN of the compute pool. */ if (IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) exec_init_poolhandles(); /* "IS_PGXC_DATANODE && plan->in_compute_pool" means on the DN of the compute pool. */ if (IS_PGXC_DATANODE && plan->in_compute_pool) u_sess->exec_cxt.executorStopFlag = false; /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after * one of those, else bad things will happen in xact.c. (Note that this * will normally change current memory context.) */ start_xact_command(); /* * Zap any pre-existing unnamed statement. (While not strictly necessary, * it seems best to define simple-Query mode as if it used the unnamed * statement and portal; this ensures we recover any storage used by prior * unnamed operations.) */ drop_unnamed_stmt(); /* * We'll tell PortalRun it's a top-level command iff there's exactly one * raw parsetree. If more than one, it's effectively a transaction block * and we want PreventTransactionChain to reject unsafe commands. (Note: * we're assuming that query rewrite cannot add commands that are * significant to PreventTransactionChain.) */ isTopLevel = true; { const char* commandTag = NULL; char completionTag[COMPLETION_TAG_BUFSIZE]; Portal portal; DestReceiver* receiver = NULL; int16 format; /* * By default we do not want Datanodes or client Coordinators to contact GTM directly, * it should get this information passed down to it. */ SetForceXidFromGTM(false); /* * Get the tag automatically for plan shipping. * For now plan shipping is used only for SELECT\INSERT\DELETE\UPDATA\MERGE. */ commandTag = CreateCommandTagForPlan(plan->commandType); set_ps_display(commandTag, false); BeginCommand(commandTag, dest); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ABORT. It is important that this test occur before we try * to do parse analysis, rewrite, or planning, since all those phases * try to do database accesses, which may fail in abort state. (It * might be safe to allow some additional utility commands in this * state, but not many...) */ if (IsAbortedTransactionBlockState()) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command(); /* * OK to analyze, rewrite, and plan this query. * * Switch to appropriate context for constructing querytrees (again, * these must outlive the execution context). */ oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); WLMGeneralParam* g_wlm_params = &u_sess->wlm_cxt->wlm_params; /* local dn has vcgroup */ if (*g_instance.wlm_cxt->local_dn_ngname && *g_wlm_params->ngroup && 0 != strcmp(g_instance.wlm_cxt->local_dn_ngname, g_wlm_params->ngroup)) { /* Get the node group information */ t_thrd.wlm_cxt.thread_node_group = g_instance.wlm_cxt->local_dn_nodegroup; /* check if the control group is valid and set it for foreign user */ if (t_thrd.wlm_cxt.thread_node_group->foreignrp) { u_sess->wlm_cxt->local_foreign_respool = t_thrd.wlm_cxt.thread_node_group->foreignrp; /* update the resource pool information if it is from foreign users */ WLMSetControlGroup(t_thrd.wlm_cxt.thread_node_group->foreignrp->cgroup); /* reset the resource pool name */ char* foreignrp_name = get_resource_pool_name(t_thrd.wlm_cxt.thread_node_group->foreignrp->rpoid); if (foreignrp_name && *foreignrp_name) { errno_t rc = snprintf_s(g_wlm_params->rpdata.rpname, sizeof(g_wlm_params->rpdata.rpname), sizeof(g_wlm_params->rpdata.rpname) - 1, "%s", foreignrp_name); securec_check_ss(rc, "", ""); pfree(foreignrp_name); /* only the foreign resource pool has rpoid in hash table */ g_wlm_params->rpdata.rpoid = t_thrd.wlm_cxt.thread_node_group->foreignrp->rpoid; } else { errno_t rc = snprintf_s(g_wlm_params->rpdata.rpname, sizeof(g_wlm_params->rpdata.rpname), sizeof(g_wlm_params->rpdata.rpname) - 1, "%s", DEFAULT_POOL_NAME); securec_check_ss(rc, "", ""); } } else { WLMSetControlGroup(GSCGROUP_INVALID_GROUP); } /* reset the ngname */ errno_t rc = snprintf_s(g_wlm_params->ngroup, sizeof(g_wlm_params->ngroup), sizeof(g_wlm_params->ngroup) - 1, "%s", g_instance.wlm_cxt->local_dn_ngname); securec_check_ss(rc, "", ""); } GSCGROUP_ATTACH_TASK(); /* bypass log */ if (IS_PGXC_DATANODE && u_sess->attr.attr_sql.enable_opfusion == true && u_sess->attr.attr_sql.opfusion_debug_mode == BYPASS_LOG) { BypassUnsupportedReason(NOBYPASS_STREAM_NOT_SUPPORT); } /* * Create unnamed portal to run the query or queries in. If there * already is one, silently drop it. */ portal = CreatePortal("", true, true); /* Don't display the portal in pg_cursors */ portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in t_thrd.mem_cxt.msg_mem_cxt, which will outlive the * portal anyway. */ PortalDefineQuery(portal, NULL, "DUMMY", commandTag, lappend(NULL, plan), // vam plantree_list, NULL); /* * Start the portal. No parameters here. */ PortalStart(portal, NULL, 0, InvalidSnapshot); /* * Select the appropriate output format: text unless we are doing a * FETCH from a binary cursor. (Pretty grotty to have to do this here * --- but it avoids grottiness in other places. Ah, the joys of * backward compatibility...) */ format = 0; /* TEXT is default */ PortalSetResultFormat(portal, 1, &format); /* * Now we can create the destination receiver object. */ receiver = CreateDestReceiver(dest); if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * Switch back to transaction context for execution. */ MemoryContextSwitchTo(oldcontext); /* * Run the portal to completion, and then drop it (and the receiver). */ (void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag); (*receiver->rDestroy)(receiver); PortalDrop(portal, false); /* Flush messages left in PqSendBuffer before entering syncQuit. */ (void)pq_flush(); /* obs_instr->serializeSend() must be before finish_xact_command() */ if (StreamTopConsumerAmI() && u_sess->instr_cxt.obs_instr != NULL) { u_sess->instr_cxt.obs_instr->serializeSend(); } finish_xact_command(); EndCommand(completionTag, dest); /* Set sync point for waiting all stream threads complete. */ StreamNodeGroup::syncQuit(STREAM_COMPLETE); UnRegisterStreamSnapshots(); /* * send the stream instrumentation to the coordinator until all the stream thread quit. */ if (IS_PGXC_DATANODE && StreamTopConsumerAmI() && u_sess->instr_cxt.global_instr != NULL) { u_sess->instr_cxt.global_instr->serializeSend(); if (u_sess->instr_cxt.global_instr->needTrack()) { u_sess->instr_cxt.global_instr->serializeSendTrack(); } } /* * send the stream instrumentation to DWS DN until all the stream thread quit. * just run on the CN of the compute pool. */ if (IS_PGXC_COORDINATOR && StreamTopConsumerAmI() && u_sess->instr_cxt.global_instr != NULL) { Plan* aplan = plan->planTree->lefttree; int plannode_num = 0; while (aplan != NULL) { plannode_num++; /* plantree is always left-hand tree on the compute pool currently. */ aplan = aplan->lefttree; } u_sess->instr_cxt.global_instr->aggregate(plannode_num); u_sess->instr_cxt.global_instr->serializeSend(); if (u_sess->instr_cxt.global_instr->needTrack()) { u_sess->instr_cxt.global_instr->serializeSendTrack(); } } } /* end loop over parsetrees */ /* * Close down transaction statement, if one is open. */ finish_xact_command(); /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, false)) { case 1: Assert(false); break; case 2: ereport(LOG, (errmsg("duration: %s ms, debug id %ld unique id %lu statement: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, "TODO: deparse plan"), // vam query_string), errhidestmt(true) // vam errdetail_execute(parsetree_list) )); break; default: break; } if (save_log_statement_stats) ShowUsage("QUERY STATISTICS"); ereport(DEBUG2, (errmsg("exec_simple_plan() completed plan\n"))); t_thrd.postgres_cxt.debug_query_string = NULL; } /* * @hdfs * spilt_querystring_sql_info * * when we got a hybirdmesage. We call this function to split query_string into two parts. * Sql_query_string and info_query_string, both of them will be set value in this function. * For example if we got a hybridmessage like "analyze table_name;information......" storing * in query_string, sql_query_string and info_query_string are set into "analyze table_name;" * and "information......", separately. */ static size_t split_querystring_sql_info(const char* query_string, char*& sql_query_string, char*& info_query_string) { const char* position = query_string; int iLen = 0; uint32 queryStringLen = 0; errno_t errorno = EOK; /* Find the serialized Const in order to get queryStringLen. */ position = strchr(query_string, '}'); if (position == NULL) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Could not find \'}\' in the query string."))); } iLen = position - query_string + 1; char* constNodeString = (char*)palloc0(iLen + 1); errorno = memcpy_s(constNodeString, iLen, query_string, iLen); securec_check(errorno, "\0", "\0"); constNodeString[iLen] = '\0'; Const* n = (Const*)stringToNode(constNodeString); if (n == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("Invaild query string."))); } queryStringLen = DatumGetUInt32(n->constvalue); /* skip the end char '}' which serialized Const info of queryStringLen. */ position += 1; if (strlen(position) <= queryStringLen) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Input queryStringLen is illegal."))); } /* * Get sql_query_string from query_string */ sql_query_string = (char*)palloc(queryStringLen + 1); errorno = memcpy_s(sql_query_string, queryStringLen, position, queryStringLen); securec_check(errorno, "\0", "\0"); sql_query_string[queryStringLen] = '\0'; /* * Get info_query_string from query_string */ position += queryStringLen; info_query_string = pstrdup(position); pfree(constNodeString); pfree(n); return size_t(position - query_string) + strlen(position); } /* * @hdfs * attach_info_to_plantree_list * * If we got a hybridmessage, the second part of the messaget is information string. * We use this function transfer information string to a struct whose type is related to * query string type. The struct will be add to plantree_list. */ static void attach_info_to_plantree_list(List* plantree_list, AttachInfoContext* attachInfoCtx) { #define READ_MEM_USAGE(type) \ do { \ type* indexStmt = (type*)stmt; \ List* mem_list = (List*)stringToNode(info_query_string); \ if (unlikely(list_length(mem_list) != 2)) \ ereport(ERROR, \ (errmsg("unexpect list length %d of mem_list from info_query_string.", \ mem_list->length))); \ indexStmt->memUsage.work_mem = list_nth_int(mem_list, 0); \ indexStmt->memUsage.max_mem = list_nth_int(mem_list, 1); \ list_free(mem_list); \ MEMCTL_LOG(DEBUG3, "DN receive (%d, %d)", indexStmt->memUsage.work_mem, indexStmt->memUsage.max_mem); \ } while (0) ListCell* query_list = NULL; char* info_query_string = attachInfoCtx->info_query_string; foreach (query_list, plantree_list) { Node* stmt = (Node*)lfirst(query_list); switch (nodeTag(stmt)) { case T_VacuumStmt: { VacuumStmt* vacuumStmt = (VacuumStmt*)stmt; if (((unsigned int)vacuumStmt->options & VACOPT_ANALYZE) || ((unsigned int)vacuumStmt->options & VACOPT_FULL)) { /* * @hdfs * We define HDFSTableAnalyze in hdfs_fdw.h. When we got a hybridmesage. * If stmt has VACOPT_ANALYZE, we transfer info_query_string from string to * HDFSTableAnalyz struct. */ Node* analyzeNode = NULL; analyzeNode = (Node*)stringToNode(info_query_string); if (analyzeNode == NULL) { ereport(ERROR,(errmsg("analyzeNode is null."))); } /* * @hdfs * Hybridmessage must come from a coordinator node and the length of * HDFSNode->DnWorkFlow is 1. We are coordinator node or data node * is not important when we got hybridmessage. If we are coordinator node, * it means we must collect statistics information from some data node defined * in HDFSNode->DnWorkFlow. If we are data node, it means a coordinator * node told us to analyze some foreign hdfs table. We don't judge whether * info_query_string belongs to me or not here. We do this in standard_ProcessUtility. * * We must notice that: HDFSNode->DnWorkFlow which is got from CN * may be null in some situation. */ if (IsConnFromCoord()) { switch (nodeTag(analyzeNode)) { case T_HDFSTableAnalyze: { AnalyzeMode eAnalyzeMode; HDFSTableAnalyze* HDFSNode = (HDFSTableAnalyze*)analyzeNode; if (!HDFSNode->isHdfsStore) eAnalyzeMode = ANALYZENORMAL; else /* we should malloc memory of three tables for HDFS table. */ eAnalyzeMode = ANALYZEMAIN; vacuumStmt->tmpSampleTblNameList = HDFSNode->tmpSampleTblNameList; vacuumStmt->disttype = HDFSNode->disttype; if (NULL == HDFSNode->DnWorkFlow) { vacuumStmt->HDFSDnWorkFlow = NULL; vacuumStmt->DnCnt = 0; vacuumStmt->nodeNo = 0; global_stats_set_samplerate(eAnalyzeMode, vacuumStmt, HDFSNode->sampleRate); vacuumStmt->sampleTableRequired = false; } else { vacuumStmt->orgCnNodeNo = HDFSNode->orgCnNodeNo; vacuumStmt->DnCnt = HDFSNode->DnCnt; global_stats_set_samplerate(eAnalyzeMode, vacuumStmt, HDFSNode->sampleRate); vacuumStmt->sampleTableRequired = HDFSNode->sampleTableRequired; /* identify the analyze table is common table or hdfs table. */ if (!HDFSNode->isHdfsForeignTbl) { vacuumStmt->HDFSDnWorkFlow = NULL; } else { /* * hdfs foreign table * we must set default node as -1, because the nodeNo of the dn1 is 0, * we can't discriminate if do analyze on dn1. */ vacuumStmt->nodeNo = -1; /* * when we do global analyze for foreign table, * the list contain all files to each DN, we should get the dn task of local dn. */ ListCell* splitToDnMapCell = NULL; foreach (splitToDnMapCell, HDFSNode->DnWorkFlow) { Node* data = (Node*)lfirst(splitToDnMapCell); if (IsA(data, SplitMap)) { SplitMap* dnTask = (SplitMap*)data; /* only get dn files for local dn. */ if ((u_sess->pgxc_cxt.PGXCNodeId == dnTask->nodeId || LOCATOR_TYPE_REPLICATED == dnTask->locatorType) && NIL != dnTask->splits) { vacuumStmt->HDFSDnWorkFlow = (void*)dnTask; vacuumStmt->nodeNo = u_sess->pgxc_cxt.PGXCNodeId; break; } } else { DistFdwDataNodeTask* dnTask = (DistFdwDataNodeTask*)data; /* only get dn files for local dn. */ if (0 == pg_strcasecmp(dnTask->dnName, g_instance.attr.attr_common.PGXCNodeName) && NIL != dnTask->task) { vacuumStmt->HDFSDnWorkFlow = (void*)dnTask; vacuumStmt->nodeNo = u_sess->pgxc_cxt.PGXCNodeId; break; } } } } } vacuumStmt->memUsage.work_mem = HDFSNode->memUsage.work_mem; vacuumStmt->memUsage.max_mem = HDFSNode->memUsage.max_mem; break; } case T_IntList: { READ_MEM_USAGE(VacuumStmt); break; } default: break; } } } break; } case T_IndexStmt: { READ_MEM_USAGE(IndexStmt); break; } case T_ReindexStmt: { READ_MEM_USAGE(ReindexStmt); break; } case T_ClusterStmt: { READ_MEM_USAGE(ClusterStmt); break; } case T_CopyStmt: { READ_MEM_USAGE(CopyStmt); break; } case T_CreateSeqStmt: { Node* n = NULL; List* info_list = NULL; CreateSeqStmt* seqstmt = (CreateSeqStmt*)stmt; if (attachInfoCtx->info_node == NULL) { n = (Node*)stringToNode(info_query_string); if (n == NULL) { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("node n is null."))); } if (IsA(n, Const)) { Const* c = (Const*)n; attachInfoCtx->info_node = (Node*)list_make1(c); } else if (IsA(n, List)) { attachInfoCtx->info_node = n; } else { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Invaild UUID Message for CREATE SEQUENCE."))); } } info_list = (List*)attachInfoCtx->info_node; if (attachInfoCtx->info_index < info_list->length) { Const* c = (Const*)list_nth(info_list, attachInfoCtx->info_index); seqstmt->uuid = DatumGetInt64(c->constvalue); ereport(DEBUG2, (errmodule(MOD_SEQ), (errmsg("Create Sequence %s with UUID %ld ", seqstmt->sequence->relname, seqstmt->uuid)))); } else { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Not enough UUID (%d/%d) in CREATE SEQUENCE.", info_list->length, plantree_list->length))); } attachInfoCtx->info_index++; break; } #ifdef ENABLE_MOT case T_CreateStmt: /* fall through */ case T_CreateForeignTableStmt: { #else case T_CreateStmt: { #endif Node* l = (Node*)stringToNode(info_query_string); CreateStmt* cstmt = (CreateStmt*)stmt; if (IsA(l, List)) { List* uuids = (List*)l; cstmt->uuids = uuids; } else { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Invaild UUID Message while CREATE SEQUENCE by serial."))); } break; } case T_CreateSchemaStmt: { Node* l = (Node*)stringToNode(info_query_string); CreateSchemaStmt* csstmt = (CreateSchemaStmt*)stmt; if (IsA(l, List)) { List* uuids = (List*)l; csstmt->uuids = uuids; } else { ereport( ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("Invaild UUID Message while CREATE SCHEMA."))); } break; } default: ereport(WARNING, (errmsg("Wrong statment which should be without additional information."))); break; } } } void exec_init_poolhandles(void) { #ifdef ENABLE_MULTIPLE_NODES /* If this postmaster is launched from another Coord, do not initialize handles. skip it */ if (IS_PGXC_COORDINATOR && IsPostmasterEnvironment) { ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; /* we use session memory context to remember all node info in this cluster. */ MemoryContext old = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(currentOwner, "ForPGXCNodes", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION)); if (currentOwner != NULL) { InitMultinodeExecutor(false); if (!IsConnFromGTMTool() && !IsConnFromCoord() && !IsPoolHandle()) { PoolHandle* pool_handle = GetPoolManagerHandle(); if (pool_handle == NULL) { ereport(ERROR, (errcode(ERRCODE_IO_ERROR), errmsg("Can not connect to pool manager"))); return; } /* * We must switch to old memory context, if we use TopMemContext, * that will cause memory leak when call elog(ERROR), because TopMemContext * only is reset during thread exit. later we need refactor code section which * allocate from TopMemContext for better. TopMemcontext is a session level memory * context, we forbid allocate temp memory from TopMemcontext. */ MemoryContextSwitchTo(old); char* session_options_ptr = session_options(); /* Pooler initialization has to be made before ressource is released */ PoolManagerConnect(pool_handle, u_sess->proc_cxt.MyProcPort->database_name, u_sess->proc_cxt.MyProcPort->user_name, session_options_ptr); if (session_options_ptr != NULL) pfree(session_options_ptr); MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); } } else { PG_TRY(); { InitMultinodeExecutor(false); if (!IsConnFromGTMTool() && !IsConnFromCoord() && !IsPoolHandle()) { PoolHandle* pool_handle = GetPoolManagerHandle(); if (pool_handle == NULL) { ereport(ERROR, (errcode(ERRCODE_IO_ERROR), errmsg("Can not connect to pool manager"))); return; } /* * We must switch to old memory context, if we use TopMemContext, * that will cause memory leak when call elog(ERROR), because TopMemContext * only is reset during thread exit. later we need refactor code section which * allocate from TopMemContext for better. TopMemcontext is a session level memory * context, we forbid allocate temp memory from TopMemcontext. */ MemoryContextSwitchTo(old); char* session_options_ptr = session_options(); /* Pooler initialization has to be made before ressource is released */ PoolManagerConnect(pool_handle, u_sess->proc_cxt.MyProcPort->database_name, u_sess->proc_cxt.MyProcPort->user_name, session_options_ptr); if (session_options_ptr != NULL) pfree(session_options_ptr); MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); } } PG_CATCH(); { /* Report the error to the server log */ EmitErrorReport(); /* * These operations are really just a minimal subset of * AbortTransaction(). We don't have very many resources to worry * about in checkpointer, but we do have LWLocks, buffers, and temp * files. */ LWLockReleaseAll(); AbortBufferIO(); UnlockBuffers(); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, false, true); AtEOXact_Buffers(false); /* If waiting, get off wait queue (should only be needed after error) */ LockErrorCleanup(); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, false, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, false, true); ResourceOwner newOwner = t_thrd.utils_cxt.CurrentResourceOwner; t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(newOwner); (void)MemoryContextSwitchTo(old); PG_RE_THROW(); } PG_END_TRY(); } ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); ResourceOwner newOwner = t_thrd.utils_cxt.CurrentResourceOwner; t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(newOwner); (void)MemoryContextSwitchTo(old); } #endif } /* * exec_simple_query * * Execute a "simple Query" protocol message. * * @hdfs * Add default parameter unint16 messageType. Its default vaule is 0. If we receive * hybridmesage, this parameter will be set to 1 to tell us the normal query string * followed by information string. query_string = normal querystring + message. */ static void exec_simple_query(const char* query_string, MessageType messageType, StringInfo msg = NULL) { CommandDest dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; MemoryContext oldcontext; MemoryContext OptimizerContext; AttachInfoContext attachInfoCtx = {0}; List* parsetree_list = NULL; ListCell* parsetree_item = NULL; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; bool was_logged = false; bool isTopLevel = false; char msec_str[PRINTF_DST_MAX]; List* query_string_locationlist = NIL; int stmt_num = 0; size_t query_string_len = 0; char** query_string_single = NULL; bool is_multistmt = false; bool is_compl_sql = true; bool savedisAllowCommitRollback = false; bool needResetErrMsg = false; /* * @hdfs * When messageType is 1, we get hybridmessage. This message * can be splitted into two parts. sql_query_string will store normal * query string. info_query_string will store information string. */ char* sql_query_string = NULL; char* info_query_string = NULL; #ifdef ENABLE_DISTRIBUTE_TEST if (IS_PGXC_COORDINATOR && IsConnFromCoord()) { if (TEST_STUB(NON_EXEC_CN_IS_DOWN, twophase_default_error_emit)) { ereport(g_instance.distribute_test_param_instance->elevel, (errmsg("SUBXACT_TEST %s: non-exec cn is down.", g_instance.attr.attr_common.PGXCNodeName))); } /* white box test start */ if (execute_whitebox(WHITEBOX_LOC, NULL, WHITEBOX_DEFAULT, DEFAULT_PROBABILITY)) { ereport(g_instance.distribute_test_param_instance->elevel, (errmsg("WHITE_BOX TEST %s: non-exec cn is down.", g_instance.attr.attr_common.PGXCNodeName))); } } /* white box test end */ #endif /* * Set query dop at the first beginning of a query. */ u_sess->opt_cxt.query_dop = u_sess->opt_cxt.query_dop_store; u_sess->opt_cxt.skew_strategy_opt = u_sess->attr.attr_sql.skew_strategy_store; /* * Rest PTFastQueryShippingStore.(We may set it in ExplainQuery routine). */ PTFastQueryShippingStore = true; /* * Report query to various monitoring facilities. */ t_thrd.explain_cxt.explain_perf_mode = u_sess->attr.attr_sql.guc_explain_perf_mode; plpgsql_estate = NULL; /* * @hdfs * 1. We compare meesageType with 1. When messageType is 1, it means * this message is a hybrid message. query_string is followed * by some information we must process. We split query_stirng into two * part, normal query string and information string. Parser will parse * normal query string. Normal query string and information string is separated * by ";" in query_string, like this: "analyze table_name;information....." * 2. Do basic parsing of the query or queries (this should be safe even if * we are in aborted transaction state! */ if (HYBRID_MESSAGE != messageType) { pgstat_report_activity(STATE_RUNNING, query_string); t_thrd.postgres_cxt.debug_query_string = query_string; query_string_len = strlen(query_string); } else { MemoryContext oldcontext_tmp = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); query_string_len = split_querystring_sql_info(query_string, sql_query_string, info_query_string); attachInfoCtx.info_query_string = info_query_string; (void)MemoryContextSwitchTo(oldcontext_tmp); /* report original sql query string with no info query for analyze command. */ pgstat_report_activity(STATE_RUNNING, sql_query_string); t_thrd.postgres_cxt.debug_query_string = sql_query_string; } instr_stmt_report_start_time(); /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after * one of those, else bad things will happen in xact.c. (Note that this * will normally change current memory context.) */ start_xact_command(); if (ENABLE_WORKLOAD_CONTROL && SqlIsValid(query_string) && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE)) { if (IsConnFromCoord()) t_thrd.wlm_cxt.wlmalarm_dump_active = WLMIsDumpActive(query_string); else { u_sess->wlm_cxt->is_active_statements_reset = false; if (g_instance.wlm_cxt->dynamic_workload_inited) { dywlm_parallel_ready(query_string); dywlm_client_max_reserve(); } else { WLMParctlReady(query_string); WLMParctlReserve(PARCTL_GLOBAL); } } } // Init pool handlers exec_init_poolhandles(); TRACE_POSTGRESQL_QUERY_START(query_string); /* * We use save_log_statement_stats so ShowUsage doesn't report incorrect * results because ResetUsage wasn't called. */ if (save_log_statement_stats) ResetUsage(); /* * Zap any pre-existing unnamed statement. (While not strictly necessary, * it seems best to define simple-Query mode as if it used the unnamed * statement and portal; this ensures we recover any storage used by prior * unnamed operations.) */ drop_unnamed_stmt(); /* * Switch to appropriate context for constructing parsetrees. */ oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); if (HYBRID_MESSAGE == messageType) { parsetree_list = pg_parse_query(sql_query_string); } else { if (copy_need_to_be_reparse != NULL && g_instance.status == NoShutdown) { bool reparse_query = false; gs_stl::gs_string reparsed_query; do { parsetree_list = pg_parse_query(reparsed_query.empty() ? query_string : reparsed_query.c_str(), &query_string_locationlist); reparse_query = copy_need_to_be_reparse(parsetree_list, query_string, reparsed_query); } while (reparse_query); } else { parsetree_list = pg_parse_query(query_string, &query_string_locationlist); } } /* Log immediately if dictated by log_statement */ if (check_log_statement(parsetree_list)) { char* mask_string = NULL; mask_string = maskPassword(query_string); if (NULL == mask_string) { mask_string = (char*)query_string; } ereport(LOG, (errmsg("statement: %s", mask_string), errhidestmt(true), errdetail_execute(parsetree_list))); if (mask_string != query_string) pfree(mask_string); was_logged = true; } /* Light proxy is only enabled for single query from 'Q' message */ bool runLightProxyCheck = (msg != NULL) && IS_PGXC_COORDINATOR && !IsConnFromCoord() && u_sess->attr.attr_sql.enable_light_proxy && (list_length(parsetree_list) == 1) && (query_string_len < SECUREC_MEM_MAX_LEN); bool runOpfusionCheck = (msg != NULL) && IS_PGXC_DATANODE && u_sess->attr.attr_sql.enable_opfusion && (list_length(parsetree_list) == 1) && (query_string_len < SECUREC_MEM_MAX_LEN); /* * Switch back to transaction context to enter the loop. */ MemoryContextSwitchTo(oldcontext); /* * Before executor, check the status of password. */ if (!t_thrd.postgres_cxt.password_changed) { ForceModifyInitialPwd((const char*)query_string, parsetree_list); } /* * Read password status from pg_user_status. If is in aborted block, don't check. */ Oid current_user = GetUserId(); if (current_user != BOOTSTRAP_SUPERUSERID) { if (!IsAbortedTransactionBlockState() && GetAccountPasswordExpired(current_user) == EXPIRED_STATUS) { ForceModifyExpiredPwd((const char*)query_string, parsetree_list); } } /* * We'll tell PortalRun it's a top-level command iff there's exactly one * raw parsetree. If more than one, it's effectively a transaction block * and we want PreventTransactionChain to reject unsafe commands. (Note: * we're assuming that query rewrite cannot add commands that are * significant to PreventTransactionChain.) */ isTopLevel = (list_length(parsetree_list) == 1); stp_retore_old_xact_stmt_state(true); if (!isTopLevel) t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; /* Apply for a new memory context for analyze and rewrite as well as pg_plan_queries */ OptimizerContext = AllocSetContextCreate(t_thrd.mem_cxt.msg_mem_cxt, "OptimizerContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); bool is_multi_query_text = false; /* When the query is multi query like "begin;...end;" , we need update query statement for WLM collect Info */ if (t_thrd.wlm_cxt.collect_info->sdetail.statement != NULL && list_length(parsetree_list) > 1) { is_multi_query_text = true; } /* just for cooperation analysis. */ if (IS_PGXC_COORDINATOR && u_sess->pgxc_cxt.is_gc_fdw) { u_sess->attr.attr_sql.enable_stream_operator = false; u_sess->opt_cxt.qrw_inlist2join_optmode = QRW_INLIST2JOIN_DISABLE; } /* reset unique sql start time */ u_sess->unique_sql_cxt.unique_sql_start_time = 0; /* * Run through the raw parsetree(s) and process each one. */ foreach (parsetree_item, parsetree_list) { instr_unique_sql_handle_multi_sql(parsetree_item == list_head(parsetree_list)); Node* parsetree = (Node*)lfirst(parsetree_item); bool snapshot_set = false; const char* commandTag = NULL; char completionTag[COMPLETION_TAG_BUFSIZE]; List* querytree_list = NULL; List* plantree_list = NULL; Portal portal = NULL; DestReceiver* receiver = NULL; int16 format; char* randomPlanInfo = NULL; /* Reset the single_shard_stmt flag */ u_sess->exec_cxt.single_shard_stmt = false; /* * When dealing with a multi-query, get the snippets of each single querys through * get_next_snippet which cut the multi-query by query_string_locationlist. */ if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && PointerIsValid(query_string_locationlist) && list_length(query_string_locationlist) > 1) { oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); is_multistmt = true; query_string_single = get_next_snippet(query_string_single, query_string, query_string_locationlist, &stmt_num); (void)MemoryContextSwitchTo(oldcontext); /* INSTR: for multi-query case, unique sql needs to use every single sql * to generate unique sql id & string */ if (is_unique_sql_enabled() && !IsConnFromCoord() && query_string_single != NULL) { u_sess->unique_sql_cxt.is_multi_unique_sql = true; u_sess->unique_sql_cxt.curr_single_unique_sql = query_string_single[stmt_num - 1]; if (stmt_num > 1 && stmt_num <= list_length(query_string_locationlist)) { u_sess->unique_sql_cxt.multi_sql_offset = list_nth_int(query_string_locationlist, stmt_num - 2) + 1; } } } #ifdef ENABLE_MULTIPLE_NODES /* * By default we do not want Datanodes or client Coordinators to contact GTM directly, * it should get this information passed down to it. */ if (IS_PGXC_DATANODE || IsConnFromCoord()) SetForceXidFromGTM(false); #endif /* * Get the command name for use in status display (it also becomes the * default completion tag, down inside PortalRun). Set ps_status and * do any special start-of-SQL-command processing needed by the * destination. */ commandTag = CreateCommandTag(parsetree); set_ps_display(commandTag, false); BeginCommand(commandTag, dest); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ABORT. It is important that this test occur before we try * to do parse analysis, rewrite, or planning, since all those phases * try to do database accesses, which may fail in abort state. (It * might be safe to allow some additional utility commands in this * state, but not many...) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(parsetree)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command(); /* If we got a cancel signal in parsing or prior command, quit */ CHECK_FOR_INTERRUPTS(); // We can't load system cache when transaction is TBLOCK_ABORT // even if it is 'commit/rollback' // u_sess->wlm_cxt->wlm_num_streams = 0; /* * Set up a snapshot if parse analysis/planning will need one. */ if (analyze_requires_snapshot(parsetree)) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Before going into planner, set default work mode. */ set_default_stream(); /* * OK to analyze, rewrite, and plan this query. * * Switch to appropriate context for constructing querytrees (again, * these must outlive the execution context). */ oldcontext = MemoryContextSwitchTo(OptimizerContext); /* * sqladvisor check if it can be collected * select into... only can be checked in parsetree, it will transfrom to insert into after rewrite. */ bool isCollect = checkAdivsorState() && checkParsetreeTag(parsetree); /* Does not allow commit in pre setting scenario */ is_compl_sql = CheckElementParsetreeTag(parsetree); if (is_compl_sql) { needResetErrMsg = stp_disable_xact_and_set_err_msg(&savedisAllowCommitRollback, STP_XACT_COMPL_SQL); } /* * @hdfs * If we received a hybridmessage, we use sql_query_string to analyze and rewrite. */ if (HYBRID_MESSAGE != messageType) querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); else querytree_list = pg_analyze_and_rewrite(parsetree, sql_query_string, NULL, 0); isCollect = checkCollectSimpleQuery(isCollect, querytree_list); #ifdef ENABLE_MOT /* check cross engine queries and transactions violation for MOT */ StorageEngineType storageEngineType = SE_TYPE_UNSPECIFIED; if (querytree_list) { CheckTablesStorageEngine((Query*)linitial(querytree_list), &storageEngineType); } SetCurrentTransactionStorageEngine(storageEngineType); if (!IsTransactionExitStmt(parsetree) && storageEngineType == SE_TYPE_MIXED) { ereport(ERROR, (errcode(ERRCODE_FDW_CROSS_STORAGE_ENGINE_QUERY_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Cross storage engine query is not supported"))); } if (!IsTransactionExitStmt(parsetree) && IsMixedEngineUsed()) { ereport(ERROR, (errcode(ERRCODE_FDW_CROSS_STORAGE_ENGINE_TRANSACTION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Cross storage engine transaction is not supported"))); } /* block MOT engine queries in sub-transactions */ if (!IsTransactionExitStmt(parsetree) && IsMOTEngineUsedInParentTransaction() && IsMOTEngineUsed()) { ereport(ERROR, (errcode(ERRCODE_FDW_OPERATION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("SubTransaction is not supported for memory table"))); } if (IsTransactionPrepareStmt(parsetree) && (IsMOTEngineUsed() || IsMixedEngineUsed())) { /* Explicit prepare transaction is not supported for memory table */ ereport(ERROR, (errcode(ERRCODE_FDW_OPERATION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Explicit prepare transaction is not supported for memory table"))); } /* check for MOT update of indexed field. Can check only the querytree head, no need for drill down */ if (!IsTransactionExitStmt(parsetree) && (querytree_list != NULL && CheckMotIndexedColumnUpdate((Query*)linitial(querytree_list)))) { ereport(ERROR, (errcode(ERRCODE_FDW_UPDATE_INDEXED_FIELD_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Update of indexed column is not supported for memory table"))); } #endif /* Try using light proxy to execute query */ if (runLightProxyCheck && exec_query_through_light_proxy(querytree_list, parsetree, snapshot_set, msg, OptimizerContext)) { (void)MemoryContextSwitchTo(oldcontext); /* sqladvisor collect query */ collectSimpleQuery(query_string, isCollect); break; } plantree_list = pg_plan_queries(querytree_list, 0, NULL); /* sqladvisor collect query */ collectSimpleQuery(query_string, isCollect); randomPlanInfo = get_random_plan_string(); if (was_logged != false && randomPlanInfo != NULL) { ereport(LOG, (errmsg("%s", randomPlanInfo), errhidestmt(true))); pfree(randomPlanInfo); } (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); /* * @hdfs * If we received a hybridmessage, we attach additional information to plantree_list's node. * Additional information is a string which can be recovered into a struct by call string to node. */ if (HYBRID_MESSAGE == messageType) attach_info_to_plantree_list(plantree_list, &attachInfoCtx); /* Done with the snapshot used for parsing/planning */ if (snapshot_set != false) PopActiveSnapshot(); /* If we got a cancel signal in analysis or planning, quit */ CHECK_FOR_INTERRUPTS(); #ifdef PGXC /* PGXC_DATANODE */ /* Flag to indicate whether vacuum needs getting Xid from GTM */ bool vacuumForceXid = false; /* If the parsetree is a vacuum statement */ if (IsA(parsetree, VacuumStmt)) { /* copy the parsetree */ VacuumStmt* vacstmt = (VacuumStmt*)parsetree; /* Initially, vacuum forces getting Xid from GTM */ vacuumForceXid = true; /* If vaccum(analyze) one relation, don't force getting xid from GTM. Instead, use Xid sent down by CN */ if (!((uint32)vacstmt->options & VACOPT_VACUUM) && vacstmt->relation) vacuumForceXid = false; } /* Force getting Xid from GTM if neither an autovacuum nor a vacuum(analyze) to one relation */ if ((IS_PGXC_DATANODE || IsConnFromCoord()) && (vacuumForceXid || IsA(parsetree, ClusterStmt)) && IsPostmasterEnvironment) SetForceXidFromGTM(true); /* Commands like reindex database ..., coordinator don't send snapshot down, need to get from GTM. */ if ((IS_PGXC_DATANODE || IsConnFromCoord()) && IsA(parsetree, ReindexStmt) && ((ReindexStmt*)parsetree)->kind == OBJECT_DATABASE && IsPostmasterEnvironment) SetForceXidFromGTM(true); #endif /* SQL bypass */ if (runOpfusionCheck) { (void)MemoryContextSwitchTo(oldcontext); void* opFusionObj = OpFusion::FusionFactory( OpFusion::getFusionType(NULL, NULL, plantree_list), oldcontext, NULL, plantree_list, NULL); if (opFusionObj != NULL) { ((OpFusion*)opFusionObj)->setCurrentOpFusionObj((OpFusion*)opFusionObj); if (OpFusion::process(FUSION_EXECUTE, NULL, completionTag, isTopLevel, NULL)) { CommandCounterIncrement(); finish_xact_command(); EndCommand(completionTag, dest); MemoryContextReset(OptimizerContext); break; } Assert(0); } (void)MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); } /* * Create unnamed portal to run the query or queries in. If there * already is one, silently drop it. */ portal = CreatePortal("", true, true); /* Don't display the portal in pg_cursors */ portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in t_thrd.mem_cxt.msg_mem_cxt, which will outlive the * portal anyway. If we received a hybridmesage, we send sql_query_string * to PortalDefineQuery as the original query string. */ if (HYBRID_MESSAGE != messageType) { if (is_multistmt && (IsConnFromApp() || IsConnFromInternalTool())) { PortalDefineQuery(portal, NULL, query_string_single[stmt_num - 1], commandTag, plantree_list, NULL); } else PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL); } else { PortalDefineQuery(portal, NULL, sql_query_string, commandTag, plantree_list, NULL); } if (ENABLE_WORKLOAD_CONTROL && IS_PGXC_COORDINATOR && is_multi_query_text) { if (t_thrd.wlm_cxt.collect_info->sdetail.statement) { pfree_ext(t_thrd.wlm_cxt.collect_info->sdetail.statement); } AutoContextSwitch memSwitch(t_thrd.wlm_cxt.query_resource_track_mcxt); /* g_collectInfo.sdetail.statement will be free in WLMReleaseStmtDetailItem() */ if (strlen(portal->sourceText) < 8 * KBYTES) { t_thrd.wlm_cxt.collect_info->sdetail.statement = pstrdup(portal->sourceText); } else { t_thrd.wlm_cxt.collect_info->sdetail.statement = (char*)palloc0(8 * KBYTES); errno_t rc = strncpy_s( t_thrd.wlm_cxt.collect_info->sdetail.statement, 8 * KBYTES, portal->sourceText, 8 * KBYTES - 1); securec_check(rc, "\0", "\0"); } } /* * Start the portal. No parameters here. */ PortalStart(portal, NULL, 0, InvalidSnapshot); /* * Select the appropriate output format: text unless we are doing a * FETCH from a binary cursor. (Pretty grotty to have to do this here * --- but it avoids grottiness in other places. Ah, the joys of * backward compatibility...) */ format = 0; /* TEXT is default */ if (IsA(parsetree, FetchStmt)) { FetchStmt* stmt = (FetchStmt*)parsetree; if (!stmt->ismove) { Portal fportal = GetPortalByName(stmt->portalname); if (PortalIsValid(fportal) && ((uint32)fportal->cursorOptions & CURSOR_OPT_BINARY)) format = 1; /* BINARY */ } } PortalSetResultFormat(portal, 1, &format); /* * Now we can create the destination receiver object. */ receiver = CreateDestReceiver(dest); if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * Switch back to transaction context for execution. */ (void)MemoryContextSwitchTo(oldcontext); if (u_sess->attr.attr_resource.use_workload_manager && g_instance.wlm_cxt->gscgroup_init_done && !IsAbortedTransactionBlockState()) { u_sess->wlm_cxt->cgroup_last_stmt = u_sess->wlm_cxt->cgroup_stmt; u_sess->wlm_cxt->cgroup_stmt = WLMIsSpecialCommand(parsetree, portal); } /* * Run the portal to completion, and then drop it (and the receiver). */ (void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag); (*receiver->rDestroy)(receiver); PortalDrop(portal, false); CleanHotkeyCandidates(true); /* restore is_allow_commit_rollback */ if (is_compl_sql) { stp_reset_xact_state_and_err_msg(savedisAllowCommitRollback, needResetErrMsg); } if (IsA(parsetree, TransactionStmt)) { /* * If this was a transaction control statement, commit it. We will * start a new xact command for the next command (if any). */ finish_xact_command(); } else if (lnext(parsetree_item) == NULL) { /* * If this is the last parsetree of the query string, close down * transaction statement before reporting command-complete. This * is so that any end-of-transaction errors are reported before * the command-complete message is issued, to avoid confusing * clients who will expect either a command-complete message or an * error, not one and then the other. But for compatibility with * historical openGauss behavior, we do not force a transaction * boundary between queries appearing in a single query string. */ finish_xact_command(); } else { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. */ CommandCounterIncrement(); } /* * Tell client that we're done with this query. Note we emit exactly * one EndCommand report for each raw parsetree, thus one for each SQL * command the client sent, regardless of rewriting. (But a command * aborted by error will not send an EndCommand report at all.) */ EndCommand(completionTag, dest); MemoryContextReset(OptimizerContext); } /* end loop over parsetrees */ /* Reset the single_shard_stmt flag */ u_sess->exec_cxt.single_shard_stmt = false; MemoryContextDelete(OptimizerContext); /* * Close down transaction statement, if one is open. */ finish_xact_command(); /* * If there were no parsetrees, return EmptyQueryResponse message. */ if (parsetree_list == NULL) NullCommand(dest); /* release global active counts */ if (ENABLE_WORKLOAD_CONTROL) { if (g_instance.wlm_cxt->dynamic_workload_inited) { if (t_thrd.wlm_cxt.parctl_state.simple == 0) dywlm_client_release(&t_thrd.wlm_cxt.parctl_state); else WLMReleaseGroupActiveStatement(); dywlm_client_max_release(&t_thrd.wlm_cxt.parctl_state); } else { WLMParctlRelease(&t_thrd.wlm_cxt.parctl_state); } WLMSetCollectInfoStatusFinish(); } /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, was_logged)) { case 1: ereport(LOG, (errmsg("duration: %s ms, queryid %ld, unique id %lu", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id), errhidestmt(true))); break; case 2: { char* mask_string = NULL; MASK_PASSWORD_START(mask_string, query_string); ereport(LOG, (errmsg("duration: %s ms queryid %ld unique id %ld statement: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, mask_string), errhidestmt(true), errdetail_execute(parsetree_list))); MASK_PASSWORD_END(mask_string, query_string); break; } default: break; } if (save_log_statement_stats) ShowUsage("QUERY STATISTICS"); TRACE_POSTGRESQL_QUERY_DONE(query_string); t_thrd.postgres_cxt.debug_query_string = NULL; /* * @hdfs * If we received a hybridmesage, we applied additional memory. At the end of * exec_simple_query, we free them. */ if (HYBRID_MESSAGE == messageType) { pfree_ext(info_query_string); pfree_ext(sql_query_string); } /* Free the memory of query_string_single malloced in get_next_snippet. */ if (is_multistmt) { for (int i = 0; i < stmt_num; i++) { pfree(query_string_single[i]); query_string_single[i] = NULL; } pfree(query_string_single); } } #ifdef ENABLE_MULTIPLE_NODES /* * exec_plan_with_params * * Execute a "extend plan with params" received from coordinator */ static void exec_plan_with_params(StringInfo input_message) { CommandDest dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; PlannedStmt* planstmt = NULL; Node* node = NULL; int numParams; Oid* paramTypes = NULL; char** paramTypeNames = NULL; int numPFormats; int16* pformats = NULL; int numRFormats; int16* rformats = NULL; ParamListInfo params; long max_rows; if (save_log_statement_stats) ResetUsage(); /* "IS_PGXC_DATANODE && StreamTopConsumerAmI()" means on the CN of the compute pool. */ if (IS_PGXC_COORDINATOR && StreamTopConsumerAmI()) exec_init_poolhandles(); start_xact_command(); /* Switch back to message context */ MemoryContext oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); /* get plan */ const char* plan_string = pq_getmsgstring(input_message); if (strlen(plan_string) > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long plan_string."))); } node = (Node*)stringToNode((char*)plan_string); if (node != NULL && !IsA(node, PlannedStmt)) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NODE_STATE), errmsg("Received unexpected node type."))); } planstmt = (PlannedStmt*) node; InitGlobalNodeDefinition(planstmt); if (planstmt == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("Invaild parameter."))); } if (ThreadIsDummy(planstmt->planTree)) { u_sess->stream_cxt.dummy_thread = true; u_sess->exec_cxt.executorStopFlag = true; } /* nodeid in planstmt->planTree->exec_nodes->nodeList is from DWS, * and PGXCNodeId is from the compute pool. so ignore the result of ThreadIsDummy() * and set u_sess->exec_cxt.executor_stop_flag and u_sess->stream_cxt.dummy_thread to proper value. */ if (planstmt->in_compute_pool) { u_sess->stream_cxt.dummy_thread = false; u_sess->exec_cxt.executorStopFlag = false; } /* get parameter numbers */ numParams = pq_getmsgint(input_message, 2); /* get parameter types */ paramTypes = (Oid*)palloc(numParams * sizeof(Oid)); if (numParams > 0) { int i; paramTypeNames = (char**)palloc(numParams * sizeof(char*)); for (i = 0; i < numParams; i++) paramTypeNames[i] = (char*)pq_getmsgstring(input_message); } if (paramTypeNames != NULL) { if (IsConnFromCoord() || (IS_PGXC_COORDINATOR && planstmt->in_compute_pool)) { int cnt_param; for (cnt_param = 0; cnt_param < numParams; cnt_param++) parseTypeString(paramTypeNames[cnt_param], ¶mTypes[cnt_param], NULL); } } /* Get the parameter format codes */ numPFormats = pq_getmsgint(input_message, 2); if (numPFormats > 0) { int i; pformats = (int16*)palloc(numPFormats * sizeof(int16)); for (i = 0; i < numPFormats; i++) pformats[i] = pq_getmsgint(input_message, 2); } /* Get the parameter value count */ numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); /* Get the parameter value */ if (numParams > 0) { int paramno; params = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData)); /* we have static list of params, so no hooks needed */ params->paramFetch = NULL; params->paramFetchArg = NULL; params->parserSetup = NULL; params->parserSetupArg = NULL; params->params_need_process = false; params->numParams = numParams; for (paramno = 0; paramno < numParams; paramno++) { Oid ptype = paramTypes[paramno]; int32 plength; Datum pval = 0; bool isNull = false; StringInfoData pbuf; char csave; int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); if (!isNull) { const char* pvalue = pq_getmsgbytes(input_message, plength); /* * Rather than copying data around, we just set up a phony * StringInfo pointing to the correct portion of the message * buffer. We assume we can scribble on the message buffer so * as to maintain the convention that StringInfos have a * trailing null. This is grotty but is a big win when * dealing with very large parameter strings. */ pbuf.data = (char*)pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; } else { pbuf.data = NULL; /* keep compiler quiet */ csave = 0; } if (numPFormats > 1) { Assert(NULL != pformats); pformat = pformats[paramno]; } else if (numPFormats > 0) { Assert(NULL != pformats); pformat = pformats[0]; } else { pformat = 0; /* default = text */ } if (pformat == 0) { /* text mode */ Oid typinput; Oid typioparam; char* pstring = NULL; getTypeInputInfo(ptype, &typinput, &typioparam); /* * We have to do encoding conversion before calling the * typinput routine. */ if (isNull) { pstring = NULL; } else { pstring = pg_client_to_server(pbuf.data, plength); } pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); /* Free result of encoding conversion, if any */ if (pstring != NULL && pstring != pbuf.data) { pfree(pstring); } } else { ereport( ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); } /* Restore message buffer contents */ if (!isNull) { pbuf.data[plength] = csave; } params->params[paramno].value = pval; params->params[paramno].isnull = isNull; /* * We mark the params as CONST. This has no effect if we already * did planning, but if we didn't, it licenses the planner to * substitute the parameters directly into the one-shot plan we * will generate below. */ params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; params->params[paramno].tabInfo = NULL; } } else params = NULL; /* Get the result format codes */ numRFormats = pq_getmsgint(input_message, 2); if (numRFormats != 0) { ereport( ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Invalid result format codes: %d.", numRFormats))); } /* get the fetch size */ max_rows = pq_getmsgint(input_message, 4); /* End of message */ pq_getmsgend(input_message); { const char* commandTag = NULL; char completionTag[COMPLETION_TAG_BUFSIZE]; Portal portal; DestReceiver* receiver = NULL; SetForceXidFromGTM(false); commandTag = "SELECT"; set_ps_display(commandTag, false); BeginCommand(commandTag, dest); if (IsAbortedTransactionBlockState()) { ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); } /* Make sure we are in a transaction command */ start_xact_command(); /* If we got a cancel signal in parsing or prior command, quit */ CHECK_FOR_INTERRUPTS(); GSCGROUP_ATTACH_TASK(); MemoryContextSwitchTo(oldcontext); portal = CreatePortal("", true, true); /* Don't display the portal in pg_cursors */ portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in t_thrd.mem_cxt.msg_mem_cxt, which will outlive the * portal anyway. */ PortalDefineQuery(portal, NULL, "DUMMY", commandTag, lappend(NULL, planstmt), NULL); PortalStart(portal, params, 0, InvalidSnapshot); PortalSetResultFormat(portal, numRFormats, rformats); /* * Now we can create the destination receiver object. */ receiver = CreateDestReceiver(dest); if (dest == DestRemote) { SetRemoteDestReceiverParams(receiver, portal); } if (max_rows <= 0) { max_rows = FETCH_ALL; } (void)PortalRun(portal, max_rows, true, receiver, receiver, completionTag); (*receiver->rDestroy)(receiver); PortalDrop(portal, false); /* Flush messages left in PqSendBuffer before entering syncQuit. */ (void)pq_flush(); /* Set sync point for waiting all stream threads complete. */ StreamNodeGroup::syncQuit(STREAM_COMPLETE); UnRegisterStreamSnapshots(); /* * send the stream instrumentation to the coordinator until all the stream thread quit. */ if (StreamTopConsumerAmI() && u_sess->instr_cxt.global_instr != NULL) { u_sess->instr_cxt.global_instr->serializeSend(); if (u_sess->instr_cxt.global_instr->needTrack()) { u_sess->instr_cxt.global_instr->serializeSendTrack(); } } if (StreamTopConsumerAmI() && u_sess->instr_cxt.obs_instr != NULL) { u_sess->instr_cxt.obs_instr->serializeSend(); } finish_xact_command(); EndCommand(completionTag, dest); } finish_xact_command(); } #endif #ifdef ENABLE_MOT static void TryMotJitCodegenQuery(const char* queryString, CachedPlanSource* psrc, Query* query) { // Try to generate LLVM jitted code - first cleanup jit of previous run. if (psrc->mot_jit_context != NULL) { // NOTE: context is cleaned up during end of session, this should not happen, // maybe a warning should be issued psrc->mot_jit_context = NULL; } if (JitExec::IsMotCodegenPrintEnabled()) { elog(LOG, "Attempting to generate MOT jitted code for query: %s\n", queryString); } JitExec::JitPlan* jitPlan = JitExec::IsJittable(query, queryString); if (jitPlan != NULL) { psrc->mot_jit_context = JitExec::JitCodegenQuery(query, queryString, jitPlan); if ((psrc->mot_jit_context == NULL) && JitExec::IsMotCodegenPrintEnabled()) { elog(LOG, "Failed to generate jitted MOT function for query %s\n", queryString); } } } #endif /* * exec_parse_message * * Execute a "Parse" protocol message. * If paramTypeNames is specified, paraTypes is filled with corresponding OIDs. * The caller is expected to allocate space for the paramTypes. */ static void exec_parse_message(const char* query_string, /* string to execute */ const char* stmt_name, /* name for prepared stmt */ Oid* paramTypes, /* parameter types */ char** paramTypeNames, /* parameter type names */ const char* paramModes, int numParams) /* number of parameters */ { MemoryContext unnamed_stmt_context = NULL; MemoryContext oldcontext; List* parsetree_list = NULL; Node* raw_parse_tree = NULL; const char* commandTag = NULL; List* querytree_list = NULL; CachedPlanSource* psrc = NULL; bool is_named = false; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; char msec_str[PRINTF_DST_MAX]; char* mask_string = NULL; #ifdef ENABLE_MULTIPLE_NODES bool runOnSingleNode = false; #endif ExecNodes* single_exec_node = NULL; bool is_read_only = false; gstrace_entry(GS_TRC_ID_exec_parse_message); /* * Report query to various monitoring facilities. */ t_thrd.postgres_cxt.debug_query_string = query_string; plpgsql_estate = NULL; /* * Only support normal perf mode for PBE, as DestRemoteExecute can not send T message automatically. */ t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; /* * Set query dop at the first beginning of a query. */ u_sess->opt_cxt.query_dop = u_sess->opt_cxt.query_dop_store; pgstat_report_activity(STATE_RUNNING, query_string); instr_stmt_report_start_time(); // Init pool handlers exec_init_poolhandles(); set_ps_display("PARSE", false); if (save_log_statement_stats) { ResetUsage(); } if (log_min_messages <= DEBUG2 || client_min_messages <= DEBUG2) { mask_string = maskPassword(query_string); if (mask_string == NULL) { mask_string = (char*)query_string; } ereport(DEBUG2, (errmsg("parse %s: %s", *stmt_name ? stmt_name : "", mask_string))); if (mask_string != query_string) { pfree(mask_string); } } /* just for cooperation analysis. */ if (IS_PGXC_COORDINATOR && u_sess->pgxc_cxt.is_gc_fdw) { u_sess->attr.attr_sql.enable_stream_operator = false; u_sess->opt_cxt.qrw_inlist2join_optmode = QRW_INLIST2JOIN_DISABLE; } /* * Start up a transaction command so we can run parse analysis etc. (Note * that this will normally change current memory context.) Nothing happens * if we are already in one. */ start_xact_command(); if (ENABLE_DN_GPC) CleanSessGPCPtr(u_sess); is_named = (stmt_name[0] != '\0'); if (ENABLE_GPC) { CachedPlanSource * plansource = g_instance.plan_cache->Fetch(query_string, strlen(query_string), numParams, paramTypes, NULL); if (plansource != NULL) { bool hasGetLock = false; if (is_named) { if (ENABLE_CN_GPC) StorePreparedStatementCNGPC(stmt_name, plansource, false, true); else { u_sess->pcache_cxt.cur_stmt_psrc = plansource; if (g_instance.plan_cache->CheckRecreateCachePlan(plansource, &hasGetLock)) g_instance.plan_cache->RecreateCachePlan(plansource, stmt_name, NULL, NULL, NULL, hasGetLock); } goto pass_parsing; } else { drop_unnamed_stmt(); u_sess->pcache_cxt.unnamed_stmt_psrc = plansource; if (ENABLE_DN_GPC) u_sess->pcache_cxt.private_refcount--; /* don't share unnamed invalid or lightproxy plansource */ if (!g_instance.plan_cache->CheckRecreateCachePlan(plansource, &hasGetLock) && plansource->gplan) { goto pass_parsing; } else { if (hasGetLock) { AcquirePlannerLocks(plansource->query_list, false); if (plansource->gplan) { AcquireExecutorLocks(plansource->gplan->stmt_list, false); } } } } } } #ifdef ENABLE_MULTIPLE_NODES /* in smooth open gpc status, when dn get same prepare statement saved in this session, * just skip it, don't throw error */ if (IS_PGXC_DATANODE && !ENABLE_GPC && IN_GPC_GRAYRELEASE_CHANGE && is_named) { PreparedStatement* ps = FetchPreparedStatement(stmt_name, false, false); if (ps != NULL && strcmp(ps->plansource->query_string, query_string) == 0) { goto pass_parsing; } else if (ps != NULL) { DropPreparedStatement(stmt_name, false); } } #endif /* * Switch to appropriate context for constructing parsetrees. * * We have two strategies depending on whether the prepared statement is * named or not. For a named prepared statement, we do parsing in * u_sess->parser_cxt.temp_parse_message_context and copy the finished trees * into the prepared statement's plancache entry; then the reset of * u_sess->parser_cxt.temp_parse_message_context temporary space used by * parsing and rewriting. For an unnamed prepared statement, we assume * the statement isn't going to hang around long, so getting rid of temp * space quickly is probably not worth the costs of copying parse trees. * So in this case, we create the plancache entry's query_context here, * and do all the parsing work therein. */ if (is_named) { /* Named prepared statement --- parse in u_sess->parser_cxt.temp_parse_message_context */ oldcontext = MemoryContextSwitchTo(u_sess->temp_mem_cxt); } else { /* Unnamed prepared statement --- release any prior unnamed stmt */ drop_unnamed_stmt(); /* Create context for parsing */ unnamed_stmt_context = AllocSetContextCreate(u_sess->temp_mem_cxt, "unnamed prepared statement", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); oldcontext = MemoryContextSwitchTo(unnamed_stmt_context); } #ifdef ENABLE_MULTIPLE_NODES /* * if we have the parameter types passed, which happens only in case of * connection from Coordinators, fill paramTypes with their OIDs for * subsequent use. We have to do name to OID conversion, in a transaction * context. */ if (IsConnFromCoord() && paramTypeNames) { int cnt_param; // It can't be commit/rollback xact if numParams > 0 // And if transaction is abort state, we should abort query // if (IsAbortedTransactionBlockState() && numParams > 0) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* we don't expect type mod */ for (cnt_param = 0; cnt_param < numParams; cnt_param++) parseTypeString(paramTypeNames[cnt_param], ¶mTypes[cnt_param], NULL); } #endif /* PGXC */ /* * Do basic parsing of the query or queries (this should be safe even if * we are in aborted transaction state!) */ parsetree_list = pg_parse_query(query_string); /* * We only allow a single user statement in a prepared statement. This is * mainly to keep the protocol simple --- otherwise we'd need to worry * about multiple result tupdescs and things like that. */ if (list_length(parsetree_list) > 1) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("cannot insert multiple commands into a prepared statement"))); if (parsetree_list != NIL) { Query* query = NULL; bool snapshot_set = false; int i; raw_parse_tree = (Node*)linitial(parsetree_list); /* * Get the command name for possible use in status display. */ commandTag = CreateCommandTag(raw_parse_tree); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ROLLBACK. It is important that this test occur before we * try to do parse analysis, rewrite, or planning, since all those * phases try to do database accesses, which may fail in abort state. * (It might be safe to allow some additional utility commands in this * state, but not many...) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(raw_parse_tree)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* * Read password status from pg_user_status. */ Oid current_user = GetUserId(); if (current_user != BOOTSTRAP_SUPERUSERID) { if (!IsAbortedTransactionBlockState() && GetAccountPasswordExpired(current_user) == EXPIRED_STATUS) { ForceModifyExpiredPwd((const char*)query_string, parsetree_list); } } /* * Create the CachedPlanSource before we do parse analysis, since it * needs to see the unmodified raw parse tree. */ #ifdef PGXC psrc = CreateCachedPlan(raw_parse_tree, query_string, stmt_name, commandTag); #else psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag); #endif /* initialized it to false first. */ psrc->single_shard_stmt = false; /* * Set up a snapshot if parse analysis will need one. */ if (analyze_requires_snapshot(raw_parse_tree)) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Analyze and rewrite the query. Note that the originally specified * parameter set is not required to be complete, so we have to use * parse_analyze_varparams(). */ if (u_sess->attr.attr_common.log_parser_stats) ResetUsage(); query = parse_analyze_varparams(raw_parse_tree, query_string, ¶mTypes, &numParams); #ifdef ENABLE_MOT /* check cross engine queries */ StorageEngineType storageEngineType = SE_TYPE_UNSPECIFIED; CheckTablesStorageEngine(query, &storageEngineType); SetCurrentTransactionStorageEngine(storageEngineType); /* set the plan's storage engine */ psrc->storageEngineType = storageEngineType; if (!IsTransactionExitStmt(raw_parse_tree) && storageEngineType == SE_TYPE_MIXED) { ereport(ERROR, (errcode(ERRCODE_FDW_CROSS_STORAGE_ENGINE_QUERY_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Cross storage engine query is not supported"))); } if (psrc->storageEngineType == SE_TYPE_MOT && !IS_PGXC_COORDINATOR && JitExec::IsMotCodegenEnabled()) { // MOT LLVM TryMotJitCodegenQuery(query_string, psrc, query); } /* gpc does not support MOT engine */ if (ENABLE_CN_GPC && psrc->gpc.status.IsSharePlan() && (psrc->storageEngineType == SE_TYPE_MOT || psrc->storageEngineType == SE_TYPE_MIXED)) { psrc->gpc.status.SetKind(GPC_UNSHARED); } if (!IsTransactionExitStmt(raw_parse_tree) && CheckMotIndexedColumnUpdate(query)) { ereport(ERROR, (errcode(ERRCODE_FDW_UPDATE_INDEXED_FIELD_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Update of indexed column is not supported for memory table"))); } #endif if (ENABLE_CN_GPC && psrc->gpc.status.IsSharePlan() && contains_temp_tables(query->rtable)) { /* temp table unsupport shared */ psrc->gpc.status.SetKind(GPC_UNSHARED); } /* * Check all parameter types got determined. */ for (i = 0; i < numParams; i++) { Oid ptype = paramTypes[i]; if (ptype == InvalidOid || ptype == UNKNOWNOID) ereport(ERROR, (errcode(ERRCODE_INDETERMINATE_DATATYPE), errmsg("could not determine data type of parameter $%d", i + 1))); } if (u_sess->attr.attr_common.log_parser_stats) ShowUsage("PARSE ANALYSIS STATISTICS"); #ifndef ENABLE_MULTIPLE_NODES /* store normalized uniquesQl text into Query in P phase of PBE, only if auto-cleanup is enabled */ if (is_unique_sql_enabled() && g_instance.attr.attr_common.enable_auto_clean_unique_sql) { query->unique_sql_text = FindCurrentUniqueSQL(); } #endif querytree_list = pg_rewrite_query(query); #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) { ListCell* lc = NULL; runOnSingleNode = u_sess->attr.attr_sql.enable_light_proxy && list_length(querytree_list) == 1; foreach (lc, querytree_list) { Query* cur_query = (Query*)lfirst(lc); if (runOnSingleNode) { if (ENABLE_ROUTER(cur_query->commandType)) { single_exec_node = lightProxy::checkRouterQuery(cur_query); } else { single_exec_node = lightProxy::checkLightQuery(cur_query); } CleanHotkeyCandidates(true); /* only deal with single node/param */ if (single_exec_node == NULL || list_length(single_exec_node->nodeList) + list_length(single_exec_node->primarynodelist) > 1) { runOnSingleNode = false; FreeExecNodes(&single_exec_node); } else { psrc->single_shard_stmt = true; is_read_only = (cur_query->commandType == CMD_SELECT && !cur_query->hasForUpdate); /* do here but not bind to only process once */ if (is_named) { (void)light_set_datanode_queries(stmt_name); } LPROXY_DEBUG(ereport(DEBUG2, (errmsg("[LIGHT PROXY] Got Parse slim: name %s, query %s", stmt_name, query_string)))); } } if (cur_query->sql_statement == NULL) cur_query->sql_statement = pstrdup(query_string); } } else if (IS_PGXC_DATANODE) { /* * Light proxy would send the parse request to the single DN, so if we are * doing PARSE at DN, then statement must be single shard */ psrc->single_shard_stmt = true; } #endif /* Done with the snapshot used for parsing */ if (snapshot_set) PopActiveSnapshot(); } else { /* Empty input string. This is legal. */ raw_parse_tree = NULL; commandTag = NULL; #ifdef PGXC psrc = CreateCachedPlan(raw_parse_tree, query_string, stmt_name, commandTag); #else psrc = CreateCachedPlan(raw_parse_tree, query_string, commandTag); #endif querytree_list = NIL; } /* * CachedPlanSource must be a direct child of u_sess->parser_cxt.temp_parse_message_context * before we reparent unnamed_stmt_context under it, else we have a disconnected circular * subgraph. Klugy, but less so than flipping contexts even more above. */ if (unnamed_stmt_context) MemoryContextSetParent(psrc->context, u_sess->temp_mem_cxt); /* Finish filling in the CachedPlanSource */ CompleteCachedPlan(psrc, querytree_list, unnamed_stmt_context, paramTypes, paramModes, numParams, NULL, NULL, 0, /* default cursor options */ true, stmt_name, single_exec_node, is_read_only); /* fixed result */ /* If we got a cancel signal during analysis, quit */ CHECK_FOR_INTERRUPTS(); if (is_named) { /* * Store the query as a prepared statement. */ StorePreparedStatement(stmt_name, psrc, false); } else { /* * We just save the CachedPlanSource into unnamed_stmt_psrc. */ SaveCachedPlan(psrc); u_sess->pcache_cxt.unnamed_stmt_psrc = psrc; } MemoryContextSwitchTo(oldcontext); pass_parsing: /* * We do NOT close the open transaction command here; that only happens * when the client sends Sync. Instead, do CommandCounterIncrement just * in case something happened during parse/plan. */ CommandCounterIncrement(); /* * Send ParseComplete. */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('1'); /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, false)) { case 1: Assert(false); break; case 2: { char* cur_mask_string = NULL; MASK_PASSWORD_START(cur_mask_string, query_string); ereport(LOG, (errmsg( "duration: %s ms queryid %ld unique id %ld parse %s: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, *stmt_name ? stmt_name : "", cur_mask_string), errhidestmt(true))); MASK_PASSWORD_END(cur_mask_string, query_string); break; } default: break; } if (save_log_statement_stats) ShowUsage("PARSE MESSAGE STATISTICS"); t_thrd.postgres_cxt.debug_query_string = NULL; gstrace_exit(GS_TRC_ID_exec_parse_message); } static int getSingleNodeIdx(StringInfo input_message, CachedPlanSource* psrc, const char* stmt_name) { ParamListInfo params = NULL; /* Get the parameter format codes */ int numPFormats = pq_getmsgint(input_message, 2); int16* pformats = NULL; bool snapshot_set = false; int idx = -1; if (numPFormats > 0) { int i; pformats = (int16*)palloc(numPFormats * sizeof(int16)); for (i = 0; i < numPFormats; i++) pformats[i] = pq_getmsgint(input_message, 2); } /* Get the parameter value count */ int numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); if (numParams != psrc->num_params) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d", numParams, stmt_name, psrc->num_params))); /* * If we are in aborted transaction state, the only portals we can * actually run are those containing COMMIT or ROLLBACK commands. We * disallow binding anything else to avoid problems with infrastructure * that expects to run inside a valid transaction. We also disallow * binding any parameters, since we can't risk calling user-defined I/O * functions. */ if (IsAbortedTransactionBlockState() && (!IsTransactionExitStmt(psrc->raw_parse_tree) || numParams != 0)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* * Set a snapshot if we have parameters to fetch (since the input * functions might need it) or the query isn't a utility command (and * hence could require redoing parse analysis and planning). We keep the * snapshot active till we're done, so that plancache.c doesn't have to * take new ones. */ #ifdef ENABLE_MOT if (!(psrc->storageEngineType == SE_TYPE_MOT) && !GTM_LITE_MODE && (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))) { #else if (!GTM_LITE_MODE && (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))) { #endif PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Fetch parameters, if any, and store in the portal's memory context. */ if (numParams > 0) { int paramno; params = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData)); /* we have static list of params, so no hooks needed */ params->paramFetch = NULL; params->paramFetchArg = NULL; params->parserSetup = NULL; params->parserSetupArg = NULL; params->params_need_process = false; params->numParams = numParams; for (paramno = 0; paramno < numParams; paramno++) { Oid ptype = psrc->param_types[paramno]; int32 plength; Datum pval; bool isNull = false; StringInfoData pbuf; char csave; int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); /* add null value process for date type */ if ((VARCHAROID == ptype || TIMESTAMPOID == ptype || TIMESTAMPTZOID == ptype || TIMEOID == ptype || TIMETZOID == ptype || INTERVALOID == ptype || SMALLDATETIMEOID == ptype) && 0 == plength && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) isNull = true; /* * Insert into bind values support illegal characters import, * and this just wroks for char type attribute. */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = IsCharType(ptype); if (!isNull) { const char* pvalue = pq_getmsgbytes(input_message, plength); /* * Rather than copying data around, we just set up a phony * StringInfo pointing to the correct portion of the message * buffer. We assume we can scribble on the message buffer so * as to maintain the convention that StringInfos have a * trailing null. This is grotty but is a big win when * dealing with very large parameter strings. */ pbuf.data = (char*)pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; } else { pbuf.data = NULL; /* keep compiler quiet */ csave = 0; } if (numPFormats > 1) { Assert(NULL != pformats); pformat = pformats[paramno]; } else if (numPFormats > 0) { Assert(NULL != pformats); pformat = pformats[0]; } else { pformat = 0; /* default = text */ } if (pformat == 0) { /* text mode */ Oid typinput; Oid typioparam; char* pstring = NULL; getTypeInputInfo(ptype, &typinput, &typioparam); /* * We have to do encoding conversion before calling the * typinput routine. */ if (isNull) pstring = NULL; else pstring = pg_client_to_server(pbuf.data, plength); pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); /* Free result of encoding conversion, if any */ if (pstring != NULL && pstring != pbuf.data) pfree(pstring); } else if (pformat == 1) { /* binary mode */ Oid typreceive; Oid typioparam; StringInfo bufptr; /* * Call the parameter type's binary input converter */ getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); if (isNull) bufptr = NULL; else bufptr = &pbuf; pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in bind parameter %d", paramno + 1))); } else { ereport( ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); pval = 0; /* keep compiler quiet */ } /* Restore message buffer contents */ if (!isNull) pbuf.data[plength] = csave; params->params[paramno].value = pval; params->params[paramno].isnull = isNull; /* * We mark the params as CONST. This ensures that any custom plan * makes full use of the parameter values. */ params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; params->params[paramno].tabInfo = NULL; /* Reset the compatible illegal chars import flag */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = false; } } /* Make sure the querytree list is valid and we have parse-time locks */ RevalidateCachedQuery(psrc); if (!psrc->single_exec_node) { ereport(LOG, (errmsg("[LIGHT PROXY] distribute key of table in cached plan is changed"))); if (snapshot_set) PopActiveSnapshot(); pfree_ext(pformats); pfree_ext(params); return -1; } /* Only when dynamically one datanode pushdwn is ok, we find single node */ if (pgxc_check_dynamic_param(psrc->single_exec_node->dynamic_en_expr, params)) idx = getSingleNodeIdx_internal(psrc->single_exec_node, params); /* Done with the snapshot used for parameter I/O and parsing/planning */ if (snapshot_set) PopActiveSnapshot(); pfree_ext(pformats); pfree_ext(params); return idx; } static ExecNodes* GetSingleNodeForEqualExpr(ExecNodes* execNodes, ParamListInfo params, bool* singleNodeFlag) { ExecNodes* singleNode = NULL; RelationLocInfo* relLocInfo = GetRelationLocInfo(execNodes->en_relid); if (relLocInfo == NULL) { ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("rel_loc_info is NULL."))); } int len = list_length(relLocInfo->partAttrNum); Datum* distcolValue = (Datum*)palloc(len * sizeof(Datum)); bool* distcolIsnull = (bool*)palloc(len * sizeof(bool)); Oid* distcolType = (Oid*)palloc(len * sizeof(Oid)); List* idxDist = NULL; int i = 0; ListCell* cell = NULL; *singleNodeFlag = true; foreach (cell, execNodes->en_expr) { Expr* distcolExpr = (Expr*)lfirst(cell); distcolExpr = (Expr*)eval_const_expressions_params(NULL, (Node*)distcolExpr, params); if (distcolExpr && IsA(distcolExpr, Const)) { Const* constExpr = (Const*)distcolExpr; distcolValue[i] = constExpr->constvalue; distcolIsnull[i] = constExpr->constisnull; distcolType[i] = constExpr->consttype; idxDist = lappend_int(idxDist, i); i++; } else { Assert(distcolExpr != NULL); ereport(LOG, (errmsg("[LIGHT PROXY] param of distribute key is not const"))); *singleNodeFlag = false; return NULL; } } singleNode = GetRelationNodes(relLocInfo, distcolValue, distcolIsnull, distcolType, idxDist, execNodes->accesstype); pfree(distcolValue); pfree(distcolIsnull); pfree(distcolType); list_free(idxDist); FreeRelationLocInfo(relLocInfo); return singleNode; } static int getSingleNodeIdx_internal(ExecNodes* exec_nodes, ParamListInfo params) { int idx = -1; ExecNodes* single_node = NULL; Assert(exec_nodes->nodeList != NIL || exec_nodes->en_expr != NIL); if (exec_nodes->en_expr != NIL) { if (exec_nodes->need_range_prune) { single_node = makeNode(ExecNodes); *single_node = *exec_nodes; PruningDatanode(single_node, params); } else { bool singleNodeFlag = false; single_node = GetSingleNodeForEqualExpr(exec_nodes, params, &singleNodeFlag); if (!singleNodeFlag) { return -1; } } } else { single_node = exec_nodes; } /* make sure it is one dn */ if (list_length(single_node->nodeList) != 1) { ereport(LOG, (errmsg("[LIGHT PROXY] nodelist is computed to be not single node"))); return -1; } idx = linitial_int(single_node->nodeList); return idx; } #ifdef ENABLE_MULTIPLE_NODES /* * exec_get_ddl_params * just get params info from CN */ #ifndef ENABLE_UT static void exec_get_ddl_params(StringInfo input_message) #else void exec_get_ddl_params(StringInfo input_message) #endif { int numPFormats; int16* pformats = NULL; int numParams; int numRFormats; int16* rformats = NULL; ParamListInfo params = NULL; if (unlikely(!((IS_PGXC_COORDINATOR || IS_PGXC_DATANODE) && IsConnFromCoord()))) { ereport(ERROR, (errcode(ERRCODE_IO_ERROR), errmsg("The current node should not receive z messages"))); } if (u_sess->top_transaction_mem_cxt == NULL) { ereport(ERROR, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), errmsg("current transaction is not start"))); } if (u_sess->parser_cxt.ddl_pbe_context == NULL) { u_sess->parser_cxt.ddl_pbe_context = AllocSetContextCreate(u_sess->top_mem_cxt, "DDLPBEContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); } MemoryContext old_context = MemoryContextSwitchTo(u_sess->parser_cxt.ddl_pbe_context); /* * Get the fixed part of the message, This information is not useful to us, but we still have to process it, * because the cursor will change during the processing of the information, otherwise the subsequent information * cannot be parsed. */ (void)pq_getmsgstring(input_message); (void)pq_getmsgstring(input_message); /* Get the parameter format codes */ numPFormats = pq_getmsgint(input_message, 2); if (numPFormats > 0) { int i; pformats = (int16*)palloc(numPFormats * sizeof(int16)); for (i = 0; i < numPFormats; i++) pformats[i] = pq_getmsgint(input_message, 2); } /* Get the parameter value count */ numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); if (numParams > 0) { int paramno; params = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData)); params->paramFetch = NULL; params->paramFetchArg = NULL; params->parserSetup = NULL; params->parserSetupArg = NULL; params->params_need_process = false; params->numParams = numParams; for (paramno = 0; paramno < numParams; paramno++) { Oid ptype = TEXTOID; int32 plength; Datum pval; bool isNull = false; StringInfoData pbuf; char csave; int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); if (!isNull) { const char* pvalue = pq_getmsgbytes(input_message, plength); pbuf.data = (char*)pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; } else { pbuf.data = NULL; /* keep compiler quiet */ csave = 0; } if (numPFormats > 1) { Assert(NULL != pformats); pformat = pformats[paramno]; } else if (numPFormats > 0) { Assert(NULL != pformats); pformat = pformats[0]; } else { pformat = 0; /* default = text */ } if (pformat == 0) /* text mode */ { Oid typinput; Oid typioparam; char* pstring = NULL; getTypeInputInfo(ptype, &typinput, &typioparam); pstring = isNull ? NULL : pg_client_to_server(pbuf.data, plength); pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); /* Free result of encoding conversion, if any */ if (pstring != NULL && pstring != pbuf.data) pfree(pstring); } else if (pformat == 1) /* binary mode */ { Oid typreceive; Oid typioparam; StringInfo bufptr; /* * Call the parameter type's binary input converter */ getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); bufptr = isNull ? NULL : &pbuf; pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in bind parameter %d", paramno + 1))); } else { ereport( ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); pval = 0; /* keep compiler quiet */ } /* Restore message buffer contents */ if (!isNull) pbuf.data[plength] = csave; params->params[paramno].value = pval; params->params[paramno].isnull = isNull; /* * We mark the params as CONST. This ensures that any custom plan * makes full use of the parameter values. */ params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; params->params[paramno].tabInfo = NULL; } } else { params = NULL; } u_sess->parser_cxt.param_info = (void*)params; MemoryContextSwitchTo(old_context); /* Get the result format codes */ numRFormats = pq_getmsgint(input_message, 2); if (numRFormats > 0) { int i; rformats = (int16*)palloc(numRFormats * sizeof(int16)); for (i = 0; i < numRFormats; i++) rformats[i] = pq_getmsgint(input_message, 2); } if (pformats != NULL) pfree_ext(pformats); if (rformats != NULL) pfree_ext(rformats); pq_getmsgend(input_message); pq_putemptymessage('z'); } #endif /* * exec_bind_message * * Process a "Bind" message to create a portal from a prepared statement */ static void exec_bind_message(StringInfo input_message) { const char* portal_name = NULL; const char* stmt_name = NULL; int numPFormats; int16* pformats = NULL; int numParams; int numRFormats; int16* rformats = NULL; CachedPlanSource* psrc = NULL; CachedPlan* cplan = NULL; Portal portal; char* query_string = NULL; char* saved_stmt_name = NULL; ParamListInfo params = NULL; MemoryContext oldContext; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; bool snapshot_set = false; char msec_str[PRINTF_DST_MAX]; u_sess->parser_cxt.param_info = NULL; u_sess->parser_cxt.param_message = NULL; StringInfo temp_message = makeStringInfo(); copyStringInfo(temp_message, input_message); gstrace_entry(GS_TRC_ID_exec_bind_message); /* Instrumentation: PBE - reset unique sql elapsed start time */ if ((IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && is_unique_sql_enabled()) u_sess->unique_sql_cxt.unique_sql_start_time = GetCurrentTimestamp(); /* Get the fixed part of the message */ portal_name = pq_getmsgstring(input_message); stmt_name = pq_getmsgstring(input_message); /* Check not NULL */ if (portal_name == NULL || stmt_name == NULL) { ereport(ERROR, (errcode(ERRCODE_DATA_CORRUPTED), errmsg("portal_name or stmt_name is null."))); } if (strlen(portal_name) > SECUREC_MEM_MAX_LEN || strlen(stmt_name) > SECUREC_MEM_MAX_LEN) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("Too long portal_name and stmt_name."))); /* * Only support normal perf mode for PBE, as DestRemoteExecute can not send T message automatically. */ t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; plpgsql_estate = NULL; u_sess->xact_cxt.pbe_execute_complete = true; if (SHOW_DEBUG_MESSAGE()) { ereport(DEBUG2, (errmsg("bind %s to %s", *portal_name ? portal_name : "", *stmt_name ? stmt_name : ""))); } /* Find prepared statement */ PreparedStatement *pstmt = NULL; if (stmt_name[0] != '\0') { if (ENABLE_DN_GPC) { psrc = u_sess->pcache_cxt.cur_stmt_psrc; if (SECUREC_UNLIKELY(psrc == NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("dn gpc's prepared statement %s does not exist", stmt_name))); } else { pstmt = FetchPreparedStatement(stmt_name, true, true); psrc = pstmt->plansource; } } else { /* special-case the unnamed statement */ psrc = u_sess->pcache_cxt.unnamed_stmt_psrc; if (SECUREC_UNLIKELY(psrc == NULL)) ereport( ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("unnamed prepared statement does not exist"))); } Assert(NULL != psrc); /* * Report query to various monitoring facilities. */ t_thrd.postgres_cxt.debug_query_string = psrc->query_string; pgstat_report_activity(STATE_RUNNING, psrc->query_string); instr_stmt_report_start_time(); set_ps_display("BIND", false); if (save_log_statement_stats) ResetUsage(); /* * Start up a transaction command so we can call functions etc. (Note that * this will normally change current memory context.) Nothing happens if * we are already in one. */ start_xact_command(); #ifdef ENABLE_MOT /* set transaction storage engine and check for cross transaction violation */ SetCurrentTransactionStorageEngine(psrc->storageEngineType); if (!IsTransactionExitStmt(psrc->raw_parse_tree) && IsMixedEngineUsed()) { ereport(ERROR, (errcode(ERRCODE_FDW_CROSS_STORAGE_ENGINE_TRANSACTION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Cross storage engine transaction is not supported"))); } /* block MOT engine queries in sub-transactions */ if (!IsTransactionExitStmt(psrc->raw_parse_tree) && IsMOTEngineUsedInParentTransaction() && IsMOTEngineUsed()) { ereport(ERROR, (errcode(ERRCODE_FDW_OPERATION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("SubTransaction is not supported for memory table"))); } if (IsTransactionPrepareStmt(psrc->raw_parse_tree) && (IsMOTEngineUsed() || IsMixedEngineUsed())) { /* Explicit prepare transaction is not supported for memory table */ ereport(ERROR, (errcode(ERRCODE_FDW_OPERATION_NOT_SUPPORTED), errmodule(MOD_MOT), errmsg("Explicit prepare transaction is not supported for memory table"))); } /* * MOT JIT Execution: * Assist in distinguishing query boundaries in case of range query when client uses batches. This allows us to * know a new query started, and in case a previous execution did not fetch all records (since user is working in * batch-mode, and can decide to quit fetching in the middle), using this information we can infer this is a new * scan, and old scan state should be discarded. */ if (psrc->mot_jit_context != NULL) { JitResetScan(psrc->mot_jit_context); } #endif /* set unique sql id to current context */ SetUniqueSQLIdFromCachedPlanSource(psrc); OpFusion::clearForCplan((OpFusion*)psrc->opFusionObj, psrc); if (psrc->opFusionObj != NULL) { Assert(psrc->cplan == NULL); (void)RevalidateCachedQuery(psrc); OpFusion *opFusionObj = (OpFusion *)(psrc->opFusionObj); if (opFusionObj != NULL) { if (opFusionObj->IsGlobal()) { opFusionObj = (OpFusion *)OpFusion::FusionFactory(opFusionObj->m_global->m_type, u_sess->cache_mem_cxt, psrc, NULL, params); Assert(opFusionObj != NULL); } opFusionObj->clean(); opFusionObj->updatePreAllocParamter(input_message); opFusionObj->setCurrentOpFusionObj(opFusionObj); opFusionObj->storeFusion(portal_name); CachedPlanSource* cps = opFusionObj->m_global->m_psrc; if (cps != NULL && cps->gplan) { setCachedPlanBucketId(cps->gplan, opFusionObj->m_local.m_params); } if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('2'); gstrace_exit(GS_TRC_ID_exec_bind_message); return; } } if (ENABLE_WORKLOAD_CONTROL && SqlIsValid(t_thrd.postgres_cxt.debug_query_string) && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && !IsConnFromCoord()) { u_sess->wlm_cxt->is_active_statements_reset = false; if (g_instance.wlm_cxt->dynamic_workload_inited) { dywlm_parallel_ready(t_thrd.postgres_cxt.debug_query_string); dywlm_client_max_reserve(); } else { WLMParctlReady(t_thrd.postgres_cxt.debug_query_string); WLMParctlReserve(PARCTL_GLOBAL); } } /* Switch back to message context */ MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); /* light proxy and not set fetch size */ if (psrc->single_exec_node && (unsigned int)input_message->len <= SECUREC_MEM_MAX_LEN) { /* save the cursor in case of error */ int msg_cursor = input_message->cursor; int nodeIdx = getSingleNodeIdx(input_message, psrc, stmt_name); if (nodeIdx != -1) { lightProxy* scn = NULL; bool enable_gpc = (ENABLE_CN_GPC && stmt_name[0] != '\0'); bool enable_unamed_gpc = psrc->gpc.status.InShareTable() && stmt_name[0] == '\0'; if (enable_gpc) scn = lightProxy::locateLpByStmtName(stmt_name); else scn = (lightProxy *)psrc->lightProxyObj; if (scn == NULL) { /* initialize session cache context; typically it won't store much */ MemoryContext context = AllocSetContextCreate(u_sess->cache_mem_cxt, "LightProxyMemory", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); scn = New(context)lightProxy(context, psrc, portal_name, stmt_name); if (enable_gpc) { scn->storeLpByStmtName(stmt_name); psrc->lightProxyObj = NULL; } else if (enable_unamed_gpc) { Assert(u_sess->pcache_cxt.unnamed_gpc_lp == NULL); Assert(psrc->lightProxyObj == NULL); u_sess->pcache_cxt.unnamed_gpc_lp = scn; } else { psrc->lightProxyObj = scn; } } else { Assert(!enable_unamed_gpc); if (portal_name[0] != '\0') { scn->storeLightProxy(portal_name); } } if (enable_gpc) { /* cngpc need fill gpc msg just like BuildCachedPlan. */ GPCFillMsgForLp(psrc); } /* set nodeidx to router node if in router */ if (HAS_ROUTER) { scn->m_nodeIdx = u_sess->exec_cxt.CurrentRouter->GetRouterNodeId(); } else { scn->m_nodeIdx = nodeIdx; } lightProxy::setCurrentProxy(scn); lightProxy::processMsg(BIND_MESSAGE, input_message); LPROXY_DEBUG(ereport(DEBUG2, (errmsg("[LIGHT PROXY] Got Bind slim: name %s, query %s", psrc->stmt_name, psrc->query_string)))); gstrace_exit(GS_TRC_ID_exec_bind_message); return; } else { /* gpc's shared plan used lightproxy before but cannot go through in current sessioin because of param */ if (unlikely(psrc->gpc.status.InShareTable())) { psrc->gpc.status.SetStatus(GPC_INVALID); Assert (pstmt != NULL); g_instance.plan_cache->RecreateCachePlan(psrc, stmt_name, pstmt, NULL, NULL, true); psrc = pstmt->plansource; } /* fail to get node idx */ ExecNodes* tmp_en = psrc->single_exec_node; psrc->single_exec_node = NULL; lightProxy::setCurrentProxy(NULL); input_message->cursor = msg_cursor; if (tmp_en != NULL) FreeExecNodes(&tmp_en); lightProxy* lp = NULL; if (ENABLE_CN_GPC && stmt_name[0] != '\0') lp = lightProxy::locateLpByStmtName(stmt_name); else lp = (lightProxy *)psrc->lightProxyObj; /* clean lightProxyObj if exists */ if (lp != NULL) { lightProxy::tearDown(lp); psrc->lightProxyObj = NULL; } } } else /* it may be not NULL if last time report error */ lightProxy::setCurrentProxy(NULL); /* Get the parameter format codes */ numPFormats = pq_getmsgint(input_message, 2); if (numPFormats > 0) { int i; pformats = (int16*)palloc(numPFormats * sizeof(int16)); for (i = 0; i < numPFormats; i++) pformats[i] = pq_getmsgint(input_message, 2); } /* Get the parameter value count */ numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); if (numParams != psrc->num_params) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d", numParams, stmt_name, psrc->num_params))); /* * If we are in aborted transaction state, the only portals we can * actually run are those containing COMMIT or ROLLBACK commands. We * disallow binding anything else to avoid problems with infrastructure * that expects to run inside a valid transaction. We also disallow * binding any parameters, since we can't risk calling user-defined I/O * functions. */ if (IsAbortedTransactionBlockState() && (!IsTransactionExitStmt(psrc->raw_parse_tree) || numParams != 0)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* * Create the portal. Allow silent replacement of an existing portal only * if the unnamed portal is specified. */ if (portal_name[0] == '\0') portal = CreatePortal(portal_name, true, true); else portal = CreatePortal(portal_name, false, false); /* * Prepare to copy stuff into the portal's memory context. We do all this * copying first, because it could possibly fail (out-of-memory) and we * don't want a failure to occur between GetCachedPlan and * PortalDefineQuery; that would result in leaking our plancache refcount. */ oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); /* Version control for DDL PBE */ if (t_thrd.proc->workingVersionNum >= DDL_PBE_VERSION_NUM) { u_sess->parser_cxt.param_message = makeStringInfo(); copyStringInfo(u_sess->parser_cxt.param_message, temp_message); } if (temp_message != NULL) { if (temp_message->data != NULL) pfree_ext(temp_message->data); pfree_ext(temp_message); } /* Copy the plan's query string into the portal */ query_string = pstrdup(psrc->query_string); /* Likewise make a copy of the statement name, unless it's unnamed */ if (stmt_name[0]) saved_stmt_name = pstrdup(stmt_name); else saved_stmt_name = NULL; /* * Set a snapshot if we have parameters to fetch (since the input * functions might need it) or the query isn't a utility command (and * hence could require redoing parse analysis and planning). We keep the * snapshot active till we're done, so that plancache.c doesn't have to * take new ones. */ #ifdef ENABLE_MOT if (!(psrc->storageEngineType == SE_TYPE_MOT) && (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))) { #else if (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree)) { #endif PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Fetch parameters, if any, and store in the portal's memory context. */ if (numParams > 0) { int paramno; params = (ParamListInfo)palloc(offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData)); /* we have static list of params, so no hooks needed */ params->paramFetch = NULL; params->paramFetchArg = NULL; params->parserSetup = NULL; params->parserSetupArg = NULL; params->params_need_process = false; params->numParams = numParams; for (paramno = 0; paramno < numParams; paramno++) { Oid ptype = psrc->param_types[paramno]; #ifndef ENABLE_MULTIPLE_NODES char* pmode = NULL; if (psrc->param_modes != NULL) { pmode = &(psrc->param_modes[paramno]); } #endif int32 plength; Datum pval; bool isNull = false; StringInfoData pbuf; char csave; int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); /* add null value process for date type */ if ((VARCHAROID == ptype || TIMESTAMPOID == ptype || TIMESTAMPTZOID == ptype || TIMEOID == ptype || TIMETZOID == ptype || INTERVALOID == ptype || SMALLDATETIMEOID == ptype) && 0 == plength && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) isNull = true; /* * Insert into bind values support illegal characters import, * and this just wroks for char type attribute. */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = IsCharType(ptype); if (!isNull) { const char* pvalue = pq_getmsgbytes(input_message, plength); /* * Rather than copying data around, we just set up a phony * StringInfo pointing to the correct portion of the message * buffer. We assume we can scribble on the message buffer so * as to maintain the convention that StringInfos have a * trailing null. This is grotty but is a big win when * dealing with very large parameter strings. */ pbuf.data = (char*)pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; } else { pbuf.data = NULL; /* keep compiler quiet */ csave = 0; } if (numPFormats > 1) { Assert(NULL != pformats); pformat = pformats[paramno]; } else if (numPFormats > 0) { Assert(NULL != pformats); pformat = pformats[0]; } else { pformat = 0; /* default = text */ } if (pformat == 0) { /* text mode */ Oid typinput; Oid typioparam; char* pstring = NULL; getTypeInputInfo(ptype, &typinput, &typioparam); /* * We have to do encoding conversion before calling the * typinput routine. */ if (isNull) pstring = NULL; else pstring = pg_client_to_server(pbuf.data, plength); #ifndef ENABLE_MULTIPLE_NODES if (pmode == NULL || *pmode != PROARGMODE_OUT || !enable_out_param_override()) { pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); } else { pval = (Datum)0; } #else pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); #endif /* Free result of encoding conversion, if any */ if (pstring != NULL && pstring != pbuf.data) pfree(pstring); } else if (pformat == 1) { /* binary mode */ Oid typreceive; Oid typioparam; StringInfo bufptr; /* * Call the parameter type's binary input converter */ getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); if (isNull) bufptr = NULL; else bufptr = &pbuf; #ifndef ENABLE_MULTIPLE_NODES if (pmode == NULL || *pmode != PROARGMODE_OUT || !enable_out_param_override()) { pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); } else { pval = (Datum)0; } #else pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); #endif /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in bind parameter %d", paramno + 1))); } else { ereport( ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); pval = 0; /* keep compiler quiet */ } /* Restore message buffer contents */ if (!isNull) { pbuf.data[plength] = csave; } params->params[paramno].value = pval; #ifndef ENABLE_MULTIPLE_NODES isNull = isNull || (enable_out_param_override() && pmode != NULL && *pmode == PROARGMODE_OUT); #endif params->params[paramno].isnull = isNull; /* * We mark the params as CONST. This ensures that any custom plan * makes full use of the parameter values. */ params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; params->params[paramno].tabInfo = NULL; /* Reset the compatible illegal chars import flag */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = false; } } else { params = NULL; } /* u_sess->parser_cxt.param_info is for ddl pbe. If the logic is not ddl pbe, It will not be used*/ if (t_thrd.proc->workingVersionNum >= DDL_PBE_VERSION_NUM) { u_sess->parser_cxt.param_info = (void*)params; } /* Done storing stuff in portal's context */ MemoryContextSwitchTo(oldContext); /* Get the result format codes */ numRFormats = pq_getmsgint(input_message, 2); if (numRFormats > 0) { int i; rformats = (int16*)palloc(numRFormats * sizeof(int16)); for (i = 0; i < numRFormats; i++) rformats[i] = pq_getmsgint(input_message, 2); } pq_getmsgend(input_message); /* * 'create table as select' is divided into 'create table' and 'insert into select', * and 'create table' is executed in sql rewrite, which will be called in parse and bind * both, when we use jdbc to execute 'create table as'. So when bind is executed, * an error 'table already exists' will raise. table_created_in_CTAS is to solve this. */ t_thrd.postgres_cxt.table_created_in_CTAS = true; /* * Obtain a plan from the CachedPlanSource. Any cruft from (re)planning * will be generated in t_thrd.mem_cxt.msg_mem_cxt. The plan refcount will be * assigned to the Portal, so it will be released at portal destruction. */ cplan = GetCachedPlan(psrc, params, false); t_thrd.postgres_cxt.table_created_in_CTAS = false; /* * copy the single_shard info from plan source into plan. * With this, we can determine if we should use global snapshot or local snapshot after. */ cplan->single_shard_stmt = psrc->single_shard_stmt; /* * Now we can define the portal. * * DO NOT put any code that could possibly throw an error between the * above GetCachedPlan call and here. */ PortalDefineQuery(portal, saved_stmt_name, query_string, psrc->commandTag, cplan->stmt_list, cplan); if (IS_PGXC_DATANODE && psrc->cplan == NULL && !psrc->gpc.status.InShareTable() && psrc->is_checked_opfusion == false) { psrc->opFusionObj = OpFusion::FusionFactory(OpFusion::getFusionType(cplan, params, NULL), u_sess->cache_mem_cxt, psrc, NULL, params); psrc->is_checked_opfusion = true; if (psrc->opFusionObj != NULL) { ((OpFusion*)psrc->opFusionObj)->clean(); ((OpFusion*)psrc->opFusionObj)->useOuterParameter(params); ((OpFusion*)psrc->opFusionObj)->setCurrentOpFusionObj((OpFusion*)psrc->opFusionObj); ((OpFusion*)psrc->opFusionObj)->CopyFormats(rformats, numRFormats); ((OpFusion*)psrc->opFusionObj)->storeFusion(portal_name); if (snapshot_set) PopActiveSnapshot(); if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('2'); gstrace_exit(GS_TRC_ID_exec_bind_message); return; } } if (ENABLE_GPC && psrc->gplan) { portal->stmts = CopyLocalStmt(cplan->stmt_list, u_sess->top_portal_cxt, &portal->copyCxt); } /* Done with the snapshot used for parameter I/O and parsing/planning */ if (snapshot_set) PopActiveSnapshot(); /* * And we're ready to start portal execution. */ PortalStart(portal, params, 0, InvalidSnapshot); /* * Apply the result format requests to the portal. */ PortalSetResultFormat(portal, numRFormats, rformats); if (u_sess->attr.attr_resource.use_workload_manager && g_instance.wlm_cxt->gscgroup_init_done && !IsAbortedTransactionBlockState()) { u_sess->wlm_cxt->cgroup_last_stmt = u_sess->wlm_cxt->cgroup_stmt; u_sess->wlm_cxt->cgroup_stmt = WLMIsSpecialCommand(psrc->raw_parse_tree, portal); } /* * Send BindComplete. */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('2'); /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, false)) { case 1: Assert(false); break; case 2: { char* mask_string = NULL; MASK_PASSWORD_START(mask_string, psrc->query_string); ereport(LOG, (errmsg("duration: %s ms queryid %ld unique id %ld bind %s%s%s: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, *stmt_name ? stmt_name : "", *portal_name ? "/" : "", *portal_name ? portal_name : "", mask_string), errhidestmt(true), errdetail_params(params))); MASK_PASSWORD_END(mask_string, psrc->query_string); break; } default: break; } if (save_log_statement_stats) ShowUsage("BIND MESSAGE STATISTICS"); t_thrd.postgres_cxt.debug_query_string = NULL; gstrace_exit(GS_TRC_ID_exec_bind_message); } /* * exec_execute_message * * Process an "Execute" message for a portal */ static void exec_execute_message(const char* portal_name, long max_rows) { CommandDest dest; DestReceiver* receiver = NULL; Portal portal; bool completed = false; char completionTag[COMPLETION_TAG_BUFSIZE]; const char* sourceText = NULL; const char* prepStmtName = NULL; ParamListInfo portalParams; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; bool is_xact_command = false; bool execute_is_fetch = false; bool was_logged = false; char msec_str[PRINTF_DST_MAX]; gstrace_entry(GS_TRC_ID_exec_execute_message); /* Adjust destination to tell printtup.c what to do */ dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; if (dest == DestRemote) dest = DestRemoteExecute; /* * Only support normal perf mode for PBE, as DestRemoteExecute can not send T message automatically. */ t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; portal = GetPortalByName(portal_name); if (!PortalIsValid(portal)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("portal \"%s\" does not exist", portal_name))); /* * If the original query was a null string, just return * EmptyQueryResponse. */ if (portal->commandTag == NULL) { if (portal->stmts != NIL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("stmts is not NULL"))); } NullCommand(dest); if (ENABLE_WORKLOAD_CONTROL) { if (g_instance.wlm_cxt->dynamic_workload_inited) { dywlm_client_max_release(&t_thrd.wlm_cxt.parctl_state); } else { WLMParctlRelease(&t_thrd.wlm_cxt.parctl_state); } } gstrace_exit(GS_TRC_ID_exec_execute_message); return; } #ifndef ENABLE_MULTIPLE_NODES portal->streamInfo.AttachToSession(); if (u_sess->stream_cxt.global_obj != NULL) { StreamTopConsumerIam(); } #endif /* Does the portal contain a transaction command? */ is_xact_command = IsTransactionStmtList(portal->stmts); /* * We must copy the sourceText and prepStmtName into t_thrd.mem_cxt.msg_mem_cxt in * case the portal is destroyed during finish_xact_command. Can avoid the * copy if it's not an xact command, though. */ sourceText = pstrdup(portal->sourceText); if (is_xact_command) { if (portal->prepStmtName) prepStmtName = pstrdup(portal->prepStmtName); else prepStmtName = ""; /* * An xact command shouldn't have any parameters, which is a good * thing because they wouldn't be around after finish_xact_command. */ portalParams = NULL; } else { if (portal->prepStmtName) prepStmtName = portal->prepStmtName; else prepStmtName = ""; portalParams = portal->portalParams; } /* * Report query to various monitoring facilities. */ t_thrd.postgres_cxt.debug_query_string = sourceText; pgstat_report_activity(STATE_RUNNING, sourceText); set_ps_display(portal->commandTag, false); if (save_log_statement_stats) ResetUsage(); BeginCommand(portal->commandTag, dest); /* * Create dest receiver in t_thrd.mem_cxt.msg_mem_cxt (we don't want it in transaction * context, because that may get deleted if portal contains VACUUM). */ receiver = CreateDestReceiver(dest); if (dest == DestRemoteExecute) SetRemoteDestReceiverParams(receiver, portal); /* * Ensure we are in a transaction command (this should normally be the * case already due to prior BIND). */ start_xact_command(); /* set unique sql id */ SetUniqueSQLIdFromPortal(portal, u_sess->pcache_cxt.unnamed_stmt_psrc); /* * If we re-issue an Execute protocol request against an existing portal, * then we are only fetching more rows rather than completely re-executing * the query from the start. atStart is never reset for a v3 portal, so we * are safe to use this check. */ execute_is_fetch = !portal->atStart; /* Log immediately if dictated by log_statement */ if (check_log_statement(portal->stmts)) { char* mask_string = NULL; MASK_PASSWORD_START(mask_string, sourceText); ereport(LOG, (errmsg("%s %s%s%s: %s", execute_is_fetch ? _("execute fetch from") : _("execute"), prepStmtName, *portal_name ? "/" : "", *portal_name ? portal_name : "", mask_string), errhidestmt(true), errdetail_params(portalParams))); MASK_PASSWORD_END(mask_string, sourceText); was_logged = true; } /* * If we are in aborted transaction state, the only portals we can * actually run are those containing COMMIT or ROLLBACK commands. */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmtList(portal->stmts)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); /* * Okay to run the portal. */ if (max_rows <= 0) max_rows = FETCH_ALL; bool savedisAllowCommitRollback = false; savedisAllowCommitRollback = stp_enable_and_get_old_xact_stmt_state(); completed = PortalRun(portal, max_rows, true, /* always top level */ receiver, receiver, completionTag); // for fetch size, free stringinfo each time if (!completed && dest == DestRemoteExecute) { DR_printtup* myState = (DR_printtup*)receiver; StringInfo buf = &myState->buf; pfree_ext(buf->data); } stp_retore_old_xact_stmt_state(savedisAllowCommitRollback); (*receiver->rDestroy)(receiver); if (completed) { if (is_xact_command) { /* * If this was a transaction control statement, commit it. We * will start a new xact command for the next command (if any). */ finish_xact_command(); } else { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. */ CommandCounterIncrement(); } /* Send appropriate CommandComplete to client */ EndCommand(completionTag, dest); u_sess->xact_cxt.pbe_execute_complete = true; } else { /* Portal run not complete, so send PortalSuspended */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('s'); u_sess->xact_cxt.pbe_execute_complete = false; } if (ENABLE_WORKLOAD_CONTROL) { if (g_instance.wlm_cxt->dynamic_workload_inited) { if (t_thrd.wlm_cxt.parctl_state.simple == 0) dywlm_client_release(&t_thrd.wlm_cxt.parctl_state); else WLMReleaseGroupActiveStatement(); dywlm_client_max_release(&t_thrd.wlm_cxt.parctl_state); } else WLMParctlRelease(&t_thrd.wlm_cxt.parctl_state); } /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, was_logged)) { case 1: ereport(LOG, (errmsg("duration: %s ms queryid %ld unique id %ld", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id), errhidestmt(true))); break; case 2: { char* mask_string = NULL; MASK_PASSWORD_START(mask_string, sourceText); ereport(LOG, (errmsg("duration: %s ms queryid %ld unique id %ld %s %s%s%s: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, execute_is_fetch ? _("execute fetch from") : _("execute"), prepStmtName, *portal_name ? "/" : "", *portal_name ? portal_name : "", mask_string), errhidestmt(true), errdetail_params(portalParams))); MASK_PASSWORD_END(mask_string, sourceText); break; } default: break; } if (MEMORY_TRACKING_QUERY_PEAK) ereport(LOG, (errmsg("execute portal %s, peak memory %ld(kb)", sourceText, (int64)(t_thrd.utils_cxt.peakedBytesInQueryLifeCycle/1024)))); if (save_log_statement_stats) ShowUsage("EXECUTE MESSAGE STATISTICS"); t_thrd.postgres_cxt.debug_query_string = NULL; gstrace_exit(GS_TRC_ID_exec_execute_message); } /* * check_log_statement * Determine whether command should be logged because of log_statement * * parsetree_list can be either raw grammar output or a list of planned * statements */ static bool check_log_statement(List* stmt_list) { ListCell* stmt_item = NULL; if (u_sess->attr.attr_common.log_statement == LOGSTMT_NONE) return false; if (u_sess->attr.attr_common.log_statement == LOGSTMT_ALL) return true; /* Else we have to inspect the statement(s) to see whether to log */ foreach (stmt_item, stmt_list) { Node* stmt = (Node*)lfirst(stmt_item); if (GetCommandLogLevel(stmt) <= u_sess->attr.attr_common.log_statement) return true; } return false; } /* * check_log_duration * Determine whether current command's duration should be logged * * Returns: * 0 if no logging is needed * 1 if just the duration should be logged * 2 if duration and query details should be logged * * If logging is needed, the duration in msec is formatted into msec_str[], * which must be a 32-byte buffer. * * was_logged should be TRUE if caller already logged query details (this * essentially prevents 2 from being returned). */ int check_log_duration(char* msec_str, bool was_logged) { if (u_sess->attr.attr_sql.log_duration || u_sess->attr.attr_storage.log_min_duration_statement >= 0) { long secs; int usecs; int msecs; bool exceeded = false; u_sess->slow_query_cxt.slow_query.debug_query_sql_id = u_sess->debug_query_id; TimestampDifference(GetCurrentStatementLocalStartTimestamp(), GetCurrentTimestamp(), &secs, &usecs); msecs = usecs / 1000; /* * This odd-looking test for log_min_duration_statement being exceeded * is designed to avoid integer overflow with very long durations: * don't compute secs * 1000 until we've verified it will fit in int. */ exceeded = (u_sess->attr.attr_storage.log_min_duration_statement == 0 || (u_sess->attr.attr_storage.log_min_duration_statement > 0 && (secs > u_sess->attr.attr_storage.log_min_duration_statement / 1000 || secs * 1000 + msecs >= u_sess->attr.attr_storage.log_min_duration_statement))); /* * Only record the time, which is larger than log_min_duration_statement. * This condition can reduce the impactation on performance. */ if (exceeded) { if (u_sess->attr.attr_sql.log_duration) { errno_t rc = snprintf_s(msec_str, PRINTF_DST_MAX, PRINTF_DST_MAX - 1, "%ld.%03d", secs * 1000 + msecs, usecs % 1000); securec_check_ss(rc, "", ""); if (exceeded && !was_logged) { return 2; } else { return 1; } } } } msec_str[0] = '\0'; return 0; } /* * errdetail_execute * * Add an errdetail() line showing the query referenced by an EXECUTE, if any. * The argument is the raw parsetree list. */ static int errdetail_execute(List* raw_parsetree_list) { ListCell* parsetree_item = NULL; foreach (parsetree_item, raw_parsetree_list) { Node* parsetree = (Node*)lfirst(parsetree_item); if (IsA(parsetree, ExecuteStmt)) { ExecuteStmt* stmt = (ExecuteStmt*)parsetree; PreparedStatement *pstmt = NULL; pstmt = FetchPreparedStatement(stmt->name, false, false); if (pstmt != NULL) { errdetail("prepare: %s", pstmt->plansource->query_string); return 0; } } } return 0; } /* * errdetail_params * * Add an errdetail() line showing bind-parameter data, if available. */ static int errdetail_params(ParamListInfo params) { /* We mustn't call user-defined I/O functions when in an aborted xact */ if (params && params->numParams > 0 && !IsAbortedTransactionBlockState()) { StringInfoData param_str; MemoryContext oldcontext; int paramno; /* Make sure any trash is generated in t_thrd.mem_cxt.msg_mem_cxt */ oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); initStringInfo(¶m_str); for (paramno = 0; paramno < params->numParams; paramno++) { ParamExternData* prm = ¶ms->params[paramno]; Oid typoutput; bool typisvarlena = false; char* pstring = NULL; char* p = NULL; appendStringInfo(¶m_str, "%s$%d = ", (paramno > 0) ? ", " : "", paramno + 1); if (prm->isnull || !OidIsValid(prm->ptype)) { appendStringInfoString(¶m_str, "NULL"); continue; } getTypeOutputInfo(prm->ptype, &typoutput, &typisvarlena); pstring = OidOutputFunctionCall(typoutput, prm->value); appendStringInfoCharMacro(¶m_str, '\''); for (p = pstring; *p; p++) { if (*p == '\'') /* double single quotes */ appendStringInfoCharMacro(¶m_str, *p); appendStringInfoCharMacro(¶m_str, *p); } appendStringInfoCharMacro(¶m_str, '\''); pfree(pstring); } errdetail("parameters: %s", param_str.data); pfree(param_str.data); MemoryContextSwitchTo(oldcontext); } return 0; } /* * errdetail_abort * * Add an errdetail() line showing abort reason, if any. */ int errdetail_abort(void) { if (t_thrd.proc->recoveryConflictPending) errdetail("abort reason: recovery conflict"); return 0; } /* * errdetail_recovery_conflict * * Add an errdetail() line showing conflict source. */ static int errdetail_recovery_conflict(void) { switch (t_thrd.postgres_cxt.RecoveryConflictReason) { case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: errdetail("User was holding shared buffer pin for too long."); break; case PROCSIG_RECOVERY_CONFLICT_LOCK: errdetail("User was holding a relation lock for too long."); break; case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: errdetail("User was or might have been using tablespace that must be dropped."); break; case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: errdetail("User query might have needed to see row versions that must be removed."); break; case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: errdetail("User transaction caused buffer deadlock with recovery."); break; case PROCSIG_RECOVERY_CONFLICT_DATABASE: errdetail("User was connected to a database that must be dropped."); break; default: break; /* no errdetail */ } return 0; } /* * exec_describe_statement_message * * Process a "Describe" message for a prepared statement */ #ifndef ENABLE_UT static void exec_describe_statement_message(const char* stmt_name) #else void exec_describe_statement_message(const char* stmt_name) #endif { CachedPlanSource* psrc = NULL; int i; /* * Start up a transaction command. (Note that this will normally change * current memory context.) Nothing happens if we are already in one. */ start_xact_command(); /* Switch back to message context */ MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); /* Find prepared statement */ if (stmt_name[0] != '\0') { if (ENABLE_DN_GPC) { psrc = u_sess->pcache_cxt.cur_stmt_psrc; if (SECUREC_UNLIKELY(psrc == NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("dn gpc's prepared statement %s does not exist", stmt_name))); } else { PreparedStatement *pstmt = NULL; pstmt = FetchPreparedStatement(stmt_name, true, true); psrc = pstmt->plansource; } } else { /* special-case the unnamed statement */ psrc = u_sess->pcache_cxt.unnamed_stmt_psrc; if (psrc == NULL) ereport( ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("unnamed prepared statement does not exist"))); } Assert(NULL != psrc); /* Prepared statements shouldn't have changeable result descs */ Assert(psrc->fixed_result); #ifdef ENABLE_MOT /* set current transaction storage engine */ SetCurrentTransactionStorageEngine(psrc->storageEngineType); #endif /* * If we are in aborted transaction state, we can't run * SendRowDescriptionMessage(), because that needs catalog accesses. * Hence, refuse to Describe statements that return data. (We shouldn't * just refuse all Describes, since that might break the ability of some * clients to issue COMMIT or ROLLBACK commands, if they use code that * blindly Describes whatever it does.) We can Describe parameters * without doing anything dangerous, so we don't restrict that. */ if (IsAbortedTransactionBlockState() && psrc->resultDesc != NULL) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); if (t_thrd.postgres_cxt.whereToSendOutput != DestRemote) return; /* can't actually do anything... */ /* * First describe the parameters... */ pq_beginmessage_reuse(&(*t_thrd.postgres_cxt.row_description_buf), 't'); /* parameter description message type */ pq_sendint16(&(*t_thrd.postgres_cxt.row_description_buf), psrc->num_params); for (i = 0; i < psrc->num_params; i++) { Oid ptype = psrc->param_types[i]; pq_sendint32(&(*t_thrd.postgres_cxt.row_description_buf), (int)ptype); } pq_endmessage_reuse(&(*t_thrd.postgres_cxt.row_description_buf)); /* * Next send RowDescription or NoData to describe the result... */ if (psrc->resultDesc) { List* tlist = NIL; /* Get the plan's primary targetlist */ tlist = CachedPlanGetTargetList(psrc); SendRowDescriptionMessage(&(*t_thrd.postgres_cxt.row_description_buf), psrc->resultDesc, tlist, NULL); } else pq_putemptymessage('n'); /* NoData */ } /* * exec_describe_portal_message * * Process a "Describe" message for a portal */ static void exec_describe_portal_message(const char* portal_name) { Portal portal; /* * Start up a transaction command. (Note that this will normally change * current memory context.) Nothing happens if we are already in one. */ start_xact_command(); /* Switch back to message context */ MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); portal = GetPortalByName(portal_name); if (!PortalIsValid(portal)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_CURSOR), errmsg("portal \"%s\" does not exist", portal_name))); Assert(NULL != portal); /* * If we are in aborted transaction state, we can't run * SendRowDescriptionMessage(), because that needs catalog accesses. * Hence, refuse to Describe portals that return data. (We shouldn't just * refuse all Describes, since that might break the ability of some * clients to issue COMMIT or ROLLBACK commands, if they use code that * blindly Describes whatever it does.) */ if (IsAbortedTransactionBlockState() && portal->tupDesc) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); if (t_thrd.postgres_cxt.whereToSendOutput != DestRemote) return; /* can't actually do anything... */ if (portal->tupDesc) SendRowDescriptionMessage(&(*t_thrd.postgres_cxt.row_description_buf), portal->tupDesc, FetchPortalTargetList(portal), portal->formats); else pq_putemptymessage('n'); /* NoData */ } /* * Convenience routines for starting/committing a single command. */ void start_xact_command(void) { if (!t_thrd.postgres_cxt.xact_started) { if (SHOW_DEBUG_MESSAGE()) { ereport(DEBUG3, (errmsg_internal("StartTransactionCommand"))); } StartTransactionCommand(); /* Set statement timeout running, if any */ /* NB: this mustn't be enabled until we are within an xact */ if (u_sess->attr.attr_common.StatementTimeout > 0) enable_sig_alarm(u_sess->attr.attr_common.StatementTimeout, true); else t_thrd.storage_cxt.cancel_from_timeout = false; t_thrd.postgres_cxt.xact_started = true; } } void finish_xact_command(void) { if (t_thrd.postgres_cxt.xact_started) { /* Cancel any active statement timeout before committing */ disable_sig_alarm(true); /* Now commit the command */ if (SHOW_DEBUG_MESSAGE()) { ereport(DEBUG3, (errmsg_internal("CommitTransactionCommand"))); } CommitTransactionCommand(); #ifdef MEMORY_CONTEXT_CHECKING /* Check all memory contexts that weren't freed during commit */ /* (those that were, were checked before being deleted) */ MemoryContextCheck(t_thrd.top_mem_cxt, false); if (IS_THREAD_POOL_WORKER && u_sess && u_sess->session_id > 0) MemoryContextCheck(u_sess->top_mem_cxt, true); #endif #ifdef SHOW_MEMORY_STATS /* Print mem stats after each commit for leak tracking */ MemoryContextStats(t_thrd.top_mem_cxt); #endif t_thrd.postgres_cxt.xact_started = false; } } /* * Convenience routines for checking whether a statement is one of the * ones that we allow in transaction-aborted state. */ /* Test a bare parsetree */ static bool IsTransactionExitStmt(Node* parsetree) { if (parsetree && IsA(parsetree, TransactionStmt)) { TransactionStmt* stmt = (TransactionStmt*)parsetree; if (stmt->kind == TRANS_STMT_COMMIT || stmt->kind == TRANS_STMT_PREPARE || stmt->kind == TRANS_STMT_ROLLBACK || stmt->kind == TRANS_STMT_ROLLBACK_TO) return true; } return false; } /* Test a list that might contain Query nodes or bare parsetrees */ static bool IsTransactionExitStmtList(List* parseTrees) { if (list_length(parseTrees) == 1) { Node* stmt = (Node*)linitial(parseTrees); if (IsA(stmt, Query)) { Query* query = (Query*)stmt; if (query->commandType == CMD_UTILITY && IsTransactionExitStmt(query->utilityStmt)) return true; } else if (IsTransactionExitStmt(stmt)) return true; } return false; } /* Test a list that might contain Query nodes or bare parsetrees */ static bool IsTransactionStmtList(List* parseTrees) { if (list_length(parseTrees) == 1) { Node* stmt = (Node*)linitial(parseTrees); if (IsA(stmt, Query)) { Query* query = (Query*)stmt; if (query->commandType == CMD_UTILITY && IsA(query->utilityStmt, TransactionStmt)) return true; } else if (IsA(stmt, TransactionStmt)) return true; } return false; } #ifdef ENABLE_MOT static bool IsTransactionPrepareStmt(const Node* parsetree) { if (parsetree && IsA(parsetree, TransactionStmt)) { TransactionStmt* stmt = (TransactionStmt*)parsetree; if (stmt->kind == TRANS_STMT_PREPARE) { return true; } } return false; } #endif /* Release any existing unnamed prepared statement */ static void drop_unnamed_stmt(void) { /* paranoia to avoid a dangling pointer in case of error */ if (u_sess->pcache_cxt.unnamed_stmt_psrc) { CachedPlanSource* psrc = u_sess->pcache_cxt.unnamed_stmt_psrc; u_sess->pcache_cxt.unnamed_stmt_psrc = NULL; if (psrc->gpc.status.InShareTable()) { /* drop unnamed lightproxy */ if (u_sess->pcache_cxt.unnamed_gpc_lp) { lightProxy* lp = u_sess->pcache_cxt.unnamed_gpc_lp; u_sess->pcache_cxt.unnamed_gpc_lp = NULL; Assert(lp->m_cplan == psrc); Assert(lp->m_stmtName == NULL || lp->m_stmtName[0] == '\0'); if (lp->m_portalName == NULL || lightProxy::locateLightProxy(lp->m_portalName) == NULL) { lightProxy::tearDown(lp); } } GPC_LOG("delete shared unnamed plansource", psrc, psrc->stmt_name); psrc->gpc.status.SubRefCount(); } else { Assert(u_sess->pcache_cxt.unnamed_gpc_lp == NULL); if (ENABLE_GPC) { GPC_LOG("drop private plansource", psrc, psrc->stmt_name); } DropCachedPlan(psrc); if (ENABLE_GPC) { GPC_LOG("drop private plansource end", psrc, 0); } } } } /* -------------------------------- * signal handler routines used in PostgresMain() * -------------------------------- */ /* * quickdie() occurs when signalled SIGQUIT by the postmaster. * * Some backend has bought the farm, * so we need to stop what we're doing and exit. */ void quickdie(SIGNAL_ARGS) { if (t_thrd.int_cxt.ignoreBackendSignal) { return; } sigaddset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); /* prevent nested calls */ gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); /* * If we're aborting out of client auth, don't risk trying to send * anything to the client; we will likely violate the protocol, not to * mention that we may have interrupted the guts of OpenSSL or some * authentication library. */ if (u_sess->ClientAuthInProgress && t_thrd.postgres_cxt.whereToSendOutput == DestRemote) t_thrd.postgres_cxt.whereToSendOutput = DestNone; /* * Ideally this should be ereport(FATAL), but then we'd not get control * back... */ ereport(WARNING, (errcode(ERRCODE_CRASH_SHUTDOWN), errmsg("terminating connection because of crash of another server process"), errdetail("The postmaster has commanded this server process to roll back" " the current transaction and exit, because another" " server process exited abnormally and possibly corrupted" " shared memory."), errhint("In a moment you should be able to reconnect to the" " database and repeat your command."))); /* * We DO NOT want to run proc_exit() callbacks -- we're here because * shared memory may be corrupted, so we don't want to try to clean up our * transaction. Just nail the windows shut and get out of town. Now that * there's an atexit callback to prevent third-party code from breaking * things by calling exit() directly, we have to reset the callbacks * explicitly to make this work as intended. */ on_exit_reset(); /* * Note we do exit(2) not exit(0). This is to force the postmaster into a * system reset cycle if some idiot DBA sends a manual SIGQUIT to a random * backend. This is necessary precisely because we don't clean up our * shared memory state. (The "dead man switch" mechanism in pmsignal.c * should ensure the postmaster sees this as a crash, too, but no harm in * being doubly sure.) */ exit(2); } /* * Shutdown signal from postmaster: abort transaction and exit * at soonest convenient time */ void die(SIGNAL_ARGS) { if (t_thrd.int_cxt.ignoreBackendSignal) { return; } int save_errno = errno; /* Don't joggle the elbow of proc_exit */ if (!t_thrd.proc_cxt.proc_exit_inprogress) { instrSnapshotCancel(); InterruptPending = true; t_thrd.int_cxt.ProcDiePending = true; /* Set flag to handle SIGUSER2 and SIGTERM during connection */ if (t_thrd.proc_cxt.pooler_connection_inprogress) { g_pq_interrupt_happened = true; } /* * in libcomm interrupt is not allow, * gs_r_cancel will signal libcomm and * libcomm will then check for interrupt. */ gs_r_cancel(); /* * If it's safe to interrupt, and we're waiting for input or a lock, * service the interrupt immediately */ if (t_thrd.int_cxt.ImmediateInterruptOK && t_thrd.int_cxt.InterruptHoldoffCount == 0 && t_thrd.int_cxt.CritSectionCount == 0) { /* bump holdoff count to make ProcessInterrupts() a no-op */ /* until we are done getting ready for it */ t_thrd.int_cxt.InterruptHoldoffCount++; LockErrorCleanup(); /* prevent CheckDeadLock from running */ t_thrd.int_cxt.InterruptHoldoffCount--; ProcessInterrupts(); } } /* If we're still here, waken anything waiting on the process latch */ if (t_thrd.proc) SetLatch(&t_thrd.proc->procLatch); errno = save_errno; } /* * Query-cancel signal from postmaster: abort current transaction * at soonest convenient time */ void StatementCancelHandler(SIGNAL_ARGS) { if (t_thrd.int_cxt.ignoreBackendSignal) { return; } int save_errno = errno; /* * Don't joggle the elbow of proc_exit */ if (!t_thrd.proc_cxt.proc_exit_inprogress) { InterruptPending = true; t_thrd.int_cxt.QueryCancelPending = true; /* * in libcomm interrupt is not allow, * gs_r_cancel will signal libcomm and * libcomm will then check for interrupt. */ gs_r_cancel(); /* * If it's safe to interrupt, and we're waiting for input or a lock, * service the interrupt immediately */ if (t_thrd.int_cxt.ImmediateInterruptOK && t_thrd.int_cxt.InterruptHoldoffCount == 0 && t_thrd.int_cxt.CritSectionCount == 0) { /* bump holdoff count to make ProcessInterrupts() a no-op */ /* until we are done getting ready for it */ t_thrd.int_cxt.InterruptHoldoffCount++; LockErrorCleanup(); /* prevent CheckDeadLock from running */ t_thrd.int_cxt.InterruptHoldoffCount--; ProcessInterrupts(); } } /* If we're still here, waken anything waiting on the process latch */ if (t_thrd.proc) SetLatch(&t_thrd.proc->procLatch); errno = save_errno; } /* * Query-cancel signal from pg_pool_validate: abort current transaction * at soonest convenient time */ void PoolValidateCancelHandler(SIGNAL_ARGS) { #ifdef ENABLE_MULTIPLE_NODES if (SignalCancelGTMConnection()) { /* If it is going to cancel GTM connection, return immediately. */ return; } #endif int save_errno = errno; t_thrd.int_cxt.InterruptByCN = true; /* * Don't joggle the elbow of proc_exit */ if (!t_thrd.proc_cxt.proc_exit_inprogress) { InterruptPending = true; t_thrd.int_cxt.QueryCancelPending = true; t_thrd.int_cxt.PoolValidateCancelPending = true; /* Set flag to handle SIGUSER2 and SIGTERM during connection */ if (t_thrd.proc_cxt.pooler_connection_inprogress) { g_pq_interrupt_happened = true; } /* * in libcomm interrupt is not allow, * gs_r_cancel will signal libcomm and * libcomm will then check for interrupt. */ gs_r_cancel(); /* * If it's safe to interrupt, and we're waiting for input or a lock, * service the interrupt immediately */ if (t_thrd.int_cxt.ImmediateInterruptOK && t_thrd.int_cxt.InterruptHoldoffCount == 0 && t_thrd.int_cxt.CritSectionCount == 0) { /* bump holdoff count to make ProcessInterrupts() a no-op */ /* until we are done getting ready for it */ t_thrd.int_cxt.InterruptHoldoffCount++; LockErrorCleanup(); /* prevent CheckDeadLock from running */ t_thrd.int_cxt.InterruptHoldoffCount--; ProcessInterrupts(); } } /* If we're still here, waken anything waiting on the process latch */ if (t_thrd.proc) SetLatch(&t_thrd.proc->procLatch); errno = save_errno; } /* signal handler for floating point exception */ void FloatExceptionHandler(SIGNAL_ARGS) { Assert(0); /* We're not returning, so no need to save errno */ ereport(ERROR, (errcode(ERRCODE_FLOATING_POINT_EXCEPTION), errmsg("floating-point exception"), errdetail("An invalid floating-point operation was signaled. " "This probably means an out-of-range result or an " "invalid operation, such as division by zero."))); } /* SIGHUP: set flag to re-read config file at next convenient time */ static void SigHupHandler(SIGNAL_ARGS) { if (IS_THREAD_POOL_WORKER) { return; } int save_errno = errno; t_thrd.int_cxt.InterruptByCN = true; u_sess->sig_cxt.got_SIGHUP = true; if (t_thrd.proc) SetLatch(&t_thrd.proc->procLatch); errno = save_errno; } // HandlePoolerReload // set the flag got_PoolReload, and do pooler reload in the main loop. // void HandlePoolerReload(void) { /* A Datanode has no pooler active, so do not bother about that */ if (IS_PGXC_DATANODE) return; ResetGotPoolReload(true); u_sess->sig_cxt.cp_PoolReload = true; } // HandleMemoryContextDump // call memory dump function directly in aset.cpp to dump all memory info. // void HandleMemoryContextDump(void) { #ifdef MEMORY_CONTEXT_CHECKING DumpMemoryContext(STANDARD_DUMP); #endif } void HandleExecutorFlag(void) { if (IS_PGXC_DATANODE) u_sess->exec_cxt.executorStopFlag = true; } /* * RecoveryConflictInterrupt: out-of-line portion of recovery conflict * handling following receipt of SIGUSR1. Designed to be similar to die() * and StatementCancelHandler(). Called only by a normal user backend * that begins a transaction during recovery. */ void RecoveryConflictInterrupt(ProcSignalReason reason) { int save_errno = errno; /* * Don't joggle the elbow of proc_exit */ if (!t_thrd.proc_cxt.proc_exit_inprogress) { t_thrd.postgres_cxt.RecoveryConflictReason = reason; switch (reason) { case PROCSIG_RECOVERY_CONFLICT_STARTUP_DEADLOCK: /* * If we aren't waiting for a lock we can never deadlock. */ if (!IsWaitingForLock()) return; /* Intentional drop through to check wait for pin */ /* fall through */ case PROCSIG_RECOVERY_CONFLICT_BUFFERPIN: /* * If we aren't blocking the Startup process there is nothing * more to do. */ if (!HoldingBufferPinThatDelaysRecovery()) return; t_thrd.proc->recoveryConflictPending = true; /* Intentional drop through to error handling */ /* fall through */ case PROCSIG_RECOVERY_CONFLICT_LOCK: case PROCSIG_RECOVERY_CONFLICT_TABLESPACE: case PROCSIG_RECOVERY_CONFLICT_SNAPSHOT: /* * If we aren't in a transaction any longer then ignore. */ if (!IsTransactionOrTransactionBlock()) return; /* * If we can abort just the current subtransaction then we are * OK to throw an ERROR to resolve the conflict. Otherwise * drop through to the FATAL case. * * XXX other times that we can throw just an ERROR *may* be * PROCSIG_RECOVERY_CONFLICT_LOCK if no locks are held in * parent transactions * * PROCSIG_RECOVERY_CONFLICT_SNAPSHOT if no snapshots are held * by parent transactions and the transaction is not * transaction-snapshot mode * * PROCSIG_RECOVERY_CONFLICT_TABLESPACE if no temp files or * cursors open in parent transactions */ if (!IsSubTransaction()) { /* * If we already aborted then we no longer need to cancel. * We do this here since we do not wish to ignore aborted * subtransactions, which must cause FATAL, currently. */ if (IsAbortedTransactionBlockState()) return; t_thrd.postgres_cxt.RecoveryConflictPending = true; t_thrd.int_cxt.QueryCancelPending = true; InterruptPending = true; break; } /* Intentional drop through to session cancel */ /* fall through */ case PROCSIG_RECOVERY_CONFLICT_DATABASE: t_thrd.postgres_cxt.RecoveryConflictPending = true; t_thrd.int_cxt.ProcDiePending = true; InterruptPending = true; break; default: ereport(FATAL, (errmsg("unrecognized conflict mode: %d", (int)reason))); break; } Assert(t_thrd.postgres_cxt.RecoveryConflictPending && (t_thrd.int_cxt.QueryCancelPending || t_thrd.int_cxt.ProcDiePending)); /* * All conflicts apart from database cause dynamic errors where the * command or transaction can be retried at a later point with some * potential for success. No need to reset this, since non-retryable * conflict errors are currently FATAL. */ if (reason == PROCSIG_RECOVERY_CONFLICT_DATABASE) t_thrd.postgres_cxt.RecoveryConflictRetryable = false; /* * If it's safe to interrupt, and we're waiting for input or a lock, * service the interrupt immediately */ if (t_thrd.int_cxt.ImmediateInterruptOK && t_thrd.int_cxt.InterruptHoldoffCount == 0 && t_thrd.int_cxt.CritSectionCount == 0) { /* bump holdoff count to make ProcessInterrupts() a no-op */ /* until we are done getting ready for it */ t_thrd.int_cxt.InterruptHoldoffCount++; LockErrorCleanup(); /* prevent CheckDeadLock from running */ t_thrd.int_cxt.InterruptHoldoffCount--; ProcessInterrupts(); } } /* * Set the process latch. This function essentially emulates signal * handlers like die() and StatementCancelHandler() and it seems prudent * to behave similarly as they do. Alternatively all plain backend code * waiting on that latch, expecting to get interrupted by query cancels et * al., would also need to set set_latch_on_sigusr1. */ if (t_thrd.proc) SetLatch(&t_thrd.proc->procLatch); errno = save_errno; } static void PrintWaitEvent() { volatile PgBackendStatus* beentry = t_thrd.shemem_ptr_cxt.MyBEEntry; if (beentry && beentry->st_waitevent != 0) { uint32 raw_wait_event = beentry->st_waitevent; const char* waitStatus = pgstat_get_waitstatusdesc(raw_wait_event); const char* waitEvent = pgstat_get_wait_event(raw_wait_event); ereport(LOG, (errmodule(MOD_PGSTAT), errmsg("Received interrupt signal, current wait status:%s, wait event:%s.", waitStatus, waitEvent))); } } /* * ProcessInterrupts: out-of-line portion of CHECK_FOR_INTERRUPTS() macro * * If an interrupt condition is pending, and it's safe to service it, * then clear the flag and accept the interrupt. Called only when * InterruptPending is true. */ void ProcessInterrupts(void) { /* OK to accept interrupt now? */ if (t_thrd.int_cxt.InterruptHoldoffCount != 0 || t_thrd.int_cxt.CritSectionCount != 0) return; if (t_thrd.bn && ((unsigned int)(t_thrd.bn->flag) & THRD_SIGTERM)) { t_thrd.int_cxt.ProcDiePending = true; t_thrd.bn->flag = ((unsigned int)(t_thrd.bn->flag)) & ~THRD_SIGTERM; } // The 'u_sess->stream_cxt.in_waiting_quit' flag is set to true to enable signal handling when waiting sub stream // threads quit. At the same time, if we get a SIGTERM signal, this signal should be held and the 'InterruptPending' // flag should not be set to false immediately. After all sub thread quit and the top consumer goes back to // ReadCommand again, the pending interrupt can be safely handled in function prepare_for_client_read. // if (t_thrd.int_cxt.ProcDiePending && u_sess->stream_cxt.in_waiting_quit) { // It's more efficient to notify all stream threads to cancel the query first // and then top consumer can quit quickly. // StreamNodeGroup::cancelStreamThread(); return; } if (StreamThreadAmI() && u_sess->debug_query_id == 0) { Assert(0); } InterruptPending = false; if (t_thrd.wlm_cxt.wlmalarm_pending) { t_thrd.wlm_cxt.wlmalarm_pending = false; (void)WLMProcessWorkloadManager(); } if (t_thrd.int_cxt.ProcDiePending && !u_sess->stream_cxt.in_waiting_quit) { t_thrd.int_cxt.ProcDiePending = false; t_thrd.int_cxt.QueryCancelPending = false; /* ProcDie trumps QueryCancel */ t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ t_thrd.int_cxt.ClientConnectionLost = false; /* omit ClientConnectionLost, otherwise it will be rehandle in proc_exit */ if (u_sess->stream_cxt.global_obj != NULL) u_sess->stream_cxt.global_obj->signalStreamThreadInNodeGroup(SIGTERM); if (u_sess->ClientAuthInProgress) { if (t_thrd.storage_cxt.cancel_from_timeout) { force_backtrace_messages = true; } else if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) { t_thrd.postgres_cxt.whereToSendOutput = DestNone; } } if (IsAutoVacuumWorkerProcess()) { ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating autovacuum process due to administrator command"))); #ifndef ENABLE_MULTIPLE_NODES } else if (IsLogicalLauncher()) { ereport(DEBUG1, (errmsg("logical replication launcher shutting down"))); /* The logical replication launcher can be stopped at any time. */ proc_exit(0); } else if (IsLogicalWorker()) { ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating logical replication worker due to administrator command"))); #endif } else if (IsTxnSnapCapturerProcess()) { ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating txnsnapcapturer process due to administrator command"))); } else if (IsTxnSnapWorkerProcess()) { ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating txnsnapcapturer worker process due to administrator command"))); } #ifdef ENABLE_MULTIPLE_NODES else if (IsRedistributionWorkerProcess()) ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating data redistribution process due to administrator command"))); #endif else if (t_thrd.postgres_cxt.RecoveryConflictPending && t_thrd.postgres_cxt.RecoveryConflictRetryable) { pgstat_report_recovery_conflict(t_thrd.postgres_cxt.RecoveryConflictReason); ereport(FATAL, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("terminating connection due to conflict with recovery"), errdetail_recovery_conflict())); } else if (t_thrd.postgres_cxt.RecoveryConflictPending) { /* Currently there is only one non-retryable recovery conflict */ Assert(t_thrd.postgres_cxt.RecoveryConflictReason == PROCSIG_RECOVERY_CONFLICT_DATABASE); pgstat_report_recovery_conflict(t_thrd.postgres_cxt.RecoveryConflictReason); ereport(FATAL, (errcode(ERRCODE_DATABASE_DROPPED), errmsg("terminating connection due to conflict with recovery"), errdetail_recovery_conflict())); } else if (IsJobSnapshotProcess()) { pgstat_report_activity(STATE_IDLE, NULL); ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating snapshot process due to administrator command"))); } else ereport(FATAL, (errcode(ERRCODE_ADMIN_SHUTDOWN), errmsg("terminating connection due to administrator command"))); } if (t_thrd.int_cxt.ClientConnectionLost && !u_sess->stream_cxt.in_waiting_quit) { t_thrd.int_cxt.QueryCancelPending = false; /* lost connection trumps QueryCancel */ t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ /* don't send to client, we already know the connection to be dead. */ t_thrd.postgres_cxt.whereToSendOutput = DestNone; if (StreamThreadAmI() == false && !WLMProcessExiting) { // for thread pool, it's too waste to shutdown worker for session close. if (IS_THREAD_POOL_WORKER && t_thrd.threadpool_cxt.worker) { t_thrd.int_cxt.ClientConnectionLost = false; t_thrd.threadpool_cxt.reaper_dead_session = true; ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("detach and reaper session from thread due to session connection lost"))); } else // if in the stream thread, we do not abort transaction, // it means the consumer thread has close the fd, so we got connection lost // error,if we abort transaction here, it may result a consistency transaction // status. // Neither we can set u_sess->exec_cxt.executor_stop_flag to true for now, as it only means // some consumer has close the fd, not all of the consumer // we can only set this if we are sure all the consumer has gone. // we need further optimize the logic here ereport(FATAL, (errcode(ERRCODE_CONNECTION_FAILURE), errmsg("connection to client lost"))); } } if (t_thrd.int_cxt.QueryCancelPending) { int pool_validate_cancel_pending = t_thrd.int_cxt.PoolValidateCancelPending; t_thrd.int_cxt.QueryCancelPending = false; t_thrd.int_cxt.PoolValidateCancelPending = false; PrintWaitEvent(); if (u_sess->ClientAuthInProgress) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ if (t_thrd.storage_cxt.cancel_from_timeout) { force_backtrace_messages = true; ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("terminate because authentication timeout(%ds)", u_sess->attr.attr_network.PoolerConnectTimeout))); } else { if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) { t_thrd.postgres_cxt.whereToSendOutput = DestNone; } ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("terminate because cancel interrupts"))); } } if (t_thrd.storage_cxt.cancel_from_timeout || u_sess->wlm_cxt->cancel_from_wlm || u_sess->wlm_cxt->cancel_from_space_limit || u_sess->wlm_cxt->cancel_from_defaultXact_readOnly || g_pq_interrupt_happened) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ StreamNodeGroup::cancelStreamThread(); char* str = NULL; int err_code = ERRCODE_QUERY_CANCELED; if (t_thrd.storage_cxt.cancel_from_timeout) { if (IsAutoVacuumWorkerProcess()) str = "autovacuum task timeout"; else if (IsTxnSnapCapturerProcess()) { str = "txnsnapcapturer task timeout"; } else if (IsTxnSnapWorkerProcess()) { str = "txnsnapworker task timeout"; } else str = "statement timeout"; } else if (u_sess->wlm_cxt->cancel_from_wlm) { str = "workload manager exception"; u_sess->wlm_cxt->cancel_from_wlm = false; } else if (u_sess->wlm_cxt->cancel_from_space_limit) { str = "space limit exceed"; u_sess->wlm_cxt->cancel_from_space_limit = false; } else if (u_sess->wlm_cxt->cancel_from_defaultXact_readOnly) { str = "default_transaction_read_only is on."; u_sess->wlm_cxt->cancel_from_defaultXact_readOnly = false; } else if (g_pq_interrupt_happened) { str = "connecting interrupted by pool validation."; g_pq_interrupt_happened = false; err_code = ERRCODE_CONNECTION_FAILURE; } CancelAutoAnalyze(); lightProxy::setCurrentProxy(NULL); if (!u_sess->stream_cxt.in_waiting_quit) { if (t_thrd.wlm_cxt.collect_info->sdetail.msg) ereport(ERROR, (errcode(err_code), errmsg("canceling statement due to %s.%s", str, t_thrd.wlm_cxt.collect_info->sdetail.msg))); else ereport(ERROR, (errcode(err_code), errmsg("canceling statement due to %s", str))); } } if (IsAutoVacuumWorkerProcess()) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling autovacuum task"))); } if (IsTxnSnapCapturerProcess()) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling txnsnapcapturer task"))); } if (IsTxnSnapWorkerProcess()) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling txnsnapworker task"))); } #ifdef ENABLE_MULTIPLE_NODES if (IsRedistributionWorkerProcess()) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling data redistribution task"))); } #endif if (t_thrd.postgres_cxt.RecoveryConflictPending && !u_sess->stream_cxt.in_waiting_quit) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ t_thrd.postgres_cxt.RecoveryConflictPending = false; pgstat_report_recovery_conflict(t_thrd.postgres_cxt.RecoveryConflictReason); if (t_thrd.postgres_cxt.DoingCommandRead) ereport(FATAL, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("terminating connection due to conflict with recovery"), errdetail_recovery_conflict(), errhint("In a moment you should be able to reconnect to the" " database and repeat your command."))); else ereport(ERROR, (errcode(ERRCODE_T_R_SERIALIZATION_FAILURE), errmsg("canceling statement due to conflict with recovery"), errdetail_recovery_conflict())); } /* * If we are reading a command from the client, just ignore the cancel * request --- sending an extra error message won't accomplish * anything. Otherwise, go ahead and throw the error. */ if (!t_thrd.postgres_cxt.DoingCommandRead) { t_thrd.int_cxt.ImmediateInterruptOK = false; /* not idle anymore */ StreamNodeGroup::cancelStreamThread(); CancelAutoAnalyze(); lightProxy::setCurrentProxy(NULL); if (!u_sess->stream_cxt.in_waiting_quit) { /* * User direct cancel and error cancel are both driven by Coordinator. Internal cancel * (including canceling due to coordinator request and canceling child stream thread) * can be ignored in Coordinator. So we distinguish it from user request one by setting * different error code. * * After pg_pool_validate, we should use ERRCODE_CONNECTION_EXCEPTION error code to * realize cn retry. */ /* If a single node, always user request */ bool is_datanode = IS_PGXC_DATANODE && !IS_SINGLE_NODE; int error_code = is_datanode ? ERRCODE_QUERY_INTERNAL_CANCEL : ERRCODE_QUERY_CANCELED; if (pool_validate_cancel_pending && IS_PGXC_COORDINATOR) { ereport(ERROR, (errcode(ERRCODE_CONNECTION_EXCEPTION), errmsg("canceling statement due to failover, pending"))); } else { ereport(ERROR, (errcode(error_code), errmsg("canceling statement due to %s request", is_datanode ? "coordinator" : "user"))); } } } else if (pool_validate_cancel_pending && IS_PGXC_COORDINATOR) { InterruptPending = true; t_thrd.int_cxt.QueryCancelPending = true; t_thrd.int_cxt.PoolValidateCancelPending = true; ereport(WARNING, (errmsg("thread receive SIGUSR2 signal but can not INTERRUPT while in DoingCommandRead. " "Set validate and interrupt flag for checking next time."))); } if (IsJobSnapshotProcess()) { ereport(ERROR, (errcode(ERRCODE_QUERY_CANCELED), errmsg("canceling snapshot task"))); } } /* If we get here, do nothing (probably, t_thrd.int_cxt.QueryCancelPending was reset) */ } /* * IA64-specific code to fetch the AR.BSP register for stack depth checks. * * We currently support gcc, icc, and HP-UX inline assembly here. */ #if defined(__ia64__) || defined(__ia64) #if defined(__hpux) && !defined(__GNUC__) && !defined __INTEL_COMPILER #include #define ia64_get_bsp() ((char*)(_Asm_mov_from_ar(_AREG_BSP, _NO_FENCE))) #else #ifdef __INTEL_COMPILER #include #endif static __inline__ char* ia64_get_bsp(void) { char* ret = NULL; #ifndef __INTEL_COMPILER /* the ;; is a "stop", seems to be required before fetching BSP */ __asm__ __volatile__(";;\n" " mov %0=ar.bsp \n" : "=r"(ret)); #else ret = (char*)__getReg(_IA64_REG_AR_BSP); #endif return ret; } #endif #endif /* IA64 */ /* * set_stack_base: set up reference point for stack depth checking * * Returns the old reference point, if any. */ pg_stack_base_t set_stack_base(void) { char stack_base; pg_stack_base_t old; #if defined(__ia64__) || defined(__ia64) old.t_thrd.postgres_cxt.stack_base_ptr = t_thrd.postgres_cxt.stack_base_ptr; old.register_stack_base_ptr = register_stack_base_ptr; #else old = t_thrd.postgres_cxt.stack_base_ptr; #endif /* Set up reference point for stack depth checking */ t_thrd.postgres_cxt.stack_base_ptr = &stack_base; #if defined(__ia64__) || defined(__ia64) register_stack_base_ptr = ia64_get_bsp(); #endif return old; } /* * restore_stack_base: restore reference point for stack depth checking * * This can be used after set_stack_base() to restore the old value. This * is currently only used in PL/Java. When PL/Java calls a backend function * from different thread, the thread's stack is at a different location than * the main thread's stack, so it sets the base pointer before the call, and * restores it afterwards. */ void restore_stack_base(pg_stack_base_t base) { #if defined(__ia64__) || defined(__ia64) t_thrd.postgres_cxt.stack_base_ptr = base.t_thrd.postgres_cxt.stack_base_ptr; register_stack_base_ptr = base.register_stack_base_ptr; #else t_thrd.postgres_cxt.stack_base_ptr = base; #endif } /* * check_stack_depth/stack_is_too_deep: check for excessively deep recursion * * This should be called someplace in any recursive routine that might possibly * recurse deep enough to overflow the stack. Most Unixen treat stack * overflow as an unrecoverable SIGSEGV, so we want to error out ourselves * before hitting the hardware limit. * * check_stack_depth() just throws an error summarily. stack_is_too_deep() * can be used by code that wants to handle the error condition itself. */ void check_stack_depth(void) { if (stack_is_too_deep()) { ereport(ERROR, (errcode(ERRCODE_STATEMENT_TOO_COMPLEX), errmsg("stack depth limit exceeded"), errhint("Increase the configuration parameter \"max_stack_depth\" (currently %dkB), " "after ensuring the platform's stack depth limit is adequate.", u_sess->attr.attr_common.max_stack_depth))); } } bool stack_is_too_deep(void) { char stack_top_loc; long stack_depth; /* * Compute distance from reference point to my local variables */ stack_depth = (long)(t_thrd.postgres_cxt.stack_base_ptr - &stack_top_loc); /* * Take abs value, since stacks grow up on some machines, down on others */ if (stack_depth < 0) { stack_depth = -stack_depth; } /* * Trouble? * * The test on stack_base_ptr prevents us from erroring out if called * during process setup or in a non-backend process. Logically it should * be done first, but putting it here avoids wasting cycles during normal * cases. */ if (stack_depth > t_thrd.postgres_cxt.max_stack_depth_bytes && t_thrd.postgres_cxt.stack_base_ptr != NULL) return true; /* * On IA64 there is a separate "register" stack that requires its own * independent check. For this, we have to measure the change in the * "BSP" pointer from PostgresMain to here. Logic is just as above, * except that we know IA64's register stack grows up. * * Note we assume that the same max_stack_depth applies to both stacks. */ #if defined(__ia64__) || defined(__ia64) stack_depth = (long)(ia64_get_bsp() - register_stack_base_ptr); if (stack_depth > t_thrd.postgres_cxt.max_stack_depth_bytes && register_stack_base_ptr != NULL) return true; #endif /* IA64 */ return false; } /* GUC check hook for max_stack_depth */ bool check_max_stack_depth(int* newval, void** extra, GucSource source) { long newval_bytes = *newval * 1024L; long stack_rlimit = get_stack_depth_rlimit(); if (stack_rlimit > 0 && newval_bytes > stack_rlimit - STACK_DEPTH_SLOP) { GUC_check_errdetail("\"max_stack_depth\" must not exceed %ldkB.", (stack_rlimit - STACK_DEPTH_SLOP) / 1024L); GUC_check_errhint("Increase the platform's stack depth limit via \"ulimit -s\" or local equivalent."); return false; } return true; } /* GUC assign hook for max_stack_depth */ void assign_max_stack_depth(int newval, void* extra) { long newval_bytes = newval * 1024L; t_thrd.postgres_cxt.max_stack_depth_bytes = newval_bytes; } /* * set_debug_options --- apply "-d N" command line option * * -d is not quite the same as setting log_min_messages because it enables * other output options. */ void set_debug_options(int debug_flag, GucContext context, GucSource source) { int rcs = 0; if (debug_flag > 0) { char debugstr[PRINFT_DST_MAX_DOUBLE]; rcs = snprintf_s(debugstr, PRINFT_DST_MAX_DOUBLE, PRINFT_DST_MAX_DOUBLE - 1, "debug%d", debug_flag); securec_check_ss(rcs, "\0", "\0"); SetConfigOption("log_min_messages", debugstr, context, source); } else { SetConfigOption("log_min_messages", "notice", context, source); } if (debug_flag >= 1 && context == PGC_POSTMASTER) { SetConfigOption("log_connections", "true", context, source); SetConfigOption("log_disconnections", "true", context, source); } if (debug_flag >= 2) SetConfigOption("log_statement", "all", context, source); if (debug_flag >= 3) SetConfigOption("debug_print_parse", "true", context, source); if (debug_flag >= 4) SetConfigOption("debug_print_plan", "true", context, source); if (debug_flag >= 5) SetConfigOption("debug_print_rewritten", "true", context, source); } bool set_plan_disabling_options(const char* arg, GucContext context, GucSource source) { const char* tmp = NULL; switch (arg[0]) { case 's': /* seqscan */ tmp = "enable_seqscan"; break; case 'i': /* indexscan */ tmp = "enable_indexscan"; break; case 'o': /* indexonlyscan */ tmp = "enable_indexonlyscan"; break; case 'b': /* bitmapscan */ tmp = "enable_bitmapscan"; break; case 't': /* tidscan */ tmp = "enable_tidscan"; break; case 'n': /* nestloop */ tmp = "enable_nestloop"; break; case 'm': /* mergejoin */ tmp = "enable_mergejoin"; break; case 'h': /* hashjoin */ tmp = "enable_hashjoin"; break; default: break; } if (tmp != NULL) { SetConfigOption(tmp, "false", context, source); return true; } else return false; } const char* get_stats_option_name(const char* arg) { switch (arg[0]) { case 'p': if (arg[1] == 'a') { /* "parser" */ return "log_parser_stats"; } else if (arg[1] == 'l') { /* "planner" */ return "log_planner_stats"; } break; case 'e': /* "executor" */ return "log_executor_stats"; default: break; } return NULL; } /* ---------------------------------------------------------------- * process_postgres_switches * Parse command line arguments for PostgresMain * * This is called twice, once for the "secure" options coming from the * postmaster or command line, and once for the "insecure" options coming * from the client's startup packet. The latter have the same syntax but * may be restricted in what they can do. * * argv[0] is ignored in either case (it's assumed to be the program name). * * ctx is PGC_POSTMASTER for secure options, PGC_BACKEND for insecure options * coming from the client, or PGC_SUSET for insecure options coming from * a superuser client. * * If a database name is present in the command line arguments, it's * returned into *dbname (this is allowed only if *dbname is initially NULL). * ---------------------------------------------------------------- */ void process_postgres_switches(int argc, char* argv[], GucContext ctx, const char** dbname) { bool secure = (ctx == PGC_POSTMASTER); int errs = 0; GucSource gucsource; int flag; #ifdef PGXC bool singleuser = false; #endif OptParseContext optCtxt; if (secure) { gucsource = PGC_S_ARGV; /* switches came from command line */ /* Ignore the initial --single argument, if present */ if (argc > 1 && strcmp(argv[1], "--single") == 0) { argv++; argc--; #ifdef PGXC singleuser = true; #endif } } else { gucsource = PGC_S_CLIENT; /* switches came from client */ } #ifdef HAVE_INT_OPTERR /* * Turn this off because it's either printed to stderr and not the log * where we'd want it, or argv[0] is now "--single", which would make for * a weird error message. We print our own error message below. */ opterr = 0; #endif /* * Parse command-line options. CAUTION: keep this in sync with * postmaster/postmaster.c (the option sets should not conflict) and with * the common help() function in main/main.c. */ initOptParseContext(&optCtxt); while ((flag = getopt_r(argc, argv, "A:B:bc:C:D:d:EeFf:h:ijk:lN:nOo:Pp:r:S:sTt:v:W:g:Q:-:", &optCtxt)) != -1) { switch (flag) { case 'A': SetConfigOption("debug_assertions", optCtxt.optarg, ctx, gucsource); break; case 'B': SetConfigOption("shared_buffers", optCtxt.optarg, ctx, gucsource); break; case 'b': /* Undocumented flag used for binary upgrades */ if (secure) u_sess->proc_cxt.IsBinaryUpgrade = true; break; case 'C': /* ignored for consistency with the postmaster */ break; case 'D': if (secure) t_thrd.postgres_cxt.userDoption = MemoryContextStrdup( THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), optCtxt.optarg); break; case 'd': set_debug_options(atoi(optCtxt.optarg), ctx, gucsource); break; case 'E': if (secure) t_thrd.postgres_cxt.EchoQuery = true; break; case 'e': SetConfigOption("datestyle", "euro", ctx, gucsource); break; case 'F': SetConfigOption("fsync", "false", ctx, gucsource); break; case 'f': if (!set_plan_disabling_options(optCtxt.optarg, ctx, gucsource)) errs++; break; case 'g': SetConfigOption("xlog_file_path", optCtxt.optarg, ctx, gucsource); break; case 'h': SetConfigOption("listen_addresses", optCtxt.optarg, ctx, gucsource); break; case 'i': SetConfigOption("listen_addresses", "*", ctx, gucsource); break; case 'j': if (secure) t_thrd.postgres_cxt.UseNewLine = 0; break; case 'k': SetConfigOption("unix_socket_directory", optCtxt.optarg, ctx, gucsource); break; case 'l': SetConfigOption("ssl", "true", ctx, gucsource); break; case 'N': SetConfigOption("max_connections", optCtxt.optarg, ctx, gucsource); break; case 'n': /* ignored for consistency with postmaster */ break; case 'O': SetConfigOption("allow_system_table_mods", "true", ctx, gucsource); break; case 'o': errs++; break; case 'P': SetConfigOption("ignore_system_indexes", "true", ctx, gucsource); break; case 'p': SetConfigOption("port", optCtxt.optarg, ctx, gucsource); break; case 'Q': if (optCtxt.optarg != NULL && strcmp(optCtxt.optarg, "copy_from_local") == 0) { xlogCopyMode = XLOG_COPY_FROM_LOCAL; } else if (optCtxt.optarg != NULL && strcmp(optCtxt.optarg, "force_copy_from_local") == 0) { xlogCopyMode = XLOG_FORCE_COPY_FROM_LOCAL; } else if (optCtxt.optarg != NULL && strcmp(optCtxt.optarg, "copy_from_share") == 0) { xlogCopyMode = XLOG_COPY_FROM_SHARE; } break; case 'r': /* send output (stdout and stderr) to the given file */ if (secure) (void)strlcpy(t_thrd.proc_cxt.OutputFileName, optCtxt.optarg, MAXPGPATH); break; case 'S': SetConfigOption("work_mem", optCtxt.optarg, ctx, gucsource); break; case 's': SetConfigOption("log_statement_stats", "true", ctx, gucsource); break; case 'T': /* ignored for consistency with the postmaster */ break; case 't': { const char* tmp = get_stats_option_name(optCtxt.optarg); if (tmp != NULL) SetConfigOption(tmp, "true", ctx, gucsource); else errs++; break; } case 'v': /* * -v is no longer used in normal operation, since * FrontendProtocol is already set before we get here. We keep * the switch only for possible use in standalone operation, * in case we ever support using normal FE/BE protocol with a * standalone backend. */ if (secure) FrontendProtocol = (ProtocolVersion)atoi(optCtxt.optarg); break; case 'W': SetConfigOption("post_auth_delay", optCtxt.optarg, ctx, gucsource); break; case 'c': case '-': { char* name = NULL; char* value = NULL; ParseLongOption(optCtxt.optarg, &name, &value); #ifdef PGXC #ifndef ENABLE_MULTIPLE_NODES if (flag == '-' && (strcmp(name, "coordinator") == 0 || strcmp(name, "datanode") == 0)) { ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Single node mode: must start as single node (--single_node)\n"))); } #endif /* A Coordinator is being activated */ if (strcmp(name, "coordinator") == 0 && value == NULL) g_instance.role = VCOORDINATOR; /* A Datanode is being activated */ else if (strcmp(name, "datanode") == 0 && value == NULL) g_instance.role = VDATANODE; /* A SingleDN mode is being activated */ else if (strcmp(name, "single_node") == 0 && value == NULL) { g_instance.role = VSINGLENODE; useLocalXid = true; } else if (strcmp(name, "localxid") == 0 && value == NULL) { if (!singleuser) ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("local xids can be used only in single user mode"))); useLocalXid = true; } else /* default case */ { #endif /* PGXC */ if (value == NULL) { if (flag == '-') ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("--%s requires a value", optCtxt.optarg))); else ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("-c %s requires a value", optCtxt.optarg))); } #ifndef ENABLE_MULTIPLE_NODES /* Only support 'internaltool' and 'application' for remotetype in single-node mode */ if (strcmp(name, "remotetype") == 0 && strcmp(value, "application") != 0 && strcmp(value, "internaltool") != 0) { ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid remote type:%s", value))); } #endif SetConfigOption(name, value, ctx, gucsource); } pfree(name); pfree_ext(value); break; } default: errs++; break; } if (errs) break; } #ifdef PGXC /* * Make sure we specified the mode if Coordinator or Datanode. * Allow for the exception of initdb by checking config option */ if (!IS_PGXC_COORDINATOR && !IS_PGXC_DATANODE && IsUnderPostmaster) { ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("openGauss: must start as either a Coordinator (--coordinator) or Datanode (--datanode)\n"))); } if (!IsPostmasterEnvironment) { /* Treat it as a Datanode for initdb to work properly */ g_instance.role = VDATANODE; isSingleMode = true; useLocalXid = true; } #endif /* * Optional database name should be there only if *dbname is NULL. */ if (errs == 0 && dbname != NULL && *dbname == NULL && argc - optCtxt.optind >= 1) { char* tmp_str = argv[optCtxt.optind++]; *dbname = MemoryContextStrdup(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR), tmp_str); } if (errs || argc != optCtxt.optind) { if (errs) optCtxt.optind--; /* complain about the previous argument */ /* spell the error message a bit differently depending on context */ if (IsUnderPostmaster) ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("invalid command-line argument for server process: %s", argv[optCtxt.optind]), errhint("Try \"%s --help\" for more information.", progname))); else ereport(FATAL, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("%s: invalid command-line argument: %s", progname, argv[optCtxt.optind]), errhint("Try \"%s --help\" for more information.", progname))); } /* * Reset getopt(3) library so that it will work correctly in subprocesses * or when this function is called a second time with another array. */ #ifdef HAVE_INT_OPTRESET optreset = 1; /* some systems need this too */ #endif } /* clear key message that may appear in core file for security */ static void clear_memory(char* qstr, char* msg, int maxlen) { if (qstr == NULL || msg == NULL) return; if (t_thrd.postgres_cxt.clear_key_memory) { errno_t errorno = EOK; PgBackendStatus* beentry = NULL; errorno = memset_s(qstr, strlen(qstr), 0, strlen(qstr)); securec_check(errorno, "", ""); errorno = memset_s(msg, maxlen, 0, maxlen); securec_check(errorno, "", ""); beentry = GetMyBEEntry(); if (beentry != NULL) { errorno = memset_s(beentry->st_activity, g_instance.attr.attr_common.pgstat_track_activity_query_size, 0, g_instance.attr.attr_common.pgstat_track_activity_query_size); securec_check(errorno, "", ""); } t_thrd.postgres_cxt.clear_key_memory = false; } } /* read and process the configuration file, just for openGauss backend workers */ void reload_configfile(void) { if (u_sess->sig_cxt.got_SIGHUP) { ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; if (currentOwner == NULL) { /* we use t_thrd.mem_cxt.msg_mem_cxt to remember config info in this cluster. */ MemoryContext old = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "ForReloadConfig", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); (void)MemoryContextSwitchTo(old); } u_sess->sig_cxt.got_SIGHUP = false; ProcessConfigFile(PGC_SIGHUP); most_available_sync = (volatile bool) u_sess->attr.attr_storage.guc_most_available_sync; SyncRepUpdateSyncStandbysDefined(); if (currentOwner == NULL) { ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); ResourceOwner newOwner = t_thrd.utils_cxt.CurrentResourceOwner; t_thrd.utils_cxt.CurrentResourceOwner = NULL; ResourceOwnerDelete(newOwner); } } } /* reload pooler for online business in expansion. */ void reload_online_pooler() { if (IsGotPoolReload() && !IsConnFromGTMTool()) { if (!IsTransactionBlock()) { processPoolerReload(); ResetGotPoolReload(false); } else { ereport(ERROR, (errcode(ERRCODE_ACTIVE_SQL_TRANSACTION), errmsg("Cannot execute DDL in a transaction block when need reconnect pooler"))); } } } #ifdef ENABLE_MULTIPLE_NODES /* * @Description: Initialize or refresh global node definition * * @param[IN] planstmt: PlannedStmt node which holds the "one time" information needed by the executor * @return: void */ static void InitGlobalNodeDefinition(PlannedStmt* planstmt) { if (planstmt == NULL) return; if ((global_node_definition != NULL && global_node_definition->num_nodes == planstmt->num_nodes) || planstmt->nodesDefinition == NULL) return; AutoMutexLock copyLock(&nodeDefCopyLock); copyLock.lock(); /* first initialization or need update when cluster size changes */ if (global_node_definition == NULL || global_node_definition->num_nodes != planstmt->num_nodes) { MemoryContext oldMemory = MemoryContextSwitchTo(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_CBB)); Size nodeDefSize; errno_t rc = EOK; /* free the old one before refresh */ if (global_node_definition != NULL) { if (global_node_definition->nodesDefinition) pfree(global_node_definition->nodesDefinition); pfree(global_node_definition); } global_node_definition = (GlobalNodeDefinition*)palloc(sizeof(GlobalNodeDefinition)); if ((planstmt->num_nodes > 0) && ((uint)(INT_MAX / planstmt->num_nodes) > sizeof(NodeDefinition))) { global_node_definition->num_nodes = planstmt->num_nodes; nodeDefSize = mul_size(sizeof(NodeDefinition), (Size)planstmt->num_nodes); global_node_definition->nodesDefinition = (NodeDefinition*)palloc(nodeDefSize); rc = memcpy_s(global_node_definition->nodesDefinition, nodeDefSize, planstmt->nodesDefinition, nodeDefSize); securec_check(rc, "\0", "\0"); } else { copyLock.unLock(); ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("invalid number of data nodes when initializing global node definition."))); } (void)MemoryContextSwitchTo(oldMemory); } copyLock.unLock(); } #endif void InitThreadLocalWhenSessionExit() { t_thrd.postgres_cxt.xact_started = false; } /* * Remove temp namespace. */ void RemoveTempNamespace() { /* * isSingleMode means we are doing initdb. Some temp tables * will be created to store intermediate result, so should do cleaning * when finished. * xc_maintenance_mode is for cluster resize, temp table created * during it should be clean too. * Drop temp schema if IS_SINGLE_NODE. */ if ((IS_PGXC_COORDINATOR || isSingleMode || u_sess->attr.attr_common.xc_maintenance_mode || IS_SINGLE_NODE) && u_sess->catalog_cxt.deleteTempOnQuiting) { MemoryContext current_context = CurrentMemoryContext; ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; bool need_rebuild_lsc = true; PG_TRY(); { t_thrd.proc_cxt.PostInit->InitLoadLocalSysCache(u_sess->proc_cxt.MyDatabaseId, u_sess->proc_cxt.MyDatabaseId == TemplateDbOid ? NULL : u_sess->proc_cxt.MyProcPort->database_name); need_rebuild_lsc = false; StringInfoData str; initStringInfo(&str); if (u_sess->catalog_cxt.myTempNamespace) { t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "ForTempTableDrop", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_OPTIMIZER)); char* nspname = get_namespace_name(u_sess->catalog_cxt.myTempNamespace); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); ResourceOwner newOwner = t_thrd.utils_cxt.CurrentResourceOwner; t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(newOwner); if (nspname != NULL) { ereport(LOG, (errmsg("Session quiting, drop temp schema %s", nspname))); appendStringInfo(&str, "DROP SCHEMA %s, pg_toast_temp_%s CASCADE", nspname, &nspname[8]); pgstatCountSQL4SessionLevel(); t_thrd.postgres_cxt.whereToSendOutput = DestNone; exec_simple_query(str.data, QUERY_MESSAGE); u_sess->catalog_cxt.myTempNamespace = InvalidOid; u_sess->catalog_cxt.myTempToastNamespace = InvalidOid; } } pfree_ext(str.data); } PG_CATCH(); { EmitErrorReport(); t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; if (need_rebuild_lsc) { ReBuildLSC(); } MemoryContextSwitchTo(current_context); FlushErrorState(); ereport(WARNING, (errmsg("Drop temp schema failed. The temp schema will be drop by TwoPhaseCleanner."))); } PG_END_TRY(); } } #ifndef ENABLE_MULTIPLE_NODES void LoadSqlPlugin() { if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(B_FORMAT)) { if (!u_sess->attr.attr_sql.dolphin) { /* recheck and load dolphin within lock */ pthread_mutex_lock(&g_instance.loadPluginLock[DB_CMPT_B]); u_sess->attr.attr_sql.dolphin = CheckIfExtensionExists("dolphin"); if (!u_sess->attr.attr_sql.dolphin) { LoadDolphinIfNeeded(); } else { InitBSqlPluginHookIfNeeded(); } pthread_mutex_unlock(&g_instance.loadPluginLock[DB_CMPT_B]); } else { InitBSqlPluginHookIfNeeded(); } } else if (u_sess->proc_cxt.MyDatabaseId != InvalidOid && DB_IS_CMPT(A_FORMAT) && u_sess->attr.attr_sql.whale) { InitASqlPluginHookIfNeeded(); } } #endif /* ---------------------------------------------------------------- * PostgresMain * openGauss main loop -- all backends, interactive or otherwise start here * * argc/argv are the command line arguments to be used. (When being forked * by the postmaster, these are not the original argv array of the process.) * dbname is the name of the database to connect to, or NULL if the database * name should be extracted from the command line arguments or defaulted. * username is the openGauss user name to be used for the session. * ---------------------------------------------------------------- */ int PostgresMain(int argc, char* argv[], const char* dbname, const char* username) { int firstchar; StringInfoData input_message = {NULL, 0, 0, 0}; sigjmp_buf local_sigjmp_buf; volatile bool send_ready_for_query = true; #ifdef ENABLE_MULTIPLE_NODES /* PGXC_DATANODE */ /* Snapshot info */ uint64 csn; bool cn_xc_maintain_mode = false; bool remote_gtm_mode = false; /* Timestamp info */ TimestampTz gtmstart_timestamp; TimestampTz stmtsys_timestamp; int ss_need_sync_wait_all = 0; errno_t rc = EOK; CsnType csn_type; #endif CommandDest saved_whereToSendOutput = DestNone; gstrace_entry(GS_TRC_ID_PostgresMain); /* * Initialize globals (already done if under postmaster, but not if * standalone). */ if (!IsUnderPostmaster) { PostmasterPid = gs_thread_self(); t_thrd.proc_cxt.MyProcPid = gs_thread_self(); t_thrd.proc_cxt.MyStartTime = time(NULL); t_thrd.proc_cxt.MyProgName = "gaussdb"; /* * Initialize random() for the first time, like PostmasterMain() * would. In a regular IsUnderPostmaster backend, BackendRun() * computes a high-entropy seed before any user query. Fewer distinct * initial seeds can occur here. */ srandom((unsigned int)(t_thrd.proc_cxt.MyProcPid ^ (unsigned int)t_thrd.proc_cxt.MyStartTime)); } else { t_thrd.proc_cxt.MyProgName = "postgres"; #ifdef DEBUG_UHEAP UHeapStatInit(UHeapStat_local); #endif } /* * Fire up essential subsystems: error and memory management * * If we are running under the postmaster, this is done already. */ if (!IsUnderPostmaster) { MemoryContextInit(); init_plog_global_mem(); } SetProcessingMode(InitProcessing); /* Compute paths, if we didn't inherit them from postmaster */ if (my_exec_path[0] == '\0') { if (find_my_exec(argv[0], my_exec_path) < 0) { gstrace_exit(GS_TRC_ID_PostgresMain); ereport(FATAL, (errmsg("%s: could not locate my own executable path", argv[0]))); } } if (t_thrd.proc_cxt.pkglib_path[0] == '\0') get_pkglib_path(my_exec_path, t_thrd.proc_cxt.pkglib_path); /* * Set default values for command-line options. */ if (!IsUnderPostmaster) { InitializeGUCOptions(); } /* * Parse command-line options. */ process_postgres_switches(argc, argv, PGC_POSTMASTER, &dbname); if (!IS_THREAD_POOL_WORKER) { /* Must have gotten a database name, or have a default (the username) */ if (dbname == NULL) { gstrace_exit(GS_TRC_ID_PostgresMain); ereport(FATAL, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("%s: no database nor user name specified", progname))); } } /* Acquire configuration parameters, unless inherited from postmaster */ if (!IsUnderPostmaster) { if (!SelectConfigFiles(t_thrd.postgres_cxt.userDoption, progname)) { gstrace_exit(GS_TRC_ID_PostgresMain); proc_exit(1); } InitializeNumLwLockPartitions(); } /* initialize guc variables which need to be sended to stream threads */ if (IS_PGXC_DATANODE && IsUnderPostmaster) init_sync_guc_variables(); /* * You might expect to see a setsid() call here, but it's not needed, * because if we are under a postmaster then BackendInitialize() did it. */ /* * Set up signal handlers and masks. * * Note that postmaster blocked all signals before forking child process, * so there is no race condition whereby we might receive a signal before * we have set up the handler. * * Also note: it's best not to use any signals that are SIG_IGNored in the * postmaster. If such a signal arrives before we are able to change the * handler to non-SIG_IGN, it'll get dropped. Instead, make a dummy * handler in the postmaster to reserve the signal. (Of course, this isn't * an issue for signals that are locally generated, such as SIGALRM and * SIGPIPE.) */ if (t_thrd.datasender_cxt.am_datasender) DataSndSignals(); else if (AM_WAL_SENDER) WalSndSignals(); else { if (!IsUnderPostmaster) { (void)gs_signal_createtimer(); (void)pqsignal(SIGALRM, SIG_IGN); /* ignored */ (void)pqsignal(SIGPIPE, SIG_IGN); /* ignored */ (void)pqsignal(SIGFPE, FloatExceptionHandler); } (void)gspqsignal(SIGHUP, SigHupHandler); /* set flag to read config * file */ (void)gspqsignal(SIGINT, StatementCancelHandler); /* cancel current query */ (void)gspqsignal(SIGTERM, die); /* cancel current query and exit */ /* * In a standalone backend, SIGQUIT can be generated from the keyboard * easily, while SIGTERM cannot, so we make both signals do die() * rather than quickdie(). */ if (IsUnderPostmaster) (void)gspqsignal(SIGQUIT, quickdie); /* hard crash time */ else (void)gspqsignal(SIGQUIT, die); /* cancel current query and exit */ (void)gspqsignal(SIGALRM, handle_sig_alarm); /* timeout conditions */ /* * Ignore failure to write to frontend. Note: if frontend closes * connection, we will notice it and exit cleanly when control next * returns to outer loop. This seems safer than forcing exit in the * midst of output during who-knows-what operation... */ (void)gspqsignal(SIGUSR1, procsignal_sigusr1_handler); (void)gspqsignal(SIGUSR2, PoolValidateCancelHandler); /* * Reset some signals that are accepted by postmaster but not by * backend */ (void)gspqsignal(SIGCHLD, SIG_DFL); /* system() requires this on some * platforms */ } (void)gs_signal_unblock_sigusr2(); if (IsUnderPostmaster) { /* We allow SIGQUIT (quickdie) at all times */ (void)sigdelset(&t_thrd.libpq_cxt.BlockSig, SIGQUIT); } gs_signal_setmask(&t_thrd.libpq_cxt.BlockSig, NULL); /* block everything except SIGQUIT */ if (IsInitdb) { Assert(!IsUnderPostmaster); g_instance.global_sysdbcache.Init(INSTANCE_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); CreateLocalSysDBCache(); } if (!IsUnderPostmaster) { /* * Validate we have been given a reasonable-looking t_thrd.proc_cxt.DataDir (if under * postmaster, assume postmaster did this already). */ Assert(t_thrd.proc_cxt.DataDir); ValidatePgVersion(t_thrd.proc_cxt.DataDir); /* Change into t_thrd.proc_cxt.DataDir (if under postmaster, was done already) */ ChangeToDataDir(); /* * Create lockfile for data directory. */ CreateDataDirLockFile(false); } /* Early initialization */ BaseInit(); if (!IsUnderPostmaster) { ShareStorageInit(); } if (!IsUnderPostmaster && xlogCopyMode != XLOG_COPY_NOT) { if (g_instance.attr.attr_storage.xlog_file_path == NULL) { proc_exit(1); } bool copySuccess = false; if (xlogCopyMode == XLOG_COPY_FROM_LOCAL) { copySuccess = XLogOverwriteFromLocal(); } else if (xlogCopyMode == XLOG_FORCE_COPY_FROM_LOCAL) { copySuccess = XLogOverwriteFromLocal(true); } else if (xlogCopyMode == XLOG_COPY_FROM_SHARE) { copySuccess = XLogOverwriteFromShare(); } if (copySuccess) { proc_exit(0); } else { proc_exit(1); } } #ifdef ENABLE_MULTIPLE_NODES /* PGXC_COORD */ if (IS_PGXC_COORDINATOR && IsPostmasterEnvironment) { /* If we exit, first try and clean connections and send to pool */ /* * pooler thread does NOT exist any more, PoolerLock of LWlock is used instead. * * PoolManagerDisconnect() which is called by PGXCNodeCleanAndRelease() * is the last call to pooler in the openGauss thread, and PoolerLock is * used in PoolManagerDisconnect(), but it is called after ProcKill() * when postgres thread exits. * ProcKill() releases any of its held LW locks. So Assert(!(proc == NULL ...)) * will fail in LWLockAcquire() which is called by PoolManagerDisconnect(). * * All exit functions in "on_shmem_exit_list" will be called before those functions * in "on_proc_exit_list", so move PGXCNodeCleanAndRelease() to "on_shmem_exit_list" * and registers it after ProcKill(), and PGXCNodeCleanAndRelease() will * be called before ProcKill(). * * the register sequence of exit functions on openGauss thread: * on_proc_exit_list: * pq_close, ^ * AtProcExit_Files, | * smgrshutdown, | * AtProcExit_SnapshotData, | * audit_processlogout, | * PGXCNodeCleanAndRelease, --+ | * | ^ * on_shmem_exit_list: | | * ProcKill, | | * PGXCNodeCleanAndRelease, <-+ | * RemoveProcFromArray, | * CleanupInvalidationState, | * CleanupProcSignalState, ^ called on openGauss thread * AtProcExit_Buffers, | exit. * pgstat_beshutdown_hook, | * ShutdownPostgres, | * endMySessionTimeEntry, | * endMySessionStatEntry, | */ on_shmem_exit(PGXCNodeCleanAndRelease, 0); } #endif #ifndef ENABLE_MULTIPLE_NODES if (!IS_THREAD_POOL_WORKER) { on_shmem_exit(PlDebugerCleanUp, 0); } #endif if (ENABLE_GPC) { on_shmem_exit(cleanGPCPlanProcExit, 0); } /* * Create a per-backend PGPROC struct in shared memory, except in the * EXEC_BACKEND case where this was done in SubPostmasterMain. We must do * this before we can use LWLocks (and in the EXEC_BACKEND case we already * had to do some stuff with LWLocks). */ #ifdef EXEC_BACKEND if (!IsUnderPostmaster) InitProcess(); #else InitProcess(); #endif if (t_thrd.proc) { errno_t rc = snprintf_s(t_thrd.proc->myProgName, sizeof(t_thrd.proc->myProgName), sizeof(t_thrd.proc->myProgName) - 1, "%s", t_thrd.proc_cxt.MyProgName); securec_check_ss(rc, "", ""); t_thrd.proc->myStartTime = t_thrd.proc_cxt.MyStartTime; t_thrd.proc->sessMemorySessionid = (IS_THREAD_POOL_WORKER ? u_sess->session_id : t_thrd.proc_cxt.MyProcPid); } /* We need to allow SIGINT, etc during the initial transaction */ gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); /* Initialize the memory tracking information */ MemoryTrackingInit(); /* * General initialization. * * NOTE: if you are tempted to add code in this vicinity, consider putting * it inside InitPostgres() instead. In particular, anything that * involves database access should be there, not here. */ t_thrd.proc_cxt.PostInit->SetDatabaseAndUser(dbname, InvalidOid, username); /* * PostgresMain thread can be user for wal sender, which will call * DataSenderMain() or WalSenderMain() later. */ if (unlikely(AM_WAL_SENDER)) t_thrd.proc_cxt.PostInit->InitWAL(); else t_thrd.proc_cxt.PostInit->InitBackendWorker(); /* * If the t_thrd.mem_cxt.postmaster_mem_cxt is still around, recycle the space; we don't * need it anymore after InitPostgres completes. Note this does not trash * *u_sess->proc_cxt.MyProcPort, because ConnCreate() allocated that space with malloc() * ... else we'd need to copy the Port data first. Also, subsidiary data * such as the username isn't lost either; see ProcessStartupPacket(). */ if (t_thrd.mem_cxt.postmaster_mem_cxt) { MemoryContextDelete(t_thrd.mem_cxt.postmaster_mem_cxt); t_thrd.mem_cxt.postmaster_mem_cxt = NULL; } SetProcessingMode(NormalProcessing); if (!IS_THREAD_POOL_WORKER) { /* * Now all GUC states are fully set up. Report them to client if * appropriate. */ BeginReportingGUCOptions(); } /* * Also set up handler to log session end; we have to wait till now to be * sure Log_disconnections has its final value. */ if (IsUnderPostmaster && !IS_THREAD_POOL_WORKER && u_sess->attr.attr_common.Log_disconnections) on_proc_exit(log_disconnections, 0); /* Data sender process */ if (t_thrd.datasender_cxt.am_datasender) proc_exit(DataSenderMain()); /* If this is a WAL sender process, we're done with initialization. */ if (AM_WAL_SENDER) proc_exit(WalSenderMain()); if (IsUnderPostmaster && !IS_THREAD_POOL_WORKER) { on_proc_exit(audit_processlogout, 0); } /* * process any libraries that should be preloaded at backend start (this * likewise can't be done until GUC settings are complete) */ process_local_preload_libraries(); /* * Send this backend's cancellation info to the frontend. */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote && PG_PROTOCOL_MAJOR(FrontendProtocol) >= 2 && !IS_THREAD_POOL_WORKER) { StringInfoData buf; pq_beginmessage(&buf, 'K'); pq_sendint32(&buf, (int32)t_thrd.proc_cxt.MyPMChildSlot); pq_sendint32(&buf, (int32)t_thrd.proc_cxt.MyCancelKey); pq_endmessage(&buf); /* DN send thread pid to CN */ if (IsConnFromCoord() && IS_PGXC_DATANODE && (t_thrd.proc && t_thrd.proc->workingVersionNum >= 92060)) { StringInfoData buf_pid; pq_beginmessage(&buf_pid, 'k'); pq_sendint64(&buf_pid, t_thrd.proc_cxt.MyProcPid); pq_endmessage(&buf_pid); } /* Need not flush since ReadyForQuery will do it. */ } /* Welcome banner for standalone case */ if (t_thrd.postgres_cxt.whereToSendOutput == DestDebug) printf("\nPostgreSQL stand-alone backend %s\n", PG_VERSION); /* * Create the memory context we will use in the main loop. * * t_thrd.mem_cxt.msg_mem_cxt is reset once per iteration of the main loop, ie, upon * completion of processing of each command message from the client. */ t_thrd.mem_cxt.msg_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "MessageContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); /* * Create memory context and buffer used for RowDescription messages. As * SendRowDescriptionMessage(), via exec_describe_statement_message(), is * frequently executed for ever single statement, we don't want to * allocate a separate buffer every time. */ t_thrd.mem_cxt.row_desc_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "RowDescriptionContext", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); MemoryContext old_mc = MemoryContextSwitchTo(t_thrd.mem_cxt.row_desc_mem_cxt); initStringInfo(&(*t_thrd.postgres_cxt.row_description_buf)); MemoryContextSwitchTo(old_mc); t_thrd.mem_cxt.mask_password_mem_cxt = AllocSetContextCreate(t_thrd.top_mem_cxt, "MaskPasswordCtx", ALLOCSET_DEFAULT_MINSIZE, ALLOCSET_DEFAULT_INITSIZE, ALLOCSET_DEFAULT_MAXSIZE); InitVecFuncMap(); /* init param hash table for sending set message */ if (IS_PGXC_COORDINATOR) init_set_params_htab(); #ifndef ENABLE_MULTIPLE_NODES LoadSqlPlugin(); #endif /* * Remember stand-alone backend startup time */ if (!IsUnderPostmaster) t_thrd.time_cxt.pg_start_time = GetCurrentTimestamp(); t_thrd.wlm_cxt.thread_node_group = &g_instance.wlm_cxt->MyDefaultNodeGroup; // initialize the default value t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr; t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr; /* * Initialize key pair to be used as object id while using advisory lock * for backup */ t_thrd.postmaster_cxt.xc_lockForBackupKey1 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_1); t_thrd.postmaster_cxt.xc_lockForBackupKey2 = Int32GetDatum(XC_LOCK_FOR_BACKUP_KEY_2); #ifdef ENABLE_MULTIPLE_NODES if (IS_PGXC_DATANODE) { /* If we exit, first try and clean connection to GTM */ on_proc_exit(DataNodeShutdown, 0); } #endif /* make sure that each module name is unique */ Assert(check_module_name_unique()); if (IS_THREAD_POOL_WORKER) { u_sess->proc_cxt.MyProcPort->sock = PGINVALID_SOCKET; t_thrd.threadpool_cxt.worker->NotifyReady(); } /* * openGauss main processing loop begins here * * If an exception is encountered, processing resumes here so we abort the * current transaction and start a new one. * * You might wonder why this isn't coded as an infinite loop around a * PG_TRY construct. The reason is that this is the bottom of the * exception stack, and so with PG_TRY there would be no exception handler * in force at all during the CATCH part. By leaving the outermost setjmp * always active, we have at least some chance of recovering from an error * during error recovery. (If we get into an infinite loop thereby, it * will soon be stopped by overflow of elog.c's internal state stack.) */ int curTryCounter; int* oldTryCounter = NULL; if (sigsetjmp(local_sigjmp_buf, 1) != 0) { /* reset signal block flag for threadpool worker */ ResetInterruptCxt(); if (g_threadPoolControler) { g_threadPoolControler->GetSessionCtrl()->releaseLockIfNecessary(); } gstrace_tryblock_exit(true, oldTryCounter); Assert(t_thrd.proc->dw_pos == -1); (void)pgstat_report_waitstatus(STATE_WAIT_UNDEFINED); t_thrd.pgxc_cxt.GlobalNetInstr = NULL; /* output the memory tracking information when error happened */ MemoryTrackingOutputFile(); /* * NOTE: if you are tempted to add more code in this if-block, * consider the high probability that it should be in * AbortTransaction() instead. The only stuff done directly here * should be stuff that is guaranteed to apply *only* for outer-level * error recovery, such as adjusting the FE/BE protocol status. */ /* Since not using PG_TRY, must reset error stack by hand */ u_sess->plsql_cxt.cur_exception_cxt = NULL; t_thrd.log_cxt.error_context_stack = NULL; t_thrd.log_cxt.call_stack = NULL; /* reset buffer strategy flag */ t_thrd.storage_cxt.is_btree_split = false; SetForceXidFromGTM(false); /* reset timestamp_from_cn if error happen between 't' message and start transaction */ t_thrd.xact_cxt.timestamp_from_cn = false; /* reset create schema flag if error happen */ u_sess->catalog_cxt.setCurCreateSchema = false; pfree_ext(u_sess->catalog_cxt.curCreateSchema); /* Prevent interrupts while cleaning up */ HOLD_INTERRUPTS(); /* * Forget any pending QueryCancel request, since we're returning to * the idle loop anyway, and cancel the statement timer if running. */ t_thrd.int_cxt.QueryCancelPending = false; disable_sig_alarm(true); t_thrd.int_cxt.QueryCancelPending = false; /* again in case timeout occurred */ /* * Turn off these interrupts too. This is only needed here and not in * other exception-catching places since these interrupts are only * enabled while we wait for client input. */ t_thrd.postgres_cxt.DoingCommandRead = false; /* Make sure libpq is in a good state */ pq_comm_reset(); /* statement retry phase : long jump */ if (IsStmtRetryEnabled()) { bool is_extended_query = u_sess->postgres_cxt.doing_extended_query_message; if (!is_extended_query && (u_sess->exec_cxt.RetryController->MessageOnExecuting() == S_MESSAGE)) { /* * if ereport error when processing a sync message, then doing_extended_query_message will be false, * but we want it to be retried as a pbe message. */ is_extended_query = true; } if (IsStmtRetryCapable(u_sess->exec_cxt.RetryController, is_extended_query)) { u_sess->exec_cxt.RetryController->TriggerRetry(is_extended_query); } else { u_sess->exec_cxt.RetryController->DisableRetry(); if (!pq_disk_is_temp_file_created()) pq_disk_disable_temp_file(); } } /* Report the error to the client and/or server log */ EmitErrorReport(); /* reset global values of perm space */ perm_space_value_reset(); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) u_sess->exec_cxt.RetryController->stub_.CloseOneStub(); #endif /* * when clearing the BCM encounter ERROR, we should ResetBCMArray, or it * will enter ClearBCMArray infinite loop, then coredump. */ ResetBCMArray(); /* Now release the active statement reserved. */ if (ENABLE_WORKLOAD_CONTROL) { /* save error to history info */ save_error_message(); if (g_instance.wlm_cxt->dynamic_workload_inited) { t_thrd.wlm_cxt.parctl_state.errjmp = 1; if (t_thrd.wlm_cxt.parctl_state.simple == 0) dywlm_client_release(&t_thrd.wlm_cxt.parctl_state); else WLMReleaseGroupActiveStatement(); dywlm_client_max_release(&t_thrd.wlm_cxt.parctl_state); } else WLMParctlRelease(&t_thrd.wlm_cxt.parctl_state); if (IS_PGXC_COORDINATOR && t_thrd.wlm_cxt.collect_info->sdetail.msg) { pfree_ext(t_thrd.wlm_cxt.collect_info->sdetail.msg); } } u_sess->plsql_cxt.pragma_autonomous = false; u_sess->plsql_cxt.curr_compile_context = NULL; u_sess->pcache_cxt.gpc_in_batch = false; u_sess->pcache_cxt.gpc_in_try_store = false; u_sess->plsql_cxt.have_error = false; OpFusion::tearDown(u_sess->exec_cxt.CurrentOpFusionObj); /* init pbe execute status when long jump */ u_sess->xact_cxt.pbe_execute_complete = true; /* init row trigger shipping status when long jump */ u_sess->tri_cxt.exec_row_trigger_on_datanode = false; u_sess->statement_cxt.executer_run_level = 0; /* reset stream for-loop flag when long jump */ u_sess->SPI_cxt.has_stream_in_cursor_or_forloop_sql = false; /* release operator-level hash table in memory */ releaseExplainTable(); if (StreamTopConsumerAmI() && u_sess->debug_query_id != 0) { gs_close_all_stream_by_debug_id(u_sess->debug_query_id); } /* Mark recursive vfd is invalid before aborting transaction. */ StreamNodeGroup::MarkRecursiveVfdInvalid(); BgworkerListSyncQuit(); /* * Abort the current transaction in order to recover. */ if (u_sess->is_autonomous_session) { AbortOutOfAnyTransaction(); } else { AbortCurrentTransaction(); } /* release resource held by lsc */ AtEOXact_SysDBCache(false); /* Notice: at the most time it isn't necessary to call because * all the LWLocks are released in AbortCurrentTransaction(). * but in some rare exception not in one transaction (for * example the following InitMultinodeExecutor() calling ) * maybe hold LWLocks unused. */ LWLockReleaseAll(); /* We should syncQuit after LWLockRelease to avoid dead lock of LWLocks. */ RESUME_INTERRUPTS(); StreamNodeGroup::syncQuit(STREAM_ERROR); StreamNodeGroup::destroy(STREAM_ERROR); #ifndef ENABLE_MULTIPLE_NODES clean_up_debug_client(true); DebugInfo* debug_server = u_sess->plsql_cxt.cur_debug_server; u_sess->plsql_cxt.has_step_into = false; clean_up_debug_server(debug_server, false, true); u_sess->plsql_cxt.cur_debug_server = NULL; #endif HOLD_INTERRUPTS(); ForgetRegisterStreamSnapshots(); /* reset query_id after sync quit */ pgstat_report_queryid(0); pgstat_report_unique_sql_id(true); /* * Make sure debug_query_string gets reset before we possibly clobber * the storage it points at. */ t_thrd.postgres_cxt.debug_query_string = NULL; if (u_sess->unique_sql_cxt.need_update_calls && is_unique_sql_enabled() && is_local_unique_sql()) { UpdateUniqueSQLStat(NULL, NULL, u_sess->unique_sql_cxt.unique_sql_start_time); } /* reset unique sql */ ResetCurrentUniqueSQL(true); SetIsTopUniqueSQL(false); PortalErrorCleanup(); SPICleanup(); /* * We can't release replication slots inside AbortTransaction() as we * need to be able to start and abort transactions while having a slot * acquired. But we never need to hold them across top level errors, * so releasing here is fine. There's another cleanup in ProcKill() * ensuring we'll correctly cleanup on FATAL errors as well. */ if (t_thrd.slot_cxt.MyReplicationSlot != NULL) ReplicationSlotRelease(); if (AlignMemoryContext != NULL) MemoryContextReset(AlignMemoryContext); /* * Now return to normal top-level context and clear ErrorContext for * next time. */ MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_DEFAULT)); FlushErrorState(); /* * If we were handling an extended-query-protocol message, initiate * skip till next Sync. This also causes us not to issue * ReadyForQuery (until we get Sync). */ if (u_sess->postgres_cxt.doing_extended_query_message) u_sess->postgres_cxt.ignore_till_sync = true; /* We don't have a transaction command open anymore */ t_thrd.postgres_cxt.xact_started = false; t_thrd.xact_cxt.isSelectInto = false; u_sess->pcache_cxt.cur_stmt_name = NULL; /* Now we can allow interrupts again */ RESUME_INTERRUPTS(); /* Now we not allow pool_validate interrupts again */ PREVENT_POOL_VALIDATE_SIGUSR2(); } oldTryCounter = gstrace_tryblock_entry(&curTryCounter); /* We can now handle ereport(ERROR) */ t_thrd.log_cxt.PG_exception_stack = &local_sigjmp_buf; gs_signal_setmask(&t_thrd.libpq_cxt.UnBlockSig, NULL); gs_signal_unblock_sigusr2(); if (!u_sess->postgres_cxt.ignore_till_sync) send_ready_for_query = true; /* initially, or after error */ t_thrd.proc_cxt.postgres_initialized = true; PG_TRY(); { pq_disk_discard_temp_file(); } PG_CATCH(); { elog(LOG, "catch error while discard temp file"); } PG_END_TRY(); /* statement retry phase : RI */ if (IsStmtRetryEnabled() && u_sess->exec_cxt.RetryController->IsQueryRetrying()) { /* * if stmt is retring, we can't send ready for query * if we are retrying a extend query message, we can't ignore message before * sync message is received */ u_sess->debug_query_id = 0; send_ready_for_query = false; ++u_sess->exec_cxt.RetryController->retry_times; /* * here are some actions can't be done in long jump */ /* clean send buffer and temp file */ pq_abandon_sendbuffer(); u_sess->exec_cxt.RetryController->cached_msg_context.Reset(); if (u_sess->exec_cxt.RetryController->IsExtendQueryRetrying()) { u_sess->postgres_cxt.ignore_till_sync = false; u_sess->exec_cxt.RetryController->CleanPreparedStmt(); } } /* * Non-error queries loop here. */ for (;;) { /* * Since max_query_rerty_times is a USERSET GUC, so must check Statement retry * in each query loop here. */ StmtRetryValidate(u_sess->exec_cxt.RetryController); /* Add the pg_delete_audit operation to audit log */ t_thrd.audit.Audit_delete = false; /* clear key message that may appear in core file for security */ clear_memory((char*)t_thrd.postgres_cxt.clobber_qstr, input_message.data, input_message.maxlen); t_thrd.postgres_cxt.clobber_qstr = NULL; /* * Release storage left over from prior query cycle, and create a new * query input buffer in the cleared t_thrd.mem_cxt.msg_mem_cxt. */ MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); MemoryContextResetAndDeleteChildren(t_thrd.mem_cxt.msg_mem_cxt); MemoryContextResetAndDeleteChildren(u_sess->temp_mem_cxt); MemoryContextResetAndDeleteChildren(u_sess->stat_cxt.hotkeySessContext); u_sess->stat_cxt.hotkeyCandidates = NIL; u_sess->plsql_cxt.pass_func_tupdesc = NULL; /* reset plpgsql compile flag */ u_sess->plsql_cxt.compile_context_list = NULL; u_sess->plsql_cxt.curr_compile_context = NULL; u_sess->plsql_cxt.compile_status = NONE_STATUS; u_sess->plsql_cxt.func_tableof_index = NULL; u_sess->plsql_cxt.portal_depth = 0; u_sess->statement_cxt.executer_run_level = 0; initStringInfo(&input_message); t_thrd.postgres_cxt.debug_query_string = NULL; t_thrd.postgres_cxt.g_NoAnalyzeRelNameList = NIL; u_sess->analyze_cxt.is_under_analyze = false; u_sess->exec_cxt.isLockRows = false; t_thrd.postgres_cxt.mark_explain_analyze = false; t_thrd.postgres_cxt.mark_explain_only = false; u_sess->SPI_cxt.has_stream_in_cursor_or_forloop_sql = false; if (unlikely(t_thrd.log_cxt.msgbuf->data != NULL)) { pfree_ext(t_thrd.log_cxt.msgbuf->data); } t_thrd.log_cxt.msgbuf->cursor = 0; t_thrd.log_cxt.msgbuf->len = 0; lc_replan_nodegroup = InvalidOid; /* * (1) If we've reached idle state, tell the frontend we're ready for * a new query. * * Note: this includes fflush()'ing the last of the prior output. * * This is also a good time to send collected statistics to the * collector, and to update the PS stats display. We avoid doing * those every time through the message loop because it'd slow down * processing of batched messages, and because we don't want to report * uncommitted updates (that confuses autovacuum). The notification * processor wants a call too, if we are not in a transaction block. */ if (send_ready_for_query) { /* * Instrumentation: should update unique sql stat here * * when send_ready_for_query is true, each SQL should be run finished. * we don't care abort transaction status, we focus on unique sql * row activity and Cache/IO on DN nodes. * * when shutdown, will call pgstat_report_stat, * we maybe need to hanlde this case */ if (is_unique_sql_enabled()) { if (need_update_unique_sql_row_stat()) UpdateUniqueSQLStatOnRemote(); UniqueSQLStatCountResetReturnedRows(); UniqueSQLStatCountResetParseCounter(); } if (IsStmtRetryEnabled()) { #ifdef USE_RETRY_STUB u_sess->exec_cxt.RetryController->stub_.FinishStubTest(u_sess->exec_cxt.RetryController->PBEFlowStr()); #endif u_sess->exec_cxt.RetryController->FinishRetry(); } if (IsAbortedTransactionBlockState()) { set_ps_display("idle in transaction (aborted)", false); pgstat_report_activity(STATE_IDLEINTRANSACTION_ABORTED, NULL); } else if (IsTransactionOrTransactionBlock()) { set_ps_display("idle in transaction", false); pgstat_report_activity(STATE_IDLEINTRANSACTION, NULL); } else { ProcessCompletedNotifies(); pgstat_report_stat(false); set_ps_display("idle", false); pgstat_report_activity(STATE_IDLE, NULL); } /* We're ready for a new query, reset wait status and u_sess->debug_query_id */ pgstat_report_waitstatus(STATE_WAIT_UNDEFINED); pgstat_report_queryid(0); pgstat_report_unique_sql_id(true); u_sess->trace_cxt.trace_id[0] = '\0'; /* * If connection to client is lost, we do not need to send message to client. */ if (IS_CLIENT_CONN_VALID(u_sess->proc_cxt.MyProcPort) && (!t_thrd.int_cxt.ClientConnectionLost)) { /* * before send 'Z' to frontend, we should check if INTERRUPTS happends or not. * In SyncRepWaitForLSN() function, take HOLD_INTERRUPTS() to prevent interrupt happending and set * t_thrd.postgres_cxt.whereToSendOutput to 'DestNone' when t_thrd.int_cxt.ProcDiePending is true. Then * this session will not send 'Z' message to frontend and will not report error if it not check * interrupt. So frontend will be reveiving message forever and do nothing, which is wrong. */ CHECK_FOR_INTERRUPTS(); ReadyForQuery((CommandDest)t_thrd.postgres_cxt.whereToSendOutput); } #ifdef ENABLE_MULTIPLE_NODES /* * Helps us catch any problems where we did not send down a snapshot * when it was expected. However if any deferred trigger is supposed * to be fired at commit time we need to preserve the snapshot sent previously */ if ((IS_PGXC_DATANODE || IsConnFromCoord()) && !IsAnyAfterTriggerDeferred()) { UnsetGlobalSnapshotData(); } #endif /* update our elapsed time statistics. */ timeInfoRecordEnd(); /* reset unique_sql_id & stat * * cannot reset unique_sql_cn_id here, as unique_sql_cn_id * is generated in parser hook, and it related to current * node, so can be reused, if reset here, PBE will * lost the unique sql entry. */ ReportQueryStatus(); statement_commit_metirc_context(); ResetCurrentUniqueSQL(); send_ready_for_query = false; } else { /* update our elapsed time statistics. */ timeInfoRecordEnd(); } /* * INSTR: when track type is TOP, we reset is_top_unique_sql to false, * for P messages, * - call SetIsTopUniqueSQL(false), so each P message can generate unique * sql id */ if (IS_UNIQUE_SQL_TRACK_TOP) SetIsTopUniqueSQL(false); /* disable tempfile anyway */ pq_disk_disable_temp_file(); if (IS_THREAD_POOL_WORKER) { t_thrd.threadpool_cxt.worker->WaitMission(); Assert(CheckMyDatabaseMatch()); if (!g_instance.archive_obs_cxt.in_switchover && !g_instance.streaming_dr_cxt.isInSwitchover) { Assert(u_sess->status != KNL_SESS_FAKE); } } else { /* if we do alter db, reinit syscache */ ReLoadLSCWhenWaitMission(); } if (isRestoreMode && !IsAbortedTransactionBlockState()) { ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; /* we use t_thrd.top_mem_cxt to remember all node info in this cluster. */ MemoryContext old = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); t_thrd.utils_cxt.CurrentResourceOwner = ResourceOwnerCreate(NULL, "ForPGXCNodes", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_COMMUNICATION)); /* Update node table in the shared memory */ PgxcNodeListAndCount(); /* Get classified list of node Oids */ PgxcNodeGetOids(NULL, NULL, &u_sess->pgxc_cxt.NumCoords, &u_sess->pgxc_cxt.NumDataNodes, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(t_thrd.utils_cxt.CurrentResourceOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); ResourceOwner newOwner = t_thrd.utils_cxt.CurrentResourceOwner; t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(newOwner); (void)MemoryContextSwitchTo(old); } /* * Record client connection establish time, which start on incommining resuest arrives e.g. poll() * invoked to accept() and end on returning message by server side clientfd. * One session records only once. */ u_sess->clientConnTime_cxt.checkOnlyInConnProcess = false; /* * Check cache size to see if we need to AcceptInvalidationMessages. */ CleanSystemCaches(true); CHECK_FOR_INTERRUPTS(); /* * (2) Allow asynchronous signals to be executed immediately if they * come in while we are waiting for client input. (This must be * conditional since we don't want, say, reads on behalf of COPY FROM * STDIN doing the same thing.) */ t_thrd.postgres_cxt.DoingCommandRead = true; #ifdef MEMORY_CONTEXT_CHECKING MemoryContextCheck(t_thrd.top_mem_cxt, false); #endif /* * (3) read a command (loop blocks here) */ if (saved_whereToSendOutput != DestNone) t_thrd.postgres_cxt.whereToSendOutput = saved_whereToSendOutput; firstchar = ReadCommand(&input_message); /* update our elapsed time statistics. */ timeInfoRecordStart(); /* stmt retry routine phase : pack input_message */ if (IsStmtRetryEnabled()) { #ifdef USE_ASSERT_CHECKING if (u_sess->exec_cxt.RetryController->IsExtendQueryRetrying()) { u_sess->exec_cxt.RetryController->ValidateExecuting(firstchar); } #endif if (IsQueryMessage(firstchar)) { u_sess->exec_cxt.RetryController->CacheCommand(firstchar, &input_message); pq_disk_enable_temp_file(); } u_sess->exec_cxt.RetryController->TrackMessageOnExecuting(firstchar); u_sess->exec_cxt.RetryController->LogTraceInfo(MessageInfo(firstchar, input_message.len)); ereport(DEBUG2, (errmodule(MOD_CN_RETRY), errmsg("%s cache command.", PRINT_PREFIX_TYPE_PATH))); } /* * (4) disable async signal conditions again. */ t_thrd.postgres_cxt.DoingCommandRead = false; CHECK_FOR_INTERRUPTS(); /* * (5) check for any other interesting events that happened while we * slept. */ reload_configfile(); // (6) process pooler reload before the next transaction begin. // if (IsGotPoolReload() && !IsTransactionOrTransactionBlock() && !IsConnFromGTMTool()) { processPoolerReload(); ResetGotPoolReload(false); } /* * (7) process the command. But ignore it if we're skipping till * Sync. */ if (u_sess->postgres_cxt.ignore_till_sync && firstchar != EOF) continue; #ifdef ENABLE_MULTIPLE_NODES // reset some flag related to stream ResetStreamEnv(); #else t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->initMemInChunks = t_thrd.utils_cxt.trackedMemChunks; t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->queryMemInChunks = t_thrd.utils_cxt.trackedMemChunks; t_thrd.shemem_ptr_cxt.mySessionMemoryEntry->peakChunksQuery = t_thrd.utils_cxt.trackedMemChunks; #endif t_thrd.utils_cxt.peakedBytesInQueryLifeCycle = 0; t_thrd.utils_cxt.basedBytesInQueryLifeCycle = 0; t_thrd.codegen_cxt.codegen_IRload_thr_count = 0; IsExplainPlanStmt = false; t_thrd.codegen_cxt.g_runningInFmgr = false; PTFastQueryShippingStore = true; u_sess->opt_cxt.is_under_append_plan = false; u_sess->attr.attr_sql.explain_allow_multinode = false; /* Reset store procedure's session variables. */ stp_reset_stmt(); MemoryContext oldMemory = MemoryContextSwitchTo(THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); #ifdef ENABLE_LLVM_COMPILE CodeGenThreadInitialize(); #endif (void)MemoryContextSwitchTo(oldMemory); u_sess->exec_cxt.single_shard_stmt = false; /* Set statement_timestamp */ SetCurrentStatementStartTimestamp(); if (u_sess->proc_cxt.MyProcPort && u_sess->proc_cxt.MyProcPort->is_logic_conn) LIBCOMM_DEBUG_LOG("postgres to node[nid:%d,sid:%d] with msg:%c.", u_sess->proc_cxt.MyProcPort->gs_sock.idx, u_sess->proc_cxt.MyProcPort->gs_sock.sid, firstchar); switch (firstchar) { #ifdef ENABLE_MULTIPLE_NODES case 'Z': // exeute plan directly. { char* plan_string = NULL; PlannedStmt* planstmt = NULL; int oLen_msg = 0; int cLen_msg = 0; /* Set top consumer at the very beginning. */ StreamTopConsumerIam(); /* Build stream context for stream plan. */ InitStreamContext(); u_sess->exec_cxt.under_stream_runtime = true; // get the node id. u_sess->pgxc_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); // Get original length and length of compressed plan. // oLen_msg = pq_getmsgint(&input_message, 4); cLen_msg = pq_getmsgint(&input_message, 4); if (unlikely(oLen_msg <= 0 || cLen_msg <= 0 || cLen_msg > oLen_msg)) { ereport(ERROR, (errmodule(MOD_OPT), errcode(ERRCODE_DATA_CORRUPTED), errmsg( "unexpected original length %d and length of compressed plan %d", oLen_msg, cLen_msg))); } // Copy compressed data from message buffer and then decompress it. // plan_string = (char*)palloc0(cLen_msg); pq_copymsgbytes(&input_message, plan_string, cLen_msg); plan_string = DecompressSerializedPlan(plan_string, cLen_msg, oLen_msg); pq_getmsgend(&input_message); ereport(DEBUG2, (errmsg("PLAN END RECEIVED : %s", plan_string))); // Starting the transaction early to initialize ResourceOwner, // else, Plan de-serialization code path fails start_xact_command(); MemoryContext old_cxt = MemoryContextSwitchTo(u_sess->stream_cxt.stream_runtime_mem_cxt); planstmt = (PlannedStmt*)stringToNode(plan_string); /* It is safe to free plan_string after deserializing the message */ if (plan_string != NULL) pfree(plan_string); InitGlobalNodeDefinition(planstmt); statement_init_metric_context(); exec_simple_plan(planstmt); MemoryContextSwitchTo(old_cxt); // After query done, producer container is not usable anymore. StreamNodeGroup::destroy(STREAM_COMPLETE); u_sess->debug_query_id = 0; send_ready_for_query = true; } break; case 'Y': /* plan with params */ { if (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type '%c'.", firstchar))); /* Set top consumer at the very beginning. */ StreamTopConsumerIam(); /* Build stream context for stream plan. */ InitStreamContext(); u_sess->exec_cxt.under_stream_runtime = true; u_sess->pgxc_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); exec_plan_with_params(&input_message); /* After query done, producer container is not usable anymore */ StreamNodeGroup::destroy(STREAM_COMPLETE); u_sess->debug_query_id = 0; send_ready_for_query = true; } break; #endif case 'u': /* Autonomous transaction */ { u_sess->is_autonomous_session = true; Oid currentUserId = pq_getmsgint(&input_message, 4); u_sess->autonomous_parent_sessionid = pq_getmsgint64(&input_message); if (currentUserId != GetCurrentUserId()) { /* Set Session Authorization */ ResourceOwner currentOwner = NULL; currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; /* we use session memory context to remember all node info in this cluster. */ MemoryContext old = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "CheckUserOid", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_SECURITY)); t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner; SetSessionAuthorization(currentUserId, superuser_arg(currentUserId)); if (u_sess->proc_cxt.MyProcPort->user_name) pfree(u_sess->proc_cxt.MyProcPort->user_name); u_sess->proc_cxt.MyProcPort->user_name = pstrdup(GetUserNameFromId(currentUserId)); u_sess->misc_cxt.CurrentUserName = u_sess->proc_cxt.MyProcPort->user_name; #ifdef ENABLE_MULTIPLE_NODES InitMultinodeExecutor(false); PoolHandle* pool_handle = GetPoolManagerHandle(); if (pool_handle == NULL) { ereport(ERROR, (errcode(ERRCODE_IO_ERROR), errmsg("Can not connect to pool manager"))); break; } /* * We must switch to old memory context, if we use TopMemContext, * that will cause memory leak when call elog(ERROR), because TopMemContext * only is reset during thread exit. later we need refactor code section which * allocate from TopMemContext for better. TopMemcontext is a session level memory * context, we forbid allocate temp memory from TopMemcontext. */ MemoryContextSwitchTo(old); char* session_options_ptr = session_options(); /* Pooler initialization has to be made before ressource is released */ PoolManagerConnect(pool_handle, u_sess->proc_cxt.MyProcPort->database_name, u_sess->proc_cxt.MyProcPort->user_name, session_options_ptr); if (session_options_ptr != NULL) pfree(session_options_ptr); MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); #endif ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(tmpOwner); (void)MemoryContextSwitchTo(old); /* fall through */ } } case 'Q': /* simple query */ { const char* query_string = NULL; pgstat_report_trace_id(&u_sess->trace_cxt, true); query_string = pq_getmsgstring(&input_message); if (query_string == NULL) { ereport(ERROR, (errcode(ERRCODE_UNEXPECTED_NULL_VALUE), errmsg("query_string is NULL."))); } if (strlen(query_string) > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long query_string."))); } t_thrd.postgres_cxt.clobber_qstr = query_string; pq_getmsgend(&input_message); pgstatCountSQL4SessionLevel(); statement_init_metric_context(); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) { u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); u_sess->exec_cxt.RetryController->stub_.ECodeStubTest(); } #endif exec_simple_query(query_string, QUERY_MESSAGE, &input_message); /* @hdfs Add the second parameter */ if (MEMORY_TRACKING_QUERY_PEAK) ereport(LOG, (errmsg("query_string %s, peak memory %ld(kb)", query_string, (int64)(t_thrd.utils_cxt.peakedBytesInQueryLifeCycle/1024)))); u_sess->debug_query_id = 0; send_ready_for_query = true; } break; #ifdef ENABLE_MULTIPLE_NODES case 'O': /* In pooler stateless resue mode reset connection params */ { const char* query_string = NULL; char sql[PARAMS_LEN] = {0}; char* sql_strtok_r = NULL; char* slot_session_reset = NULL; char* user_name_reset = NULL; char* pgoptions_reset = NULL; char* connection_params = NULL; char* connection_temp_namespace = NULL; int err = -1; MemoryContext oldcontext; /* Only pooler stateless reuse mode in the cluster uses 'O' packets. Other paths are invalid. */ if(!IsConnFromCoord()) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Invalid packet path, remoteConnType[%d], remote_host[%s], remote_port[%s].", u_sess->attr.attr_common.remoteConnType, u_sess->proc_cxt.MyProcPort->remote_host, u_sess->proc_cxt.MyProcPort->remote_port))); } query_string = pq_getmsgstring(&input_message); t_thrd.postgres_cxt.clobber_qstr = query_string; pq_getmsgend(&input_message); pgstatCountSQL4SessionLevel(); if (strlen(query_string) + 1 > sizeof(sql)) { PrintUnexpectedBufferContent(query_string, sizeof(sql)); ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Acceptter in pooler stateless resue mode reset connection params %d > sql[%d].", (int)strlen(query_string) + 1, (int)sizeof(sql)))); } err = sprintf_s(sql, sizeof(sql), "%s", query_string); securec_check_ss(err, "", ""); slot_session_reset = strtok_r(sql, "@", &sql_strtok_r); user_name_reset = strtok_r(NULL, "@", &sql_strtok_r); pgoptions_reset = strtok_r(NULL, "@", &sql_strtok_r); connection_temp_namespace = strtok_r(NULL, "@", &sql_strtok_r); connection_params = sql_strtok_r; /* To reset slot session */ if (slot_session_reset != NULL) { exec_simple_query(slot_session_reset, QUERY_MESSAGE); } /* Reset user_name pgoptions */ if (user_name_reset != NULL && pgoptions_reset != NULL) { oldcontext = MemoryContextSwitchTo(SESS_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); /* check if role exits */ ResourceOwner currentOwner = NULL; currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "CheckUserOid", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_SECURITY)); t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner; if (!OidIsValid(get_role_oid(user_name_reset, false))) { ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("role \"%s\" does not exist", user_name_reset))); } ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(tmpOwner); if (u_sess->proc_cxt.MyProcPort->user_name) pfree(u_sess->proc_cxt.MyProcPort->user_name); if (u_sess->proc_cxt.MyProcPort->cmdline_options) pfree(u_sess->proc_cxt.MyProcPort->cmdline_options); u_sess->proc_cxt.MyProcPort->user_name = pstrdup(user_name_reset); u_sess->proc_cxt.MyProcPort->cmdline_options = pstrdup(pgoptions_reset); (void)MemoryContextSwitchTo(oldcontext); t_thrd.postgres_cxt.isInResetUserName = true; PostgresResetUsernamePgoption(u_sess->proc_cxt.MyProcPort->user_name, true); t_thrd.postgres_cxt.isInResetUserName = false; } /* set connection sesseion params , if sender params is sent "null" not exec. */ char params_null[] = "null;"; if (connection_params != NULL && strcmp(params_null, connection_params) != 0) { exec_simple_query(connection_params, QUERY_MESSAGE); } /* set temp_namespace , if sender temp_namespace is sent "null" not exec. */ if (connection_temp_namespace != NULL && strcmp(params_null, connection_temp_namespace) != 0) { exec_simple_query(connection_temp_namespace, QUERY_MESSAGE); } u_sess->debug_query_id = 0; send_ready_for_query = true; } break; case 'I': { // Procedure overrideStack int pushtype; const char* schema_name = NULL; pushtype = pq_getmsgbyte(&input_message); schema_name = pq_getmsgstring(&input_message); if (strlen(schema_name) > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long schema_name."))); } pq_getmsgend(&input_message); switch (pushtype) { case 'P': { // check the list is override max depth if (list_length(u_sess->catalog_cxt.overrideStack) > OVERRIDE_STACK_LENGTH_MAX) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("The overrideStack list has been reach the max length."))); } // push ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; MemoryContext old = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "ForPushProcedure", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner; Oid namespaceOid = get_namespace_oid(schema_name, false); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(tmpOwner); MemoryContextSwitchTo(old); // construct OverrideSearchPath struct OverrideSearchPath* search_path = (OverrideSearchPath*)palloc0(sizeof(OverrideSearchPath)); search_path->addCatalog = true; search_path->addTemp = true; search_path->schemas = list_make1_oid(namespaceOid); ereport(DEBUG2, (errmodule(MOD_SCHEMA), errmsg("recv pushschema:%s", schema_name))); if (SUPPORT_BIND_SEARCHPATH) { if (!u_sess->catalog_cxt.baseSearchPathValid) { recomputeNamespacePath(); } /* * If SUPPORT_BIND_SEARCHPATH is true, * add system's search_path. * When objects cannot be found in the * namespace of current function, find * them in search_path list. * Otherwise, we can only find objects in * the namespace of current function. */ ListCell* l = NULL; /* Use baseSearchPath not activeSearchPath. */ foreach (l, u_sess->catalog_cxt.baseSearchPath) { Oid namespaceId = lfirst_oid(l); /* * Append namespaceId to searchpath. */ if (!list_member_oid(search_path->schemas, namespaceId)) { search_path->schemas = lappend_oid(search_path->schemas, namespaceId); } } } PushOverrideSearchPath(search_path, true); pfree(search_path); } break; case 'p': { // pop if (u_sess->catalog_cxt.overrideStack) PopOverrideSearchPath(); } break; case 'C': { /* set create command schema */ u_sess->catalog_cxt.setCurCreateSchema = true; u_sess->catalog_cxt.curCreateSchema = MemoryContextStrdup(u_sess->top_transaction_mem_cxt, schema_name); } break; case 'F': { u_sess->catalog_cxt.setCurCreateSchema = false; pfree_ext(u_sess->catalog_cxt.curCreateSchema); } break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("Invalid message type %d for procedure overrideStack.", pushtype))); break; } } break; case 'i': /* used for instrumentation */ { int sub_command = 0; sub_command = pq_getmsgbyte(&input_message); switch (sub_command) { case 'q': /* recv unique sql id from CN, and set it to DN session */ { u_sess->unique_sql_cxt.unique_sql_cn_id = (uint32)pq_getmsgint(&input_message, sizeof(uint32)); u_sess->unique_sql_cxt.unique_sql_user_id = (Oid)pq_getmsgint(&input_message, sizeof(uint32)); u_sess->unique_sql_cxt.unique_sql_id = (uint64)pq_getmsgint64(&input_message); u_sess->slow_query_cxt.slow_query.unique_sql_id = u_sess->unique_sql_cxt.unique_sql_id; pgstat_report_unique_sql_id(false); Oid procId = 0; uint64 queryId = 0; int64 stamp = 0; if (t_thrd.proc->workingVersionNum >= SLOW_QUERY_VERSION || input_message.cursor < input_message.len ) { procId = (Oid)pq_getmsgint(&input_message, sizeof(uint32)); queryId = (uint64)pq_getmsgint64(&input_message); stamp = (int64)pq_getmsgint64(&input_message); u_sess->wlm_cxt->wlm_params.qid.procId = procId; u_sess->wlm_cxt->wlm_params.qid.queryId = queryId; u_sess->wlm_cxt->wlm_params.qid.stamp = stamp; } ereport(DEBUG1, (errmodule(MOD_INSTR), errmsg("[UniqueSQL] " "Received new unique cn_id: %u, user_id: %u, sql id: %lu, procId %u, queryId %lu, stamp %ld", u_sess->unique_sql_cxt.unique_sql_cn_id, u_sess->unique_sql_cxt.unique_sql_user_id, u_sess->unique_sql_cxt.unique_sql_id, procId, queryId, stamp))); } break; case 'S': { rc = memcpy_s(&u_sess->globalSessionId.sessionId, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "\0", "\0"); u_sess->globalSessionId.nodeId = (uint32)pq_getmsgint(&input_message, sizeof(uint32)); u_sess->globalSessionId.seq = pg_atomic_add_fetch_u64(&g_instance.global_session_seq, 1); pgstat_report_global_session_id(u_sess->globalSessionId); t_thrd.proc->globalSessionId = u_sess->globalSessionId; ereport(DEBUG5, (errmodule(MOD_INSTR), errmsg("[Global session id] node:%u, session id: %lu, seq id:%lu", u_sess->globalSessionId.nodeId, u_sess->globalSessionId.sessionId, u_sess->globalSessionId.seq))); } break; case 's': { /* get unique sql ids, then reply the unique sqls stat */ uint32 count = pq_getmsgint(&input_message, sizeof(uint32)); ReplyUniqueSQLsStat(&input_message, count); } break; case 'K': /* msg type for get sql-RT count */ { pgstat_reply_percentile_record_count(); pq_getmsgend(&input_message); } break; case 'k': /* msg type for replay sql-RT info */ { pgstat_reply_percentile_record(); pq_getmsgend(&input_message); } break; default: break; } pq_getmsgend(&input_message); /* q - for unique sql id */ } break; case 'h': /* @hdfs hybridmessage query */ { const char* query_string = NULL; /* get the node id. */ u_sess->pgxc_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); /* get hybridmesage */ query_string = pq_getmsgstring(&input_message); t_thrd.postgres_cxt.clobber_qstr = query_string; pq_getmsgend(&input_message); pgstatCountSQL4SessionLevel(); statement_init_metric_context(); /* * @hdfs * exec_simpel_query. We set the second paramter to 1 * when we get the hybirmessage. It's default value is 0. */ exec_simple_query(query_string, HYBRID_MESSAGE); u_sess->debug_query_id = 0; send_ready_for_query = true; } break; #endif case 'P': /* parse */ { const char* stmt_name = NULL; const char* query_string = NULL; int numParams; Oid* paramTypes = NULL; char* paramModes = NULL; char** paramTypeNames = NULL; /* DN: get the node id. */ if (IS_PGXC_DATANODE && IsConnFromCoord()) u_sess->pgxc_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); stmt_name = pq_getmsgstring(&input_message); query_string = pq_getmsgstring(&input_message); if (strlen(query_string) > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long query_string."))); } numParams = pq_getmsgint(&input_message, 2); paramTypes = (Oid*)palloc(numParams * sizeof(Oid)); if (numParams > 0) { int i; #ifdef ENABLE_MULTIPLE_NODES if (IsConnFromCoord()) { paramTypeNames = (char**)palloc(numParams * sizeof(char*)); for (i = 0; i < numParams; i++) { paramTypeNames[i] = (char*)pq_getmsgstring(&input_message); } } else #endif /* ENABLE_MULTIPLE_NODES */ { for (i = 0; i < numParams; i++) paramTypes[i] = pq_getmsgint(&input_message, 4); } } #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); #endif #ifndef ENABLE_MULTIPLE_NODES if (enable_out_param_override()) { int numModes = pq_getmsgint(&input_message, 2); if (numModes > 0) { if (numModes != numParams) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("param type num %d does't equal to param mode num %d.", numParams, numModes))); } paramModes = (char*)palloc(numModes * sizeof(char)); for (int i = 0; i < numParams; i++) { const char *mode = pq_getmsgbytes(&input_message, 1); if (*mode != PROARGMODE_IN && *mode != PROARGMODE_OUT && *mode != PROARGMODE_INOUT) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("The %dth param mode %s does't exist.", i, mode))); } paramModes[i] = *mode; } } } #endif pq_getmsgend(&input_message); statement_init_metric_context(); instr_stmt_report_trace_id(u_sess->trace_cxt.trace_id); exec_parse_message(query_string, stmt_name, paramTypes, paramTypeNames, paramModes, numParams); statement_commit_metirc_context(); /* * since AbortTransaction can't clean named prepared statement, we need to * cache prepared statement name here, and clean it later in long jump routine. * only need to cache it if parse successfully, then cleaning is necessary if retry * happens. */ if (IsStmtRetryEnabled() && stmt_name[0] != '\0') { u_sess->exec_cxt.RetryController->CacheStmtName(stmt_name); } } break; case 'B': /* bind */ #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); #endif statement_init_metric_context(); /* * this message is complex enough that it seems best to put * the field extraction out-of-line */ instr_stmt_report_trace_id(u_sess->trace_cxt.trace_id); exec_bind_message(&input_message); break; case 'E': /* execute */ { const char* portal_name = NULL; int max_rows; pgstat_report_trace_id(&u_sess->trace_cxt, true); if ((unsigned int)input_message.len > SECUREC_MEM_MAX_LEN) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid execute message"))); if (lightProxy::processMsg(EXEC_MESSAGE, &input_message)) { break; } char* completionTag = (char*)palloc0(COMPLETION_TAG_BUFSIZE * sizeof(char)); if (u_sess->exec_cxt.CurrentOpFusionObj != NULL && IS_SINGLE_NODE) { if (IS_UNIQUE_SQL_TRACK_TOP) { SetIsTopUniqueSQL(true); } } bool isQueryCompleted = false; if (OpFusion::process(FUSION_EXECUTE, &input_message, completionTag, true, &isQueryCompleted)) { if(isQueryCompleted) { CommandCounterIncrement(); EndCommand(completionTag, (CommandDest)t_thrd.postgres_cxt.whereToSendOutput); } else { if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) pq_putemptymessage('s'); } pfree_ext(completionTag); t_thrd.postgres_cxt.debug_query_string = NULL; if (MEMORY_TRACKING_QUERY_PEAK) ereport(LOG, (errmsg("execute opfusion, peak memory %ld(kb)", (int64)(t_thrd.utils_cxt.peakedBytesInQueryLifeCycle/1024)))); break; } pfree_ext(completionTag); /* Set statement_timestamp() */ SetCurrentStatementStartTimestamp(); portal_name = pq_getmsgstring(&input_message); max_rows = pq_getmsgint(&input_message, 4); pq_getmsgend(&input_message); pgstatCountSQL4SessionLevel(); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); #endif exec_execute_message(portal_name, max_rows); } break; #ifdef ENABLE_MULTIPLE_NODES case 'k': { /* no k msg from cn now, but keep these code for gray upgrade */ if (!ENABLE_DN_GPC) ereport(ERROR, (errmodule(MOD_GPC), errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("cn has enabled global plan cache but dn %s disabled", g_instance.attr.attr_common.PGXCNodeName))); if (t_thrd.proc->workingVersionNum >= 92128) { rc = memcpy_s(&u_sess->sess_ident.cn_sessid, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc,"\0","\0"); rc = memcpy_s(&u_sess->sess_ident.cn_timeline, sizeof(uint32), pq_getmsgbytes(&input_message, sizeof(uint32)), sizeof(uint32)); securec_check(rc,"",""); rc = memcpy_s(&u_sess->sess_ident.cn_nodeid, sizeof(uint32), pq_getmsgbytes(&input_message, sizeof(uint32)), sizeof(uint32)); securec_check(rc,"",""); PGXCNode_HandleGPC handle_type; rc = memcpy_s(&handle_type, sizeof(PGXCNode_HandleGPC), pq_getmsgbytes(&input_message, sizeof(PGXCNode_HandleGPC)), sizeof(PGXCNode_HandleGPC)); securec_check(rc,"",""); u_sess->pgxc_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); pq_getmsgend(&input_message); } else { uint64 origin_global_session_id; rc = memcpy_s(&origin_global_session_id, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc,"",""); rc = memcpy_s(&u_sess->sess_ident.cn_timeline, sizeof(uint32), pq_getmsgbytes(&input_message, sizeof(uint32)), sizeof(uint32)); securec_check(rc,"",""); u_sess->sess_ident.cn_nodeid = (uint32)(origin_global_session_id >> 48); u_sess->sess_ident.cn_sessid = (uint64)(origin_global_session_id & 0xffffffffffff); PGXCNode_HandleGPC handle_type; rc = memcpy_s(&handle_type, sizeof(PGXCNode_HandleGPC), pq_getmsgbytes(&input_message, sizeof(PGXCNode_HandleGPC)), sizeof(PGXCNode_HandleGPC)); securec_check(rc,"",""); pq_getmsgend(&input_message); } } break; #endif case 'F': /* fastpath function call */ /* Report query to various monitoring facilities. */ pgstat_report_activity(STATE_FASTPATH, NULL); set_ps_display("", false); exec_init_poolhandles(); /* start an xact for this function invocation */ start_xact_command(); /* * Note: we may at this point be inside an aborted * transaction. We can't throw error for that until we've * finished reading the function-call message, so * HandleFunctionRequest() must check for it after doing so. * Be careful not to do anything that assumes we're inside a * valid transaction here. */ /* switch back to message context */ MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); if (HandleFunctionRequest(&input_message) == EOF) { /* lost frontend connection during F message input */ /* * Reset whereToSendOutput to prevent ereport from * attempting to send any more messages to client. */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) t_thrd.postgres_cxt.whereToSendOutput = DestNone; proc_exit(0); } /* commit the function-invocation transaction */ finish_xact_command(); u_sess->debug_query_id = 0; send_ready_for_query = true; break; case 'C': /* close */ { int close_type; const char* closeTarget = NULL; close_type = pq_getmsgbyte(&input_message); closeTarget = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); if (closeTarget && strlen(closeTarget) > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long closeTarget."))); } exec_init_poolhandles(); switch (close_type) { case 'S': if (closeTarget[0] != '\0') { DropPreparedStatement(closeTarget, false); } else { /* special-case the unnamed statement */ drop_unnamed_stmt(); } break; case 'P': { Portal portal; if (IS_PGXC_COORDINATOR && closeTarget[0] != '\0') { lightProxy::removeLightProxy(closeTarget); } if (IS_PGXC_DATANODE && closeTarget[0] != '\0') { OpFusion* curr = OpFusion::locateFusion(closeTarget); if (curr != NULL) { if (curr->IsGlobal()) { curr->clean(); OpFusion::tearDown(curr); } else { curr->clean(); OpFusion::removeFusionFromHtab(closeTarget); } } } portal = GetPortalByName(closeTarget); if (PortalIsValid(portal)) { PortalDrop(portal, false); } } break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid CLOSE message subtype %d", close_type))); break; } if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) { pq_putemptymessage('3'); /* CloseComplete */ } } break; case 'D': /* describe */ { int describe_type; const char* describe_target = NULL; if ((unsigned int)input_message.len > SECUREC_MEM_MAX_LEN) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid describe message"))); } if (lightProxy::processMsg(DESC_MESSAGE, &input_message)) { break; } if (OpFusion::process(FUSION_DESCRIB, &input_message, NULL, false, NULL)) { break; } /* Set statement_timestamp() (needed for xact) */ SetCurrentStatementStartTimestamp(); describe_type = pq_getmsgbyte(&input_message); describe_target = pq_getmsgstring(&input_message); pq_getmsgend(&input_message); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) { u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); } #endif exec_init_poolhandles(); switch (describe_type) { case 'S': exec_describe_statement_message(describe_target); break; case 'P': exec_describe_portal_message(describe_target); break; default: ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid DESCRIBE message subtype %d", describe_type))); break; } } break; case 'H': /* flush */ pq_getmsgend(&input_message); if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) { StmtRetrySetQuerytUnsupportedFlag(); pq_flush(); } break; case 'S': /* sync */ pq_getmsgend(&input_message); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) { u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); } #endif if (u_sess->xact_cxt.pbe_execute_complete == true) { finish_xact_command(); } else { u_sess->xact_cxt.pbe_execute_complete = true; } u_sess->debug_query_id = 0; send_ready_for_query = true; if (IsStmtRetryEnabled()) { t_thrd.log_cxt.flush_message_immediately = false; } break; /* * 'X' means that the frontend is closing down the socket. EOF * means unexpected loss of frontend connection. Either way, * perform normal shutdown. */ case 'X': case EOF: /* unified auditing logout */ audit_processlogout_unified(); /* * isSingleMode means we are doing initdb. Some temp tables * will be created to store intermediate result, so should do cleaning * when finished. * xc_maintenance_mode is for cluster resize, temp table created * during it should be clean too. * Drop temp schema if IS_SINGLE_NODE. */ RemoveTempNamespace(); InitThreadLocalWhenSessionExit(); if (IS_THREAD_POOL_WORKER) { (void)gs_signal_block_sigusr2(); t_thrd.threadpool_cxt.worker->CleanUpSession(false); (void)gs_signal_unblock_sigusr2(); break; } else { /* * Reset whereToSendOutput to prevent ereport from attempting * to send any more messages to client. */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) t_thrd.postgres_cxt.whereToSendOutput = DestNone; /* * NOTE: if you are tempted to add more code here, DON'T! * Whatever you had in mind to do should be set up as an * on_proc_exit or on_shmem_exit callback, instead. Otherwise * it will fail to be called during other backend-shutdown * scenarios. */ ResetDfsHandlerPtrs(); proc_exit(0); } /* fall through */ case 'd': /* copy data */ case 'c': /* copy done */ case 'f': /* copy fail */ /* * Accept but ignore these messages, per protocol spec; we * probably got here because a COPY failed, and the frontend * is still sending data. */ break; #ifdef ENABLE_MULTIPLE_NODES case 'M': /* Command ID */ { CommandId cid = (CommandId)pq_getmsgint(&input_message, 4); ereport(DEBUG1, (errmsg("Received cmd id %u", cid))); SaveReceivedCommandId(cid); } break; case 'q': /* query id */ { errno_t rc = EOK; /* Set the query id we were passed down */ rc = memcpy_s(&u_sess->debug_query_id, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "\0", "\0"); ereport(DEBUG1, (errmsg("Received new query id %lu", u_sess->debug_query_id))); pq_getmsgend(&input_message); pgstat_report_queryid(u_sess->debug_query_id); } break; case 'e': /* threadid */ { /* Set the thread id we were passed down */ u_sess->instr_cxt.gs_query_id->procId = (Oid)pq_getmsgint(&input_message, 4); u_sess->exec_cxt.need_track_resource = (bool)pq_getmsgbyte(&input_message); pq_getmsgend(&input_message); } break; case 'r': /* query id with sync */ { /* We only process 'r' message on PGCX_DATANODE. */ if (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type '%c'.", firstchar))); /* Set top consumer at the very beginning. */ StreamTopConsumerIam(); /* Set the query id we were passed down */ rc = memcpy_s(&u_sess->debug_query_id, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "\0", "\0"); ereport(DEBUG1, (errmsg("Received new query id %lu", u_sess->debug_query_id))); pq_getmsgend(&input_message); /* * Set top consumer for stream plan to ensure debug_query_id * being removed when errors occurs between 'r' message and 'Z' message. * The flag isStreamTopConsumer will be reset in StreamNodeGroup::destroy * 'Z' and 'Y' messages. */ StreamNodeGroup::grantStreamConnectPermission(); pq_putemptymessage('O'); /* PlanIdComplete */ pq_flush(); } break; case 'g': /* gxid */ { errno_t rc = EOK; /* Set the GXID we were passed down */ TransactionId gxid; bool is_check_xid = false; rc = memcpy_s(&gxid, sizeof(TransactionId), pq_getmsgbytes(&input_message, sizeof(TransactionId)), sizeof(TransactionId)); securec_check(rc, "\0", "\0"); /* get the tag indicates if it's a special xid for check, true(xid for check) */ rc = memcpy_s(&is_check_xid, sizeof(bool), pq_getmsgbytes(&input_message, sizeof(bool)), sizeof(bool)); securec_check(rc, "\0", "\0"); ereport(DEBUG1, (errmsg("Received new gxid %lu", gxid))); /* CN function may use gxid from CN to create tmp lib name */ t_thrd.xact_cxt.cn_xid = gxid; pq_getmsgend(&input_message); } break; case 's': /* snapshot */ { errno_t rc = EOK; int gtm_snapshot_type = -1; if (GTM_LITE_MODE) { /* gtm lite mode */ rc = memcpy_s(&ss_need_sync_wait_all, sizeof(int), pq_getmsgbytes(&input_message, sizeof(bool)), sizeof(bool)); securec_check(rc,"\0","\0"); rc = memcpy_s(&cn_xc_maintain_mode, sizeof(bool), pq_getmsgbytes(&input_message, sizeof(bool)), sizeof(bool)); securec_check(rc,"\0","\0"); rc = memcpy_s(&csn, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc,"\0","\0"); rc = memcpy_s(>m_snapshot_type, sizeof(int), pq_getmsgbytes(&input_message, sizeof(int)), sizeof(int)); securec_check(rc,"\0","\0"); remote_gtm_mode = pq_getmsgbyte(&input_message); pq_getmsgend(&input_message); /* if message length is correct, set u_sess variables */ u_sess->utils_cxt.cn_xc_maintain_mode = cn_xc_maintain_mode; if (gtm_snapshot_type == GTM_SNAPSHOT_TYPE_LOCAL) { UnsetGlobalSnapshotData(); } else { u_sess->utils_cxt.is_autovacuum_snapshot = (gtm_snapshot_type == GTM_SNAPSHOT_TYPE_AUTOVACUUM) ? true : false; SetGlobalSnapshotData(InvalidTransactionId, InvalidTransactionId, csn, InvalidTransactionTimeline, ss_need_sync_wait_all); /* quickly set my recent global xmin */ u_sess->utils_cxt.RecentGlobalXmin = GetOldestXmin(NULL, true); u_sess->utils_cxt.RecentGlobalCatalogXmin = GetOldestCatalogXmin(); } } /* check gtm mode, remote should be false, local cannot be true */ if (remote_gtm_mode != g_instance.attr.attr_storage.enable_gtm_free && (t_thrd.proc->workingVersionNum >= 92012)) ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("gtm mode unconsistency, remote mode is %s, local mode is %s.", remote_gtm_mode ? "on" : "off", g_instance.attr.attr_storage.enable_gtm_free ? "on" : "off"))); /* Should not do any distributed operation when CN u_sess->attr.attr_common.xc_maintenance_mode is true */ if (u_sess->utils_cxt.cn_xc_maintain_mode != u_sess->attr.attr_common.xc_maintenance_mode) ereport(WARNING, (errmsg("cn_xc_maintain_mode: %s, xc_maintain_mode: %s", u_sess->utils_cxt.cn_xc_maintain_mode ? "on" : "off", u_sess->attr.attr_common.xc_maintenance_mode ? "on" : "off"))); break; } case 't': /* timestamp */ /* Set statement_timestamp() */ gtmstart_timestamp = (TimestampTz)pq_getmsgint64(&input_message); stmtsys_timestamp = (TimestampTz)pq_getmsgint64(&input_message); pq_getmsgend(&input_message); /* * Set in xact.x the static Timestamp difference value with GTM * and the timestampreceivedvalues for Datanode reference */ SetCurrentGTMTimestamp(gtmstart_timestamp); t_thrd.xact_cxt.timestamp_from_cn = true; SetCurrentStmtTimestamp(stmtsys_timestamp); SetCurrentGTMDeltaTimestamp(); break; case 'b': /* barrier */ { int command; char* id = NULL; command = pq_getmsgbyte(&input_message); id = (char*)pq_getmsgstring(&input_message); pq_getmsgend(&input_message); switch (command) { case CREATE_BARRIER_PREPARE: ProcessCreateBarrierPrepare(id, false); break; case CREATE_SWITCHOVER_BARRIER_PREPARE: ProcessCreateBarrierPrepare(id, true); break; case CREATE_BARRIER_END: ProcessCreateBarrierEnd(id); break; case CREATE_BARRIER_COMMIT: ProcessCreateBarrierCommit(id); break; case CREATE_BARRIER_EXECUTE: ProcessCreateBarrierExecute(id); break; case CREATE_SWITCHOVER_BARRIER_EXECUTE: ProcessCreateBarrierExecute(id, true); break; case BARRIER_QUERY_ARCHIVE: ProcessBarrierQueryArchive(id); break; default: ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Invalid command received"))); break; } } break; case 'W': { WLMGeneralParam* g_wlm_params = &u_sess->wlm_cxt->wlm_params; g_wlm_params->qid.procId = (Oid)pq_getmsgint(&input_message, 4); g_wlm_params->qid.queryId = (uint64)pq_getmsgint64(&input_message); int flags[2]; flags[0] = (int)pq_getmsgint(&input_message, 4); g_wlm_params->cpuctrl = *((unsigned char*)&flags[0]); g_wlm_params->memtrack = *((unsigned char*)&flags[0] + sizeof(char)); g_wlm_params->iostate = *((unsigned char*)&flags[0] + 2 * sizeof(char)); g_wlm_params->iotrack = *((unsigned char*)&flags[0] + 3 * sizeof(char)); flags[1] = (int)pq_getmsgint(&input_message, 4); g_wlm_params->iocontrol = *((unsigned char*)&flags[1]); g_wlm_params->complicate = *((unsigned char*)&flags[1] + sizeof(char)) ? 0 : 1; g_wlm_params->dopvalue = (unsigned char)pq_getmsgint(&input_message, 4); g_wlm_params->io_priority = pq_getmsgint(&input_message, 4); g_wlm_params->iops_limits = pq_getmsgint(&input_message, 4); g_wlm_params->qid.stamp = (TimestampTz)pq_getmsgint64(&input_message); /* get the name of cgroup */ const char* cgname = pq_getmsgstring(&input_message); /* get the name of respool */ const char* respool = pq_getmsgstring(&input_message); if (StringIsValid(respool)) { rc = snprintf_s(g_wlm_params->rpdata.rpname, sizeof(g_wlm_params->rpdata.rpname), sizeof(g_wlm_params->rpdata.rpname) - 1, "%s", respool); securec_check_ss(rc, "", ""); } /* get the node group name */ const char* ngname = pq_getmsgstring(&input_message); if (StringIsValid(ngname)) { rc = snprintf_s(g_wlm_params->ngroup, sizeof(g_wlm_params->ngroup), sizeof(g_wlm_params->ngroup) - 1, "%s", ngname); securec_check_ss(rc, "", ""); } /* local dn has vcgroup */ if (*g_instance.wlm_cxt->local_dn_ngname && *g_wlm_params->ngroup && 0 != strcmp(g_instance.wlm_cxt->local_dn_ngname, g_wlm_params->ngroup)) { /* Get the node group information */ t_thrd.wlm_cxt.thread_node_group = g_instance.wlm_cxt->local_dn_nodegroup; /* check if the control group is valid and set it for foreign user */ if (t_thrd.wlm_cxt.thread_node_group->foreignrp) { u_sess->wlm_cxt->local_foreign_respool = t_thrd.wlm_cxt.thread_node_group->foreignrp; } else { WLMSetControlGroup(GSCGROUP_INVALID_GROUP); } } else { /* Get the node group information */ t_thrd.wlm_cxt.thread_node_group = WLMMustGetNodeGroupFromHTAB(g_wlm_params->ngroup); WLMSetControlGroup(cgname); } t_thrd.wlm_cxt.thread_climgr = &t_thrd.wlm_cxt.thread_node_group->climgr; t_thrd.wlm_cxt.thread_srvmgr = &t_thrd.wlm_cxt.thread_node_group->srvmgr; pq_getmsgend(&input_message); } break; case 'w': { Assert(StringIsValid(g_instance.attr.attr_common.PGXCNodeName)); exec_init_poolhandles(); /* start an xact for this function invocation */ start_xact_command(); if (is_pgxc_central_nodename(g_instance.attr.attr_common.PGXCNodeName)) { dywlm_server_receive(&input_message); } else { dywlm_client_receive(&input_message); } finish_xact_command(); } break; case 'R': /* reply collect info */ { /* start an xact for this function invocation */ start_xact_command(); WLMLocalInfoCollector(&input_message); finish_xact_command(); } break; case 'A': /* msg type for compute pool */ process_request(&input_message); break; #ifdef ENABLE_MULTIPLE_NODES case 'L': /* link gc_fdw */ { start_xact_command(); PgFdwRemoteReply(&input_message); finish_xact_command(); } break; #endif case 'n': /* commiting */ { /* Get the csn passed down */ rc = memcpy_s(&csn, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "", ""); pq_getmsgend(&input_message); int nchildren; TransactionId *children = NULL; TransactionId xid = GetTopTransactionIdIfAny(); Assert(TransactionIdIsValid(xid)); nchildren = xactGetCommittedChildren(&children); /* Set the commit csn to commit_in_progress */ SetXact2CommitInProgress(InvalidTransactionId, csn); XLogInsertStandbyCSNCommitting(xid, csn, children, nchildren); /* Send back response */ pq_putemptymessage('m'); pq_flush(); break; } case 'N': /* commit csn */ /* Set the commit csn passed down */ rc = memcpy_s(&csn, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "", ""); pq_getmsgend(&input_message); if (!COMMITSEQNO_IS_COMMITTED(csn)) { ereport(ERROR, (errmsg("Received an invalid commit csn: %lu.", csn))); } setCommitCsn(csn); ereport(DEBUG1, (errmsg( "proc %lu %d set csn %lu", t_thrd.proc->pid, t_thrd.proc->pgprocno, t_thrd.proc->commitCSN))); break; #ifdef ENABLE_MULTIPLE_NODES case 'l': /* get and handle csn for csnminsync */ rc = memcpy_s(&csn, sizeof(uint64), pq_getmsgbytes(&input_message, sizeof(uint64)), sizeof(uint64)); securec_check(rc, "", ""); rc = memcpy_s(&csn_type, sizeof(CsnType), pq_getmsgbytes(&input_message, sizeof(CsnType)), sizeof(CsnType)); securec_check(rc, "", ""); pq_getmsgend(&input_message); if (module_logging_is_on(MOD_TRANS_SNAPSHOT)) { ereport(LOG, (errmodule(MOD_TRANS_SNAPSHOT), errmsg("get csn : %lu, csn_type: %d", csn, (int)csn_type))); } if (!COMMITSEQNO_IS_COMMITTED(csn)) { ereport(ERROR, (errmsg("Received an invalid commit csn: %lu.", csn))); } if (csn_type == FETCH_CSN) { if (csn >= COMMITSEQNO_FIRST_NORMAL) { UpdateNextMaxKnownCSN(csn); } /* calculate and send local_csn_min to Center Cn */ t_thrd.xact_cxt.ShmemVariableCache->local_csn_min = calculate_local_csn_min(); ereport(DEBUG1, (errmsg("report local_csn_min %lu to CCN", t_thrd.xact_cxt.ShmemVariableCache->local_csn_min))); send_local_csn_min_to_ccn(); } else { /* get global_csn_min */ CommitSeqNo currentNextCommitSeqNo = pg_atomic_read_u64(&t_thrd.xact_cxt.ShmemVariableCache->nextCommitSeqNo); t_thrd.xact_cxt.ShmemVariableCache->cutoff_csn_min = (csn < currentNextCommitSeqNo) ? csn : currentNextCommitSeqNo; ereport(DEBUG1, (errmsg("set the cutoff_csn_min %lu", t_thrd.xact_cxt.ShmemVariableCache->cutoff_csn_min))); forward_recent_global_xmin(); } break; #endif case 'j': /* check gtm mode */ remote_gtm_mode = pq_getmsgbyte(&input_message); pq_getmsgend(&input_message); /* check gtm mode, remote should be true, local cannot be false */ if (remote_gtm_mode != g_instance.attr.attr_storage.enable_gtm_free) ereport(FATAL, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("gtm mode unconsistency, remote mode is %s, local mode is %s.", remote_gtm_mode ? "on" : "off", g_instance.attr.attr_storage.enable_gtm_free ? "on" : "off"))); break; #endif case 'U': /* msg type for batch Bind-Execute for PBE */ { if (!u_sess->attr.attr_common.support_batch_bind) ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Need to set support_batch_bind=true if executing batch"))); /* * reset unique sql start time, otherwise fusionExecute will repeatly report * elapsed time more. */ u_sess->unique_sql_cxt.unique_sql_start_time = 0; pgstatCountSQL4SessionLevel(); statement_init_metric_context(); #ifdef USE_RETRY_STUB if (IsStmtRetryEnabled()) u_sess->exec_cxt.RetryController->stub_.StartOneStubTest(firstchar); #endif exec_init_poolhandles(); /* * Enable pbe optimization in batch mode, cause it may generate too many cplan * when enable_pbe_optimization is false, which may consume lots of memory and * lead to 'memory alloc failed'. To avoid this problem, enable pbe optimization * to use gplan in this batch. */ bool original = u_sess->attr.attr_sql.enable_pbe_optimization; u_sess->attr.attr_sql.enable_pbe_optimization = true; exec_batch_bind_execute(&input_message); u_sess->attr.attr_sql.enable_pbe_optimization = original; if (is_unique_sql_enabled() && is_local_unique_sql()) { UpdateUniqueSQLStat(NULL, NULL, GetCurrentStatementLocalStartTimestamp()); } } break; #ifdef ENABLE_MULTIPLE_NODES case 'z': /* pbe for ddl */ exec_get_ddl_params(&input_message); break; case 'G': /* MSG_TYPE_PGXC_BUCKET_MAP for PGXCBucketMap and PGXCNodeId */ { if (u_sess->top_transaction_mem_cxt == NULL) { ereport( ERROR, (errcode(ERRCODE_INVALID_TRANSACTION_STATE), errmsg("current transaction is not start"))); } if (!IS_PGXC_DATANODE) ereport( ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Only dn can receive the bucket map"))); t_thrd.xact_cxt.PGXCNodeId = pq_getmsgint(&input_message, 4); if (t_thrd.xact_cxt.PGXCNodeId != MAX_DN_NODE_NUM) { int len = input_message.len - 4; if (t_thrd.xact_cxt.PGXCBucketCnt != (int)(len / sizeof(uint2))) { pfree_ext(t_thrd.xact_cxt.PGXCBucketMap); t_thrd.xact_cxt.PGXCBucketCnt = len / sizeof(uint2); t_thrd.xact_cxt.PGXCBucketMap = (uint2*)MemoryContextAlloc(u_sess->top_transaction_mem_cxt, len); } rc = memcpy_s(t_thrd.xact_cxt.PGXCBucketMap, len, pq_getmsgbytes(&input_message, len), len); securec_check(rc, "", ""); } pq_getmsgend(&input_message); } break; #endif case 'y': /* sequence from cn 2 dn */ { if (!IS_PGXC_DATANODE) ereport( ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Only cn can receive the sequence update"))); int num = pq_getmsgint(&input_message, 4); for (int i = 0; i < num; i++) { int64 result; char* dbname = NULL; char* schemaname = NULL; char* seqname = NULL; int result_signed; result_signed = pq_getmsgbyte(&input_message); result = pq_getmsgint64(&input_message); if (result_signed == '-') { result *= -1; } dbname = (char*)pq_getmsgstring(&input_message); schemaname = (char*)pq_getmsgstring(&input_message); seqname = (char*)pq_getmsgstring(&input_message); List* namelist = NIL; namelist = lappend(namelist, makeString(dbname)); namelist = lappend(namelist, makeString(schemaname)); namelist = lappend(namelist, makeString(seqname)); processUpdateSequenceMsg(namelist, result); } flushSequenceMsg(); } break; case 'J': { char* trace_id = NULL; trace_id = (char*)pq_getmsgstring(&input_message); pq_getmsgend(&input_message); if (strlen(trace_id) > MAX_TRACE_ID_SIZE -1) { trace_id[MAX_TRACE_ID_SIZE - 1] = '\0'; ereport(WARNING, (errmsg("trace_id length cannot exceed %d", MAX_TRACE_ID_SIZE - 1))); } errno_t rc = memcpy_s(u_sess->trace_cxt.trace_id, MAX_TRACE_ID_SIZE, trace_id, strlen(trace_id) + 1); securec_check(rc, "\0", "\0"); elog(DEBUG1, "trace_id:%s start", u_sess->trace_cxt.trace_id); } break; #ifdef ENABLE_MULTIPLE_NODES case 'T': { LWLockAcquire(XLogMaxCSNLock, LW_SHARED); CommitSeqNo maxCSN = t_thrd.xact_cxt.ShmemVariableCache->xlogMaxCSN; LWLockRelease(XLogMaxCSNLock); StringInfoData buf; pq_beginmessage(&buf, 'J'); pq_sendint64(&buf, maxCSN); pq_sendint8(&buf, t_thrd.postmaster_cxt.HaShmData->is_hadr_main_standby); pq_endmessage(&buf); pq_flush(); } break; #endif default: ereport(FATAL, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid frontend message type %c", firstchar))); break; } } /* end of input-reading loop */ gstrace_exit(GS_TRC_ID_PostgresMain); /* can't get here because the above loop never exits */ Assert(false); return 1; /* keep compiler quiet */ } /* * Obtain platform stack depth limit (in bytes) * * Return -1 if unknown */ long get_stack_depth_rlimit(void) { #if defined(HAVE_GETRLIMIT) && defined(RLIMIT_STACK) /* This won't change after process launch, so check just once */ if (t_thrd.postgres_cxt.val == 0) { struct rlimit rlim; if (getrlimit(RLIMIT_STACK, &rlim) < 0) t_thrd.postgres_cxt.val = -1; else if (rlim.rlim_cur == RLIM_INFINITY) t_thrd.postgres_cxt.val = LONG_MAX; /* rlim_cur is probably of an unsigned type, so check for overflow */ else if (rlim.rlim_cur >= LONG_MAX) t_thrd.postgres_cxt.val = LONG_MAX; else t_thrd.postgres_cxt.val = rlim.rlim_cur; } return t_thrd.postgres_cxt.val; #else /* no getrlimit */ #if defined(WIN32) || defined(__CYGWIN__) /* On Windows we set the backend stack size in src/backend/Makefile */ return WIN32_STACK_RLIMIT; #else /* not windows ... give up */ return -1; #endif #endif } static THR_LOCAL struct rusage Save_r; void ResetUsage(void) { getrusage(RUSAGE_THREAD, &Save_r); gettimeofday(&t_thrd.postgres_cxt.Save_t, NULL); } void ShowUsage(const char* title) { StringInfoData str; struct timeval user, sys; struct timeval elapse_t; struct rusage r; errno_t errorno = EOK; errorno = memset_s(&elapse_t, sizeof(elapse_t), 0, sizeof(struct timeval)); securec_check(errorno, "\0", "\0"); getrusage(RUSAGE_THREAD, &r); gettimeofday(&elapse_t, NULL); errorno = memcpy_s((char*)&user, sizeof(user), (char*)&r.ru_utime, sizeof(user)); securec_check(errorno, "\0", "\0"); errorno = memcpy_s((char*)&sys, sizeof(sys), (char*)&r.ru_stime, sizeof(sys)); securec_check(errorno, "\0", "\0"); if (elapse_t.tv_usec < t_thrd.postgres_cxt.Save_t.tv_usec) { elapse_t.tv_sec--; elapse_t.tv_usec += 1000000; } if (r.ru_utime.tv_usec < Save_r.ru_utime.tv_usec) { r.ru_utime.tv_sec--; r.ru_utime.tv_usec += 1000000; } if (r.ru_stime.tv_usec < Save_r.ru_stime.tv_usec) { r.ru_stime.tv_sec--; r.ru_stime.tv_usec += 1000000; } /* * the only stats we don't show here are for memory usage -- i can't * figure out how to interpret the relevant fields in the rusage struct, * and they change names across o/s platforms, anyway. if you can figure * out what the entries mean, you can somehow extract resident set size, * shared text size, and unshared data and stack sizes. */ initStringInfo(&str); appendStringInfo(&str, "! system usage stats:\n"); appendStringInfo(&str, "!\t%ld.%06ld elapsed %ld.%06ld user %ld.%06ld system sec\n", (long)(elapse_t.tv_sec - t_thrd.postgres_cxt.Save_t.tv_sec), (long)(elapse_t.tv_usec - t_thrd.postgres_cxt.Save_t.tv_usec), (long)(r.ru_utime.tv_sec - Save_r.ru_utime.tv_sec), (long)(r.ru_utime.tv_usec - Save_r.ru_utime.tv_usec), (long)(r.ru_stime.tv_sec - Save_r.ru_stime.tv_sec), (long)(r.ru_stime.tv_usec - Save_r.ru_stime.tv_usec)); appendStringInfo(&str, "!\t[%ld.%06ld user %ld.%06ld sys total]\n", (long)user.tv_sec, (long)user.tv_usec, (long)sys.tv_sec, (long)sys.tv_usec); #if defined(HAVE_GETRUSAGE) appendStringInfo(&str, "!\t%ld/%ld [%ld/%ld] filesystem blocks in/out\n", r.ru_inblock - Save_r.ru_inblock, /* they only drink coffee at dec */ r.ru_oublock - Save_r.ru_oublock, r.ru_inblock, r.ru_oublock); appendStringInfo(&str, "!\t%ld/%ld [%ld/%ld] page faults/reclaims, %ld [%ld] swaps\n", r.ru_majflt - Save_r.ru_majflt, r.ru_minflt - Save_r.ru_minflt, r.ru_majflt, r.ru_minflt, r.ru_nswap - Save_r.ru_nswap, r.ru_nswap); appendStringInfo(&str, "!\t%ld [%ld] signals rcvd, %ld/%ld [%ld/%ld] messages rcvd/sent\n", r.ru_nsignals - Save_r.ru_nsignals, r.ru_nsignals, r.ru_msgrcv - Save_r.ru_msgrcv, r.ru_msgsnd - Save_r.ru_msgsnd, r.ru_msgrcv, r.ru_msgsnd); appendStringInfo(&str, "!\t%ld/%ld [%ld/%ld] voluntary/involuntary context switches\n", r.ru_nvcsw - Save_r.ru_nvcsw, r.ru_nivcsw - Save_r.ru_nivcsw, r.ru_nvcsw, r.ru_nivcsw); #endif /* HAVE_GETRUSAGE */ /* remove trailing newline */ if (str.data[str.len - 1] == '\n') str.data[--str.len] = '\0'; ereport(LOG, (errmsg_internal("%s", title), errdetail_internal("%s", str.data))); pfree(str.data); } /* * on_proc_exit handler to log end of session */ void log_disconnections(int code, Datum arg) { if (!u_sess->attr.attr_common.Log_disconnections) { return; } Port* port = u_sess->proc_cxt.MyProcPort; long secs; int usecs; int msecs; int hours, minutes, seconds; TimestampDifference(port->SessionStartTime, GetCurrentTimestamp(), &secs, &usecs); msecs = usecs / 1000; hours = secs / SECS_PER_HOUR; secs %= SECS_PER_HOUR; minutes = secs / SECS_PER_MINUTE; seconds = secs % SECS_PER_MINUTE; if (port->user_name != NULL && port->database_name != NULL && port->remote_host != NULL && port->remote_port != NULL) { ereport(LOG, (errmsg("disconnection: session time: %d:%02d:%02d.%03d " "user=%s database=%s host=%s%s%s", hours, minutes, seconds, msecs, port->user_name, port->database_name, port->remote_host, port->remote_port[0] ? " port=" : "", port->remote_port))); } else { ereport(LOG, (errmsg("disconnection: session time: %d:%02d:%02d.%03d ", hours, minutes, seconds, msecs))); } } void cleanGPCPlanProcExit(int code, Datum arg) { GPCCleanUpSessionSavedPlan(); } /* Aduit user logout */ /* * Brief : audit_processlogout * Description : audit logout */ void audit_processlogout(int code, Datum arg) { pgaudit_user_logout(); return; } /* Aduit user logout */ /* * Brief : audit_processlogout_unified * Description : audit logout */ void audit_processlogout_unified() { /* it's unsafe to deal with plugins hooks as dynamic lib may be released */ if (!(g_instance.status > NoShutdown) && user_login_hook) { user_login_hook(u_sess->proc_cxt.MyProcPort->database_name, u_sess->proc_cxt.MyProcPort->user_name, true, false); } return; } /* * Brief : Check if the initial password of the user has been changed or not. * If not, all the commands from APP should be forbidden except the * "ALTER USER ***". * * Description : Firstly, we only need to take care of the sqls from client. * For synchronization between coordinators, remoteConnType should be * introduced to distinct different status. Secondly, we should not * impact the process of initdb. We make a difference here with * 'IsUnderPostmaster' status. Thirdly, we only support the gsql * client, the sqls from jdbc && odbc should be passed. * * Notes : In default, we only deal with the initial user(UserOid == 10). */ static void ForceModifyInitialPwd(const char* query_string, List* parsetree_list) { Oid current_user = GetUserId(); if (current_user != BOOTSTRAP_SUPERUSERID || parsetree_list == NIL) { return; } char* current_user_name = GetUserNameFromId(current_user); password_info pass_info = {NULL, 0, 0, false, false}; /* return when the initial user's password is not empty */ if (get_stored_password(current_user_name, &pass_info)) { t_thrd.postgres_cxt.password_changed = true; pfree_ext(pass_info.shadow_pass); return; } pfree_ext(pass_info.shadow_pass); if (IsUnderPostmaster && IsConnFromApp() && strcasecmp(u_sess->attr.attr_common.application_name, "gsql") == 0) { if (strcmp(query_string, "SELECT intervaltonum(gs_password_deadline())") != 0 && strcmp(query_string, "SELECT gs_password_notifytime()") != 0 && strcmp(query_string, "SELECT VERSION()") != 0 && strcasecmp(query_string, "delete from pgxc_node;") != 0 && strcasecmp(query_string, "delete from pgxc_group;") != 0 && strncasecmp(query_string, "CREATE NODE", 11) != 0) { /* * Just focuse on the first parsetree_list element, since before * each operation, the initial password should be changed. */ Node* parsetree = (Node*)linitial(parsetree_list); char* current_user_name = GetUserNameFromId(current_user); if (IsA(parsetree, AlterRoleStmt)) { /* * Check if the role in "AlterRoleStmt" matches the current_user. */ char* alter_name = ((AlterRoleStmt*)parsetree)->role; if (strcasecmp(current_user_name, alter_name) != 0) ereport(ERROR, (errcode(ERRCODE_INITIAL_PASSWORD_NOT_MODIFIED), errmsg("Please use \"ALTER ROLE \"%s\" PASSWORD 'password';\" " "to set the password of the user before other operations!", current_user_name))); } else ereport(ERROR, (errcode(ERRCODE_INITIAL_PASSWORD_NOT_MODIFIED), errmsg("Please use \"ALTER ROLE \"%s\" PASSWORD 'password';\" " "to set the password of the user before other operations!", current_user_name))); } } } /* * Require user to modify password since password is expired, * all the commands from APP should be forbidden except the * "ALTER USER ***". */ static void ForceModifyExpiredPwd(const char* queryString, const List* parsetreeList) { if (parsetreeList == NIL) { return; } if (!IsUnderPostmaster || !IsConnFromApp()) { return; } /* check if gsql use -U -W */ if (strcmp(queryString, "SELECT intervaltonum(gs_password_deadline())") == 0 || strcmp(queryString, "SELECT gs_password_notifytime()") == 0 || strcmp(queryString, "SELECT VERSION()") == 0) { return; } /* Check if the role in "AlterRoleStmt" matches the current_user. */ Oid current_user = GetUserId(); Node* parsetree = (Node*)linitial(parsetreeList); char* current_user_name = GetUserNameFromId(current_user); if (IsA(parsetree, AlterRoleStmt)) { char* alter_name = ((AlterRoleStmt*)parsetree)->role; ListCell* option = NULL; foreach (option, ((AlterRoleStmt*)parsetree)->options) { DefElem* defel = (DefElem*)lfirst(option); DefElem* dpassword = NULL; if ((strcmp(defel->defname, "encryptedPassword") == 0 || strcmp(defel->defname, "unencryptedPassword") == 0 || strcmp(defel->defname, "password") == 0) && strcasecmp(current_user_name, alter_name) == 0) { dpassword = defel; } if (dpassword != NULL && dpassword->arg != NULL) { return; } } } ereport(ERROR, (errcode(ERRCODE_INITIAL_PASSWORD_NOT_MODIFIED), errmsg("Please use \"ALTER ROLE user_name IDENTIFIED BY 'password' REPLACE 'old " "password';\" to modify the expired password of user %s before operation!", current_user_name))); } /* * get_query_result() is used to replace DestReceiver::receiveSlot in * exec_query_for_merge() and get result tuple. */ static void get_query_result(TupleTableSlot* slot, DestReceiver* self) { ereport(DEBUG1, (errmsg("deltamerge: %s()", __FUNCTION__))); Assert(slot); Assert(self); /* * save result to query_result as Datum, and query_result will be * deconstructed correctly in merge_one_relation() later. */ t_thrd.postgres_cxt.query_result = slot->tts_values[0]; /* * keep going, pre_receiveSlot_func should be donothingReceive() */ (pre_receiveSlot_func)(slot, self); } /* * The implement of "vacuum deltamerge" on DN is to call exec_query_for_merge() * inside executor with CTE sql. */ void exec_query_for_merge(const char* query_string) { CommandDest dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; MemoryContext oldcontext; List* parsetree_list = NULL; ListCell* parsetree_item = NULL; bool isTopLevel = false; /* * Report query to various monitoring facilities. */ t_thrd.postgres_cxt.debug_query_string = query_string; t_thrd.explain_cxt.explain_perf_mode = u_sess->attr.attr_sql.guc_explain_perf_mode; pgstat_report_activity(STATE_RUNNING, query_string); TRACE_POSTGRESQL_QUERY_START(query_string); /* * Start up a transaction command. All queries generated by the * query_string will be in this same command block, *unless* we find a * BEGIN/COMMIT/ABORT statement; we have to force a new xact command after * one of those, else bad things will happen in xact.c. (Note that this * will normally change current memory context.) */ start_xact_command(); /* * Zap any pre-existing unnamed statement. (While not strictly necessary, * it seems best to define simple-Query mode as if it used the unnamed * statement and portal; this ensures we recover any storage used by prior * unnamed operations.) */ drop_unnamed_stmt(); /* * Switch to appropriate context for constructing parsetrees. */ oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); parsetree_list = pg_parse_query(query_string); MemoryContextSwitchTo(oldcontext); /* * We'll tell PortalRun it's a top-level command iff there's exactly one * raw parsetree. If more than one, it's effectively a transaction block * and we want PreventTransactionChain to reject unsafe commands. (Note: * we're assuming that query rewrite cannot add commands that are * significant to PreventTransactionChain.) */ isTopLevel = (list_length(parsetree_list) == 1); if (isTopLevel != 1) t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; /* * Run through the raw parsetree(s) and process each one. */ foreach (parsetree_item, parsetree_list) { Node* parsetree = (Node*)lfirst(parsetree_item); bool snapshot_set = false; const char* commandTag = NULL; char completionTag[COMPLETION_TAG_BUFSIZE]; List* querytree_list = NULL; List* plantree_list = NULL; Portal portal; DestReceiver* receiver = NULL; int16 format; #ifdef ENABLE_MULTIPLE_NODES /* * By default we do not want Datanodes or client Coordinators to contact GTM directly, * it should get this information passed down to it. */ if (IS_PGXC_DATANODE || IsConnFromCoord()) SetForceXidFromGTM(false); #endif /* * Get the command name for use in status display (it also becomes the * default completion tag, down inside PortalRun). Set ps_status and * do any special start-of-SQL-command processing needed by the * destination. */ commandTag = CreateCommandTag(parsetree); set_ps_display(commandTag, false); BeginCommand(commandTag, dest); /* * If we are in an aborted transaction, reject all commands except * COMMIT/ABORT. It is important that this test occur before we try * to do parse analysis, rewrite, or planning, since all those phases * try to do database accesses, which may fail in abort state. (It * might be safe to allow some additional utility commands in this * state, but not many...) */ if (IsAbortedTransactionBlockState() && !IsTransactionExitStmt(parsetree)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* Make sure we are in a transaction command */ start_xact_command(); /* If we got a cancel signal in parsing or prior command, quit */ CHECK_FOR_INTERRUPTS(); /* * Set up a snapshot if parse analysis/planning will need one. */ if (analyze_requires_snapshot(parsetree)) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* * Before going into planner, set default work mode. */ set_default_stream(); /* * OK to analyze, rewrite, and plan this query. * * Switch to appropriate context for constructing querytrees (again, * these must outlive the execution context). */ oldcontext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); querytree_list = pg_analyze_and_rewrite(parsetree, query_string, NULL, 0); plantree_list = pg_plan_queries(querytree_list, 0, NULL); /* Done with the snapshot used for parsing/planning */ if (snapshot_set) PopActiveSnapshot(); /* If we got a cancel signal in analysis or planning, quit */ CHECK_FOR_INTERRUPTS(); #ifdef ENABLE_MULTIPLE_NODES /* PGXC_DATANODE */ /* Force getting Xid from GTM because of "deltamerge" */ SetForceXidFromGTM(true); #endif /* * portalName is named as "deltamerge" just for "vacuum deltamerge", */ portal = CreatePortal("deltamerge", true, true); /* Don't display the portal in pg_cursors */ portal->visible = false; /* * We don't have to copy anything into the portal, because everything * we are passing here is in t_thrd.mem_cxt.msg_mem_cxt, which will outlive the * portal anyway. If we received a hybridmesage, we send sql_query_string * to PortalDefineQuery as the original query string. */ PortalDefineQuery(portal, NULL, query_string, commandTag, plantree_list, NULL); PortalStart(portal, NULL, 0, InvalidSnapshot); /* * Select the appropriate output format: text unless we are doing a * FETCH from a binary cursor. (Pretty grotty to have to do this here * --- but it avoids grottiness in other places. Ah, the joys of * backward compatibility...) */ format = 0; /* TEXT is default */ if (IsA(parsetree, FetchStmt)) { FetchStmt* stmt = (FetchStmt*)parsetree; if (!stmt->ismove) { Portal fportal = GetPortalByName(stmt->portalname); if (PortalIsValid(fportal) && ((uint32)fportal->cursorOptions & CURSOR_OPT_BINARY)) format = 1; /* BINARY */ } } PortalSetResultFormat(portal, 1, &format); /* * Now we can create the destination receiver object. */ receiver = CreateReceiverForMerge(dest); if (dest == DestRemote) SetRemoteDestReceiverParams(receiver, portal); /* * just for delta merge, save result for use later, * run here just exec_query_for_merge() called by merge_one_relation(). */ pre_receiveSlot_func = receiver->receiveSlot; receiver->receiveSlot = get_query_result; /* * Switch back to transaction context for execution. */ MemoryContextSwitchTo(oldcontext); if (u_sess->attr.attr_resource.use_workload_manager && g_instance.wlm_cxt->gscgroup_init_done && !IsAbortedTransactionBlockState()) { u_sess->wlm_cxt->cgroup_last_stmt = u_sess->wlm_cxt->cgroup_stmt; u_sess->wlm_cxt->cgroup_stmt = WLMIsSpecialCommand(parsetree, portal); } /* * Run the portal to completion, and then drop it (and the receiver). */ (void)PortalRun(portal, FETCH_ALL, isTopLevel, receiver, receiver, completionTag); (*receiver->rDestroy)(receiver); PortalDrop(portal, false); if (IsA(parsetree, TransactionStmt)) { /* * If this was a transaction control statement, commit it. We will * start a new xact command for the next command (if any). */ finish_xact_command(); } else if (lnext(parsetree_item) == NULL) { /* * If this is the last parsetree of the query string, close down * transaction statement before reporting command-complete. This * is so that any end-of-transaction errors are reported before * the command-complete message is issued, to avoid confusing * clients who will expect either a command-complete message or an * error, not one and then the other. But for compatibility with * historical Postgres behavior, we do not force a transaction * boundary between queries appearing in a single query string. */ finish_xact_command(); } else { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. */ CommandCounterIncrement(); } /* * Tell client that we're done with this query. Note we emit exactly * one EndCommand report for each raw parsetree, thus one for each SQL * command the client sent, regardless of rewriting. (But a command * aborted by error will not send an EndCommand report at all.) */ EndCommand(completionTag, dest); } /* end loop over parsetrees */ /* * Close down transaction statement, if one is open. */ finish_xact_command(); /* * If there were no parsetrees, return EmptyQueryResponse message. */ if (parsetree_list == NULL) NullCommand(dest); TRACE_POSTGRESQL_QUERY_DONE(query_string); t_thrd.postgres_cxt.debug_query_string = NULL; } /* * merge_one_relation() will run in a new transaction, so it is necessary to * finish the previous transaction before running it, and restart a new transaction * after running it; */ void do_delta_merge(List* infos, VacuumStmt* stmt) { ListCell* cell = NULL; /* * match StartTransactionCommand() outside */ if (ActiveSnapshotSet()) PopActiveSnapshot(); finish_xact_command(); foreach (cell, infos) { void* info = lfirst(cell); if (((MergeInfo*)info)->is_hdfs) merge_one_relation(info); else merge_cu_relation(info, stmt); } /* * match CommitTransactionCommand() outside */ start_xact_command(); PushActiveSnapshot(GetTransactionSnapshot()); } /* Job worker Process, execute procedure */ void execute_simple_query(const char* query_string) { exec_simple_query(query_string, QUERY_MESSAGE); } /* * exec_one_in_batch * main entry of execute one in bind-execute message for not light cn * * Parameters: * @in psrc: CachedPlanSource * @in params: input params * @in numRFormats: num of result format codes * @in rformats: result format codes * @in send_DP_msg: if send the DP msg * @in dest: command dest * @in completionTag: used in PortalRun and to compute num of processed tuples later. * NULL value means the query is not SELECT/INSERT/UPDATE/DELETE, * and will not count the num of processed tuples. * * Returns: const char * * If query is not SELECT/INSERT/UPDATE/DELETE, return completionTag, else NULL. */ static void exec_one_in_batch(CachedPlanSource* psrc, ParamListInfo params, int numRFormats, int16* rformats, bool send_DP_msg, CommandDest dest, char* completionTag, const char* stmt_name, List** gpcCopyStmts, MemoryContext* tmpCxt, PreparedStatement* pstmt) { CachedPlan* cplan = NULL; Portal portal; DestReceiver* receiver = NULL; bool completed = false; bool hasGetLock = false; if (ENABLE_GPC && psrc->stmt_name && psrc->stmt_name[0] != '\0' && g_instance.plan_cache->CheckRecreateCachePlan(psrc, &hasGetLock)) { g_instance.plan_cache->RecreateCachePlan(psrc, stmt_name, pstmt, NULL, NULL, hasGetLock); #ifdef ENABLE_MULTIPLE_NODES psrc = IS_PGXC_DATANODE ? u_sess->pcache_cxt.cur_stmt_psrc : pstmt->plansource; #else psrc = pstmt->plansource; #endif t_thrd.postgres_cxt.debug_query_string = psrc->query_string; } int generation = psrc->generation; OpFusion::clearForCplan((OpFusion*)psrc->opFusionObj, psrc); if (psrc->opFusionObj != NULL) { (void)RevalidateCachedQuery(psrc); OpFusion *opFusionObj = (OpFusion *)(psrc->opFusionObj); if (psrc->opFusionObj != NULL) { Assert(psrc->cplan == NULL); if (opFusionObj->IsGlobal()) { opFusionObj = (OpFusion *)OpFusion::FusionFactory(opFusionObj->m_global->m_type, u_sess->cache_mem_cxt, psrc, NULL, params); Assert(opFusionObj != NULL); } opFusionObj->bindClearPosition(); opFusionObj->useOuterParameter(params); opFusionObj->setCurrentOpFusionObj(opFusionObj); opFusionObj->CopyFormats(rformats, numRFormats); opFusionObj->storeFusion(""); CachedPlanSource* cps = opFusionObj->m_global->m_psrc; if (cps != NULL && cps->gplan) { setCachedPlanBucketId(cps->gplan, params); } if (OpFusion::process(FUSION_EXECUTE, NULL, completionTag, true, NULL)) { CommandCounterIncrement(); return; } Assert(0); return; } } /* * 'create table as select' is divided into 'create table' and 'insert into select', * and 'create table' is executed in sql rewrite, which will be called in parse and bind * both, when we use jdbc to execute 'create table as'. So when bind is executed, * an error 'table already exists' will raise. table_created_in_CTAS is to solve this. */ t_thrd.postgres_cxt.table_created_in_CTAS = true; portal = CreatePortal("", true, true); MemoryContext oldContext = MemoryContextSwitchTo(PortalGetHeapMemory(portal)); const char* saved_stmt_name = NULL; const char* cur_stmt_name = NULL; if (ENABLE_CN_GPC) { saved_stmt_name = (stmt_name[0] != '\0') ? pstrdup(stmt_name) : NULL; cur_stmt_name = psrc->gpc.status.IsPrivatePlan() ? psrc->stmt_name : saved_stmt_name; } else { cur_stmt_name = psrc->stmt_name; } (void)MemoryContextSwitchTo(oldContext); /* * Obtain a plan from the CachedPlanSource. Any cruft from (re)planning * will be generated in t_thrd.mem_cxt.msg_mem_cxt. The plan refcount will be * assigned to the Portal, so it will be released at portal destruction. */ cplan = GetCachedPlan(psrc, params, false); t_thrd.postgres_cxt.table_created_in_CTAS = false; /* * copy the single_shard info from plan source into plan. * With this, we can determine if we should use global snapshot or local snapshot after. */ cplan->single_shard_stmt = psrc->single_shard_stmt; /* * Now we can define the portal. * * DO NOT put any code that could possibly throw an error between the * above GetCachedPlan call and here. */ PortalDefineQuery(portal, (psrc != u_sess->pcache_cxt.unnamed_stmt_psrc) ? cur_stmt_name : NULL, psrc->query_string, psrc->commandTag, cplan->stmt_list, cplan); if (ENABLE_GPC) { /* generated new gplan, copy it incase someone change it */ if (generation != psrc->generation && psrc->gplan) { if (*tmpCxt != NULL) MemoryContextDelete(*tmpCxt); *gpcCopyStmts = CopyLocalStmt(psrc->gplan->stmt_list, u_sess->temp_mem_cxt, tmpCxt); } else if (*gpcCopyStmts == NULL && psrc->gpc.status.InShareTable() && psrc->gplan) { /* copy for shared plan */ if (*tmpCxt != NULL) MemoryContextDelete(*tmpCxt); *gpcCopyStmts = CopyLocalStmt(psrc->gplan->stmt_list, u_sess->temp_mem_cxt, tmpCxt); } if (*gpcCopyStmts != NULL) portal->stmts = *gpcCopyStmts; } #ifdef ENABLE_MOT /* * MOT JIT Execution: * Assist in distinguishing query boundaries in case of range query when client uses batches. This allows us to * know a new query started, and in case a previous execution did not fetch all records (since user is working in * batch-mode, and can decide to quit fetching in the middle), using this information we can infer this is a new * scan, and old scan state should be discarded. */ if (psrc->mot_jit_context != NULL) { JitResetScan(psrc->mot_jit_context); } #endif bool checkSQLBypass = IS_PGXC_DATANODE && !psrc->gpc.status.InShareTable() && (psrc->cplan == NULL) && (psrc->is_checked_opfusion == false); if (checkSQLBypass) { psrc->opFusionObj = OpFusion::FusionFactory(OpFusion::getFusionType(cplan, params, NULL), u_sess->cache_mem_cxt, psrc, NULL, params); psrc->is_checked_opfusion = true; if (psrc->opFusionObj != NULL) { ((OpFusion*)psrc->opFusionObj)->bindClearPosition(); ((OpFusion*)psrc->opFusionObj)->useOuterParameter(params); ((OpFusion*)psrc->opFusionObj)->setCurrentOpFusionObj((OpFusion*)psrc->opFusionObj); ((OpFusion*)psrc->opFusionObj)->CopyFormats(rformats, numRFormats); ((OpFusion*)psrc->opFusionObj)->storeFusion(""); if (OpFusion::process(FUSION_EXECUTE, NULL, completionTag, true, NULL)) { CommandCounterIncrement(); return; } Assert(0); } } /* * And we're ready to start portal execution. */ PortalStart(portal, params, 0, InvalidSnapshot); /* * Apply the result format requests to the portal. */ PortalSetResultFormat(portal, numRFormats, rformats); /* send DP message if necessary */ if (send_DP_msg) { if (portal->tupDesc) SendRowDescriptionMessage(&(*t_thrd.postgres_cxt.row_description_buf), portal->tupDesc, FetchPortalTargetList(portal), portal->formats); else pq_putemptymessage('n'); /* NoData */ } /* * Create dest receiver in t_thrd.mem_cxt.msg_mem_cxt (we don't want it in transaction * context, because that may get deleted if portal contains VACUUM). */ receiver = CreateDestReceiver(dest); if (dest == DestRemoteExecute) SetRemoteDestReceiverParams(receiver, portal); /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); completed = PortalRun(portal, FETCH_ALL, true, /* always top level */ receiver, receiver, completionTag); (*receiver->rDestroy)(receiver); if (completed) { /* * We need a CommandCounterIncrement after every query, except * those that start or end a transaction block. */ CommandCounterIncrement(); } else { /* Portal run not complete, maybe something wrong */ ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Portal run not complete for one in Batch bind-execute: name %s, query %s", psrc->stmt_name, psrc->query_string))); } } /* * light_preprocess_batchmsg_set * do preprocessing work before constructing batch message for each dn * * Parameters: * @in psrc: plan * @in params_set: params used to compute dn index * @in params_set_end: params position for computing params size for each dn * @in batch_count: batch count * * @out node_idx_set: node index for each bind-execute * @out batch_count_dnset: batch count for each dn * @out params_size_dnset: params size for each dn * * Returns: void */ static void light_preprocess_batchmsg_set(CachedPlanSource* psrc, const ParamListInfo* params_set, const int* params_set_end, int batch_count, int* node_idx_set, int* batch_count_dnset, int* params_size_dnset) { int idx = -1; ExecNodes* exec_nodes = psrc->single_exec_node; Assert(exec_nodes->nodeList != NIL || exec_nodes->en_expr != NIL); ResourceOwner currentOwner = t_thrd.utils_cxt.CurrentResourceOwner; ResourceOwner tmpOwner = ResourceOwnerCreate(t_thrd.utils_cxt.CurrentResourceOwner, "BatchLightProxy", THREAD_GET_MEM_CXT_GROUP(MEMORY_CONTEXT_EXECUTOR)); t_thrd.utils_cxt.CurrentResourceOwner = tmpOwner; if (exec_nodes->en_expr != NIL) { /* use my own context to store tmp info */ MemoryContext old_context = CurrentMemoryContext; MemoryContext my_context = AllocSetContextCreate(u_sess->top_portal_cxt, "BatchLightPorxyMemory", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); MemoryContextSwitchTo(my_context); RelationLocInfo* rel_loc_info = GetRelationLocInfo(exec_nodes->en_relid); if (rel_loc_info == NULL) { ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("rel_loc_info is NULL."))); } int len = list_length(rel_loc_info->partAttrNum); Datum* distcol_value = (Datum*)palloc0(len * sizeof(Datum)); bool* distcol_isnull = (bool*)palloc0(len * sizeof(bool)); Oid* distcol_type = (Oid*)palloc0(len * sizeof(Oid)); errno_t ss_rc = 0; for (int j = 0; j < batch_count; j++) { List* idx_dist = NULL; int i = 0; ExecNodes* single_node = NULL; ListCell* cell = NULL; foreach (cell, exec_nodes->en_expr) { Expr* distcol_expr = (Expr*)lfirst(cell); distcol_expr = (Expr*)eval_const_expressions_params(NULL, (Node*)distcol_expr, params_set[j]); if (distcol_expr && IsA(distcol_expr, Const)) { Const* const_expr = (Const*)distcol_expr; distcol_value[i] = const_expr->constvalue; distcol_isnull[i] = const_expr->constisnull; distcol_type[i] = const_expr->consttype; idx_dist = lappend_int(idx_dist, i); i++; } else ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Batch param of distribute key only support const"))); } single_node = GetRelationNodes( rel_loc_info, distcol_value, distcol_isnull, distcol_type, idx_dist, exec_nodes->accesstype); /* make sure it is one dn */ if (single_node == NULL || list_length(single_node->nodeList) != 1) ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Failed to get DataNode id for Batch bind-execute: name %s, query %s", psrc->stmt_name, psrc->query_string))); idx = linitial_int(single_node->nodeList); node_idx_set[j] = idx; batch_count_dnset[idx]++; params_size_dnset[idx] += params_set_end[j + 1] - params_set_end[j]; /* reset */ ss_rc = memset_s(distcol_value, len * sizeof(Datum), 0, len * sizeof(Datum)); securec_check(ss_rc, "\0", "\0"); ss_rc = memset_s(distcol_isnull, len * sizeof(bool), 0, len * sizeof(bool)); securec_check(ss_rc, "\0", "\0"); ss_rc = memset_s(distcol_type, len * sizeof(Oid), 0, len * sizeof(Oid)); securec_check(ss_rc, "\0", "\0"); } MemoryContextSwitchTo(old_context); MemoryContextDelete(my_context); } else { /* make sure it is one dn */ if (list_length(exec_nodes->nodeList) != 1) ereport(ERROR, (errcode(ERRCODE_SYSTEM_ERROR), errmsg("Failed to get DataNode id for Batch bind-execute: name %s, query %s", psrc->stmt_name, psrc->query_string))); /* all same node index in this case */ idx = linitial_int(exec_nodes->nodeList); batch_count_dnset[idx] = batch_count; for (int j = 0; j < batch_count; j++) { node_idx_set[j] = idx; params_size_dnset[idx] += params_set_end[j + 1] - params_set_end[j]; } } ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_BEFORE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_LOCKS, true, true); ResourceOwnerRelease(tmpOwner, RESOURCE_RELEASE_AFTER_LOCKS, true, true); t_thrd.utils_cxt.CurrentResourceOwner = currentOwner; ResourceOwnerDelete(tmpOwner); } /* * light_construct_batchmsg_set * construct batch messages for light cn for each dn * * Parameters: * @in input: message from client * @in params_set_end: position index of params in input message * @in node_idx_set: node index set * @in batch_count_dnset: batch count for each dn * @in params_size_dnset: params size for each dn * @in desc_msg: describe message * @in exec_msg: execute message * * Returns: batch message for each dn */ static StringInfo light_construct_batchmsg_set(StringInfo input, const int* params_set_end, const int* node_idx_set, const int* batch_count_dnset, const int* params_size_dnset, StringInfo desc_msg, StringInfo exec_msg) { int batch_count; int fix_part_size; int before_size; bool send_DP_msg = (desc_msg != NULL); errno_t ss_rc = 0; int idx; int batch_count_dn; int tmp_batch_count_dn; int tmp_params_size; StringInfo batch_msg; StringInfo batch_msg_dnset; /* parse it again */ input->cursor = 0; batch_count = pq_getmsgint(input, 4); if (unlikely(batch_count <= 0)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unexpected batch_count %d get from inputmessage", batch_count))); } /* size of common part before params */ before_size = params_set_end[0] - input->cursor; Assert(before_size > 0); /* 1.construct common part before params */ fix_part_size = 4 + before_size + exec_msg->len; batch_msg_dnset = (StringInfo)palloc0(u_sess->pgxc_cxt.NumDataNodes * sizeof(StringInfoData)); for (int i = 0; i < u_sess->pgxc_cxt.NumDataNodes; i++) { batch_count_dn = batch_count_dnset[i]; /* not related to this dn */ if (batch_count_dn == 0) continue; batch_msg = &batch_msg_dnset[i]; /* only send DP message for the first dn */ if (send_DP_msg) { batch_msg->len = fix_part_size + desc_msg->len + params_size_dnset[i]; send_DP_msg = false; } else batch_msg->len = fix_part_size + params_size_dnset[i]; batch_msg->maxlen = batch_msg->len + 1; batch_msg->data = (char*)palloc0(batch_msg->maxlen); /* batch_count */ tmp_batch_count_dn = htonl(batch_count_dn); ss_rc = memcpy_s(batch_msg->data, MEMCPY_DST_NUM, &tmp_batch_count_dn, MEMCPY_DST_NUM); securec_check(ss_rc, "\0", "\0"); batch_msg->cursor = 4; /* before params */ ss_rc = memcpy_s(batch_msg->data + batch_msg->cursor, before_size, input->data + input->cursor, before_size); securec_check(ss_rc, "\0", "\0"); batch_msg->cursor += before_size; } /* 2.construct the params part */ input->cursor += before_size; /* has params */ if (params_set_end[1] > 0) { for (int i = 0; i < batch_count; i++) { idx = node_idx_set[i]; batch_msg = &batch_msg_dnset[idx]; if (unlikely(batch_msg->maxlen <= 0)) { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unexpected maxlen %d ", batch_msg->maxlen))); } /* append params */ tmp_params_size = params_set_end[i + 1] - params_set_end[i]; ss_rc = memcpy_s( batch_msg->data + batch_msg->cursor, tmp_params_size, input->data + input->cursor, tmp_params_size); securec_check(ss_rc, "\0", "\0"); batch_msg->cursor += tmp_params_size; input->cursor += tmp_params_size; } } /* 3.construct common part after params */ send_DP_msg = (desc_msg != NULL); for (int i = 0; i < u_sess->pgxc_cxt.NumDataNodes; i++) { batch_count_dn = batch_count_dnset[i]; /* not related to this dn */ if (batch_count_dn == 0) continue; batch_msg = &batch_msg_dnset[i]; if (send_DP_msg) { ss_rc = memcpy_s(batch_msg->data + batch_msg->cursor, desc_msg->len, desc_msg->data, desc_msg->len); securec_check(ss_rc, "\0", "\0"); batch_msg->cursor += desc_msg->len; send_DP_msg = false; } ss_rc = memcpy_s(batch_msg->data + batch_msg->cursor, exec_msg->len, exec_msg->data, exec_msg->len); securec_check(ss_rc, "\0", "\0"); batch_msg->cursor += exec_msg->len; /* check finished and reset cursor */ Assert(batch_msg->cursor == batch_msg->len); batch_msg->cursor = 0; } return batch_msg_dnset; } /* * light_execute_batchmsg_set * execute batch for light cn for each dn * * Parameters: * @in scn: Light proxy * @in batch_msg_dnset: batch message for each dn * @in batch_count_dnset: batch count for each dn * @in send_DP_msg: mark if send DP message * * Returns: process_count */ static int light_execute_batchmsg_set( lightProxy* scn, const StringInfo batch_msg_dnset, const int* batch_count_dnset, bool send_DP_msg) { int process_count = 0; int tmp_count = 0; bool sendDMsg = send_DP_msg; int batch_count_dn; /* set statement start timestamp */ SetCurrentStmtTimestamp(); for (int i = 0; i < u_sess->pgxc_cxt.NumDataNodes; i++) { batch_count_dn = batch_count_dnset[i]; /* not related to this dn */ if (batch_count_dn == 0) continue; /* Check for cancel signal before we start execution */ CHECK_FOR_INTERRUPTS(); /* in router, only execute batch msg on router node */ if (HAS_ROUTER) scn->m_nodeIdx = u_sess->exec_cxt.CurrentRouter->GetRouterNodeId(); else scn->m_nodeIdx = i; tmp_count = scn->runBatchMsg(&batch_msg_dnset[i], sendDMsg, batch_count_dn); process_count += tmp_count; if (sendDMsg) sendDMsg = false; } return process_count; } /* * exec_batch_bind_execute * main entry of execute batch bind-execute message * * Parameters: * @in input_message: message from client * * Returns: void */ static void exec_batch_bind_execute(StringInfo input_message) { /* special for U message */ int batch_count; CmdType cmd_type; int* params_set_end = NULL; bool send_DP_msg = false; int process_count = 0; CommandDest dest; StringInfoData process_result; /* use original logic if not SELECT/INSERT/UPDATE/DELETE */ bool use_original_logic = false; /* like B message */ const char* portal_name = NULL; const char* stmt_name = NULL; int numPFormats; int16* pformats = NULL; int numParams; int numRFormats; int16* rformats = NULL; CachedPlanSource* psrc = NULL; ParamListInfo* params_set = NULL; MemoryContext oldContext; bool save_log_statement_stats = u_sess->attr.attr_common.log_statement_stats; bool snapshot_set = false; char msec_str[PRINTF_DST_MAX]; int msg_type; /* D message */ int describe_type = 0; const char* describe_target = NULL; /* E message */ const char* exec_portal_name = NULL; int max_rows; /* reset gpc batch flag */ u_sess->pcache_cxt.gpc_in_batch = false; /* * Only support normal perf mode for PBE, as DestRemoteExecute can not send T message automatically. */ t_thrd.explain_cxt.explain_perf_mode = EXPLAIN_NORMAL; instr_stmt_report_start_time(); /* 'U' Message format: many B-like and one D and one E message together * batchCount * portal_name * stmt_name * numPFormats * PFormats * numRFormats * RFormats * numParams * Params1 * Params2... * 'D' * describe_type * describe_target * 'E' * portal_name * max_rows */ /* A. Parse message */ /* 1.specila for U message */ /* batchCount: count of bind */ batch_count = pq_getmsgint(input_message, 4); if (batch_count <= 0 || batch_count > PG_INT32_MAX / (int)sizeof(ParamListInfo)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("Batch bind-execute message with invalid batch count: %d", batch_count))); /* 2.B-like message */ /* Get the fixed part of the message */ portal_name = pq_getmsgstring(input_message); stmt_name = pq_getmsgstring(input_message); /* Check not NULL */ AssertEreport(portal_name != NULL && stmt_name != NULL, MOD_OPT, "portal_name and stmt_name can not be NULL"); if (strlen(portal_name) > SECUREC_MEM_MAX_LEN || strlen(stmt_name) > SECUREC_MEM_MAX_LEN) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Too long portal_name and stmt_name."))); if (portal_name[0] != '\0') ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Not support portal_name %s for Batch bind-execute.", portal_name))); ereport(DEBUG2, (errmsg("Batch bind-execute %s to %s, batch count %d", *portal_name ? portal_name : "", *stmt_name ? stmt_name : "", batch_count))); PreparedStatement *pstmt = NULL; /* Find prepared statement */ if (stmt_name[0] != '\0') { if (ENABLE_DN_GPC) { psrc = u_sess->pcache_cxt.cur_stmt_psrc; if (SECUREC_UNLIKELY(psrc == NULL)) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("dn gpc's prepared statement %s does not exist", stmt_name))); } else { pstmt = FetchPreparedStatement(stmt_name, true, true); psrc = pstmt->plansource; } } else { /* special-case the unnamed statement */ psrc = u_sess->pcache_cxt.unnamed_stmt_psrc; if (psrc == NULL) ereport( ERROR, (errcode(ERRCODE_UNDEFINED_PSTATEMENT), errmsg("unnamed prepared statement does not exist"))); } Assert(NULL != psrc); /* Check command type: only support IUD */ initStringInfo(&process_result); cmd_type = set_cmd_type(psrc->commandTag); switch (cmd_type) { case CMD_INSERT: appendStringInfo(&process_result, "INSERT 0 "); break; case CMD_UPDATE: appendStringInfo(&process_result, "UPDATE "); break; case CMD_DELETE: appendStringInfo(&process_result, "DELETE "); break; case CMD_SELECT: appendStringInfo(&process_result, "SELETE "); break; case CMD_MERGE: appendStringInfo(&process_result, "MERGE "); break; default: use_original_logic = true; ereport(LOG, (errmsg("Not support Batch bind-execute for %s: stmt_name %s, query %s", psrc->commandTag, psrc->stmt_name, psrc->query_string))); break; } /* * Report query to various monitoring facilities. */ t_thrd.postgres_cxt.debug_query_string = psrc->query_string; pgstat_report_activity(STATE_RUNNING, psrc->query_string); set_ps_display(psrc->commandTag, false); if (save_log_statement_stats) { ResetUsage(); } /* * Start up a transaction command so we can call functions etc. (Note that * this will normally change current memory context.) Nothing happens if * we are already in one. */ start_xact_command(); SetUniqueSQLIdFromCachedPlanSource(psrc); if (ENABLE_WORKLOAD_CONTROL && SqlIsValid(t_thrd.postgres_cxt.debug_query_string) && (IS_PGXC_COORDINATOR || IS_SINGLE_NODE) && !IsConnFromCoord()) { u_sess->wlm_cxt->is_active_statements_reset = false; if (g_instance.wlm_cxt->dynamic_workload_inited) { dywlm_parallel_ready(t_thrd.postgres_cxt.debug_query_string); dywlm_client_max_reserve(); } else { WLMParctlReady(t_thrd.postgres_cxt.debug_query_string); WLMParctlReserve(PARCTL_GLOBAL); } } /* * Begin to parse the big B-like message. * First, common part: * numPFormats, PFormats * numRFormats, RFormats * numParams */ /* Switch back to message context */ oldContext = MemoryContextSwitchTo(t_thrd.mem_cxt.msg_mem_cxt); /* Get the parameter format codes */ numPFormats = pq_getmsgint(input_message, 2); if (numPFormats > PG_UINT16_MAX) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("Batch bind-execute message with invalid parameter number: %d", numPFormats))); } if (numPFormats > 0) { pformats = (int16*)palloc0(numPFormats * sizeof(int16)); for (int i = 0; i < numPFormats; i++) { pformats[i] = pq_getmsgint(input_message, 2); } } /* Get the result format codes */ numRFormats = pq_getmsgint(input_message, 2); if (numRFormats > PG_UINT16_MAX) { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("Batch bind-execute message with invalid parameter number: %d", numRFormats))); } if (numRFormats > 0) { rformats = (int16*)palloc0(numRFormats * sizeof(int16)); for (int i = 0; i < numRFormats; i++) { rformats[i] = pq_getmsgint(input_message, 2); } } /* Get the parameter value count */ numParams = pq_getmsgint(input_message, 2); if (numPFormats > 1 && numPFormats != numParams) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message has %d parameter formats but %d parameters", numPFormats, numParams))); if (numParams != psrc->num_params) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("bind message supplies %d parameters, but prepared statement \"%s\" requires %d", numParams, stmt_name, psrc->num_params))); /* * If we are in aborted transaction state, the only portals we can * actually run are those containing COMMIT or ROLLBACK commands. We * disallow binding anything else to avoid problems with infrastructure * that expects to run inside a valid transaction. We also disallow * binding any parameters, since we can't risk calling user-defined I/O * functions. */ if (IsAbortedTransactionBlockState() && (!IsTransactionExitStmt(psrc->raw_parse_tree) || numParams != 0)) ereport(ERROR, (errcode(ERRCODE_IN_FAILED_SQL_TRANSACTION), errmsg("current transaction is aborted, " "commands ignored until end of transaction block, firstChar[%c]", u_sess->proc_cxt.firstChar), errdetail_abort())); /* * Set a snapshot if we have parameters to fetch (since the input * functions might need it) or the query isn't a utility command (and * hence could require redoing parse analysis and planning). We keep the * snapshot active till we're done, so that plancache.c doesn't have to * take new ones. */ if (!GTM_LITE_MODE && (numParams > 0 || analyze_requires_snapshot(psrc->raw_parse_tree))) { PushActiveSnapshot(GetTransactionSnapshot()); snapshot_set = true; } /* Make sure the querytree list is valid and we have parse-time locks */ if (psrc->single_exec_node != NULL) RevalidateCachedQuery(psrc); /* record the params set position for light cn to contruct batch message */ if (psrc->single_exec_node != NULL) { params_set_end = (int*)palloc0((batch_count + 1) * sizeof(int)); /* keep the end pos of message before params at last */ params_set_end[0] = input_message->cursor; } /* Second, process each set of params */ params_set = (ParamListInfo*)palloc0(batch_count * sizeof(ParamListInfo)); if (numParams > 0) { for (int i = 0; i < batch_count; i++) { ParamListInfo params = (ParamListInfo)palloc0(offsetof(ParamListInfoData, params) + numParams * sizeof(ParamExternData)); /* we have static list of params, so no hooks needed */ params->paramFetch = NULL; params->paramFetchArg = NULL; params->parserSetup = NULL; params->parserSetupArg = NULL; params->params_need_process = false; params->numParams = numParams; for (int paramno = 0; paramno < numParams; paramno++) { Oid ptype = psrc->param_types[paramno]; int32 plength; Datum pval; bool isNull = false; StringInfoData pbuf; char csave; int16 pformat; plength = pq_getmsgint(input_message, 4); isNull = (plength == -1); /* add null value process for date type */ if ((VARCHAROID == ptype || TIMESTAMPOID == ptype || TIMESTAMPTZOID == ptype || TIMEOID == ptype || TIMETZOID == ptype || INTERVALOID == ptype || SMALLDATETIMEOID == ptype) && 0 == plength && u_sess->attr.attr_sql.sql_compatibility == A_FORMAT) isNull = true; /* * Insert into bind values support illegal characters import, * and this just wroks for char type attribute. */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = IsCharType(ptype); if (!isNull) { const char* pvalue = pq_getmsgbytes(input_message, plength); /* * Rather than copying data around, we just set up a phony * StringInfo pointing to the correct portion of the message * buffer. We assume we can scribble on the message buffer so * as to maintain the convention that StringInfos have a * trailing null. This is grotty but is a big win when * dealing with very large parameter strings. */ pbuf.data = (char*)pvalue; pbuf.maxlen = plength + 1; pbuf.len = plength; pbuf.cursor = 0; csave = pbuf.data[plength]; pbuf.data[plength] = '\0'; } else { pbuf.data = NULL; /* keep compiler quiet */ csave = 0; } if (numPFormats > 1) { Assert(NULL != pformats); pformat = pformats[paramno]; } else if (numPFormats > 0) { Assert(NULL != pformats); pformat = pformats[0]; } else { pformat = 0; /* default = text */ } if (pformat == 0) { /* text mode */ Oid typinput; Oid typioparam; char* pstring = NULL; getTypeInputInfo(ptype, &typinput, &typioparam); /* * We have to do encoding conversion before calling the * typinput routine. */ if (isNull) pstring = NULL; else pstring = pg_client_to_server(pbuf.data, plength); pval = OidInputFunctionCall(typinput, pstring, typioparam, -1); /* Free result of encoding conversion, if any */ if (pstring != NULL && pstring != pbuf.data) pfree(pstring); } else if (pformat == 1) { /* binary mode */ Oid typreceive; Oid typioparam; StringInfo bufptr; /* * Call the parameter type's binary input converter */ getTypeBinaryInputInfo(ptype, &typreceive, &typioparam); if (isNull) bufptr = NULL; else bufptr = &pbuf; pval = OidReceiveFunctionCall(typreceive, bufptr, typioparam, -1); /* Trouble if it didn't eat the whole buffer */ if (!isNull && pbuf.cursor != pbuf.len) ereport(ERROR, (errcode(ERRCODE_INVALID_BINARY_REPRESENTATION), errmsg("incorrect binary data format in bind parameter %d", paramno + 1))); } else { ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("unsupported format code: %d", pformat))); pval = 0; /* keep compiler quiet */ } /* Restore message buffer contents */ if (!isNull) pbuf.data[plength] = csave; params->params[paramno].value = pval; params->params[paramno].isnull = isNull; /* * We mark the params as CONST. This ensures that any custom plan * makes full use of the parameter values. */ params->params[paramno].pflags = PARAM_FLAG_CONST; params->params[paramno].ptype = ptype; params->params[paramno].tabInfo = NULL; /* Reset the compatible illegal chars import flag */ u_sess->mb_cxt.insertValuesBind_compatible_illegal_chars = false; } /* assign to the set */ params_set[i] = params; if (params_set_end != NULL) params_set_end[i + 1] = input_message->cursor; } } /* msg_type: maybe D or E */ msg_type = pq_getmsgbyte(input_message); /* 3.D message */ if (msg_type == 'D') { describe_type = pq_getmsgbyte(input_message); describe_target = pq_getmsgstring(input_message); if (describe_type == 'S') { if (strcmp(stmt_name, describe_target)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("conflict stmt name in Batch bind-execute message: bind %s, describe %s", stmt_name, describe_target))); } else if (describe_type == 'P') { if (strcmp(portal_name, describe_target)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("conflict portal name in Batch bind-execute message: bind %s, describe %s", portal_name, describe_target))); } else { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid DESCRIBE message subtype in Batch bind-execute message: %d", describe_type))); } /* next should be E */ msg_type = pq_getmsgbyte(input_message); } /* 4.E message */ if (msg_type == 'E') { exec_portal_name = pq_getmsgstring(input_message); if (strcmp(portal_name, exec_portal_name)) ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("conflict portal name in Batch bind-execute message: bind %s, execute %s", portal_name, exec_portal_name))); max_rows = pq_getmsgint(input_message, 4); if (max_rows > 0) ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Not support max_row in Batch bind-execute message: %d", max_rows))); max_rows = 0; } else { ereport(ERROR, (errcode(ERRCODE_PROTOCOL_VIOLATION), errmsg("invalid value in Batch bind-execute message: %d", msg_type))); } /* 5.Finish the message */ pq_getmsgend(input_message); /* B.Set message for bind and describe */ if (t_thrd.postgres_cxt.whereToSendOutput == DestRemote) { /* 1.Send BindComplete */ pq_putemptymessage('2'); /* 2.Send Describe */ if (describe_type != 0) { switch (describe_type) { case 'S': { StringInfoData buf; /* Prepared statements shouldn't have changeable result descs */ Assert(psrc->fixed_result); /* * First describe the parameters... */ pq_beginmessage(&buf, 't'); /* parameter description message type */ pq_sendint(&buf, psrc->num_params, 2); for (int i = 0; i < psrc->num_params; i++) { Oid ptype = psrc->param_types[i]; pq_sendint(&buf, (int)ptype, 4); } pq_endmessage(&buf); /* * Next send RowDescription or NoData to describe the result... */ if (psrc->resultDesc) { /* Get the plan's primary targetlist */ List* tlist = CachedPlanGetTargetList(psrc); SendRowDescriptionMessage( &(*t_thrd.postgres_cxt.row_description_buf), psrc->resultDesc, tlist, NULL); } else pq_putemptymessage('n'); /* NoData */ } break; case 'P': /* set the message later before execute */ send_DP_msg = true; break; default: /* should not be here */ break; } } } /* Adjust destination to tell printtup.c what to do */ dest = (CommandDest)t_thrd.postgres_cxt.whereToSendOutput; if (dest == DestRemote) dest = DestRemoteExecute; /* C.Execute each one */ if (psrc->single_exec_node != NULL) { StringInfo desc_msg = NULL; StringInfoData describe_body; StringInfoData execute_msg; int pnameLen = strlen(portal_name) + 1; errno_t ss_rc = 0; StringInfo batch_msg_dnset; int* node_idx_set = NULL; int* batch_count_dnset = NULL; int* params_size_dnset = NULL; /* No nodes found ?? */ if (u_sess->pgxc_cxt.NumDataNodes == 0) ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("No Datanode defined in cluster"))); node_idx_set = (int*)palloc0(batch_count * sizeof(int)); batch_count_dnset = (int*)palloc0(u_sess->pgxc_cxt.NumDataNodes * sizeof(int)); params_size_dnset = (int*)palloc0(u_sess->pgxc_cxt.NumDataNodes * sizeof(int)); /* 1.get necessary info before construct batch message for each dn */ light_preprocess_batchmsg_set( psrc, params_set, params_set_end, batch_count, node_idx_set, batch_count_dnset, params_size_dnset); /* 2.construct batch message for each dn */ /* construct D message if necessary */ if (send_DP_msg) { /* 'D' + 'P' + portal_name(describe_target) */ describe_body.len = 1 + 1 + pnameLen; describe_body.maxlen = describe_body.len + 1; describe_body.cursor = 0; describe_body.data = (char*)palloc0(describe_body.maxlen); describe_body.data[0] = 'D'; describe_body.data[1] = 'P'; ss_rc = memcpy_s(describe_body.data + 2, pnameLen, portal_name, pnameLen); securec_check(ss_rc, "\0", "\0"); desc_msg = &describe_body; } /* construct E message (all same): 'E', portal name, 0 max_row */ execute_msg.len = 1 + pnameLen + 4; execute_msg.maxlen = execute_msg.len + 1; execute_msg.cursor = 0; execute_msg.data = (char*)palloc0(execute_msg.maxlen); execute_msg.data[0] = 'E'; ss_rc = memcpy_s(execute_msg.data + 1, pnameLen, portal_name, pnameLen); securec_check(ss_rc, "\0", "\0"); batch_msg_dnset = light_construct_batchmsg_set( input_message, params_set_end, node_idx_set, batch_count_dnset, params_size_dnset, desc_msg, &execute_msg); /* 3.run for each dn */ lightProxy *scn = NULL; bool enable_gpc = (ENABLE_CN_GPC && stmt_name[0] != '\0'); bool enable_unamed_gpc = psrc->gpc.status.InShareTable() && stmt_name[0] == '\0'; if (enable_gpc) scn = lightProxy::locateLpByStmtName(stmt_name); else scn = (lightProxy *)psrc->lightProxyObj; if (scn == NULL) { /* initialize session cache context; typically it won't store much */ MemoryContext context = AllocSetContextCreate(u_sess->cache_mem_cxt, "LightPorxyMemory", ALLOCSET_SMALL_MINSIZE, ALLOCSET_SMALL_INITSIZE, ALLOCSET_SMALL_MAXSIZE); scn = New(context)lightProxy(context, psrc, portal_name, stmt_name); if (enable_gpc) { scn->storeLpByStmtName(stmt_name); psrc->lightProxyObj = NULL; } else if (enable_unamed_gpc) { Assert(u_sess->pcache_cxt.unnamed_gpc_lp == NULL); Assert(psrc->lightProxyObj == NULL); u_sess->pcache_cxt.unnamed_gpc_lp = scn; } else { psrc->lightProxyObj = scn; } } else { Assert(!enable_unamed_gpc); } if (enable_gpc) { /* cngpc need fill gpc msg just like BuildCachedPlan. */ GPCFillMsgForLp(psrc); } Assert(scn != NULL); process_count = light_execute_batchmsg_set(scn, batch_msg_dnset, batch_count_dnset, send_DP_msg); } else { char* completionTag = (char*)palloc0(COMPLETION_TAG_BUFSIZE * sizeof(char)); List* copyedStmts = NULL; MemoryContext tmpCxt = NULL; if (u_sess->attr.attr_resource.use_workload_manager && g_instance.wlm_cxt->gscgroup_init_done && !IsAbortedTransactionBlockState()) { u_sess->wlm_cxt->cgroup_last_stmt = u_sess->wlm_cxt->cgroup_stmt; u_sess->wlm_cxt->cgroup_stmt = WLMIsSpecialCommand(psrc->raw_parse_tree, NULL); } if (use_original_logic) { for (int i = 0; i < batch_count; i++) { exec_one_in_batch(psrc, params_set[i], numRFormats, rformats, (i == 0) ? send_DP_msg : false, dest, completionTag, stmt_name, ©edStmts, &tmpCxt, pstmt); u_sess->pcache_cxt.gpc_in_batch = true; if (ENABLE_GPC && stmt_name[0] != '\0') { #ifdef ENABLE_MULTIPLE_NODES psrc = IS_PGXC_DATANODE ? u_sess->pcache_cxt.cur_stmt_psrc : pstmt->plansource; #else psrc = pstmt->plansource; #endif } } /* only send the last commandTag */ EndCommand(completionTag, dest); } else { int tmp_count = 0; for (int i = 0; i < batch_count; i++) { exec_one_in_batch(psrc, params_set[i], numRFormats, rformats, (i == 0) ? send_DP_msg : false, dest, completionTag, stmt_name, ©edStmts, &tmpCxt, pstmt); u_sess->pcache_cxt.gpc_in_batch = true; if (ENABLE_GPC && stmt_name[0] != '\0') { #ifdef ENABLE_MULTIPLE_NODES psrc = IS_PGXC_DATANODE ? u_sess->pcache_cxt.cur_stmt_psrc : pstmt->plansource; #else psrc = pstmt->plansource; #endif } /* Get process_count (X) from completionTag */ if (completionTag[0] == 'I') { /* INSERT 0 X */ tmp_count = pg_atoi(&completionTag[9], sizeof(int32), '\0'); } else if (completionTag[0] == 'M') { /* MERGE X */ tmp_count = pg_atoi(&completionTag[6], sizeof(int32), '\0'); } else { /* DELETE X / UPDATE X / SELECT X */ Assert(completionTag[0] == 'U' || completionTag[0] == 'D' || completionTag[0] == 'S'); tmp_count = pg_atoi(&completionTag[7], sizeof(int32), '\0'); } process_count += tmp_count; } } pfree(completionTag); } /* end batch, reset gpc batch flag */ u_sess->pcache_cxt.gpc_in_batch = false; /* Done with the snapshot used */ if (snapshot_set) PopActiveSnapshot(); if (!use_original_logic) { /* Send appropriate CommandComplete to client */ appendStringInfo(&process_result, "%d", process_count); EndCommand(process_result.data, dest); } MemoryContextSwitchTo(oldContext); /* release global active counts */ if (ENABLE_WORKLOAD_CONTROL) { if (g_instance.wlm_cxt->dynamic_workload_inited) { if (t_thrd.wlm_cxt.parctl_state.simple == 0) dywlm_client_release(&t_thrd.wlm_cxt.parctl_state); else WLMReleaseGroupActiveStatement(); dywlm_client_max_release(&t_thrd.wlm_cxt.parctl_state); } else WLMParctlRelease(&t_thrd.wlm_cxt.parctl_state); } /* * Emit duration logging if appropriate. */ switch (check_log_duration(msec_str, false)) { case 1: Assert(false); break; case 2: { char* mask_string = NULL; MASK_PASSWORD_START(mask_string, psrc->query_string); ereport(LOG, (errmsg("duration: %s ms queryid %ld unique id %ld batch bind-execute %s%s%s: %s", msec_str, u_sess->debug_query_id, u_sess->slow_query_cxt.slow_query.unique_sql_id, *stmt_name ? stmt_name : "", *portal_name ? "/" : "", *portal_name ? portal_name : "", mask_string), errhidestmt(true))); MASK_PASSWORD_END(mask_string, psrc->query_string); break; } default: break; } if (MEMORY_TRACKING_QUERY_PEAK) ereport(LOG, (errmsg("execute batch execute %s, peak memory %ld(kb)", psrc->query_string, (int64)(t_thrd.utils_cxt.peakedBytesInQueryLifeCycle/1024)))); if (save_log_statement_stats) { ShowUsage("BATCH BIND MESSAGE STATISTICS"); } t_thrd.postgres_cxt.debug_query_string = NULL; } /* lock function for g_instance.codegen_IRload_process_count Addition */ void lock_codegen_process_add() { AutoMutexLock copyLock(&nodeDefCopyLock); copyLock.lock(); g_instance.codegen_IRload_process_count++; copyLock.unLock(); } /* lock function for g_instance.codegen_IRload_process_count Subtraction */ void lock_codegen_process_sub(int count) { if (count == 0) { return; } AutoMutexLock copyLock(&nodeDefCopyLock); copyLock.lock(); g_instance.codegen_IRload_process_count = g_instance.codegen_IRload_process_count - count; copyLock.unLock(); } /* * @Description: get the om online state, expansion/node replace or other. * @return : the state of current om online operator. */ OM_ONLINE_STATE get_om_online_state() { char* gauss_home = NULL; char om_action_online_state_file[MAXPGPATH] = {0}; const int max_file_path = 4096; char om_state_file_path[max_file_path] = {0}; errno_t ret = EOK; /* Get the GAUSSHOME through security way. */ gauss_home = getGaussHome(); ret = snprintf_s(om_action_online_state_file, MAXPGPATH, MAXPGPATH - 1, "%s/bin/om_action_online.state", gauss_home); securec_check_ss(ret, "\0", "\0"); /* * Currently if om_action_online_state_file is exists, it's online expansion. * If we support other online om operations later, here can be expanded. */ realpath(om_action_online_state_file, om_state_file_path); FILE* fp = fopen(om_state_file_path, "r"); if (fp != NULL) { fclose(fp); return OM_ONLINE_EXPANSION; } else { return OM_ONLINE_NODE_REPLACE; } } /* * check whether sql_compatibility is valid */ bool checkCompArgs(const char *compFormat) { /* make sure input is not null */ if (compFormat == NULL) { return false; } if (pg_strncasecmp(compFormat, g_dbCompatArray[DB_CMPT_A].name, sizeof(g_dbCompatArray[DB_CMPT_A].name)) != 0 && pg_strncasecmp(compFormat, g_dbCompatArray[DB_CMPT_B].name, sizeof(g_dbCompatArray[DB_CMPT_B].name)) != 0 && pg_strncasecmp(compFormat, g_dbCompatArray[DB_CMPT_C].name, sizeof(g_dbCompatArray[DB_CMPT_C].name)) != 0 && pg_strncasecmp(compFormat, g_dbCompatArray[DB_CMPT_PG].name, sizeof(g_dbCompatArray[DB_CMPT_PG].name)) != 0) { return false; } return true; } void ResetInterruptCxt() { t_thrd.int_cxt.ignoreBackendSignal = false; t_thrd.int_cxt.InterruptHoldoffCount = 0; t_thrd.int_cxt.QueryCancelHoldoffCount = 0; t_thrd.int_cxt.InterruptCountResetFlag = true; t_thrd.int_cxt.CritSectionCount = 0; }