*
* utility.cpp
* Contains functions which control the execution of the openGauss utility
* commands. At one time acted as an interface between the Lisp and C
* systems.
*
* 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
*
* IDENTIFICATION
* src/gausskernel/process/tcop/utility.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.h"
#include "plugin_postgres.h"
#include "plugin_nodes/parsenodes_common.h"
#include "plugin_commands/defrem.h"
#include "nodes/print.h"
#include "access/cstore_delta.h"
#include "access/reloptions.h"
#include "access/tableam.h"
#include "access/transam.h"
#include "access/twophase.h"
#include "access/xact.h"
#include "access/multi_redo_api.h"
#include "access/xlog.h"
#include "catalog/catalog.h"
#include "catalog/index.h"
#include "catalog/namespace.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_synonym.h"
#include "catalog/pgxc_group.h"
#include "catalog/pg_streaming_fn.h"
#include "catalog/toasting.h"
#include "catalog/cstore_ctlg.h"
#include "catalog/gs_db_privilege.h"
#include "catalog/gs_global_config.h"
#include "catalog/gs_matview_dependency.h"
#include "catalog/gs_matview.h"
#include "catalog/pg_job.h"
#include "catalog/gs_job_attribute.h"
#include "catalog/pg_object.h"
#include "commands/alter.h"
#include "commands/async.h"
#include "commands/cluster.h"
#include "commands/comment.h"
#include "commands/collationcmds.h"
#include "commands/conversioncmds.h"
#include "commands/copy.h"
#include "commands/createas.h"
#include "commands/dbcommands.h"
#include "commands/defrem.h"
#include "commands/discard.h"
#include "commands/event_trigger.h"
#include "commands/explain.h"
#include "commands/extension.h"
#include "commands/matview.h"
#include "commands/lockcmds.h"
#include "commands/portalcmds.h"
#include "commands/prepare.h"
#include "commands/proclang.h"
#include "commands/publicationcmds.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
#include "commands/sec_rls_cmds.h"
#include "commands/sequence.h"
#include "commands/subscriptioncmds.h"
#include "commands/shutdown.h"
#include "commands/tablecmds.h"
#include "commands/tablespace.h"
#include "commands/directory.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "commands/user.h"
#include "commands/vacuum.h"
#include "commands/verify.h"
#include "commands/view.h"
#include "executor/node/nodeModifyTable.h"
#include "foreign/fdwapi.h"
#include "instruments/instr_unique_sql.h"
#include "gaussdb_version.h"
#include "gs_policy/gs_policy_masking.h"
#include "miscadmin.h"
#include "optimizer/cost.h"
#include "plugin_optimizer/plancat.h"
#include "nodes/makefuncs.h"
#include "plugin_parser/parse_utilcmd.h"
#include "plugin_parser/analyze.h"
#include "plugin_parser/parse_func.h"
#include "plugin_parser/parse_relation.h"
#include "postmaster/autovacuum.h"
#include "postmaster/bgwriter.h"
#include "rewrite/rewriteDefine.h"
#include "rewrite/rewriteRemove.h"
#include "storage/smgr/fd.h"
#include "storage/lmgr.h"
#include "storage/procarray.h"
#include "storage/tcap.h"
#include "tcop/pquery.h"
#include "tcop/utility.h"
#include "tsearch/ts_locale.h"
#include "utils/acl.h"
#include "utils/extended_statistics.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "access/heapam.h"
#include "utils/syscache.h"
#include "gs_policy/gs_policy_audit.h"
#include "gs_policy/policy_common.h"
#include "client_logic/client_logic.h"
#include "db4ai/create_model.h"
#include "gs_ledger/userchain.h"
#include "gs_ledger/ledger_utils.h"
#include "libpq/libpq.h"
#ifdef ENABLE_MULTIPLE_NODES
#include "pgxc/pgFdwRemote.h"
#include "pgxc/globalStatistic.h"
#include "tsdb/common/ts_tablecmds.h"
#include "tsdb/storage/delta_merge.h"
#include "tsdb/utils/ts_redis.h"
#include "tsdb/optimizer/policy.h"
#include "tsdb/time_bucket.h"
#include "streaming/streaming_catalog.h"
#include "storage/buf/bufmgr.h"
#endif
#ifdef PGXC
#include "pgxc/barrier.h"
#include "pgxc/execRemote.h"
#include "pgxc/locator.h"
#include "pgxc/pgxc.h"
#include "plugin_optimizer/pgxcplan.h"
#include "pgxc/poolutils.h"
#include "nodes/nodes.h"
#include "pgxc/poolmgr.h"
#include "pgxc/nodemgr.h"
#include "pgxc/groupmgr.h"
#include "utils/lsyscache.h"
#include "utils/rel.h"
#include "utils/rel_gs.h"
#include "utils/snapmgr.h"
#include "utils/builtins.h"
#include "utils/inval.h"
#include "catalog/pgxc_node.h"
#include "workload/workload.h"
#include "streaming/init.h"
#include "replication/archive_walreceiver.h"
#ifdef ENABLE_HTAP
#include "access/htap/imcs_ctlg.h"
#endif
static void ProcessUtilitySlow(Node *parsetree,
const char *queryString,
ParamListInfo params,
DestReceiver *dest,
#ifdef PGXC
bool sent_to_remote,
#endif
char *completionTag,
ProcessUtilityContext context,
bool isCTAS);
static void ExecDropStmt(DropStmt *parse_tree, const char *query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static ObjectAddress doRenameStmt(RenameStmt*parse_tree, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static void doGrantStmt(GrantStmt*stmt, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static void ProcessUtilitySlow(Node *parsetree,
const char *queryString,
ParamListInfo params,
DestReceiver *dest,
#ifdef PGXC
bool sent_to_remote,
#endif
char *completionTag,
ProcessUtilityContext context,
bool isCTAS);
static void ExecDropStmt(DropStmt *parse_tree, const char *query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static ObjectAddress doRenameStmt(RenameStmt*parse_tree, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static void doGrantStmt(GrantStmt*stmt, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level);
static RemoteQueryExecType ExecUtilityFindNodes(ObjectType object_type, Oid rel_id, bool* is_temp);
static RemoteQueryExecType exec_utility_find_nodes_relkind(Oid rel_id, bool* is_temp);
static RemoteQueryExecType get_nodes_4_comment_utility(CommentStmt* stmt, bool* is_temp, ExecNodes** exec_nodes);
static RemoteQueryExecType get_nodes_4_rules_utility(RangeVar* relation, bool* is_temp);
static void drop_stmt_pre_treatment(
DropStmt* stmt, const char* query_string, bool sent_to_remote, bool* is_temp, RemoteQueryExecType* exec_type);
static void ExecUtilityWithMessage(const char* query_string, bool sent_to_remote, bool is_temp);
static void exec_utility_with_message_parallel_ddl_mode(
const char* query_string, bool sent_to_remote, bool is_temp, const char* first_exec_node, RemoteQueryExecType exec_type);
static bool is_stmt_allowed_in_locked_mode(Node* parse_tree, const char* query_string);
static ExecNodes* assign_utility_stmt_exec_nodes(Node* parse_tree);
#endif
static void TransformLoadDataToCopy(LoadStmt* stmt);
static void add_remote_query_4_alter_stmt(bool is_first_node, AlterTableStmt* atstmt, const char* query_string, List** stmts,
char** drop_seq_string, ExecNodes** exec_nodes);
bool IsVariableinBlackList(const char* name);
void DoVacuumMppTable(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote);
THR_LOCAL ProcessUtility_hook_type ProcessUtility_hook = NULL;
#ifdef ENABLE_MULTIPLE_NODES
static void analyze_tmptbl_debug_cn(Oid rel_id, Oid main_relid, VacuumStmt* stmt, bool iscommit);
#endif
extern void begin_delta_merge(VacuumStmt* stmt);
#ifdef DOLPHIN
extern int dolphin_process_command(StringInfo buf);
#endif
#ifdef ENABLE_MULTIPLE_NODES
static void set_dndistinct_coors(VacuumStmt* stmt, int attnum);
#endif
static void attatch_global_info(char** query_string_with_info, VacuumStmt* stmt, const char* query_string, bool has_var,
AnalyzeMode e_analyze_mode, Oid rel_id, char* foreign_tbl_schedul_message = NULL);
static char* get_hybrid_message(ForeignTableDesc* table_desc, VacuumStmt* stmt, char* foreign_tbl_schedul_message);
static bool need_full_dn_execution(const char* group_name);
extern void check_log_ft_definition(CreateForeignTableStmt* stmt);
extern void ts_check_feature_disable();
static bool IsAllTempObjectsInVacuumStmt(Node* parsetree);
static int64 getCopySequenceMaxval(const char *nspname, const char *relname, const char *colname);
static int64 getCopySequenceCountval(const char *nspname, const char *relname);
static void PreRedoInOndemandRecovery(Node* parse_tree);
static void PreRedoIndexInOndemandRecovery(Oid indexId);
static void PreRedoTableInOndemandRecovery(Oid relId);
#define POSTGIS_VERSION_NUM 2
#define PAGE_SIZE 8
#define HALF_AMOUNT 0.5
#define BIG_MEM_RATIO 1.2
#define SMALL_MEM_RATIO 1.1
static const int LOADER_COL_BUF_CNT = 5;
Oid GetNamespaceIdbyRelId(const Oid relid)
{
HeapTuple tuple = SearchSysCache1(RELOID, ObjectIdGetDatum(relid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("relation with OID %u does not exist", relid)));
}
Oid namespaceOid = ((Form_pg_class)GETSTRUCT(tuple))->relnamespace;
ReleaseSysCache(tuple);
return namespaceOid;
}
bool IsSchemaInDistribution(const Oid namespaceOid)
{
bool isNull = false;
bool result = false;
if (!OidIsValid(namespaceOid)) {
return false;
}
HeapTuple tuple = SearchSysCache1(NAMESPACEOID, ObjectIdGetDatum(namespaceOid));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema with OID %u does not exist", namespaceOid)));
}
Datum in_redistribution = SysCacheGetAttr(NAMESPACEOID, tuple, Anum_pg_namespace_in_redistribution, &isNull);
if (!isNull) {
result = (DatumGetChar(in_redistribution) == 'y');
}
ReleaseSysCache(tuple);
return result;
}
static bool foundPgstatPartititonOperations(AlterTableType subtype)
{
return subtype == AT_TruncatePartition || subtype == AT_ExchangePartition || subtype == AT_DropPartition ||
subtype == AT_DropSubPartition;
}
* report_utility_time
*
* send the finish time of copy and exchange/truncate/drop partition operations to pgstat collector.
* ----------------------------------------------------------------
*/
static void report_utility_time(void* parse_tree)
{
ListCell* lc = NULL;
RangeVar* relation = NULL;
if (u_sess->attr.attr_sql.enable_save_datachanged_timestamp == false)
return;
if (!IS_SINGLE_NODE && !IS_PGXC_COORDINATOR)
return;
if (nodeTag(parse_tree) != T_CopyStmt && nodeTag(parse_tree) != T_AlterTableStmt)
return;
if (nodeTag(parse_tree) == T_CopyStmt) {
CopyStmt* cs = (CopyStmt*)parse_tree;
if (cs->is_from == false) {
ereport(DEBUG1, (errmsg("\"copy to\" found")));
return;
}
relation = cs->relation;
}
if (nodeTag(parse_tree) == T_AlterTableStmt) {
AlterTableStmt* ats = (AlterTableStmt*)parse_tree;
bool found = false;
AlterTableCmd* cmd = NULL;
foreach (lc, ats->cmds) {
cmd = (AlterTableCmd*)lfirst(lc);
found = foundPgstatPartititonOperations(cmd->subtype);
}
if (found == false) {
ereport(DEBUG1, (errmsg("truncate/exchange/drop partition not found")));
return;
}
relation = ats->relation;
}
if (relation == NULL) {
ereport(DEBUG1, (errmsg("relation is NULL in CopyStmt/AlterTableStmt")));
return;
}
MemoryContext current_ctx = CurrentMemoryContext;
Relation rel = NULL;
PG_TRY();
{
rel = heap_openrv(relation, AccessShareLock);
Oid rid = rel->rd_id;
if (rel->rd_rel->relkind == RELKIND_RELATION && rid >= FirstNormalObjectId) {
if (rel->rd_rel->relpersistence == RELPERSISTENCE_PERMANENT ||
rel->rd_rel->relpersistence == RELPERSISTENCE_UNLOGGED) {
pgstat_report_data_changed(rid, STATFLG_RELATION, rel->rd_rel->relisshared);
}
}
heap_close(rel, AccessShareLock);
}
PG_CATCH();
{
(void)MemoryContextSwitchTo(current_ctx);
ErrorData* edata = CopyErrorData();
ereport(DEBUG1, (errmsg("Failed to send data changed time, cause: %s", edata->message)));
FlushErrorState();
FreeErrorData(edata);
if (rel != NULL)
heap_close(rel, AccessShareLock);
}
PG_END_TRY();
}
* CommandIsReadOnly: is an executable query read-only?
*
* This is a much stricter test than we apply for XactReadOnly mode;
* the query must be *in truth* read-only, because the caller wishes
* not to do CommandCounterIncrement for it.
*
* Note: currently no need to support Query nodes here
*/
bool CommandIsReadOnly(Node* parse_tree)
{
if (IsA(parse_tree, PlannedStmt)) {
PlannedStmt* stmt = (PlannedStmt*)parse_tree;
return CommandIsReadOnly(stmt);
}
return false;
}
bool CommandIsReadOnly(PlannedStmt *stmt)
{
switch (stmt->commandType) {
case CMD_SELECT:
if (stmt->rowMarks != NIL)
return false;
else if (stmt->hasModifyingCTE)
return false;
else
return true;
case CMD_UPDATE:
case CMD_INSERT:
case CMD_DELETE:
case CMD_MERGE:
return false;
default:
elog(WARNING, "unrecognized commandType: %d", (int)stmt->commandType);
break;
}
return false;
}
* check_xact_readonly: is a utility command read-only?
*
* Here we use the loose rules of XactReadOnly mode: no permanent effects
* on the database are allowed.
*/
static void check_xact_readonly(Node* parse_tree)
{
if (!u_sess->attr.attr_common.XactReadOnly)
return;
if (libpqsw_skip_check_readonly()) {
return;
}
*Note:Disk space usage reach the threshold causing database open read-only.
*In this case,if xc_maintenance_mode=on T_DropStmt and T_TruncateStmt is allowed
*otherwise,just allow read-only stmt
*/
if (u_sess->attr.attr_common.xc_maintenance_mode) {
switch (nodeTag(parse_tree)) {
case T_DropStmt:
case T_TruncateStmt:
return;
default:
break;
}
}
* Note: Commands that need to do more complicated checking are handled
* elsewhere, in particular COPY and plannable statements do their own
* checking. However they should all call PreventCommandIfReadOnly to
* actually throw the error.
*/
switch (nodeTag(parse_tree)) {
case T_AlterDatabaseStmt:
case T_AlterDatabaseSetStmt:
case T_AlterDomainStmt:
case T_AlterFunctionStmt:
case T_CompileStmt:
case T_AlterRoleSetStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt:
case T_AlterSchemaStmt:
case T_AlterSeqStmt:
case T_AlterTableStmt:
case T_RenameStmt:
case T_CommentStmt:
case T_DefineStmt:
case T_CreateCastStmt:
case T_CreateConversionStmt:
case T_CreatedbStmt:
case T_CreateEventTrigStmt:
case T_AlterEventTrigStmt:
case T_CreateDomainStmt:
case T_CreateFunctionStmt:
case T_CreateEventStmt:
case T_AlterEventStmt:
case T_DropEventStmt:
case T_ShowEventStmt:
case T_CreateRoleStmt:
case T_IndexStmt:
case T_CreatePLangStmt:
case T_CreateOpClassStmt:
case T_CreateOpFamilyStmt:
case T_AlterOpFamilyStmt:
case T_RuleStmt:
case T_CreateSchemaStmt:
case T_CreateSeqStmt:
case T_CreateStmt:
case T_CreateTableAsStmt:
case T_RefreshMatViewStmt:
case T_CreateTableSpaceStmt:
case T_CreateTrigStmt:
case T_CompositeTypeStmt:
case T_CreateEnumStmt:
case T_CreateRangeStmt:
case T_AlterEnumStmt:
case T_ViewStmt:
case T_DropStmt:
case T_DropdbStmt:
case T_DropTableSpaceStmt:
case T_DropRoleStmt:
case T_GrantStmt:
case T_GrantRoleStmt:
case T_GrantDbStmt:
case T_AlterDefaultPrivilegesStmt:
case T_TruncateStmt:
case T_DropOwnedStmt:
case T_ReassignOwnedStmt:
case T_AlterTSDictionaryStmt:
case T_AlterTSConfigurationStmt:
case T_CreateExtensionStmt:
case T_AlterExtensionStmt:
case T_AlterExtensionContentsStmt:
case T_CreateFdwStmt:
case T_AlterFdwStmt:
case T_CreateForeignServerStmt:
case T_AlterForeignServerStmt:
case T_CreateUserMappingStmt:
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
case T_AlterTableSpaceOptionsStmt:
case T_CreateForeignTableStmt:
case T_SecLabelStmt:
case T_CreateResourcePoolStmt:
case T_AlterResourcePoolStmt:
case T_DropResourcePoolStmt:
case T_AlterGlobalConfigStmt:
case T_DropGlobalConfigStmt:
case T_CreatePolicyLabelStmt:
case T_AlterPolicyLabelStmt:
case T_DropPolicyLabelStmt:
case T_CreateAuditPolicyStmt:
case T_AlterAuditPolicyStmt:
case T_DropAuditPolicyStmt:
case T_CreateWeakPasswordDictionaryStmt:
case T_DropWeakPasswordDictionaryStmt:
case T_CreateMaskingPolicyStmt:
case T_AlterMaskingPolicyStmt:
case T_DropMaskingPolicyStmt:
case T_ClusterStmt:
case T_ReindexStmt:
case T_CreateDataSourceStmt:
case T_AlterDataSourceStmt:
case T_CreateDirectoryStmt:
case T_DropDirectoryStmt:
case T_CreateRlsPolicyStmt:
case T_AlterRlsPolicyStmt:
case T_CreateSynonymStmt:
case T_DropSynonymStmt:
case T_CreateClientLogicGlobal:
case T_CreateClientLogicColumn:
case T_CreatePackageStmt:
case T_CreatePackageBodyStmt:
case T_CreatePublicationStmt:
case T_AlterPublicationStmt:
case T_CreateSubscriptionStmt:
case T_AlterSubscriptionStmt:
case T_TimeCapsuleStmt:
case T_DropSubscriptionStmt:
case T_PurgeStmt:
case T_ShrinkStmt:
PreventCommandIfReadOnly(CreateCommandTag(parse_tree));
break;
case T_VacuumStmt: {
VacuumStmt* stmt = (VacuumStmt*)parse_tree;
if (!(stmt->options & VACOPT_VERIFY)) {
PreventCommandIfReadOnly(CreateCommandTag(parse_tree));
}
break;
}
case T_AlterRoleStmt: {
AlterRoleStmt *stmt = (AlterRoleStmt *)parse_tree;
if (!(DO_NOTHING != stmt->lockstatus &&
(t_thrd.postmaster_cxt.HaShmData->current_mode == STANDBY_MODE || SS_STANDBY_MODE))) {
PreventCommandIfReadOnly(CreateCommandTag(parse_tree));
}
break;
}
default:
break;
}
}
* PreventCommandIfReadOnly: throw error if XactReadOnly
*
* This is useful mainly to ensure consistency of the error message wording;
* most callers have checked XactReadOnly for themselves.
*/
void PreventCommandIfReadOnly(const char* cmd_name)
{
if (SSIsServerModeReadOnly()) {
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot execute %s at Standby node while DMS enabled", cmd_name)));
}
if (u_sess->attr.attr_common.XactReadOnly && u_sess->attr.attr_storage.replorigin_sesssion_origin == 0)
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot execute %s in a read-only transaction", cmd_name)));
}
* PreventCommandDuringRecovery: throw error if RecoveryInProgress
*
* The majority of operations that are unsafe in a Hot Standby slave
* will be rejected by XactReadOnly tests. However there are a few
* commands that are allowed in "read-only" xacts but cannot be allowed
* in Hot Standby mode. Those commands should call this function.
*/
void PreventCommandDuringRecovery(const char* cmd_name)
{
if (SSIsServerModeReadOnly()) {
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot execute %s at Standby node while DMS enabled", cmd_name)));
}
if (RecoveryInProgress())
ereport(ERROR,
(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
errmsg("cannot execute %s during recovery", cmd_name)));
}
void PreventCommandDuringSSOndemandRedo(Node* parseTree)
{
switch(nodeTag(parseTree)) {
case T_AlterSchemaStmt:
case T_InsertStmt:
case T_DeleteStmt:
case T_UpdateStmt:
case T_SelectStmt:
case T_AlterTableStmt:
case T_CreateStmt:
case T_DropStmt:
case T_IndexStmt:
case T_CreateFunctionStmt:
case T_AlterFunctionStmt:
case T_CompileStmt:
case T_DoStmt:
case T_RenameStmt:
case T_TransactionStmt:
case T_ViewStmt:
case T_CreateTableAsStmt:
case T_VariableSetStmt:
case T_VariableShowStmt:
case T_ReindexStmt:
case T_CreateSchemaStmt:
case T_AlterDatabaseStmt:
case T_AlterDatabaseSetStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt:
break;
default:
if (SS_IN_ONDEMAND_RECOVERY) {
ereport(ERROR,
(errcode(ERRCODE_RUN_TRANSACTION_DURING_RECOVERY),
errmsg("only support INSERT/UPDATE/DELETE/SELECT/SET/SHOW/CALL, and ALTER/CREATE/DROP on Table/Index/View/Procedure/Schema, and ALTER on Database during SS on-demand recovery, "
"command %d", nodeTag(parseTree))));
}
break;
}
return;
}
* CheckRestrictedOperation: throw error for hazardous command if we're
* inside a security restriction context.
*
* This is needed to protect session-local state for which there is not any
* better-defined protection mechanism, such as ownership.
*/
static void CheckRestrictedOperation(const char* cmd_name)
{
if (InSecurityRestrictedOperation())
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("cannot execute %s within security-restricted operation", cmd_name)));
}
* @NodeGroup Support
* @Description: Determine the list objects is in same node group
*
* @Return: 'true' in same group, 'false' in different group
*/
static bool DropObjectsInSameNodeGroup(DropStmt* stmt)
{
ListCell* cell = NULL;
ListCell* cell2 = NULL;
Oid group_oid = InvalidOid;
Oid goid = InvalidOid;
bool find_first = false;
ObjectType object_type = stmt->removeType;
Oid ins_group_oid;
if (stmt->objects == NIL || list_length(stmt->objects) == 1) {
return true;
}
ins_group_oid = ng_get_installation_group_oid();
foreach (cell, stmt->objects) {
ObjectAddress address;
List* objname = (List*)lfirst(cell);
List* objargs = NIL;
Relation relation = NULL;
if (stmt->arguments) {
cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2));
objargs = (List*)lfirst(cell2);
}
address = get_object_address(stmt->removeType, objname, objargs, &relation, AccessShareLock, stmt->missing_ok);
if (!OidIsValid(address.objectId)) {
continue;
}
if (object_type == OBJECT_FUNCTION) {
goid = GetFunctionNodeGroupByFuncid(address.objectId);
if (!OidIsValid(goid))
goid = ins_group_oid;
} else if (OBJECT_IS_SEQUENCE(object_type)) {
Oid tableId = InvalidOid;
int32 colId;
if (!sequenceIsOwned(address.objectId, &tableId, &colId))
goid = ins_group_oid;
else
goid = ng_get_baserel_groupoid(tableId, RELKIND_RELATION);
}
if (relation)
relation_close(relation, AccessShareLock);
if (!find_first) {
group_oid = goid;
find_first = true;
continue;
}
if (goid != group_oid) {
return false;
}
}
return true;
}
* @NodeGroup Support
* @Description: Get same node group for grant objects,
* Only for tables, foreign tables, sequences and functions,
* Caller should not call GrantStmtGetNodeGroup for other object type.
*
* @Return: InvalidOid means different group, or same group oid.
*/
static Oid GrantStmtGetNodeGroup(GrantStmt* stmt)
{
Oid goid = InvalidOid;
Oid ins_group_oid = InvalidOid;
List* oids = NIL;
bool find_first = false;
ListCell* cell = NULL;
Oid group_oid = InvalidOid;
oids = GrantStmtGetObjectOids(stmt);
ins_group_oid = ng_get_installation_group_oid();
foreach (cell, oids) {
Oid object_oid = lfirst_oid(cell);
char relkind;
if (!OidIsValid(object_oid)) {
continue;
}
if (stmt->objtype == ACL_OBJECT_FUNCTION) {
goid = GetFunctionNodeGroupByFuncid(object_oid);
if (!OidIsValid(goid))
goid = ins_group_oid;
} else if (stmt->objtype == ACL_OBJECT_RELATION || stmt->objtype == ACL_OBJECT_SEQUENCE) {
relkind = get_rel_relkind(object_oid);
if (relkind == RELKIND_RELATION || relkind == RELKIND_FOREIGN_TABLE || relkind == RELKIND_STREAM) {
goid = ng_get_baserel_groupoid(object_oid, RELKIND_RELATION);
} else {
continue;
}
} else {
Assert(false);
}
if (!find_first) {
group_oid = goid;
find_first = true;
continue;
}
if (goid != group_oid) {
list_free(oids);
return InvalidOid;
}
}
list_free(oids);
return find_first ? group_oid : ins_group_oid;
}
* @NodeGroup Support
* @Description: Create ExecNodes according node group oid.
*
* @Return: ExecNodes.
*/
static ExecNodes* GetNodeGroupExecNodes(Oid group_oid)
{
ExecNodes* exec_nodes = NULL;
Oid* members = NULL;
int nmembers;
bool need_full_dn = false;
exec_nodes = makeNode(ExecNodes);
if (!in_logic_cluster()) {
char* group_name = get_pgxc_groupname(group_oid);
if (group_name == NULL) {
ereport(ERROR,
(errmodule(MOD_EXECUTOR),
errcode(ERRCODE_DATA_EXCEPTION),
errmsg("computing nodegroup is not a valid group.")));
}
need_full_dn = need_full_dn_execution(group_name);
pfree_ext(group_name);
}
if (need_full_dn) {
exec_nodes->nodeList = GetAllDataNodes();
return exec_nodes;
}
nmembers = get_pgxc_groupmembers_redist(group_oid, &members);
exec_nodes->nodeList = GetNodeGroupNodeList(members, nmembers);
pfree_ext(members);
return exec_nodes;
}
* @NodeGroup Support
* @Description: Get exec nodes according table that own the sequence.
*
* @Return: exec nodes of the sequence.
*/
static ExecNodes* GetOwnedByNodes(Node* seq_stmt)
{
Oid goid;
Oid table_id;
List* owned_by = NIL;
List* rel_name = NIL;
ListCell* option = NULL;
int nnames;
RangeVar* rel = NULL;
List* options = NULL;
Assert(seq_stmt != NULL);
if (IsA(seq_stmt, CreateSeqStmt))
options = ((CreateSeqStmt*)seq_stmt)->options;
else if (IsA(seq_stmt, AlterSeqStmt))
options = ((AlterSeqStmt*)seq_stmt)->options;
else
return NULL;
foreach (option, options) {
DefElem* defel = (DefElem*)lfirst(option);
if (strcmp(defel->defname, "owned_by") == 0) {
owned_by = defGetQualifiedName(defel);
break;
}
}
if (owned_by == NULL)
return NULL;
nnames = list_length(owned_by);
Assert(nnames > 0);
if (nnames == 1) {
return NULL;
}
rel_name = list_truncate(list_copy(owned_by), nnames - 1);
rel = makeRangeVarFromNameList(rel_name);
if (rel->schemaname != NULL)
table_id = get_valid_relname_relid(rel->schemaname, rel->relname);
else
table_id = RelnameGetRelid(rel->relname);
Assert(OidIsValid(table_id));
goid = get_pgxc_class_groupoid(table_id);
if (!OidIsValid(goid))
return NULL;
return GetNodeGroupExecNodes(goid);
}
#ifdef ENABLE_MULTIPLE_NODES
* @NodeGroup Support
* @Description: Get exec nodes according table that own the sequence.
*
* @Return: exec nodes of the sequence.
*/
static ExecNodes* GetSequenceNodes(RangeVar* sequence, bool missing_ok)
{
Oid goid;
Oid seq_id;
Oid table_id = InvalidOid;
int32 col_id = 0;
seq_id = RangeVarGetRelid(sequence, NoLock, missing_ok);
if (!OidIsValid(seq_id))
return NULL;
if (!sequenceIsOwned(seq_id, &table_id, &col_id))
return NULL;
goid = get_pgxc_class_groupoid(table_id);
if (!OidIsValid(goid))
return NULL;
return GetNodeGroupExecNodes(goid);
}
#endif
* @NodeGroup Support
* @Description: Get create function query string in datanodes.
*
* @Return: new query string for sql language in logic cluster, or old query string.
*/
static const char* GetCreateFuncStringInDN(CreateFunctionStmt* stmt, const char* query_string)
{
errno_t rc;
ListCell* option = NULL;
bool is_sql_lang = false;
size_t str_len;
char* tmp = NULL;
const char* query_str = query_string;
if (!in_logic_cluster() || !u_sess->attr.attr_sql.check_function_bodies)
return query_string;
foreach (option, stmt->options) {
DefElem* defel = (DefElem*)lfirst(option);
if (strcmp(defel->defname, "language") == 0) {
if (strcmp(strVal(defel->arg), "sql") == 0) {
is_sql_lang = true;
break;
}
}
}
if (!is_sql_lang)
return query_string;
str_len = strlen(query_string) + 128;
tmp = (char*)palloc(str_len);
rc = snprintf_s(
tmp, str_len, str_len - 1, "SET check_function_bodies = off;%s;SET check_function_bodies = on;", query_string);
securec_check_ss(rc, "\0", "\0");
query_str = tmp;
return query_str;
}
* @NodeGroup Support
* @Description: Determine the list objects is in same node group
*
* @Return: 'true' in same group, 'false' in different group
*/
static ExecNodes* GetFunctionNodes(Oid func_id)
{
Oid goid;
goid = GetFunctionNodeGroupByFuncid(func_id);
if (!OidIsValid(goid))
return NULL;
return GetNodeGroupExecNodes(goid);
}
* @NodeGroup Support
* @Description: Determine the list objects is in same node group
*
* @Return: 'true' in same group, 'false' in different group
*/
static ExecNodes* GetDropFunctionNodes(DropStmt* stmt)
{
ListCell* cell = NULL;
ListCell* cell2 = NULL;
if (stmt->objects == NIL) {
return NULL;
}
foreach (cell, stmt->objects) {
ObjectAddress address;
List* obj_name = (List*)lfirst(cell);
List* obj_args = NIL;
Relation relation = NULL;
if (stmt->arguments) {
cell2 = (!cell2 ? list_head(stmt->arguments) : lnext(cell2));
obj_args = (List*)lfirst(cell2);
}
address = get_object_address(stmt->removeType, obj_name, obj_args, &relation, AccessShareLock, stmt->missing_ok);
if (relation)
heap_close(relation, AccessShareLock);
if (!OidIsValid(address.objectId)) {
continue;
}
return GetFunctionNodes(address.objectId);
}
return NULL;
}
* Check whether current user can create table in logic cluster.
*/
static void check_logic_cluster_create_priv(Oid group_oid, const char* group_name)
{
Oid redist_oid;
Oid current_group_oid;
const char* redist_group_name = NULL;
char group_kind = get_pgxc_groupkind(group_oid);
if (group_kind == 'i' || group_kind == 'e') {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Unable to create table on installation group and elastic group "
"in logic cluster.")));
}
if (superuser())
return;
current_group_oid = get_current_lcgroup_oid();
if (group_oid == current_group_oid) {
return;
}
if (!OidIsValid(current_group_oid)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("User \"%s\" need to attach to logic cluster \"%s\" to create table.",
GetUserNameFromId(GetUserId()),
group_name)));
return;
}
redist_group_name = PgxcGroupGetInRedistributionGroup();
redist_oid = get_pgxc_groupoid(redist_group_name);
if (redist_oid == current_group_oid) {
if (group_oid == PgxcGroupGetRedistDestGroupOid()) {
return;
}
}
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("User \"%s\" have no privilege to create table on logic cluster \"%s\".",
GetUserNameFromId(GetUserId()),
group_name)));
}
* append_internal_data_to_query
* append internalData to query_string
*
* internalData: the string needed to append the the end of query_string
* query_string: original source text of command
*
* If there are some semicolons in the end of query_string, change these semicolons to space
*/
static char* append_internal_data_to_query(char* internal_data, const char* query_string)
{
int i;
StringInfoData str;
initStringInfo(&str);
Assert(query_string != NULL);
Assert(internal_data != NULL);
appendStringInfoString(&str, query_string);
for (i = str.len - 1; i >= 0; i--) {
if (str.data[i] == ';') {
str.data[i] = ' ';
continue;
}
if (!isspace(str.data[i]))
break;
}
appendStringInfo(&str, " INTERNAL DATA %s ;", internal_data);
return str.data;
}
* assemble_create_sequence_msg
* assemble CREATE SEQUENCE query string for create sequences.
*
* stmts: CreateSeqStmt list
* uuids: uuids of sequences
*
* return string including multiple CREATE SEQUENCE statements.
*/
static char* assemble_create_sequence_msg(List* stmts, List* uuids)
{
char* msg = NULL;
ListCell* l = NULL;
StringInfoData str;
initStringInfo(&str);
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
if (IsA(stmt, CreateSeqStmt)) {
char query[256];
errno_t rc = EOK;
CreateSeqStmt* create_seq_stmt = (CreateSeqStmt*)stmt;
const char* schema_name = NULL;
const char* seq_name = quote_identifier(create_seq_stmt->sequence->relname);
if (create_seq_stmt->sequence->schemaname && create_seq_stmt->sequence->schemaname[0])
schema_name = quote_identifier(create_seq_stmt->sequence->schemaname);
if (schema_name == NULL) {
rc = snprintf_s(query, sizeof(query), sizeof(query) - 1, "CREATE SEQUENCE %s;", seq_name);
} else {
rc = snprintf_s(query, sizeof(query), sizeof(query) - 1, "CREATE SEQUENCE %s.%s;", schema_name, seq_name);
}
securec_check_ss(rc, "\0", "\0");
appendStringInfoString(&str, query);
if (OidIsValid(create_seq_stmt->ownerId)) {
char* rol_name = GetUserNameFromId(create_seq_stmt->ownerId);
if (schema_name == NULL) {
rc = snprintf_s(
query, sizeof(query), sizeof(query) - 1, "ALTER SEQUENCE %s OWNER TO %s;", seq_name, rol_name);
} else {
rc = snprintf_s(query,
sizeof(query),
sizeof(query) - 1,
"ALTER SEQUENCE %s.%s OWNER TO %s;",
schema_name,
seq_name,
rol_name);
}
securec_check_ss(rc, "\0", "\0");
appendStringInfoString(&str, query);
pfree_ext(rol_name);
}
if (schema_name != NULL && schema_name != create_seq_stmt->sequence->schemaname)
pfree_ext(schema_name);
if (seq_name != create_seq_stmt->sequence->relname)
pfree_ext(seq_name);
}
}
char* uuid_info = nodeToString(uuids);
AssembleHybridMessage(&msg, str.data, uuid_info);
pfree_ext(uuid_info);
pfree_ext(str.data);
return msg;
}
* assemble_drop_sequence_msg
* assemble DROP SEQUENCE query string.
*
* seqs: sequence oid list.
* str: out parameter, return drop sequence string.
*
*/
static void assemble_drop_sequence_msg(List* seqs, StringInfo str)
{
Oid seq_id;
ListCell* s = NULL;
const char* rel_name = NULL;
const char* nsp_name = NULL;
const char* schema_name = NULL;
const char* seq_name = NULL;
char query[256];
errno_t rc = EOK;
foreach (s, seqs) {
seq_id = lfirst_oid(s);
rel_name = get_rel_name(seq_id);
nsp_name = get_namespace_name(get_rel_namespace(seq_id));
schema_name = quote_identifier(nsp_name);
seq_name = quote_identifier(rel_name);
Assert(rel_name != NULL && nsp_name != NULL && seq_name != NULL && schema_name != NULL);
rc = snprintf_s(
query, sizeof(query), sizeof(query) - 1, "DROP SEQUENCE IF EXISTS %s.%s CASCADE;", schema_name, seq_name);
securec_check_ss(rc, "\0", "\0");
if (str->data == NULL) {
initStringInfo(str);
}
appendStringInfoString(str, query);
if (schema_name != nsp_name)
pfree_ext(schema_name);
if (seq_name != rel_name)
pfree_ext(seq_name);
pfree_ext(rel_name);
pfree_ext(nsp_name);
}
}
#ifdef ENABLE_MULTIPLE_NODES
* get_drop_sequence_msg
* Get SQL including multiple DROP SEQUENCE;
*
* objects: the normal table oid list.
*
* If all tables in objects have not owned sequences, return null;
* or the return string include DROP SEQUENCE for all sequences
* owned by these tables.
*/
static char* get_drop_sequence_msg(List* objects)
{
ListCell* l = NULL;
StringInfoData str;
List* seqs = NULL;
Oid rel_id;
str.data = NULL;
foreach (l, objects) {
rel_id = lfirst_oid(l);
seqs = getOwnedSequences(rel_id);
if (seqs == NULL)
continue;
assemble_drop_sequence_msg(seqs, &str);
}
return str.data;
}
#endif
* make_remote_query_for_seq
* return RemoteQuery for executing query_string in nodes without excluded_nodes.
*
* excluded_nodes: the node list that don't need to execute query.
* query_string: SQL command to execute.
*
*/
static RemoteQuery* make_remote_query_for_seq(ExecNodes* excluded_nodes, char* query_string)
{
List* all_nodes = GetAllDataNodes();
List* exec_nodes = list_difference_int(all_nodes, excluded_nodes->nodeList);
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char*)query_string;
step->exec_type = EXEC_ON_DATANODES;
step->is_temp = false;
step->exec_nodes = makeNode(ExecNodes);
step->exec_nodes->nodeList = exec_nodes;
list_free(all_nodes);
return step;
}
#ifdef ENABLE_MULTIPLE_NODES
* exec_remote_query_4_seq
* Execute query_string in nodes without excluded_nodes.
*
* excluded_nodes: the node list that don't need to execute query.
* query_string: SQL command to execute.
* uuid: Sequence uuid, if uuid is zero, ignore the parameter.
*
*/
static void exec_remote_query_4_seq(ExecNodes* excluded_nodes, char* query_string, int64 uuid)
{
char* query_string_with_uuid = query_string;
if (uuid != INVALIDSEQUUID) {
Const* n = makeConst(INT8OID, -1, InvalidOid, sizeof(int64), Int64GetDatum(uuid), false, true);
char* uuid_info = nodeToString(n);
AssembleHybridMessage(&query_string_with_uuid, query_string, uuid_info);
pfree_ext(n);
pfree_ext(uuid_info);
}
RemoteQuery* step = make_remote_query_for_seq(excluded_nodes, query_string_with_uuid);
ExecRemoteUtility(step);
list_free(step->exec_nodes->nodeList);
pfree_ext(step->exec_nodes);
pfree_ext(step);
}
* drop_sequence_4_node_group
* Drop sequences not in datanodes (in parameter excluded_nodes).
*
* stmt: DropStmt struct, we get all dropped tables from stmt parameter.
* excluded_nodes: the node list that don't need to execute query.
*
* The function is used to DROP TABLE in nodegroup.
* DROP TABLE don't drop sequences in datanodes that don't belong to the NodeGroup,
* so we need this function to drop these sequences in the execluded nodes.
*/
static void drop_sequence_4_node_group(DropStmt* stmt, ExecNodes* excluded_nodes)
{
char* query_string = NULL;
List* objects = NIL;
ListCell* cell = NULL;
Oid table_oid;
if (excluded_nodes == NULL)
return;
if (stmt->removeType != OBJECT_TABLE)
return;
if (excluded_nodes->nodeList->length == u_sess->pgxc_cxt.NumDataNodes)
return;
foreach (cell, stmt->objects) {
RangeVar* rel = NULL;
rel = makeRangeVarFromNameList((List*)lfirst(cell));
table_oid = RangeVarGetRelid(rel, NoLock, true);
if (!OidIsValid(table_oid)) {
continue;
}
if (get_rel_relkind(table_oid) != RELKIND_RELATION) {
continue;
}
objects = lappend_oid(objects, table_oid);
}
query_string = get_drop_sequence_msg(objects);
if (query_string == NULL)
return;
exec_remote_query_4_seq(excluded_nodes, query_string, INVALIDSEQUUID);
pfree_ext(query_string);
}
* alter_sequence_all_nodes
* ALTER sequences not in datanodes (in parameter excluded_nodes).
*
* seq_stmt: AlterSeqStmt struct, we get all altered sequences from this parameter.
* excluded_nodes: the node list that don't need to execute query.
*
* The function is used to ALTER SEQUENCE in nodegroup.
*
* Consider the scenario:
* Sequence s1 is owned by t1 and increment is 1;
* table t1 belong to nodegroup1 (datanode1,datanode2);
* table t2 belong to nodegroup2 (datanode3,datanode4).
* If execute ALTER SEQUENCE s1 increment by 2 owned by t2, how we do ?
*
* We have to call alter_sequence_all_nodes in nodes excluding nodegroup2, modify
* increment and set owned by none first. Then we ALTER SEQUENCE in nodegroup2,
* modify increment and set owned by t2. The final result is: increment in all nodes is 2,
* owned by is none excluding nodegroup2, owned by t2 in nodegroup2.
*/
static void alter_sequence_all_nodes(AlterSeqStmt* seq_stmt, ExecNodes* exclude_nodes)
{
if (exclude_nodes == NULL)
return;
if (exclude_nodes->nodeList->length == u_sess->pgxc_cxt.NumDataNodes)
return;
RangeVar* sequence = seq_stmt->sequence;
if (seq_stmt->options->length == 1) {
DefElem* def_elem = (DefElem*)linitial(seq_stmt->options);
if (strcmp(def_elem->defname, "owned_by") == 0) {
ExecNodes* exec_nodes = GetSequenceNodes(sequence, true);
if (exec_nodes == NULL)
return;
List* diff = list_difference_int(exec_nodes->nodeList, exclude_nodes->nodeList);
if (diff == NULL) {
FreeExecNodes(&exec_nodes);
return;
}
list_free(diff);
}
}
char* msg = deparse_alter_sequence((Node*)seq_stmt, true);
exec_remote_query_4_seq(exclude_nodes, msg, INVALIDSEQUUID);
pfree_ext(msg);
}
#endif
* get_drop_seq_query_string
* Get DROP SEQUENCE string list (used in ALTER TABLE ... DROP COLUMN).
*
* stmt: AlterTableStmt struct, we get all dropped sequences from this parameter.
* rel_id: the normal table oid. We can get it from AlterTableStmt, but we expect
* caller can give the oid for optimization.
*
* The function is used to ALTER TABLE ... DROP COLUMN in nodegroup.
* When drop column with owned sequence for nodegroup table, DROP TABLE
* is executed only in nodes in the nodegroup, sequence in other datanodes won't
* be dropped. We have to call get_drop_seq_query_string to assemble DROP SEQUENCE
* statements, execute these sqls by exec_remote_query_4_seq in other datanodes.
*/
static char* get_drop_seq_query_string(AlterTableStmt* stmt, Oid rel_id)
{
HeapTuple tuple;
char* col_name = NULL;
ListCell* cell = NULL;
AlterTableCmd* cmd = NULL;
List* seq_list = NIL;
List* attr_list = NIL;
foreach (cell, stmt->cmds) {
cmd = (AlterTableCmd*)lfirst(cell);
if (cmd->subtype == AT_DropColumn || cmd->subtype == AT_DropColumnRecurse) {
Form_pg_attribute target_att;
col_name = cmd->name;
* get the number of the attribute
*/
tuple = SearchSysCacheAttName(rel_id, col_name);
if (!HeapTupleIsValid(tuple))
continue;
target_att = (Form_pg_attribute)GETSTRUCT(tuple);
attr_list = lappend_int(attr_list, target_att->attnum);
ReleaseSysCache(tuple);
}
}
if (attr_list != NIL) {
StringInfoData str;
seq_list = getOwnedSequences(rel_id, attr_list);
list_free(attr_list);
if (seq_list != NULL) {
str.data = NULL;
assemble_drop_sequence_msg(seq_list, &str);
return str.data;
}
}
return NULL;
}
* ProcessUtility
* general utility function invoker
*
* parse_tree: the parse tree for the utility statement
* query_string: original source text of command
* readOnlyTree: if true, pstmt's node tree must not be modified
* params: parameters to use during execution
* is_top_level: true if executing a "top level" (interactively issued) command
* dest: where to send results
* completion_tag: points to a buffer of size COMPLETION_TAG_BUFSIZE
* in which to store a command completion status string.
*
* Notes: as of PG 8.4, caller MUST supply a query_string; it is not
* allowed anymore to pass NULL. (If you really don't have source text,
* you can pass a constant string, perhaps "(query not available)".)
*
* completion_tag is only set nonempty if we want to return a nondefault status.
*
* completion_tag may be NULL if caller doesn't want a status string.
*/
void ProcessUtility(processutility_context* processutility_cxt,
DestReceiver* dest,
#ifdef PGXC
bool sent_to_remote,
#endif
char* completion_tag,
ProcessUtilityContext context,
bool isCTAS)
{
AssertEreport(processutility_cxt->query_string != NULL, MOD_EXECUTOR, "query string is NULL");
* We provide a function hook variable that lets loadable plugins get
* control when ProcessUtility is called. Such a plugin would normally
* call standard_ProcessUtility().
* it's unsafe to deal with plugins hooks as dynamic lib may be released
*/
if (ProcessUtility_hook && !(g_instance.status > NoShutdown)) {
(*ProcessUtility_hook)(processutility_cxt,
dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag,
context,
isCTAS);
} else {
standard_ProcessUtility(processutility_cxt,
dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag,
context,
isCTAS);
}
* Record the number of rows affected into the session, but only support
* DML statement now, for DDL statement, always set to 0
*/
NodeTag nt = nodeTag(processutility_cxt->parse_tree);
if (nt != T_ExecuteStmt) {
t_thrd.shemem_ptr_cxt.MyBEEntry->statement_cxt.current_row_count = 0;
t_thrd.shemem_ptr_cxt.MyBEEntry->statement_cxt.last_row_count =
t_thrd.shemem_ptr_cxt.MyBEEntry->statement_cxt.current_row_count;
called twice nested, and the right data will be modified when it is
first executed (Generally in function ExecutorRun), so there do
nothing when it is called again to avoid overwriting */
if ((nt != T_FetchStmt || ((FetchStmt*)(processutility_cxt->parse_tree))->ismove) &&
u_sess->hook_cxt.rowcountHook) {
((RowcountHook)(u_sess->hook_cxt.rowcountHook))(0);
}
}
}
bool isAllTempObjects(Node* parse_tree, const char* query_string, bool sent_to_remote)
{
switch (nodeTag(parse_tree)) {
case T_CreateStmt: {
CreateStmt* stmt = (CreateStmt*)parse_tree;
char* temp_namespace_name = NULL;
if (stmt->relation) {
if (OidIsValid(u_sess->catalog_cxt.myTempNamespace) && stmt->relation->schemaname != NULL) {
temp_namespace_name = get_namespace_name(u_sess->catalog_cxt.myTempNamespace);
if (temp_namespace_name != NULL &&
strcmp(stmt->relation->schemaname, temp_namespace_name) == 0)
return true;
}
if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP)
return true;
}
break;
}
case T_ViewStmt: {
return IsViewTemp((ViewStmt*)parse_tree, query_string);
break;
}
case T_AlterFunctionStmt:
{
return IsFunctionTemp((AlterFunctionStmt*)parse_tree);
break;
}
case T_DropStmt:
switch (((DropStmt*)parse_tree)->removeType) {
case OBJECT_INDEX:
case OBJECT_TABLE:
case OBJECT_VIEW:
case OBJECT_CONTQUERY:
case OBJECT_SEQUENCE:
case OBJECT_LARGE_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
case OBJECT_LARGE_SEQUENCE_GSC: {
bool is_all_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
DropStmt* new_stmt = (DropStmt*)copyObject(parse_tree);
new_stmt->missing_ok = true;
drop_stmt_pre_treatment(new_stmt, query_string, sent_to_remote, &is_all_temp, &exec_type);
pfree_ext(new_stmt);
return is_all_temp;
}
case OBJECT_SCHEMA: {
ListCell* cell = NULL;
foreach (cell, ((DropStmt*)parse_tree)->objects) {
List* obj_name = (List*)lfirst(cell);
char* name = NameListToString(obj_name);
if (isTempNamespaceName(name) || isToastTempNamespaceName(name)
|| strcmp(name, "pg_temp") == 0)
return true;
}
break;
}
default: {
bool is_all_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
DropStmt* new_stmt = (DropStmt*)copyObject(parse_tree);
new_stmt->missing_ok = true;
drop_stmt_pre_treatment(new_stmt, query_string, sent_to_remote, &is_all_temp, &exec_type);
pfree_ext(new_stmt);
return is_all_temp;
}
}
break;
case T_CreateSchemaStmt: {
char* nsp_name = ((CreateSchemaStmt*)parse_tree)->schemaname;
if (isTempNamespaceName(nsp_name) || isToastTempNamespaceName(nsp_name))
return true;
break;
}
case T_AlterSchemaStmt: {
char* nsp_name = ((AlterSchemaStmt*)parse_tree)->schemaname;
if (isTempNamespaceName(nsp_name) || isToastTempNamespaceName(nsp_name))
return true;
break;
}
case T_AlterTableStmt: {
AlterTableStmt* alter_stmt = (AlterTableStmt*)parse_tree;
Oid rel_id;
LOCKMODE lock_mode;
* Figure out lock mode, and acquire lock. This also does
* basic permissions checks, so that we won't wait for a lock
* on (for example) a relation on which we have no
* permissions.
*/
lock_mode = AlterTableGetLockLevel(alter_stmt->cmds);
rel_id = AlterTableLookupRelation(alter_stmt, lock_mode, true);
if (OidIsValid(rel_id) && IsTempTable(rel_id)) {
return true;
}
if (OidIsValid(rel_id) && u_sess->attr.attr_sql.enable_parallel_ddl)
UnlockRelationOid(rel_id, lock_mode);
break;
}
case T_RenameStmt: {
RenameStmt* stmt = (RenameStmt*)parse_tree;
bool is_all_temp = false;
if (stmt->relation) {
* When a relation is defined, it is possible that this object does
* not exist but an IF EXISTS clause might be used. So we do not do
* any error check here but block the access to remote nodes to
* this object as it does not exisy
*/
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
(void)ExecUtilityFindNodes(stmt->renameType, rel_id, &is_all_temp);
UnlockRelationOid(rel_id, AccessShareLock);
}
return is_all_temp;
}
break;
}
case T_AlterObjectSchemaStmt: {
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parse_tree;
bool is_all_temp = false;
if (stmt->relation) {
* When a relation is defined, it is possible that this object does
* not exist but an IF EXISTS clause might be used. So we do not do
* any error check here but block the access to remote nodes to
* this object as it does not exisy
*/
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
(void)ExecUtilityFindNodes(stmt->objectType, rel_id, &is_all_temp);
UnlockRelationOid(rel_id, AccessShareLock);
}
return is_all_temp;
}
break;
}
case T_CommentStmt: {
bool is_all_temp = false;
CommentStmt* stmt = (CommentStmt*)parse_tree;
(void)get_nodes_4_comment_utility(stmt, &is_all_temp, NULL);
return is_all_temp;
}
case T_GrantStmt: {
GrantStmt* stmt = (GrantStmt*)parse_tree;
bool is_temp = false;
bool has_temp = false;
bool has_nontemp = false;
if ((stmt->objtype == ACL_OBJECT_RELATION && stmt->targtype == ACL_TARGET_OBJECT)) {
* In case object is a relation, differenciate the case
* of a sequence, a view and a table
*/
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
RangeVar* rel_var = (RangeVar*)lfirst(cell);
Oid rel_id = RangeVarGetRelid(rel_var, NoLock, true);
if (!OidIsValid(rel_id)) {
continue;
}
(void)exec_utility_find_nodes_relkind(rel_id, &is_temp);
if (is_temp) {
has_temp = true;
} else {
has_nontemp = true;
}
if (has_temp && has_nontemp) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Grant not supported for TEMP and non-TEMP objects together"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
}
return has_temp;
}
break;
}
case T_IndexStmt: {
IndexStmt* stmt = (IndexStmt*)parse_tree;
Oid rel_id;
bool is_temp = false;
rel_id = RangeVarGetRelidExtended(stmt->relation, AccessShareLock, true, false, false, true, NULL, NULL);
if (OidIsValid(rel_id)) {
(void)ExecUtilityFindNodes(OBJECT_INDEX, rel_id, &is_temp);
UnlockRelationOid(rel_id, AccessShareLock);
}
return is_temp;
}
case T_RuleStmt: {
bool is_temp = false;
(void)get_nodes_4_rules_utility(((RuleStmt*)parse_tree)->relation, &is_temp);
return is_temp;
}
case T_CreateSeqStmt: {
CreateSeqStmt* stmt = (CreateSeqStmt*)parse_tree;
bool is_temp = false;
if (!stmt->is_serial)
is_temp = stmt->sequence->relpersistence == RELPERSISTENCE_TEMP;
return is_temp;
}
case T_CreateTrigStmt: {
CreateTrigStmt* stmt = (CreateTrigStmt*)parse_tree;
bool is_temp = false;
Oid rel_id = RangeVarGetRelidExtended(stmt->relation, NoLock, false, false, false, true, NULL, NULL);
if (OidIsValid(rel_id))
(void)ExecUtilityFindNodes(OBJECT_TABLE, rel_id, &is_temp);
return is_temp;
}
case T_VacuumStmt: {
return IsAllTempObjectsInVacuumStmt(parse_tree);
}
case T_AlterSeqStmt:
case T_AlterOwnerStmt:
default:
break;
}
return false;
}
char* find_first_exec_cn()
{
char* result = NULL;
result = u_sess->pgxc_cxt.co_handles[0].remoteNodeName;
for (int i = 1; i < u_sess->pgxc_cxt.NumCoords; i++) {
result = (strcmp(u_sess->pgxc_cxt.co_handles[i].remoteNodeName, result) < 0) ?
u_sess->pgxc_cxt.co_handles[i].remoteNodeName :
result;
}
return result;
}
bool find_hashbucket_options(List* stmts)
{
ListCell* l = NULL;
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
ListCell* opt = NULL;
List* user_options = NULL;
if (!IsA(stmt, CreateStmt)) {
continue;
}
if (u_sess->attr.attr_storage.enable_hashbucket) {
return true;
}
user_options = ((CreateStmt*)stmt)->options;
foreach (opt, user_options) {
DefElem* def = (DefElem*)lfirst(opt);
char* lower_string = lowerstr(def->defname);
if (strstr(lower_string, "hashbucket") ||
strstr(lower_string, "bucketcnt")) {
return true;
}
}
}
return false;
}
* Notice: parse_tree could be from cached plan, do not modify it under other memory context
*/
#ifdef PGXC
void CreateCommand(CreateStmt *parse_tree, const char *query_string, ParamListInfo params,
bool is_top_level, bool sent_to_remote, bool isCTAS)
#else
void CreateCommand(CreateStmt *parse_tree, const char *query_string, ParamListInfo params, bool is_top_level,
bool isCTAS)
#endif
{
List* stmts = NIL;
ListCell* l = NULL;
Oid rel_oid;
#ifdef PGXC
bool is_temp = false;
bool is_object_temp = false;
PGXCSubCluster* sub_cluster = NULL;
char* tablespace_name = NULL;
char relpersistence = RELPERSISTENCE_PERMANENT;
bool table_is_exist = false;
char* internal_data = NULL;
List* uuids = (List*)copyObject(parse_tree->uuids);
char* first_exec_node = NULL;
bool is_first_node = false;
char* query_string_with_info = (char*)query_string;
char* query_string_with_data = (char*)query_string;
Oid namespace_id = InvalidOid;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
#endif
* DefineRelation() needs to know "isTopLevel"
* by "DfsDDLIsTopLevelXact" to prevent "create hdfs table" running
* inside a transaction block.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
u_sess->exec_cxt.DfsDDLIsTopLevelXact = is_top_level;
if (u_sess->attr.attr_sql.enable_parallel_ddl)
stmts = transformCreateStmt((CreateStmt*)parse_tree, query_string, NIL, true, &namespace_id, is_first_node);
else
stmts = transformCreateStmt((CreateStmt*)parse_tree, query_string, NIL, false, &namespace_id);
* If stmts is NULL, then the table is exists.
* we need record that for searching the group of table.
*/
if (stmts == NIL) {
table_is_exist = true;
* Just return here, if we continue
* to send if not exists stmt, may
* cause the inconsistency of metadata.
* If we under xc_maintenance_mode, we can do
* this to slove some problem of inconsistency.
*/
if (u_sess->attr.attr_common.xc_maintenance_mode == false)
return;
}
#ifdef PGXC
if (IS_MAIN_COORDINATOR) {
* Scan the list of objects.
* Temporary tables are created on Datanodes only.
* Non-temporary objects are created on all nodes.
* In case temporary and non-temporary objects are mized return an error.
*/
bool is_first = true;
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
if (IsA(stmt, CreateStmt)) {
CreateStmt* stmt_loc = (CreateStmt*)stmt;
sub_cluster = stmt_loc->subcluster;
tablespace_name = stmt_loc->tablespacename;
relpersistence = stmt_loc->relation->relpersistence;
is_object_temp = stmt_loc->relation->relpersistence == RELPERSISTENCE_TEMP;
internal_data = stmt_loc->internalData;
if (is_object_temp)
u_sess->exec_cxt.hasTempObject = true;
if (is_first) {
is_first = false;
if (is_object_temp)
is_temp = true;
} else {
if (is_object_temp != is_temp)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CREATE not supported for TEMP and non-TEMP objects"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
} else if (IsA(stmt, CreateForeignTableStmt)) {
#ifdef ENABLE_MULTIPLE_NODES
validate_streaming_engine_status(stmt);
#endif
if (in_logic_cluster()) {
CreateStmt* stmt_loc = (CreateStmt*)stmt;
sub_cluster = stmt_loc->subcluster;
}
if (is_first) {
is_first = false;
} else {
if (!is_temp)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CREATE not supported for TEMP and non-TEMP objects"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
} else if (IsA(stmt, CreateSeqStmt)) {
CreateSeqStmt* sstmt = (CreateSeqStmt*)stmt;
Const* n = makeConst(INT8OID, -1, InvalidOid, sizeof(int64), Int64GetDatum(sstmt->uuid), false, true);
uuids = lappend(uuids, n);
}
}
if (internal_data != NULL) {
query_string_with_data = append_internal_data_to_query(internal_data, query_string);
}
* Now package the uuids message that create table on RemoteNode need.
*/
if (uuids != NIL) {
char* uuid_info = nodeToString(uuids);
AssembleHybridMessage(&query_string_with_info, query_string_with_data, uuid_info);
} else
query_string_with_info = query_string_with_data;
}
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!sent_to_remote) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char*)query_string_with_info;
if (is_object_temp)
step->exec_type = EXEC_ON_NONE;
else
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = is_temp;
if (namespace_id != InvalidOid) {
UnlockDatabaseObject(NamespaceRelationId, namespace_id, 0, AccessShareLock);
}
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
}
}
if (u_sess->attr.attr_sql.enable_parallel_ddl) {
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !is_first_node)
stmts = transformCreateStmt((CreateStmt*)parse_tree, query_string, uuids, false, &namespace_id);
}
#endif
#ifdef PGXC
* Add a RemoteQuery node for a query at top level on a remote
* Coordinator, if not already done so
*/
if (!sent_to_remote) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node)
stmts = AddRemoteQueryNode(stmts, query_string_with_info, EXEC_ON_DATANODES, is_temp);
else
stmts = AddRemoteQueryNode(stmts, query_string_with_info, CHOOSE_EXEC_NODES(is_object_temp), is_temp);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(sub_cluster == NULL || sub_cluster->clustertype == SUBCLUSTER_GROUP)) {
const char* group_name = NULL;
Oid group_oid = InvalidOid;
* If TO-GROUP clause is specified when creating table, we
* only have to add required datanode in remote DDL execution
*/
if (sub_cluster != NULL) {
ListCell* lc = NULL;
foreach (lc, sub_cluster->members) {
group_name = strVal(lfirst(lc));
}
} else if (in_logic_cluster() && !table_is_exist) {
* for CreateForeignTableStmt ,
* CreateTableStmt with user not attached to logic cluster
*/
group_name = PgxcGroupGetCurrentLogicCluster();
if (group_name == NULL) {
ereport(ERROR, (errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Cannot find logic cluster.")));
}
} else {
Oid tablespace_id = InvalidOid;
bool dfs_tablespace = false;
if (tablespace_name != NULL) {
tablespace_id = get_tablespace_oid(tablespace_name, false);
} else {
tablespace_id = GetDefaultTablespace(relpersistence);
}
dfs_tablespace = IsSpecifiedTblspc(tablespace_id, FILESYSTEM_HDFS);
* If TO-GROUP clause is not specified we are using the installation group to
* distribute table.
*
* For HDFS table/Foreign Table we don't refer default_storage_nodegroup
* to make table creation.
*/
if (table_is_exist) {
Oid rel_id = RangeVarGetRelid(((CreateStmt*)parse_tree)->relation, NoLock, true);
if (OidIsValid(rel_id)) {
Oid table_groupoid = get_pgxc_class_groupoid(rel_id);
if (OidIsValid(table_groupoid)) {
group_name = get_pgxc_groupname(table_groupoid);
}
}
if (group_name == NULL) {
group_name = PgxcGroupGetInstallationGroup();
}
} else if (dfs_tablespace || IsA(parse_tree, CreateForeignTableStmt)) {
group_name = PgxcGroupGetInstallationGroup();
} else if (strcmp(u_sess->attr.attr_sql.default_storage_nodegroup, INSTALLATION_MODE) == 0 ||
u_sess->attr.attr_common.IsInplaceUpgrade) {
group_name = PgxcGroupGetInstallationGroup();
} else {
group_name = u_sess->attr.attr_sql.default_storage_nodegroup;
}
if (group_name == NULL) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("Installation node group is not defined in current cluster")));
}
}
group_oid = get_pgxc_groupoid(group_name);
if (!OidIsValid(group_oid)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Target node group \"%s\" doesn't exist", group_name)));
}
if (in_logic_cluster()) {
check_logic_cluster_create_priv(group_oid, group_name);
} else {
if (!u_sess->attr.attr_sql.enable_cluster_resize) {
char in_redistribution = get_pgxc_group_redistributionstatus(group_oid);
if (in_redistribution == 'y') {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Unable to create table on old installation group \"%s\" while in cluster "
"resizing.",
group_name)));
}
}
}
const int total_len = list_length(stmts);
Node* node = (Node*)list_nth(stmts, (total_len - 1));
AssertEreport(query_string != NULL, MOD_EXECUTOR, "Node type is not remote type");
RemoteQuery* rquery = (RemoteQuery*)node;
AssertEreport(!rquery->exec_nodes, MOD_EXECUTOR, "remote query is not DN");
rquery->exec_nodes = makeNode(ExecNodes);
t_thrd.xact_cxt.PGXCGroupOid = group_oid;
if (find_hashbucket_options(stmts)) {
rquery->is_send_bucket_map = true;
}
* Check node group permissions, we only do such kind of ACL check
* for user-defined nodegroup(none-installation)
*/
AclResult acl_result = pg_nodegroup_aclcheck(group_oid, GetUserId(), ACL_CREATE);
if (acl_result != ACLCHECK_OK) {
aclcheck_error(acl_result, ACL_KIND_NODEGROUP, group_name);
}
* Notice!!
* In cluster resizing stage we need special processing logics in table creation as:
* [1]. create table delete_delta ... to group old_group on all DN
* [2]. display pgxc_group.group_members
* [3]. drop table delete_delta ==> drop delete_delta on all DN
*
* So, as normal, when target node group's status is marked as 'installation' or
* 'redistribution', we have to issue a full-DN create table request, remeber
* pgxc_class.group_members still reflects table's logic distribution to tell pgxc
* planner to build Scan operator in multi_nodegroup way. The reason we have to so is
* to be compatible with current gs_switch_relfilenode() invokation in cluster expand
* and shrunk mechanism.
*/
if (need_full_dn_execution(group_name)) {
rquery->exec_nodes->nodeList = GetAllDataNodes();
} else {
Oid* members = NULL;
int nmembers = 0;
nmembers = get_pgxc_groupmembers(group_oid, &members);
rquery->exec_nodes->nodeList = GetNodeGroupNodeList(members, nmembers);
pfree_ext(members);
if (uuids && nmembers < u_sess->pgxc_cxt.NumDataNodes) {
char* create_seqs;
RemoteQuery* step;
create_seqs = assemble_create_sequence_msg(stmts, uuids);
step = make_remote_query_for_seq(rquery->exec_nodes, create_seqs);
stmts = lappend(stmts, step);
}
}
}
}
#endif
if (uuids != NIL) {
list_free_deep(uuids);
uuids = NIL;
}
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
if (IsA(stmt, CreateStmt)) {
ObjectAddress address;
Datum toast_options;
static const char* const validnsps[] = HEAP_RELOPT_NAMESPACES;
ForbidOutUsersToSetInnerOptions(((CreateStmt*)stmt)->options);
address = DefineRelation((CreateStmt*)stmt,
((CreateStmt*)stmt)->relkind == RELKIND_MATVIEW ?
RELKIND_MATVIEW : RELKIND_RELATION,
InvalidOid, NULL, isCTAS);
rel_oid = address.objectId;
* Let AlterTableCreateToastTable decide if this one
* needs a secondary relation too.
*/
CommandCounterIncrement();
toast_options =
transformRelOptions((Datum)0, ((CreateStmt*)stmt)->options, "toast", validnsps, true, false);
(void)heap_reloptions(RELKIND_TOASTVALUE, toast_options, true);
AlterTableCreateToastTable(rel_oid, toast_options, AccessShareLock);
AlterCStoreCreateTables(rel_oid, toast_options, (CreateStmt*)stmt);
AlterCreateChainTables(rel_oid, toast_options, (CreateStmt *)stmt);
#ifdef ENABLE_MULTIPLE_NODES
Datum reloptions = transformRelOptions(
(Datum)0, ((CreateStmt*)stmt)->options, NULL, validnsps, true, false);
StdRdOptions* std_opt = (StdRdOptions*)heap_reloptions(RELKIND_RELATION, reloptions, true);
if (StdRelOptIsTsStore(std_opt)) {
create_ts_store_tables(rel_oid, toast_options);
}
create_part_policy_if_needed((CreateStmt*)stmt, rel_oid);
#endif
EventTriggerCollectSimpleCommand(address, InvalidObjectAddress, stmt);
} else if (IsA(stmt, CreateForeignTableStmt)) {
ObjectAddress address;
ForbidOutUsersToSetInnerOptions(((CreateStmt*)stmt)->options);
check_log_ft_definition((CreateForeignTableStmt*)stmt);
if (pg_strcasecmp(((CreateForeignTableStmt *)stmt)->servername,
STREAMING_SERVER) == 0) {
address = DefineRelation((CreateStmt*)stmt, RELKIND_STREAM, InvalidOid, NULL);
} else {
address = DefineRelation((CreateStmt*)stmt, RELKIND_FOREIGN_TABLE, InvalidOid, NULL);
}
CreateForeignTable((CreateForeignTableStmt*)stmt, address.objectId);
EventTriggerCollectSimpleCommand(address, InvalidObjectAddress, stmt);
} else {
if (IsA(stmt, AlterTableStmt))
((AlterTableStmt*)stmt)->fromCreate = true;
processutility_context proutility_cxt;
proutility_cxt.parse_tree = stmt;
proutility_cxt.query_string = query_string_with_info;
proutility_cxt.readOnlyTree = false;
proutility_cxt.params = params;
proutility_cxt.is_top_level = false;
ProcessUtility(&proutility_cxt,
None_Receiver,
#ifdef PGXC
true,
#endif
NULL,
PROCESS_UTILITY_SUBCOMMAND,
isCTAS);
}
if (lnext(l) != NULL)
CommandCounterIncrement();
}
parse_tree->uuids = NIL;
if (IS_MAIN_COORDINATOR) {
u_sess->catalog_cxt.setCurCreateSchema = false;
pfree_ext(u_sess->catalog_cxt.curCreateSchema);
}
list_free_ext(stmts);
}
void ReindexCommand(ReindexStmt* stmt, bool is_top_level)
{
PreventCommandDuringRecovery("REINDEX");
switch (stmt->kind) {
case OBJECT_INDEX:
case OBJECT_INDEX_PARTITION:
ReindexIndex(stmt->relation, (const char*)stmt->name, &stmt->memUsage, stmt->concurrent);
break;
case OBJECT_TABLE:
case OBJECT_MATVIEW:
case OBJECT_TABLE_PARTITION:
ReindexTable(stmt->relation, (const char*)stmt->name, &stmt->memUsage, stmt->concurrent);
break;
case OBJECT_INTERNAL:
case OBJECT_INTERNAL_PARTITION:
ReindexInternal(stmt->relation, (const char*)stmt->name);
break;
case OBJECT_DATABASE:
* This cannot run inside a user transaction block; if
* we were inside a transaction, then its commit- and
* start-transaction-command calls would not have the
* intended effect!
*/
PreventTransactionChain(is_top_level, "REINDEX DATABASE");
ReindexDatabase(stmt->name, stmt->do_system, stmt->do_user, &stmt->memUsage, stmt->concurrent);
break;
default: {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE), errmsg("unrecognized object type: %d", (int)stmt->kind)));
} break;
}
}
static void set_item_arg_according_to_def_name(DefElem* item)
{
if (strcmp(item->defname, "transaction_isolation") == 0) {
SetPGVariable("transaction_isolation", list_make1(item->arg), true);
} else if (strcmp(item->defname, "transaction_read_only") == 0) {
#ifdef ENABLE_MULTIPLE_NODES
if (u_sess->attr.attr_storage.DefaultXactReadOnly == false)
#endif
{
SetPGVariable("transaction_read_only", list_make1(item->arg), true);
}
} else if (strcmp(item->defname, "transaction_deferrable") == 0) {
SetPGVariable("transaction_deferrable", list_make1(item->arg), true);
}
}
static void CheckProcCsnValid()
{
if (!(useLocalXid || !IsPostmasterEnvironment || GTM_FREE_MODE) &&
IsConnFromCoord() && (GetCommitCsn() == InvalidCommitSeqNo)) {
ereport(FATAL, (errmsg("invalid commit csn: %lu.", GetCommitCsn())));
}
}
static void add_remote_query_4_alter_stmt(bool is_first_node, AlterTableStmt* atstmt, const char* query_string, List** stmts,
char** drop_seq_string, ExecNodes** exec_nodes)
{
if (!PointerIsValid(atstmt) || !PointerIsValid(stmts) || !PointerIsValid(drop_seq_string) ||
!PointerIsValid(exec_nodes)) {
return;
}
bool is_temp = false;
RemoteQueryExecType exec_type;
Oid rel_id = RangeVarGetRelid(atstmt->relation, NoLock, true);
if (!OidIsValid(rel_id)) {
return;
}
exec_type = ExecUtilityFindNodes(atstmt->relkind, rel_id, &is_temp);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES) {
*stmts = AddRemoteQueryNode(*stmts, query_string, EXEC_ON_DATANODES, is_temp);
}
} else {
*stmts = AddRemoteQueryNode(*stmts, query_string, exec_type, is_temp);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
Node* node = (Node*)lfirst(list_tail(*stmts));
if (IsA(node, RemoteQuery)) {
RemoteQuery* rquery = (RemoteQuery*)node;
rquery->exec_nodes = RelidGetExecNodes(rel_id);
if (rquery->exec_nodes->nodeList != NULL &&
rquery->exec_nodes->nodeList->length < u_sess->pgxc_cxt.NumDataNodes) {
*drop_seq_string = get_drop_seq_query_string(atstmt, rel_id);
*exec_nodes = rquery->exec_nodes;
}
}
}
}
#ifdef ENABLE_MULTIPLE_NODES
bool check_ts_create_idx(const Node* parsetree, Oid* nspname)
{
if (nodeTag(parsetree) == T_IndexStmt) {
IndexStmt* stmt = (IndexStmt*)parsetree;
if (check_ts_idx_ddl(stmt->relation, NULL, nspname, stmt)) {
return true;
}
}
return false;
}
#endif
void ExecAlterDatabaseSetStmt(Node* parse_tree, const char* query_string, bool sent_to_remote)
{
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmodule(MOD_COMMAND),
errmsg("Alter database set guc value is not supported."),
errhint("Use gs_guc to set global guc value.")));
#endif
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterDatabaseSet((AlterDatabaseSetStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterDatabaseSet((AlterDatabaseSetStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterDatabaseSet((AlterDatabaseSetStmt*)parse_tree);
}
#else
AlterDatabaseSet((AlterDatabaseSetStmt*)parse_tree);
#endif
}
void ExecAlterRoleSetStmt(Node* parse_tree, const char* query_string, bool sent_to_remote)
{
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmodule(MOD_COMMAND),
errmsg("Alter user set guc value is not supported."),
errhint("Use gs_guc to set global guc value.")));
#endif
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterRoleSet((AlterRoleSetStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterRoleSet((AlterRoleSetStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterRoleSet((AlterRoleSetStmt*)parse_tree);
}
#else
AlterRoleSet((AlterRoleSetStmt*)parse_tree);
#endif
}
void ExecShrinkRelationChunkStmt(Node* parse_tree, const char* query_string, bool sent_to_remote)
{
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ShrinkRelationChunk((ShrinkStmt*)(void *)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ShrinkRelationChunk((ShrinkStmt*)(void *)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
ShrinkRelationChunk((ShrinkStmt*)(void *)parse_tree);
}
#else
ShrinkRelationChunk((ShrinkStmt*)(void *)parse_tree);
#endif
}
void standard_ProcessUtility(processutility_context* processutility_cxt,
DestReceiver* dest,
#ifdef PGXC
bool sent_to_remote,
#endif
char* completion_tag,
ProcessUtilityContext context,
bool isCTAS)
{
check_stack_depth();
if (processutility_cxt->readOnlyTree) {
processutility_cxt->parse_tree = (Node*)copyObject(processutility_cxt->parse_tree);
}
Node* parse_tree = processutility_cxt->parse_tree;
const char* query_string = processutility_cxt->query_string;
ParamListInfo params = processutility_cxt->params;
bool is_top_level = processutility_cxt->is_top_level;
#ifdef ENABLE_MULTIPLE_NODES
* If in scenario of create index for timeseries table, some object
* replacement needs to be performed to replace the target object
* with the timeseries tag rel.
*/
char tag_tbl_name[NAMEDATALEN] = {0};
Oid nspname;
bool ts_idx_create = check_ts_create_idx(parse_tree, &nspname);
if (ts_idx_create) {
IndexStmt* stmt = (IndexStmt*)parse_tree;
stmt->relation->schemaname = get_namespace_name(nspname);
get_ts_idx_tgt(tag_tbl_name, stmt->relation);
stmt->relation->schemaname = pstrdup("cstore");
stmt->relation->relname = tag_tbl_name;
}
#endif
BlockUnsupportedDDL(parse_tree);
#ifdef ENABLE_MULTIPLE_NODES
block_ts_rangevar_unsupport_ddl(parse_tree);
#endif
t_thrd.vacuum_cxt.vac_context = NULL;
#ifdef PGXC
#ifdef ENABLE_MULTIPLE_NODES
char* new_query = NULL;
new_query = rewrite_query_string(parse_tree);
if (new_query != NULL) {
* cannot free old queryString. it may point to other memory context,
* after switch to this context, it can be free. left to free with this context,
* rewrite to 1 table, can free, rewrite 2 table, cannot free
*/
query_string = new_query;
}
#endif
* For more detail see comments in function pgxc_lock_for_backup.
*
* Cosider the following scenario:
* Imagine a two cordinator cluster CO1, CO2
* Suppose a client connected to CO1 issues select pgxc_lock_for_backup()
* Now assume that a client connected to CO2 issues a create table
* select pgxc_lock_for_backup() would try to acquire the advisory lock
* in exclusive mode, whereas create table would try to acquire the same
* lock in shared mode. Both these requests will always try acquire the
* lock in the same order i.e. they would both direct the request first to
* CO1 and then to CO2. One of the two requests would therefore pass
* and the other would fail.
*
* Consider another scenario:
* Suppose we have a two cooridnator cluster CO1 and CO2
* Assume one client connected to each coordinator
* Further assume one client starts a transaction
* and issues a DDL. This is an unfinished transaction.
* Now assume the second client issues
* select pgxc_lock_for_backup()
* This request would fail because the unfinished transaction
* would already hold the advisory lock.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && IsNormalProcessingMode()) {
if (!is_stmt_allowed_in_locked_mode(parse_tree, query_string))
pgxc_lock_for_utility_stmt(parse_tree, isAllTempObjects(parse_tree, query_string, sent_to_remote));
}
#endif
if (t_thrd.proc->workingVersionNum >= 91275) {
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsAbortedTransactionBlockState()) {
Oid node_oid = get_pgxc_nodeoid(g_instance.attr.attr_common.PGXCNodeName);
bool nodeis_active = true;
nodeis_active = is_pgxc_nodeactive(node_oid);
if (OidIsValid(node_oid) && nodeis_active == false && !IS_CN_OBS_DISASTER_RECOVER_MODE)
ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), errmsg("Current Node is not active")));
}
}
check_xact_readonly(parse_tree);
if (completion_tag != NULL)
completion_tag[0] = '\0';
PreRedoInOndemandRecovery(parse_tree);
errno_t errorno = EOK;
switch (nodeTag(parse_tree)) {
* ******************** transactions ********************
*/
case T_TransactionStmt: {
TransactionStmt* stmt = (TransactionStmt*)parse_tree;
switch (stmt->kind) {
* START TRANSACTION, as defined by SQL99: Identical
* to BEGIN. Same code for both.
*/
case TRANS_STMT_BEGIN:
case TRANS_STMT_START: {
if (stmt->with_snapshot) {
if (u_sess->utils_cxt.XactIsoLevel == XACT_REPEATABLE_READ) {
GetTransactionSnapshot();
} else {
ereport(WARNING, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("with consistent snapshot only effected in repeatable read mode")));
}
}
ListCell* lc = NULL;
BeginTransactionBlock();
foreach (lc, stmt->options) {
DefElem* item = (DefElem*)lfirst(lc);
set_item_arg_according_to_def_name(item);
}
u_sess->need_report_top_xid = true;
} break;
case TRANS_STMT_COMMIT:
if ((IS_SINGLE_NODE || IS_PGXC_COORDINATOR) && u_sess->debug_query_id == 0) {
u_sess->debug_query_id = generate_unique_id64(>_queryId);
pgstat_report_queryid(u_sess->debug_query_id);
}
if (TransactionIdIsValid(GetTopTransactionIdIfAny())) {
CheckProcCsnValid();
}
if (!EndTransactionBlock()) {
if (completion_tag != NULL) {
errorno = strcpy_s(completion_tag, COMPLETION_TAG_BUFSIZE, "ROLLBACK");
securec_check(errorno, "\0", "\0");
}
}
FreeSavepointList();
if (SS_STANDBY_MODE_WITH_REMOTE_EXECUTE) {
ClearTxnInfoForSSLibpqsw();
if (libpqsw_get_transaction() && !libpqsw_is_end()) {
libpqsw_set_transaction(false);
}
}
break;
case TRANS_STMT_PREPARE:
PreventCommandDuringRecovery("PREPARE TRANSACTION");
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !u_sess->attr.attr_common.xc_maintenance_mode) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
(errmsg("Explicit prepare transaction is not supported."))));
if (IsXidImplicit((const char*)stmt->gid)) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
(errmsg("Invalid transaciton_id to prepare."))));
break;
}
}
#endif
if (!PrepareTransactionBlock(stmt->gid)) {
if (completion_tag != NULL) {
errorno = strcpy_s(completion_tag, COMPLETION_TAG_BUFSIZE, "ROLLBACK");
securec_check(errorno, "\0", "\0");
}
}
break;
case TRANS_STMT_COMMIT_PREPARED:
PreventTransactionChain(is_top_level, "COMMIT PREPARED");
PreventCommandDuringRecovery("COMMIT PREPARED");
if (COMMITSEQNO_IS_COMMITTING(stmt->csn)) {
stmt->csn = GET_COMMITSEQNO(stmt->csn);
}
#ifdef PGXC
* Commit a transaction which was explicitely prepared
* before
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (!u_sess->attr.attr_common.xc_maintenance_mode) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
(errmsg("Explicit commit prepared transaction is not supported."))));
} else {
if (!(useLocalXid || !IsPostmasterEnvironment || GTM_FREE_MODE) &&
COMMITSEQNO_IS_COMMITTED(stmt->csn)) {
setCommitCsn(stmt->csn);
}
FinishPreparedTransaction(stmt->gid, true);
}
} else {
#endif
#ifdef ENABLE_DISTRIBUTE_TEST
if (IS_PGXC_DATANODE && TEST_STUB(DN_COMMIT_PREPARED_SLEEP, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("GTM_TEST %s: DN commit prepare sleep",
g_instance.attr.attr_common.PGXCNodeName)));
pg_usleep(g_instance.distribute_test_param_instance->sleep_time * 1000000);
}
if (IS_PGXC_DATANODE)
execute_whitebox(WHITEBOX_LOC, stmt->gid, WHITEBOX_WAIT, 0.0001);
#endif
if (!(useLocalXid || !IsPostmasterEnvironment || GTM_FREE_MODE) &&
COMMITSEQNO_IS_COMMITTED(stmt->csn)) {
setCommitCsn(stmt->csn);
}
CheckProcCsnValid();
FinishPreparedTransaction(stmt->gid, true);
#ifdef PGXC
}
#endif
break;
case TRANS_STMT_ROLLBACK_PREPARED:
PreventTransactionChain(is_top_level, "ROLLBACK PREPARED");
PreventCommandDuringRecovery("ROLLBACK PREPARED");
#ifdef PGXC
* Abort a transaction which was explicitely prepared
* before
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (!u_sess->attr.attr_common.xc_maintenance_mode) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
(errmsg("Explicit rollback prepared transaction is not supported."))));
} else
FinishPreparedTransaction(stmt->gid, false);
} else
#endif
FinishPreparedTransaction(stmt->gid, false);
break;
case TRANS_STMT_ROLLBACK:
UserAbortTransactionBlock();
FreeSavepointList();
if (SS_STANDBY_MODE_WITH_REMOTE_EXECUTE) {
ClearTxnInfoForSSLibpqsw();
if (libpqsw_get_transaction() && !libpqsw_is_end()) {
libpqsw_set_transaction(false);
}
}
break;
case TRANS_STMT_SAVEPOINT: {
char* name = NULL;
RequireTransactionChain(is_top_level, "SAVEPOINT");
name = GetSavepointName(stmt->options);
* CN send the following info to DNs and other CNs before itself DefineSavepoint
* 1)parent xid
* 2)start transaction command if need
* 3)SAVEPOINT command
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
RecordSavepoint(query_string, name, false, SUB_STMT_SAVEPOINT);
pgxc_node_remote_savepoint(query_string, EXEC_ON_DATANODES, true, true);
#ifdef ENABLE_DISTRIBUTE_TEST
if (TEST_STUB(CN_SAVEPOINT_BEFORE_DEFINE_LOCAL_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: cn local define savepoint \"%s\" failed",
g_instance.attr.attr_common.PGXCNodeName,
name)));
}
if (execute_whitebox(WHITEBOX_LOC, NULL, WHITEBOX_DEFAULT, 0.0001)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("WHITE_BOX TEST %s: cn local define savepoint failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
#endif
} else {
* CurrentTransactionState */
GetCurrentTransactionId();
#ifdef ENABLE_DISTRIBUTE_TEST
if (TEST_STUB(DN_SAVEPOINT_BEFORE_DEFINE_LOCAL_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: dn local define savepoint \"%s\" failed",
g_instance.attr.attr_common.PGXCNodeName,
name)));
}
if (execute_whitebox(WHITEBOX_LOC, NULL, WHITEBOX_DEFAULT, 0.0001)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("WHITE_BOX TEST %s: dn local define savepoint failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
#endif
}
DefineSavepoint(name);
} break;
case TRANS_STMT_RELEASE:
RequireTransactionChain(is_top_level, "RELEASE SAVEPOINT");
ReleaseSavepoint(GetSavepointName(stmt->options), false);
#ifdef ENABLE_DISTRIBUTE_TEST
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (TEST_STUB(CN_RELEASESAVEPOINT_BEFORE_SEND_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: cn release savepoint before send to remote nodes failed.",
g_instance.attr.attr_common.PGXCNodeName)));
}
} else if (TEST_STUB(DN_RELEASESAVEPOINT_AFTER_LOCAL_DEAL_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: dn release savepoint after loal deal failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
if (execute_whitebox(WHITEBOX_LOC, NULL, WHITEBOX_DEFAULT, 0.0001)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("WHITE_BOX TEST %s: release savepoint failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
#endif
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
HandleReleaseOrRollbackSavepoint(
query_string, GetSavepointName(stmt->options), SUB_STMT_RELEASE);
pgxc_node_remote_savepoint(query_string, EXEC_ON_DATANODES, false, false);
}
break;
case TRANS_STMT_ROLLBACK_TO:
RequireTransactionChain(is_top_level, "ROLLBACK TO SAVEPOINT");
RollbackToSavepoint(GetSavepointName(stmt->options), false);
#ifdef ENABLE_DISTRIBUTE_TEST
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (TEST_STUB(CN_ROLLBACKTOSAVEPOINT_BEFORE_SEND_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: cn rollback to savepoint failed before send to remote nodes.",
g_instance.attr.attr_common.PGXCNodeName)));
}
} else if (TEST_STUB(DN_ROLLBACKTOSAVEPOINT_AFTER_LOCAL_DEAL_FAILED, twophase_default_error_emit)) {
ereport(g_instance.distribute_test_param_instance->elevel,
(errmsg("SUBXACT_TEST %s: dn rollback to savepoint failed after local deal failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
if (execute_whitebox(WHITEBOX_LOC, NULL, WHITEBOX_DEFAULT, 0.0001)) {
ereport(LOG,
(errmsg("WHITE_BOX TEST %s: rollback to savepoint failed",
g_instance.attr.attr_common.PGXCNodeName)));
}
#endif
* CN needn't send xid, as savepoint must be sent and executed before.
* And the parent xid must be in transaction state pushed and remained.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
HandleReleaseOrRollbackSavepoint(
query_string, GetSavepointName(stmt->options), SUB_STMT_ROLLBACK_TO);
pgxc_node_remote_savepoint(query_string, EXEC_ON_DATANODES, false, false);
}
* CommitTransactionCommand is in charge of
* re-defining the savepoint again
*/
break;
default:
break;
}
} break;
* Portal (cursor) manipulation
*
* Note: DECLARE CURSOR is processed mostly as a SELECT, and
* therefore what we will get here is a PlannedStmt not a bare
* DeclareCursorStmt.
*/
case T_PlannedStmt: {
PlannedStmt* stmt = (PlannedStmt*)parse_tree;
if (stmt->utilityStmt == NULL || !IsA(stmt->utilityStmt, DeclareCursorStmt)) {
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("non-DECLARE CURSOR PlannedStmt passed to ProcessUtility")));
}
#ifdef DOLPHIN
if (u_sess->proc_cxt.MyProcPort->protocol_config->fn_process_command == dolphin_process_command) {
ereport(ERROR, (errmsg("non-procedure CURSOR is not supported by mysql protocol.")));
}
#endif
PerformCursorOpen(stmt, params, query_string, is_top_level);
} break;
case T_ClosePortalStmt: {
ClosePortalStmt* stmt = (ClosePortalStmt*)parse_tree;
CheckRestrictedOperation("CLOSE");
stop_query();
PerformPortalClose(stmt->portalname);
} break;
case T_FetchStmt:
PerformPortalFetch((FetchStmt*)parse_tree, dest, completion_tag);
break;
case T_CreateTableSpaceStmt:
#ifdef ENABLE_NEON
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("CREATE TABLESPACE is not supported on Neon")));
#endif
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
PreventTransactionChain(is_top_level, "CREATE TABLESPACE");
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_COORDS);
CreateTableSpace((CreateTableSpaceStmt*)parse_tree);
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_DATANODES);
} else {
CreateTableSpace((CreateTableSpaceStmt*)parse_tree);
ExecUtilityWithMessage(query_string, sent_to_remote, false);
}
} else {
if (IS_SINGLE_NODE)
PreventTransactionChain(is_top_level, "CREATE TABLESPACE");
CreateTableSpace((CreateTableSpaceStmt*)parse_tree);
}
#else
PreventTransactionChain(is_top_level, "CREATE TABLESPACE");
CreateTableSpace((CreateTableSpaceStmt*)parse_tree);
#endif
break;
case T_DropStmt:
{
DropStmt *stmt = (DropStmt *) parse_tree;
if (EventTriggerSupportsObjectType(stmt->removeType))
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
else
ExecDropStmt((DropStmt *) parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
}
break;
case T_RenameStmt:
{
RenameStmt *stmt = (RenameStmt *) parse_tree;
if (EventTriggerSupportsObjectType(stmt->renameType))
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
else
doRenameStmt((RenameStmt *) parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
}
break;
case T_DropTableSpaceStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
PreventTransactionChain(is_top_level, "DROP TABLESPACE");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropTableSpace((DropTableSpaceStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropTableSpace((DropTableSpaceStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropTableSpace((DropTableSpaceStmt*)parse_tree);
}
#else
PreventTransactionChain(is_top_level, "DROP TABLESPACE");
DropTableSpace((DropTableSpaceStmt*)parse_tree);
#endif
break;
case T_AlterTableSpaceOptionsStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt*)parse_tree);
}
#else
AlterTableSpaceOptions((AlterTableSpaceOptionsStmt*)parse_tree);
#endif
break;
case T_CreateEventTrigStmt:
CreateEventTrigger((CreateEventTrigStmt*) parse_tree);
break;
case T_AlterEventTrigStmt:
AlterEventTrigger((AlterEventTrigStmt*) parse_tree);
break;
case T_CreateWeakPasswordDictionaryStmt:
CreateWeakPasswordDictionary((CreateWeakPasswordDictionaryStmt*)parse_tree);
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
#endif
break;
by set gs_weak_password name directly, making the system find our system table */
case T_DropWeakPasswordDictionaryStmt:
{
DropWeakPasswordDictionary();
}
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
#endif
break;
case T_CreateDataSourceStmt:
CreateDataSource((CreateDataSourceStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterDataSourceStmt:
AlterDataSource((AlterDataSourceStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_TruncateStmt:
PG_TRY();
{
u_sess->exec_cxt.isExecTrunc = true;
#ifdef PGXC
* In Postgres-XC, TRUNCATE needs to be launched to remote nodes
* before AFTER triggers. As this needs an internal control it is
* managed by this function internally.
*/
ExecuteTruncate((TruncateStmt*)parse_tree, query_string);
#else
ExecuteTruncate((TruncateStmt*)parse_tree);
#endif
u_sess->exec_cxt.isExecTrunc = false;
}
PG_CATCH();
{
u_sess->exec_cxt.isExecTrunc = false;
PG_RE_THROW();
}
PG_END_TRY();
break;
case T_PurgeStmt:
ExecutePurge((PurgeStmt*)parse_tree);
break;
case T_TimeCapsuleStmt:
ExecuteTimeCapsule((TimeCapsuleStmt*)parse_tree);
break;
case T_CommentStmt: {
if (EventTriggerSupportsObjectType(((CommentStmt*)parse_tree)->objtype))
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
else {
CommentObject((CommentStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
bool is_temp = false;
ExecNodes* exec_nodes = NULL;
CommentStmt* stmt = (CommentStmt*)parse_tree;
RemoteQueryExecType exec_type = get_nodes_4_comment_utility(stmt, &is_temp, &exec_nodes);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, false, exec_type, is_temp);
FreeExecNodes(&exec_nodes);
}
#endif
}
}break;
case T_SecLabelStmt: {
if (EventTriggerSupportsObjectType(((SecLabelStmt *)parse_tree)->objtype))
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
else {
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SECURITY LABEL is not yet supported.")));
#endif
ExecSecLabelStmt((SecLabelStmt*)parse_tree);
}
} break;
case T_CopyStmt: {
if (((CopyStmt*)parse_tree)->filename != NULL && isSecurityMode && !IsInitdb) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_OPERATION),
errmsg("operation copy with file is forbidden in security mode.")));
}
uint64 processed;
uint64 histhash;
bool has_histhash;
#ifdef DOLPHIN
GetSessionContext()->isDoCopy = true;
PG_TRY();
{
DoCopy((CopyStmt*)parse_tree, query_string, &processed);
}
PG_CATCH();
{
GetSessionContext()->isDoCopy = false;
PG_RE_THROW();
}
PG_END_TRY();
GetSessionContext()->isDoCopy = false;
#else
DoCopy((CopyStmt*)parse_tree, query_string, &processed);
#endif
has_histhash = ((CopyStmt*)parse_tree)->hashstate.has_histhash;
histhash = ((CopyStmt*)parse_tree)->hashstate.histhash;
if (completion_tag != NULL) {
if (has_histhash && !IsConnFromApp()) {
errorno = snprintf_s(completion_tag,
COMPLETION_TAG_BUFSIZE,
COMPLETION_TAG_BUFSIZE - 1,
"COPY " UINT64_FORMAT " " UINT64_FORMAT,
processed, histhash);
} else {
errorno = snprintf_s(completion_tag,
COMPLETION_TAG_BUFSIZE,
COMPLETION_TAG_BUFSIZE - 1,
"COPY " UINT64_FORMAT,
processed);
}
securec_check_ss(errorno, "\0", "\0");
}
report_utility_time(parse_tree);
} break;
case T_PrepareStmt:
CheckRestrictedOperation("PREPARE");
PrepareQuery((PrepareStmt*)parse_tree, query_string);
break;
case T_ExecuteStmt:
* Check the prepared stmt is ok for executing directly, otherwise
* RePrepareQuery proc should be called to re-generated a new prepared stmt.
*/
if (needRecompileQuery((ExecuteStmt*)parse_tree))
RePrepareQuery((ExecuteStmt*)parse_tree);
ExecuteQuery((ExecuteStmt*)parse_tree, NULL, query_string, params, dest, completion_tag);
break;
case T_DeallocateStmt:
CheckRestrictedOperation("DEALLOCATE");
DeallocateQuery((DeallocateStmt*)parse_tree);
break;
case T_AlterOwnerStmt:
CheckObjectInBlackList(((AlterOwnerStmt*)parse_tree)->objectType, query_string);
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
ExecNodes* exec_nodes = NULL;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
AlterOwnerStmt* OwnerStmt = (AlterOwnerStmt*)parse_tree;
if (OwnerStmt->objectType == OBJECT_FUNCTION) {
Oid funcid = LookupFuncNameTypeNames(OwnerStmt->object, OwnerStmt->objarg, false);
exec_nodes = GetFunctionNodes(funcid);
}
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, exec_nodes, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
if (IS_SINGLE_NODE && EventTriggerSupportsObjectType(((AlterObjectSchemaStmt*) parse_tree)->objectType)) {
ProcessUtilitySlow(parse_tree, query_string, params, dest,
sent_to_remote, completion_tag, context, isCTAS);
} else {
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
}
}
#else
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
#endif
break;
case T_GrantStmt: {
if (EventTriggerSupportsGrantObjectType(((GrantStmt*)parse_tree)->objtype))
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
else {
doGrantStmt((GrantStmt *)parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
}
}break;
case T_GrantRoleStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
GrantRole((GrantRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
GrantRole((GrantRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
GrantRole((GrantRoleStmt*)parse_tree);
}
#else
GrantRole((GrantRoleStmt*)parse_tree);
#endif
break;
case T_ShowEventStmt:
ShowEventCommand((ShowEventStmt*)parse_tree, dest);
break;
case T_CreatePackageStmt:
{
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR, (errcode(ERRCODE_INVALID_PACKAGE_DEFINITION),
errmsg("not support create package in distributed database")));
#endif
PG_TRY();
{
set_create_plsql_type_start();
u_sess->plsql_cxt.isCreatePkg = true;
CreatePackageCommand((CreatePackageStmt*)parse_tree, query_string);
set_create_plsql_type_end();
set_function_style_none();
u_sess->plsql_cxt.isCreatePkg = false;
}
PG_CATCH();
{
LockErrorCleanup();
if (u_sess->plsql_cxt.debug_query_string) {
pfree_ext(u_sess->plsql_cxt.debug_query_string);
}
set_create_plsql_type_end();
set_function_style_none();
u_sess->plsql_cxt.isCreatePkg = false;
PG_RE_THROW();
}
PG_END_TRY();
} break;
case T_CreatePackageBodyStmt:
{
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR, (errcode(ERRCODE_INVALID_PACKAGE_DEFINITION),
errmsg("not support create package in distributed database")));
#endif
PG_TRY();
{
set_create_plsql_type_start();
u_sess->plsql_cxt.isCreatePkg = true;
CreatePackageBodyCommand((CreatePackageBodyStmt*)parse_tree, query_string);
set_create_plsql_type_end();
set_function_style_none();
u_sess->plsql_cxt.isCreatePkg = false;
}
PG_CATCH();
{
if (u_sess->plsql_cxt.debug_query_string) {
pfree_ext(u_sess->plsql_cxt.debug_query_string);
}
list_free_ext(u_sess->plsql_cxt.func_compiled_list);
u_sess->plsql_cxt.real_func_num = 0;
set_create_plsql_type_end();
set_function_style_none();
u_sess->plsql_cxt.isCreatePkg = false;
PG_RE_THROW();
}
PG_END_TRY();
} break;
case T_DoStmt:
ExecuteDoStmt((DoStmt*) parse_tree,
(!u_sess->SPI_cxt.is_allow_commit_rollback));
break;
case T_CreatedbStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
PreventTransactionChain(is_top_level, "CREATE DATABASE");
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_COORDS);
createdb((CreatedbStmt*)parse_tree);
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_DATANODES);
} else {
createdb((CreatedbStmt*)parse_tree);
ExecUtilityWithMessage(query_string, sent_to_remote, false);
}
} else {
if (IS_SINGLE_NODE)
PreventTransactionChain(is_top_level, "CREATE DATABASE");
createdb((CreatedbStmt*)parse_tree);
}
#else
PreventTransactionChain(is_top_level, "CREATE DATABASE");
createdb((CreatedbStmt*)parse_tree);
#endif
break;
case T_AlterDatabaseStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
* If this is not a SET TABLESPACE statement, just propogate the
* cmd as usual.
*/
if (!IsSetTableSpace((AlterDatabaseStmt*)parse_tree))
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
else
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_COORDS);
AlterDatabase((AlterDatabaseStmt*)parse_tree, is_top_level);
if (!IsSetTableSpace((AlterDatabaseStmt*)parse_tree))
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
else
exec_utility_with_message_parallel_ddl_mode(
query_string, sent_to_remote, false, first_exec_node, EXEC_ON_DATANODES);
} else {
AlterDatabase((AlterDatabaseStmt*)parse_tree, is_top_level);
* If this is not a SET TABLESPACE statement, just propogate the
* cmd as usual.
*/
if (!IsSetTableSpace((AlterDatabaseStmt*)parse_tree))
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
else
ExecUtilityWithMessage(query_string, sent_to_remote, false);
}
} else {
AlterDatabase((AlterDatabaseStmt*)parse_tree, is_top_level);
}
#else
AlterDatabase((AlterDatabaseStmt*)parse_tree, is_top_level);
#endif
break;
case T_AlterDatabaseSetStmt:
ExecAlterDatabaseSetStmt(parse_tree, query_string, sent_to_remote);
break;
case T_DropdbStmt: {
DropdbStmt* stmt = (DropdbStmt*)parse_tree;
#ifdef PGXC
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
PreCleanAndCheckConns(stmt->dbname, stmt->missing_ok);
PreventTransactionChain(is_top_level, "DROP DATABASE");
}
#else
PreventTransactionChain(is_top_level, "DROP DATABASE");
#endif
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
dropdb(stmt->dbname, stmt->missing_ok);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
dropdb(stmt->dbname, stmt->missing_ok);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
dropdb(stmt->dbname, stmt->missing_ok);
}
#else
PreventTransactionChain(is_top_level, "DROP DATABASE");
dropdb(stmt->dbname, stmt->missing_ok);
#endif
} break;
case T_NotifyStmt:
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("NOFITY statement is not yet supported.")));
#endif
{
NotifyStmt* stmt = (NotifyStmt*)parse_tree;
PreventCommandDuringRecovery("NOTIFY");
Async_Notify(stmt->conditionname, stmt->payload);
}
break;
case T_ListenStmt:
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("LISTEN statement is not yet supported.")));
#endif
{
ListenStmt* stmt = (ListenStmt*)parse_tree;
PreventCommandDuringRecovery("LISTEN");
CheckRestrictedOperation("LISTEN");
Async_Listen(stmt->conditionname);
}
break;
case T_UnlistenStmt:
#ifdef PGXC
ereport(
ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("UNLISTEN statement is not yet supported.")));
#endif
{
UnlistenStmt* stmt = (UnlistenStmt*)parse_tree;
PreventCommandDuringRecovery("UNLISTEN");
CheckRestrictedOperation("UNLISTEN");
if (stmt->conditionname)
Async_Unlisten(stmt->conditionname);
else
Async_UnlistenAll();
}
break;
case T_LoadStmt:
{
LoadStmt* stmt = (LoadStmt*)parse_tree;
if (stmt->is_load_data) {
if (IS_PGXC_COORDINATOR) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("LOAD DATA statement is not yet supported.")));
}
TransformLoadDataToCopy(stmt);
break;
}
char str[] = "age";
int result = strcmp(stmt->filename, str);
if (result != 0) {
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("LOAD statement is not yet supported.")));
#endif
}
closeAllVfds();
load_file(stmt->filename, !superuser());
}
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false);
#endif
break;
case T_ClusterStmt:
PreventCommandDuringRecovery("CLUSTER");
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
ExecNodes* exec_nodes = NULL;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
ClusterStmt* cstmt = (ClusterStmt*)parse_tree;
if (cstmt->relation) {
Oid rel_id = RangeVarGetRelid(cstmt->relation, NoLock, true);
(void)ExecUtilityFindNodes(OBJECT_TABLE, rel_id, &is_temp);
exec_type = CHOOSE_EXEC_NODES(is_temp);
} else if (in_logic_cluster()) {
* In logic cluster mode, superuser and system DBA can execute CLUSTER
* on all nodes; logic cluster users can execute CLUSTER on its node group;
* other users can't execute CLUSTER in DNs, only CLUSTER one table.
*/
Oid group_oid = get_current_lcgroup_oid();
if (OidIsValid(group_oid)) {
exec_nodes = GetNodeGroupExecNodes(group_oid);
} else if (!superuser()) {
exec_type = EXEC_ON_NONE;
ereport(NOTICE,
(errmsg("CLUSTER do not run in DNs because User \"%s\" don't "
"attach to any logic cluster.",
GetUserNameFromId(GetUserId()))));
}
}
query_string = ConstructMesageWithMemInfo(query_string, cstmt->memUsage);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!is_temp) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
true,
EXEC_ON_COORDS,
false,
first_exec_node,
(Node*)parse_tree);
}
}
cluster((ClusterStmt*)parse_tree, is_top_level);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
exec_nodes,
sent_to_remote,
true,
EXEC_ON_DATANODES,
false,
first_exec_node,
(Node*)parse_tree);
} else {
ExecUtilityStmtOnNodes(
query_string, exec_nodes, sent_to_remote, true, exec_type, false, (Node*)parse_tree);
}
FreeExecNodes(&exec_nodes);
} else {
cluster((ClusterStmt*)parse_tree, is_top_level);
}
#else
cluster((ClusterStmt*)parse_tree, is_top_level);
#endif
break;
case T_VacuumStmt: {
bool tmp_enable_autoanalyze = u_sess->attr.attr_sql.enable_autoanalyze;
VacuumStmt* stmt = (VacuumStmt*)parse_tree;
stmt->dest = dest;
if (ENABLE_WORKLOAD_CONTROL && !(stmt->options & VACOPT_FULL) && u_sess->wlm_cxt->wlm_params.iotrack == 0)
WLMCleanIOHashTable();
* "vacuum full compact" means "vacuum full" in fact
* for dfs table if VACOPT_COMPACT is enabled
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (stmt->options & VACOPT_COMPACT)
stmt->options |= VACOPT_FULL;
}
* isForeignTableAnalyze will be set to true when we need to
* analyze a foreign table/foreign tables
*/
bool isForeignTableAnalyze = IsHDFSForeignTableAnalyzable(stmt);
#ifdef ENABLE_MOT
if (stmt->isMOTForeignTable && (stmt->options & VACOPT_VACUUM)) {
stmt->options |= VACOPT_FULL;
}
#endif
* @hdfs
* Log in data node and run analyze foreign table/tables command is illegal.
* We need to do scheduling to run analyze foreign table/tables command which
* can only be done on coordinator node.
*/
if (IS_PGXC_DATANODE && isForeignTableAnalyze && !IsConnFromCoord()) {
ereport(WARNING,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Running analyze on table/tables reside in HDFS directly from data node is not "
"supported.")));
break;
}
* @hdfs
* On data node, we got hybridmesage includeing data node No.
* We judge if analyze work belongs to us by PGXCNodeId. If not, we break out.
*/
if (IS_PGXC_DATANODE && IsConnFromCoord() && isForeignTableAnalyze &&
(u_sess->pgxc_cxt.PGXCNodeId != (int)stmt->nodeNo)) {
break;
}
if (stmt->isPgFdwForeignTables && stmt->va_cols) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE), errmsg("This relation doesn't support analyze with column.")));
}
u_sess->attr.attr_sql.enable_autoanalyze = false;
pgstat_set_io_state(IOSTATE_VACUUM);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
stmt->orgCnNodeNo = ~0;
}
DoVacuumMppTable(stmt, query_string, is_top_level, sent_to_remote);
u_sess->attr.attr_sql.enable_autoanalyze = tmp_enable_autoanalyze;
} break;
case T_ExplainStmt:
* To pass all the llt, do not generate parallel plan
* if we need to explain the query plan.
*/
if (u_sess->opt_cxt.parallel_debug_mode == LLT_MODE) {
u_sess->opt_cxt.query_dop = 1;
u_sess->opt_cxt.skew_strategy_opt = SKEW_OPT_OFF;
}
u_sess->attr.attr_sql.under_explain = true;
PTFastQueryShippingStore = u_sess->attr.attr_sql.enable_fast_query_shipping;
PG_TRY();
{
if (completion_tag != NULL) {
ExplainQuery((ExplainStmt*)parse_tree, query_string, params, dest, completion_tag);
}
}
PG_CATCH();
{
u_sess->attr.attr_sql.under_explain = false;
PG_RE_THROW();
}
PG_END_TRY();
u_sess->attr.attr_sql.under_explain = false;
if (u_sess->opt_cxt.parallel_debug_mode == LLT_MODE) {
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;
}
PTFastQueryShippingStore = true;
break;
#ifndef ENABLE_MULTIPLE_NODES
case T_AlterSystemStmt:
{
* 1.AlterSystemSet don't care whether the node is PRIMARY or STANDBY as same as gs_guc.
* 2.AlterSystemSet don't care whether the database is read-only, as same as gs_guc.
* 3.It cannot be executed in a transaction because it is not rollbackable.
*/
AlterSystemStmt* stmt = (AlterSystemStmt*)parse_tree;
if (stmt->setstmt->is_multiset) {
PreventTransactionChain(true, "ALTER SYSTEM SET");
} else {
PreventTransactionChain(is_top_level, "ALTER SYSTEM SET");
}
AlterSystemSetConfigFile((AlterSystemStmt*)parse_tree);
} break;
#endif
case T_VariableSetStmt:
ExecSetVariableStmt((VariableSetStmt*)parse_tree, params);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
VariableSetStmt* stmt = (VariableSetStmt*)parse_tree;
char* mask_string = NULL;
mask_string = maskPassword(query_string);
if (mask_string == NULL)
mask_string = (char*)query_string;
if (IsVariableinBlackList(stmt->name)) {
break;
}
if (u_sess->catalog_cxt.overrideStack && (!pg_strcasecmp(stmt->name, SEARCH_PATH_GUC_NAME) ||
!pg_strcasecmp(stmt->name, CURRENT_SCHEMA_GUC_NAME))) {
* set search_path or current_schema inside stored procedure is invalid,
* do not send to other nodes.
*/
OverrideStackEntry* entry = NULL;
entry = (OverrideStackEntry*)linitial(u_sess->catalog_cxt.overrideStack);
if (entry->inProcedure)
break;
}
* If command is local and we are not in a transaction block do NOT
* send this query to backend nodes, it is just bypassed by the backend.
*/
if (stmt->is_local) {
if (IsTransactionBlock()) {
if (PoolManagerSetCommand(POOL_CMD_LOCAL_SET, mask_string, stmt->name) < 0) {
ereport(ERROR, (errcode(ERRCODE_SET_QUERY), errmsg("openGauss: ERROR SET query")));
}
}
} else {
if (!IsTransactionBlock() && !(dest->mydest == DestSPI)) {
if (PoolManagerSetCommand(POOL_CMD_GLOBAL_SET, mask_string, stmt->name) < 0) {
ereport(ERROR, (errcode(ERRCODE_SET_QUERY), errmsg("openGauss: ERROR SET query")));
}
} else {
ExecUtilityStmtOnNodes(mask_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
int ret = check_set_message_to_send(stmt, query_string);
if (ret != -1) {
if (ret == 0)
append_set_message(query_string);
else
make_set_message();
}
}
}
if (mask_string != query_string)
pfree(mask_string);
}
#endif
break;
case T_VariableMultiSetStmt: {
VariableMultiSetStmt* n = (VariableMultiSetStmt*)parse_tree;
List* stmts = n->args;
ListCell* l = NULL;
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
processutility_context proutility_cxt;
proutility_cxt.parse_tree = stmt;
proutility_cxt.query_string = query_string;
proutility_cxt.readOnlyTree = false;
proutility_cxt.params = params;
proutility_cxt.is_top_level = false;
ProcessUtility(&proutility_cxt,
None_Receiver,
true,
NULL,
context);
}
} break;
case T_VariableShowStmt: {
VariableShowStmt *n = (VariableShowStmt *)parse_tree;
if (strcmp(n->name, "show_warnings") == 0) {
gramShowWarningsErrors(n->offset, n->count, dest, FALSE);
} else if (strcmp(n->name, "show_warnings_count") == 0) {
gramShowWarningsErrorsCount(dest, FALSE);
} else if (strcmp(n->name, "show_errors") == 0) {
gramShowWarningsErrors(n->offset, n->count, dest, TRUE);
} else if (strcmp(n->name, "show_errors_count") == 0) {
gramShowWarningsErrorsCount(dest, TRUE);
} else {
GetPGVariable(n->name, n->likename, dest);
}
} break;
case T_ShutdownStmt: {
ShutdownStmt* n = (ShutdownStmt*)parse_tree;
DoShutdown(n);
} break;
case T_DiscardStmt:
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("DISCARD statement is not yet supported.")));
#endif
CheckRestrictedOperation("DISCARD");
DiscardCommand((DiscardStmt*)parse_tree, is_top_level);
#ifdef PGXC
* Discard objects for all the sessions possible.
* For example, temporary tables are created on all Datanodes
* and Coordinators.
*/
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string,
NULL,
sent_to_remote,
true,
CHOOSE_EXEC_NODES(((DiscardStmt*)parse_tree)->target == DISCARD_TEMP),
false);
#endif
break;
* ******************************** ROLE statements ****
*/
case T_CreateRoleStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
CreateRole((CreateRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
CreateRole((CreateRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
CreateRole((CreateRoleStmt*)parse_tree);
}
#else
CreateRole((CreateRoleStmt*)parse_tree);
#endif
break;
case T_AlterRoleStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterRole((AlterRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterRole((AlterRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterRole((AlterRoleStmt*)parse_tree);
}
#else
AlterRole((AlterRoleStmt*)parse_tree);
#endif
break;
case T_AlterRoleSetStmt:
ExecAlterRoleSetStmt(parse_tree, query_string, sent_to_remote);
break;
case T_DropRoleStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
ListCell* item = NULL;
foreach (item, ((DropRoleStmt*)parse_tree)->roles) {
const char* role = strVal(lfirst(item));
DropRoleStmt* stmt = (DropRoleStmt*)parse_tree;
PreCleanAndCheckUserConns(role, stmt->missing_ok);
}
}
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropRole((DropRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropRole((DropRoleStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropRole((DropRoleStmt*)parse_tree);
}
#else
DropRole((DropRoleStmt*)parse_tree);
#endif
break;
case T_ReassignOwnedStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ReassignOwnedObjects((ReassignOwnedStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ReassignOwnedObjects((ReassignOwnedStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
ReassignOwnedObjects((ReassignOwnedStmt*)parse_tree);
}
#else
ReassignOwnedObjects((ReassignOwnedStmt*)parse_tree);
#endif
break;
case T_LockStmt:
* Since the lock would just get dropped immediately, LOCK TABLE
* outside a transaction block is presumed to be user error.
*/
#ifdef DOLPHIN
if (!((LockStmt*)parse_tree)->isLockTables)
#endif
RequireTransactionChain(is_top_level, "LOCK TABLE");
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
!(u_sess->attr.attr_common.xc_maintenance_mode &&
(strcmp(u_sess->attr.attr_common.application_name, "cm_agent") == 0))) {
ListCell* cell = NULL;
bool has_nontemp = false;
bool has_temp = false;
bool is_temp = false;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
foreach (cell, ((LockStmt*)parse_tree)->relations) {
RangeVar* r = (RangeVar*)lfirst(cell);
Oid rel_id = RangeVarGetRelid(r, NoLock, true);
(void)ExecUtilityFindNodes(OBJECT_TABLE, rel_id, &is_temp);
has_temp |= is_temp;
has_nontemp |= !is_temp;
if (has_temp && has_nontemp)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("LOCK not supported for TEMP and non-TEMP objects together"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char*)query_string;
step->exec_type = has_temp ? EXEC_ON_NONE : EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = has_temp;
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
LockTableCommand((LockStmt*)parse_tree);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_DATANODES,
false,
first_exec_node,
(Node*)parse_tree);
} else {
ExecUtilityStmtOnNodes(
query_string, NULL, sent_to_remote, false, CHOOSE_EXEC_NODES(has_temp), false, (Node*)parse_tree);
}
} else {
LockTableCommand((LockStmt*)parse_tree);
}
#else
LockTableCommand((LockStmt*)parse_tree);
#endif
break;
case T_ConstraintsSetStmt:
AfterTriggerSetState((ConstraintsSetStmt*)parse_tree);
#ifdef PGXC
* Let the pooler manage the statement, SET CONSTRAINT can just be used
* inside a transaction block, hence it has no effect outside that, so use
* it as a local one.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && IsTransactionBlock()) {
const int MAX_PARAM_LEN = 1024;
char tmpName[MAX_PARAM_LEN + 1] = {0};
char *guc_name = GetGucName(query_string, tmpName);
if (PoolManagerSetCommand(POOL_CMD_LOCAL_SET, query_string, guc_name) < 0) {
ereport(ERROR, (errcode(ERRCODE_SET_QUERY), errmsg("openGauss: ERROR SET query")));
}
}
#endif
break;
case T_CheckPointStmt:
if (!(superuser() || isOperatoradmin(GetUserId())))
ereport(
ERROR, (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("must be system admin or operator admin to do CHECKPOINT")));
* You might think we should have a PreventCommandDuringRecovery()
* here, but we interpret a CHECKPOINT command during recovery as
* a request for a restartpoint instead. We allow this since it
* can be a useful way of reducing switchover time when using
* various forms of replication.
*/
RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT | (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, true, EXEC_ON_DATANODES, false);
#endif
break;
#ifdef PGXC
case T_BarrierStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
RequestBarrier(((BarrierStmt*)parse_tree)->id, completion_tag);
break;
* Node DDL is an operation local to Coordinator.
* In case of a new node being created in the cluster,
* it is necessary to create this node on all the Coordinators independently.
*/
case T_AlterNodeStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
PgxcNodeAlter((AlterNodeStmt*)parse_tree);
break;
case T_CreateNodeStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
PgxcNodeCreate((CreateNodeStmt*)parse_tree);
break;
case T_AlterCoordinatorStmt: {
AlterCoordinatorStmt* stmt = (AlterCoordinatorStmt*)parse_tree;
if (IS_PGXC_COORDINATOR && IsConnFromCoord())
PgxcCoordinatorAlter((AlterCoordinatorStmt*)parse_tree);
const char* coor_name = stmt->node_name;
Oid noid = get_pgxc_nodeoid(coor_name);
char node_type = get_pgxc_nodetype(noid);
List* cn_list = NIL;
if (node_type != 'C')
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PGXC Node %s is not a valid coordinator", coor_name)));
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && node_type == PGXC_NODE_COORDINATOR) {
t_thrd.xact_cxt.AlterCoordinatorStmt = true;
ListCell* lc = NULL;
foreach (lc, stmt->coor_nodes) {
char* lc_name = strVal(lfirst(lc));
int lc_idx = PgxcGetNodeIndex(lc_name);
if (lc_idx == -1)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR), errmsg("Invalid value \"%s\" in WITH clause", lc_name)));
if (u_sess->pgxc_cxt.PGXCNodeId - 1 != lc_idx)
cn_list = lappend_int(cn_list, lc_idx);
else
PgxcCoordinatorAlter((AlterCoordinatorStmt*)parse_tree);
}
if (cn_list != NIL) {
ExecNodes* nodes = makeNode(ExecNodes);
nodes->nodeList = cn_list;
ExecUtilityStmtOnNodes(query_string, nodes, sent_to_remote, false, EXEC_ON_COORDS, false);
FreeExecNodes(&nodes);
}
}
} break;
case T_DropNodeStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
{
DropNodeStmt* stmt = (DropNodeStmt*)parse_tree;
char node_type = PgxcNodeRemove(stmt);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && node_type != PGXC_NODE_NONE &&
(!g_instance.attr.attr_storage.IsRoachStandbyCluster)) {
List* cn_list = NIL;
int cn_num = 0;
int cn_delete_idx = -2;
if (node_type == PGXC_NODE_COORDINATOR) {
const char* node_name = stmt->node_name;
cn_delete_idx = PgxcGetNodeIndex(node_name);
if (cn_delete_idx == -1) {
elog(DEBUG2, "Drop Node %s: get node index -1", node_name);
}
}
if (stmt->remote_nodes) {
ListCell* lc = NULL;
foreach (lc, stmt->remote_nodes) {
char* lc_name = strVal(lfirst(lc));
int lc_idx = PgxcGetNodeIndex(lc_name);
if (lc_idx == -1 || lc_idx == cn_delete_idx)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("Invalid value \"%s\" in WITH clause", lc_name)));
if (u_sess->pgxc_cxt.PGXCNodeId - 1 != lc_idx)
cn_list = lappend_int(cn_list, lc_idx);
}
} else {
cn_list = GetAllCoordNodes();
cn_num = list_length(cn_list);
if (cn_num > 0 && cn_delete_idx > -1) {
cn_list = list_delete_int(cn_list, cn_delete_idx);
Assert(cn_num - 1 == list_length(cn_list));
}
}
cn_num = list_length(cn_list);
if (cn_num > 0) {
ExecNodes* nodes = makeNode(ExecNodes);
nodes->nodeList = cn_list;
ExecUtilityStmtOnNodes(query_string, nodes, sent_to_remote, false, EXEC_ON_COORDS, false);
FreeExecNodes(&nodes);
} else if (cn_list != NULL) {
list_free_ext(cn_list);
}
}
}
break;
case T_CreateGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (IS_SINGLE_NODE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Don't support node group in single_node mode.")));
PgxcGroupCreate((CreateGroupStmt*)parse_tree);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
break;
case T_AlterGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (IS_SINGLE_NODE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Don't support node group in single_node mode.")));
#ifdef ENABLE_MULTIPLE_NODES
PgxcGroupAlter((AlterGroupStmt*)parse_tree);
{
AlterGroupType alter_type = ((AlterGroupStmt*)parse_tree)->alter_type;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(alter_type != AG_SET_DEFAULT && alter_type != AG_SET_SEQ_ALLNODES &&
alter_type != AG_SET_SEQ_SELFNODES)) {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
}
#endif
break;
case T_DropGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (IS_SINGLE_NODE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("Don't support node group in single_node mode.")));
PgxcGroupRemove((DropGroupStmt*)parse_tree);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
break;
case T_CreatePolicyLabelStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote,
false, EXEC_ON_COORDS, false, FirstExecNode);
create_policy_label((CreatePolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote,
false, EXEC_ON_DATANODES, false, FirstExecNode);
} else {
create_policy_label((CreatePolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
create_policy_label((CreatePolicyLabelStmt *) parse_tree);
}
#else
create_policy_label((CreatePolicyLabelStmt *) parse_tree);
#endif
break;
case T_AlterPolicyLabelStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote,
false, EXEC_ON_COORDS, false, FirstExecNode);
alter_policy_label((AlterPolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote,
false, EXEC_ON_DATANODES, false, FirstExecNode);
} else {
alter_policy_label((AlterPolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
alter_policy_label((AlterPolicyLabelStmt *) parse_tree);
}
#else
alter_policy_label((AlterPolicyLabelStmt *) parse_tree);
#endif
break;
case T_DropPolicyLabelStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
drop_policy_label((DropPolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES,
false, FirstExecNode);
} else {
drop_policy_label((DropPolicyLabelStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
drop_policy_label((DropPolicyLabelStmt *) parse_tree);
}
#else
drop_policy_label((DropPolicyLabelStmt *) parse_tree);
#endif
break;
case T_CreateAuditPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
create_audit_policy((CreateAuditPolicyStmt *)parse_tree);
} else {
create_audit_policy((CreateAuditPolicyStmt *)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
}
if (IS_SINGLE_NODE) {
create_audit_policy((CreateAuditPolicyStmt *) parse_tree);
}
#else
create_audit_policy((CreateAuditPolicyStmt *) parse_tree);
#endif
break;
case T_AlterAuditPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
alter_audit_policy((AlterAuditPolicyStmt *)parse_tree);
} else {
alter_audit_policy((AlterAuditPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
}
if (IS_SINGLE_NODE) {
alter_audit_policy((AlterAuditPolicyStmt *) parse_tree);
}
#else
alter_audit_policy((AlterAuditPolicyStmt *) parse_tree);
#endif
break;
case T_DropAuditPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
drop_audit_policy((DropAuditPolicyStmt *) parse_tree);
} else {
drop_audit_policy((DropAuditPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
}
if (IS_SINGLE_NODE) {
drop_audit_policy((DropAuditPolicyStmt *) parse_tree);
}
#else
drop_audit_policy((DropAuditPolicyStmt *) parse_tree);
#endif
break;
case T_CreateMaskingPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
{
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if(u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode)
{
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, FirstExecNode);
create_masking_policy((CreateMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, FirstExecNode);
} else {
create_masking_policy((CreateMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
create_masking_policy((CreateMaskingPolicyStmt *) parse_tree);
}
#else
create_masking_policy((CreateMaskingPolicyStmt *) parse_tree);
#endif
break;
case T_AlterMaskingPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
{
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if(u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode)
{
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, FirstExecNode);
alter_masking_policy((AlterMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, FirstExecNode);
} else {
alter_masking_policy((AlterMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
alter_masking_policy((AlterMaskingPolicyStmt *) parse_tree);
}
#else
alter_masking_policy((AlterMaskingPolicyStmt *) parse_tree);
#endif
break;
case T_DropMaskingPolicyStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
{
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if(u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode)
{
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, FirstExecNode);
drop_masking_policy((DropMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, FirstExecNode);
} else {
drop_masking_policy((DropMaskingPolicyStmt *) parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
drop_masking_policy((DropMaskingPolicyStmt *) parse_tree);
}
#else
drop_masking_policy((DropMaskingPolicyStmt *) parse_tree);
#endif
break;
case T_CreateSynonymStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
CreateSynonym((CreateSynonymStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
CreateSynonym((CreateSynonymStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
CreateSynonym((CreateSynonymStmt*)parse_tree);
}
#else
CreateSynonym((CreateSynonymStmt*)parse_tree);
#endif
break;
case T_DropSynonymStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropSynonym((DropSynonymStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropSynonym((DropSynonymStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropSynonym((DropSynonymStmt*)parse_tree);
}
#else
DropSynonym((DropSynonymStmt*)parse_tree);
#endif
break;
case T_CreateResourcePoolStmt:
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (in_logic_cluster() && get_current_lcgroup_name()) {
char* create_respool_stmt = NULL;
create_respool_stmt = GenerateResourcePoolStmt((CreateResourcePoolStmt*)parse_tree, query_string);
Assert(create_respool_stmt != NULL);
ExecUtilityStmtOnNodes_ParallelDDLMode(
create_respool_stmt, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
pfree_ext(create_respool_stmt);
} else {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
}
CreateResourcePool((CreateResourcePoolStmt*)parse_tree);
if (in_logic_cluster() && get_current_lcgroup_name()) {
char* create_respool_stmt = NULL;
create_respool_stmt = GenerateResourcePoolStmt((CreateResourcePoolStmt*)parse_tree, query_string);
Assert(create_respool_stmt != NULL);
ExecUtilityStmtOnNodes_ParallelDDLMode(
create_respool_stmt, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
pfree_ext(create_respool_stmt);
} else {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
}
} else {
CreateResourcePool((CreateResourcePoolStmt*)parse_tree);
if (in_logic_cluster() && get_current_lcgroup_name()) {
char* create_respool_stmt = NULL;
create_respool_stmt = GenerateResourcePoolStmt((CreateResourcePoolStmt*)parse_tree, query_string);
Assert(create_respool_stmt != NULL);
ExecUtilityStmtOnNodes(
create_respool_stmt, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
pfree_ext(create_respool_stmt);
} else {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
}
} else {
CreateResourcePool((CreateResourcePoolStmt*)parse_tree);
}
break;
case T_AlterResourcePoolStmt:
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterResourcePool((AlterResourcePoolStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterResourcePool((AlterResourcePoolStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterResourcePool((AlterResourcePoolStmt*)parse_tree);
}
break;
case T_DropResourcePoolStmt:
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropResourcePool((DropResourcePoolStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropResourcePool((DropResourcePoolStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropResourcePool((DropResourcePoolStmt*)parse_tree);
}
break;
case T_AlterGlobalConfigStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterGlobalConfig((AlterGlobalConfigStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterGlobalConfig((AlterGlobalConfigStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterGlobalConfig((AlterGlobalConfigStmt*)parse_tree);
}
#else
AlterGlobalConfig((AlterGlobalConfigStmt*)parse_tree);
#endif
break;
case T_DropGlobalConfigStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropGlobalConfig((DropGlobalConfigStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropGlobalConfig((DropGlobalConfigStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropGlobalConfig((DropGlobalConfigStmt*)parse_tree);
}
#else
DropGlobalConfig((DropGlobalConfigStmt*)parse_tree);
#endif
break;
case T_CreateWorkloadGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "CREATE WORKLOAD GROUP");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
CreateWorkloadGroup((CreateWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
CreateWorkloadGroup((CreateWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
CreateWorkloadGroup((CreateWorkloadGroupStmt*)parse_tree);
}
break;
case T_AlterWorkloadGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "ALTER WORKLOAD GROUP");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterWorkloadGroup((AlterWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterWorkloadGroup((AlterWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterWorkloadGroup((AlterWorkloadGroupStmt*)parse_tree);
}
break;
case T_DropWorkloadGroupStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "DROP WORKLOAD GROUP");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropWorkloadGroup((DropWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropWorkloadGroup((DropWorkloadGroupStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropWorkloadGroup((DropWorkloadGroupStmt*)parse_tree);
}
break;
case T_CreateAppWorkloadGroupMappingStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "CREATE APP WORKLOAD GROUP MAPPING");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
CreateAppWorkloadGroupMapping((CreateAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
CreateAppWorkloadGroupMapping((CreateAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
CreateAppWorkloadGroupMapping((CreateAppWorkloadGroupMappingStmt*)parse_tree);
}
break;
case T_AlterAppWorkloadGroupMappingStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "ALTER APP WORKLOAD GROUP MAPPING");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterAppWorkloadGroupMapping((AlterAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterAppWorkloadGroupMapping((AlterAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterAppWorkloadGroupMapping((AlterAppWorkloadGroupMappingStmt*)parse_tree);
}
break;
case T_DropAppWorkloadGroupMappingStmt:
#ifndef ENABLE_MULTIPLE_NODES
DISTRIBUTED_FEATURE_NOT_SUPPORTED();
#endif
if (!IsConnFromCoord())
PreventTransactionChain(is_top_level, "DROP APP WORKLOAD GROUP MAPPING");
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropAppWorkloadGroupMapping((DropAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropAppWorkloadGroupMapping((DropAppWorkloadGroupMappingStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropAppWorkloadGroupMapping((DropAppWorkloadGroupMappingStmt*)parse_tree);
}
break;
#endif
case T_ShrinkStmt: {
ExecShrinkRelationChunkStmt(parse_tree, query_string, sent_to_remote);
break;
}
case T_ReindexStmt: {
ReindexStmt* stmt = (ReindexStmt*)parse_tree;
RemoteQueryExecType exec_type;
bool is_temp = false;
if(stmt->concurrent)
PreventTransactionChain(is_top_level,"REINDEX CONCURRENTLY");
pgstat_set_io_state(IOSTATE_WRITE);
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (stmt->relation) {
Oid rel_id = InvalidOid;
rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, false);
if (OidIsValid(rel_id)) {
exec_type = ExecUtilityFindNodes(stmt->kind, rel_id, &is_temp);
UnlockRelationOid(rel_id, AccessShareLock);
} else
exec_type = EXEC_ON_NONE;
} else
exec_type = EXEC_ON_ALL_NODES;
query_string = ConstructMesageWithMemInfo(query_string, stmt->memUsage);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
stmt->kind == OBJECT_DATABASE,
EXEC_ON_COORDS,
false,
first_exec_node,
(Node*)stmt);
}
ReindexCommand(stmt, is_top_level);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
stmt->kind == OBJECT_DATABASE,
EXEC_ON_DATANODES,
false,
first_exec_node,
(Node*)stmt);
} else {
ExecUtilityStmtOnNodes(
query_string, NULL, sent_to_remote, stmt->kind == OBJECT_DATABASE, exec_type, false, (Node*)stmt);
}
} else {
ReindexCommand(stmt, is_top_level);
}
#else
ReindexCommand(stmt, is_top_level);
#endif
} break;
#ifdef PGXC
case T_RemoteQuery:
AssertEreport(IS_PGXC_COORDINATOR, MOD_EXECUTOR, "not a coordinator node");
* Do not launch query on Other Datanodes if remote connection is a Coordinator one
* it will cause a deadlock in the cluster at Datanode levels.
*/
#ifdef ENABLE_MULTIPLE_NODES
if (!IsConnFromCoord())
ExecRemoteUtility((RemoteQuery*)parse_tree);
#endif
break;
case T_CleanConnStmt:
CleanConnection((CleanConnStmt*)parse_tree);
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, true, EXEC_ON_COORDS, false);
break;
#endif
case T_CreateDirectoryStmt:
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
CreatePgDirectory((CreateDirectoryStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
CreatePgDirectory((CreateDirectoryStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else
CreatePgDirectory((CreateDirectoryStmt*)parse_tree);
break;
case T_DropDirectoryStmt:
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropPgDirectory((DropDirectoryStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropPgDirectory((DropDirectoryStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else
DropPgDirectory((DropDirectoryStmt*)parse_tree);
break;
case T_CreateClientLogicGlobal:
if (IS_PGXC_COORDINATOR) {
if (!IsConnFromCoord() && !u_sess->attr.attr_common.enable_full_encryption) {
ereport(ERROR,
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
errmsg("Un-support to create client master key when client encryption is disabled.")));
}
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
(void)process_global_settings((CreateClientLogicGlobal *)parse_tree);
} else {
(void)process_global_settings((CreateClientLogicGlobal *)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
} else {
(void)process_global_settings((CreateClientLogicGlobal *)parse_tree);
}
break;
case T_CreateClientLogicColumn:
if (IS_PGXC_COORDINATOR) {
if (!IsConnFromCoord() && !u_sess->attr.attr_common.enable_full_encryption) {
ereport(ERROR,
(errcode(ERRCODE_OPERATE_NOT_SUPPORTED),
errmsg("Un-support to create column encryption key when client encryption is disabled.")));
}
char *FirstExecNode = find_first_exec_cn();
bool isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS,
false, FirstExecNode);
(void)process_column_settings((CreateClientLogicColumn *)parse_tree);
} else {
(void)process_column_settings((CreateClientLogicColumn *)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
} else {
(void)process_column_settings((CreateClientLogicColumn *)parse_tree);
}
break;
case T_CreateModelStmt:{
exec_create_model((CreateModelStmt*) parse_tree, query_string, params, completion_tag);
break;
}
case T_GetDiagStmt: {
GetDiagStmt *n = (GetDiagStmt *)parse_tree;
getDiagnosticsInfo(n->condInfo, n->hasCondNum, n->condNum);
} break;
case T_DolphinCallStmt: {
ExecuteCallStmt((DolphinCallStmt *)parse_tree, params, false, dest);
} break;
default: {
ProcessUtilitySlow(parse_tree, query_string, params, dest,
#ifdef PGXC
sent_to_remote,
#endif
completion_tag, context, isCTAS);
} break;
}
}
* The "Slow" variant of ProcessUtility should only receive statements
* supported by the event triggers facility. Therefore, we always
* perform the trigger support calls if the context allows it.
*/
static void
ProcessUtilitySlow(Node *parse_tree,
const char *query_string,
ParamListInfo params,
DestReceiver *dest,
#ifdef PGXC
bool sent_to_remote,
#endif
char *completion_tag,
ProcessUtilityContext context,
bool isCTAS)
{
bool is_top_level = (context == PROCESS_UTILITY_TOPLEVEL);
bool isCompleteQuery = (context <= PROCESS_UTILITY_QUERY);
bool needCleanup;
bool commandCollected = false;
ObjectAddress address;
ObjectAddress secondaryObject = InvalidObjectAddress;
if (T_CreateStmt == nodeTag(parse_tree) && isCTAS) {
isCompleteQuery = true;
} else if (T_AlterTableStmt == nodeTag(parse_tree) && ((AlterTableStmt*)parse_tree)->fromCreate) {
isCompleteQuery = false;
}
needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
PG_TRY();
{
if (isCompleteQuery)
EventTriggerDDLCommandStart(parse_tree);
switch (nodeTag(parse_tree)) {
* relation and attribute manipulation
*/
case T_CreateSchemaStmt: {
#ifdef PGXC
CreateSchemaCommand((CreateSchemaStmt*)parse_tree, query_string, sent_to_remote);
#else
CreateSchemaCommand((CreateSchemaStmt*)parse_tree, query_string);
#endif
* EventTriggerCollectSimpleCommand called by
* CreateSchemaCommand
*/
commandCollected = true;
} break;
case T_AlterSchemaStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
AlterSchemaCommand((AlterSchemaStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterSchemaCommand((AlterSchemaStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
AlterSchemaCommand((AlterSchemaStmt*)parse_tree);
}
#else
AlterSchemaCommand((AlterSchemaStmt*)parse_tree);
commandCollected = true;
#endif
break;
case T_CreateRlsPolicyStmt:
CreateRlsPolicy((CreateRlsPolicyStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
#endif
break;
case T_AlterRlsPolicyStmt:
AlterRlsPolicy((AlterRlsPolicyStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
#endif
break;
case T_CreateForeignTableStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (!IsInitdb && IS_SINGLE_NODE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Current mode does not support FOREIGN table yet"),
errdetail("The feature is not currently supported")));
}
#endif
case T_CreateStmt: {
#ifdef PGXC
CreateCommand((CreateStmt*)parse_tree, query_string, params, is_top_level, sent_to_remote, isCTAS);
#else
CreateCommand((CreateStmt*)parse_tree, query_string, params, is_top_level, isCTAS);
#endif
* The multiple commands generated here are stashed
* individually, so disable collection below.
*/
commandCollected = true;
} break;
case T_GrantStmt: {
doGrantStmt((GrantStmt *)parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
commandCollected = true;
} break;
case T_CommentStmt: {
address = CommentObject((CommentStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
bool is_temp = false;
ExecNodes* exec_nodes = NULL;
CommentStmt* stmt = (CommentStmt*)parse_tree;
RemoteQueryExecType exec_type = get_nodes_4_comment_utility(stmt, &is_temp, &exec_nodes);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, false, exec_type, is_temp);
FreeExecNodes(&exec_nodes);
}
#endif
} break;
case T_AlterTableStmt: {
AlterTableStmt* atstmt = (AlterTableStmt*)parse_tree;
LOCKMODE lockmode;
char* first_exec_node = NULL;
bool is_first_node = false;
if (atstmt->concurrent) {
PreventTransactionChain(is_top_level, "ALTER TABLE CONCURRENTLY");
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
* Figure out lock mode, and acquire lock. This also does
* basic permissions checks, so that we won't wait for a lock
* on (for example) a relation on which we have no
* permissions.
*/
lockmode = AlterTableGetLockLevel(atstmt->cmds);
#ifdef PGXC
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!sent_to_remote) {
bool isTemp = false;
RemoteQueryExecType exec_type;
RemoteQuery* step = makeNode(RemoteQuery);
Oid rel_id = RangeVarGetRelid(atstmt->relation, lockmode, true);
if (OidIsValid(rel_id)) {
exec_type = ExecUtilityFindNodes(atstmt->relkind, rel_id, &isTemp);
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)
step->exec_type = EXEC_ON_COORDS;
else
step->exec_type = EXEC_ON_NONE;
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char*)query_string;
step->is_temp = isTemp;
step->exec_nodes = NULL;
UnlockRelationOid(rel_id, lockmode);
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
}
}
}
#endif
Oid rel_id;
List* stmts = NIL;
ListCell* l = NULL;
char* drop_seq_string = NULL;
ExecNodes* exec_nodes = NULL;
rel_id = AlterTableLookupRelation(atstmt, lockmode);
elog(DEBUG1,
"[GET LOCK] Get the lock %d successfully on relation %s for altering operator.",
lockmode,
atstmt->relation->relname);
if (OidIsValid(rel_id)) {
TrForbidAccessRbObject(RelationRelationId, rel_id, atstmt->relation->relname);
stmts = transformAlterTableStmt(rel_id, atstmt, query_string);
EventTriggerAlterTableStart(parse_tree);
EventTriggerAlterTableRelid(rel_id);
if (u_sess->attr.attr_sql.enable_cluster_resize) {
ATMatviewGroup(stmts, rel_id, lockmode);
}
#ifdef PGXC
* Add a RemoteQuery node for a query at top level on a remote
* Coordinator, if not already done so
*/
if (!sent_to_remote && !ISMATMAP(atstmt->relation->relname) && !ISMLOG(atstmt->relation->relname)) {
add_remote_query_4_alter_stmt(is_first_node, atstmt, query_string, &stmts, &drop_seq_string, &exec_nodes);
}
#endif
foreach (l, stmts) {
Node* stmt = (Node*)lfirst(l);
if (IsA(stmt, AlterTableStmt)) {
AlterTable(rel_id, lockmode, (AlterTableStmt*)stmt);
} else {
* Recurse for anything else. If we need to do
* so, "close" the current complex-command set,
* and start a new one at the bottom; this is
* needed to ensure the ordering of queued
* commands is consistent with the way they are
* executed here.
*/
EventTriggerAlterTableEnd();
processutility_context proutility_cxt;
proutility_cxt.parse_tree = stmt;
proutility_cxt.query_string = query_string;
proutility_cxt.readOnlyTree = false;
proutility_cxt.params = params;
proutility_cxt.is_top_level = false;
ProcessUtility(&proutility_cxt,
None_Receiver,
#ifdef PGXC
true,
#endif
NULL,
PROCESS_UTILITY_GENERATED);
EventTriggerAlterTableStart(parse_tree);
EventTriggerAlterTableRelid(rel_id);
}
if (lnext(l) != NULL)
CommandCounterIncrement();
}
#ifdef DOLPHIN
if (atstmt->relkind == OBJECT_TABLE) {
CheckRelAutoIncrementIndex(rel_id, NoLock);
}
#endif
#ifdef ENABLE_MULTIPLE_NODES
if (drop_seq_string != NULL) {
Assert(exec_nodes != NULL);
exec_remote_query_4_seq(exec_nodes, drop_seq_string, INVALIDSEQUUID);
}
#endif
EventTriggerAlterTableEnd();
} else {
ereport(NOTICE, (errmsg("relation \"%s\" does not exist, skipping", atstmt->relation->relname)));
}
report_utility_time(parse_tree);
pfree_ext(drop_seq_string);
commandCollected = true;
} break;
case T_AlterDomainStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("domain is not yet supported.")));
#endif
{
AlterDomainStmt* stmt = (AlterDomainStmt*)parse_tree;
* Some or all of these functions are recursive to cover
* inherited things, so permission checks are done there.
*/
switch (stmt->subtype) {
case 'T':
* Recursively alter column default for table and, if
* requested, for descendants
*/
address = AlterDomainDefault(stmt->typname, stmt->def);
break;
case 'N':
address = AlterDomainNotNull(stmt->typname, false);
break;
case 'O':
address = AlterDomainNotNull(stmt->typname, true);
break;
case 'C':
address = AlterDomainAddConstraint(stmt->typname, stmt->def);
break;
case 'X':
address = AlterDomainDropConstraint(stmt->typname, stmt->name, stmt->behavior, stmt->missing_ok);
break;
case 'V':
address = AlterDomainValidateConstraint(stmt->typname, stmt->name);
break;
default:
{
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized alter domain type: %d", (int)stmt->subtype)));
} break;
}
}
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
* ************* object creation / destruction **************
*/
case T_CreateExtensionStmt:
address = CreateExtension((CreateExtensionStmt *) parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterExtensionStmt:
#ifdef PGXC
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
#endif
address = ExecAlterExtensionStmt((AlterExtensionStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterExtensionContentsStmt:
#ifdef PGXC
FEATURE_NOT_PUBLIC_ERROR("EXTENSION is not yet supported.");
#endif
address = ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parse_tree, NULL);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreateFdwStmt:
#ifdef ENABLE_MULTIPLE_NODES
#ifdef PGXC
if (!IsInitdb && !u_sess->attr.attr_common.IsInplaceUpgrade) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support FOREIGN DATA WRAPPER yet"),
errdetail("The feature is not currently supported")));
}
#endif
#endif
address = CreateForeignDataWrapper((CreateFdwStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterFdwStmt:
#ifdef ENABLE_MULTIPLE_NODES
#ifdef PGXC
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support FOREIGN DATA WRAPPER yet"),
errdetail("The feature is not currently supported")));
#endif
#endif
address = AlterForeignDataWrapper((AlterFdwStmt *) parse_tree);
break;
case T_CreateForeignServerStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (!IsInitdb && IS_SINGLE_NODE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Current mode does not support FOREIGN server yet"),
errdetail("The feature is not currently supported")));
}
#endif
address = CreateForeignServer((CreateForeignServerStmt *) parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !IsInitdb)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterForeignServerStmt:
address = AlterForeignServer((AlterForeignServerStmt *) parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreateUserMappingStmt:
#ifdef ENABLE_MULTIPLE_NODES
#ifdef PGXC
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support USER MAPPING yet"),
errdetail("The feature is not currently supported")));
#endif
#endif
address = CreateUserMapping((CreateUserMappingStmt*)parse_tree);
break;
case T_AlterUserMappingStmt:
#ifdef ENABLE_MULTIPLE_NODES
#ifdef PGXC
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support USER MAPPING yet"),
errdetail("The feature is not currently supported")));
#endif
#endif
address = AlterUserMapping((AlterUserMappingStmt*)parse_tree);
break;
case T_DropUserMappingStmt:
#ifdef ENABLE_MULTIPLE_NODES
#ifdef PGXC
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support USER MAPPING yet"),
errdetail("The feature is not currently supported")));
#endif
#endif
commandCollected = true;
RemoveUserMapping((DropUserMappingStmt *) parse_tree);
break;
case T_CompositeTypeStmt:
{
CompositeTypeStmt* stmt = (CompositeTypeStmt*)parse_tree;
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
if (stmt->typekind == TYPE_COMPOSITE_DEFAULT)
address = DefineCompositeType(stmt->typevar, stmt->coldeflist, stmt->replace);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE)
address = DefineObjectTypeSpec(stmt);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE_BODY)
DefineObjectTypeBody(stmt);
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("create composite type kind \"%s\" error.", stmt->typevar->relname)));
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
if (stmt->typekind == TYPE_COMPOSITE_DEFAULT)
address = DefineCompositeType(stmt->typevar, stmt->coldeflist, stmt->replace);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE)
address = DefineObjectTypeSpec(stmt);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE_BODY)
DefineObjectTypeBody(stmt);
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("create composite type kind \"%s\" error.", stmt->typevar->relname)));
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else
#endif
{
if (stmt->typekind == TYPE_COMPOSITE_DEFAULT)
address = DefineCompositeType(stmt->typevar, stmt->coldeflist, stmt->replace);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE)
address = DefineObjectTypeSpec(stmt);
else if (stmt->typekind == TYPE_COMPOSITE_OBJECT_TYPE_BODY)
DefineObjectTypeBody(stmt);
else
ereport(ERROR,
(errcode(ERRCODE_DUPLICATE_OBJECT), errmsg("create composite type kind \"%s\" error.", stmt->typevar->relname)));
}
} break;
case T_CreateSetStmt:
{
address = DefineSet((CreateSetStmt*)parse_tree);
} break;
case T_CreateRangeStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user defined range type is not yet supported.")));
#endif
address = DefineRange((CreateRangeStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterEnumStmt:
{
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
#endif
{
* We disallow this in transaction blocks, because we can't cope
* with enum OID values getting into indexes and then having
* their defining pg_enum entries go away.
*/
PreventTransactionChain(is_top_level, "ALTER TYPE ... ADD");
}
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
address = AlterEnum((AlterEnumStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
address = AlterEnum((AlterEnumStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else
#endif
{
address = AlterEnum((AlterEnumStmt*)parse_tree);
}
} break;
case T_ViewStmt:
{
EventTriggerAlterTableStart(parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
ViewStmt *vstmt = (ViewStmt*)parse_tree;
* Run parse analysis to convert the raw parse tree to a Query. Note this
* also acquires sufficient locks on the source table(s).
*
* Since parse analysis scribbles on its input, copy the raw parse tree;
* this ensures we don't corrupt a prepared statement, for example.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
bool is_temp = IsViewTemp((ViewStmt*)parse_tree, query_string);
if (!is_temp) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
}
if (vstmt->relkind == OBJECT_MATVIEW) {
CreateCommand((CreateStmt *)vstmt->mv_stmt, vstmt->mv_sql, NULL, true, true);
CommandCounterIncrement();
acquire_mativew_tables_lock((Query *)vstmt->query, true);
}
address = DefineView((ViewStmt*)parse_tree, query_string, sent_to_remote, is_first_node);
if (!is_temp) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
(u_sess->attr.attr_common.IsInplaceUpgrade ? EXEC_ON_DATANODES : EXEC_ON_NONE),
false,
first_exec_node);
}
} else {
if (vstmt->relkind == OBJECT_MATVIEW) {
CreateCommand((CreateStmt *)vstmt->mv_stmt, vstmt->mv_sql, NULL, true, true);
CommandCounterIncrement();
acquire_mativew_tables_lock((Query *)vstmt->query, true);
}
address = DefineView((ViewStmt*)parse_tree, query_string, sent_to_remote, is_first_node);
char* schema_name = ((ViewStmt*)parse_tree)->view->schemaname;
if (schema_name == NULL)
schema_name = DatumGetCString(DirectFunctionCall1(current_schema, PointerGetDatum(NULL)));
bool temp_schema = false;
if (schema_name != NULL)
temp_schema = (strncasecmp(schema_name, "pg_temp", 7) == 0) ? true : false;
if (!ExecIsTempObjectIncluded() && !temp_schema)
ExecUtilityStmtOnNodes(query_string,
NULL,
sent_to_remote,
false,
(u_sess->attr.attr_common.IsInplaceUpgrade ? EXEC_ON_ALL_NODES : EXEC_ON_COORDS),
false);
}
} else {
ViewStmt *vstmt = (ViewStmt*)parse_tree;
if (vstmt->relkind == OBJECT_MATVIEW) {
CreateCommand((CreateStmt *)vstmt->mv_stmt, vstmt->mv_sql, NULL, true, true);
CommandCounterIncrement();
acquire_mativew_tables_lock((Query *)vstmt->query, true);
}
address = DefineView((ViewStmt*)parse_tree, query_string, sent_to_remote, query_string);
}
#else
address = DefineView((ViewStmt*)parse_tree, query_string);
#endif
EventTriggerCollectSimpleCommand(address, secondaryObject,
parse_tree);
commandCollected = true;
EventTriggerAlterTableEnd();
} break;
case T_CreateFunctionStmt:
{
PG_TRY();
{
set_create_plsql_type_start();
address = CreateFunction((CreateFunctionStmt*)parse_tree, query_string, InvalidOid);
set_create_plsql_type_end();
set_function_style_none();
}
PG_CATCH();
{
LockErrorCleanup();
set_create_plsql_type_end();
set_function_style_none();
#ifndef ENABLE_MULTIPLE_NODES
CreateFunctionStmt* stmt = (CreateFunctionStmt*)parse_tree;
char* schemaname = NULL;
char* funcname = NULL;
DeconstructQualifiedName(stmt->funcname, &schemaname, &funcname, NULL);
Oid nspid = SchemaNameGetSchemaOid(schemaname, true);
if (!OidIsValid(nspid)) {
ereport(WARNING,
(errcode(ERRCODE_UNDEFINED_SCHEMA),
errmsg("schema not defined, it may cause duplicate data."),
errdetail("schema not exists.")));
}
* When the GUC parameter SKIP_GS_SOURCE is false and is in the non-initialization state
* and the creation flag bit is the creation function, the creation data should be
* inserted into gs_source. However, we need to skip the ERRCODE_LOCK_WAIT_TIMEOUT error code.
* Otherwise, an uncontrollable error occurs.
* When a deadlock timeout error is reported, a warning log is added to facilitate
* the later creation of stored procedure error log tracing.
*/
ErrorData* edata = &t_thrd.log_cxt.errordata[t_thrd.log_cxt.errordata_stack_depth];
bool need_insert = !SKIP_GS_SOURCE && !IsInitdb && u_sess->plsql_cxt.isCreateFunction;
if (need_insert && edata->sqlerrcode != ERRCODE_LOCK_WAIT_TIMEOUT) {
u_sess->plsql_cxt.isCreateFunction = false;
if (stmt->isProcedure) {
InsertGsSource(InvalidOid, nspid, funcname, "procedure", false);
} else {
InsertGsSource(InvalidOid, nspid, funcname, "function", false);
}
}
if (need_insert && edata->sqlerrcode == ERRCODE_LOCK_WAIT_TIMEOUT) {
ereport(WARNING,
(errcode(ERRCODE_LOCK_WAIT_TIMEOUT),
errmsg("gs_source insert fail, it can not hold lock"),
errdetail("Lock wait timeout.")));
}
#endif
if (u_sess->plsql_cxt.debug_query_string) {
pfree_ext(u_sess->plsql_cxt.debug_query_string);
}
PG_RE_THROW();
}
PG_END_TRY();
#ifdef PGXC
Oid group_oid;
bool multi_group = false;
ExecNodes* exec_nodes = NULL;
const char* query_str = NULL;
if (IS_PGXC_COORDINATOR) {
group_oid = GetFunctionNodeGroup((CreateFunctionStmt*)parse_tree, &multi_group);
if (multi_group) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Does not support FUNCTION with multiple nodegroup table type in logic cluster."),
errdetail("The feature is not currently supported")));
}
query_str = GetCreateFuncStringInDN((CreateFunctionStmt*)parse_tree, query_string);
if (OidIsValid(group_oid)) {
exec_nodes = GetNodeGroupExecNodes(group_oid);
}
ExecUtilityStmtOnNodes(
query_str, exec_nodes, sent_to_remote, false, CHOOSE_EXEC_NODES(ExecIsTempObjectIncluded()), false);
FreeExecNodes(&exec_nodes);
if (query_str != query_string)
pfree_ext(query_str);
}
#endif
} break;
case T_AlterFunctionStmt: {
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
ExecNodes* exec_nodes = NULL;
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
bool is_temp = IsFunctionTemp((AlterFunctionStmt*)parse_tree);
if (!is_temp) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
}
address = AlterFunction((AlterFunctionStmt*)parse_tree);
Oid group_oid = GetFunctionNodeGroup((AlterFunctionStmt*)parse_tree);
if (OidIsValid(group_oid)) {
exec_nodes = GetNodeGroupExecNodes(group_oid);
}
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, exec_nodes, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
AlterFunction((AlterFunctionStmt*)parse_tree);
Oid group_oid = GetFunctionNodeGroup((AlterFunctionStmt*)parse_tree);
if (OidIsValid(group_oid)) {
exec_nodes = GetNodeGroupExecNodes(group_oid);
}
ExecUtilityStmtOnNodes(query_string,
exec_nodes,
sent_to_remote,
false,
CHOOSE_EXEC_NODES(ExecIsTempObjectIncluded()),
false);
}
FreeExecNodes(&exec_nodes);
} else {
address = AlterFunction((AlterFunctionStmt*)parse_tree);
}
#else
address = AlterFunction((AlterFunctionStmt*)parse_tree);
#endif
} break;
case T_CompileStmt:
{
if (u_sess->SPI_cxt._connected == -1) {
plpgsql_hashtable_clear_invalid_obj(true);
}
u_sess->plsql_cxt.during_compile = true;
u_sess->plsql_cxt.isCreateFunction = true;
if (!enable_plpgsql_gsdependency_guc()) {
u_sess->plsql_cxt.during_compile = false;
ereport(ERROR, (errmsg("This operation is not supported.")));
break;
}
u_sess->plsql_cxt.is_alter_compile_stmt = true;
CompileStmt* tmpStmt = (CompileStmt*)parse_tree;
if (tmpStmt->compileItem == COMPILE_FUNCTION || tmpStmt->compileItem == COMPILE_PROCEDURE) {
RecompileFunction(tmpStmt);
} else {
RecompilePackage(tmpStmt);
}
u_sess->plsql_cxt.during_compile = false;
u_sess->plsql_cxt.is_alter_compile_stmt = false;
} break;
case T_IndexStmt:
{
IndexStmt* stmt = (IndexStmt*)parse_tree;
Oid rel_id;
LOCKMODE lockmode;
if (stmt->concurrent) {
PreventTransactionChain(is_top_level, "CREATE INDEX CONCURRENTLY");
}
ForbidOutUsersToSetInnerOptions(stmt->options);
#ifdef PGXC
bool is_temp = false;
ExecNodes* exec_nodes = NULL;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
char* first_exec_node = NULL;
bool is_first_node = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
#ifdef ENABLE_MULTIPLE_NODES
if (stmt->concurrent) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PGXC does not support concurrent INDEX yet"),
errdetail("The feature is not currently supported")));
}
#endif
rel_id = RangeVarGetRelidExtended(stmt->relation, AccessShareLock, true, false, false, true, NULL, NULL);
if (OidIsValid(rel_id)) {
exec_type = ExecUtilityFindNodes(OBJECT_INDEX, rel_id, &is_temp);
UnlockRelationOid(rel_id, AccessShareLock);
} else {
exec_type = EXEC_ON_NONE;
}
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && !stmt->isconstraint) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, stmt->concurrent, EXEC_ON_COORDS, is_temp, first_exec_node);
}
}
#endif
#ifdef DOLPHIN
if (OidIsValid(rel_id)) {
TransformIndexName(stmt, GetNamespaceIdbyRelId(rel_id), stmt->relation->relname);
}
#endif
* Look up the relation OID just once, right here at the
* beginning, so that we don't end up repeating the name
* lookup later and latching onto a different relation
* partway through. To avoid lock upgrade hazards, it's
* important that we take the strongest lock that will
* eventually be needed here, so the lockmode calculation
* needs to match what DefineIndex() does.
*/
lockmode = stmt->concurrent ? ShareUpdateExclusiveLock : ShareLock;
rel_id = RangeVarGetRelidExtended(
stmt->relation, lockmode, false, false, false, true, RangeVarCallbackOwnsRelation, NULL);
stmt = transformIndexStmt(rel_id, stmt, query_string);
#ifdef PGXC
* If it's a view, skip it. */
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && get_rel_relkind(rel_id) == RELKIND_RELATION) {
int nmembers = 0;
Oid group_oid = InvalidOid;
Oid* members = NULL;
exec_nodes = makeNode(ExecNodes);
char in_redistribution = 'n';
AssertEreport(rel_id != InvalidOid, MOD_EXECUTOR, "relation OID is invalid");
group_oid = get_pgxc_class_groupoid(rel_id);
AssertEreport(group_oid != InvalidOid, MOD_EXECUTOR, "group OID is invalid");
in_redistribution = get_pgxc_group_redistributionstatus(group_oid);
char* group_name = get_pgxc_groupname(group_oid);
if (need_full_dn_execution(group_name)) {
exec_nodes->nodeList = GetAllDataNodes();
} else {
nmembers = get_pgxc_groupmembers(group_oid, &members);
exec_nodes->nodeList = GetNodeGroupNodeList(members, nmembers);
pfree_ext(members);
}
}
#endif
pgstat_set_io_state(IOSTATE_WRITE);
WaitState oldStatus = pgstat_report_waitstatus(STATE_CREATE_INDEX);
#ifdef ENABLE_MULTIPLE_NODES
* timeseries index create should be in allowSystemTableMods for
* the index should be create in cstore namespace.
*/
bool origin_sysTblMode = g_instance.attr.attr_common.allowSystemTableMods;
if (ts_idx_create && !origin_sysTblMode) {
g_instance.attr.attr_common.allowSystemTableMods = true;
}
#endif
EventTriggerAlterTableStart(parse_tree);
#ifdef ENABLE_MULTIPLE_NODES
address = DefineIndex(rel_id,
stmt,
InvalidOid,
false,
true,
!u_sess->upg_cxt.new_catalog_need_storage,
false);
#else
address = DefineIndex(rel_id,
stmt,
InvalidOid,
false,
true,
!u_sess->upg_cxt.new_catalog_need_storage,
false);
if (RelationIsCUFormatByOid(rel_id) && (stmt->primary || stmt->unique)) {
DefineDeltaUniqueIndex(rel_id, stmt, address.objectId);
}
#endif
* Add the CREATE INDEX node itself to stash right away; if
* there were any commands stashed in the ALTER TABLE code,
* we need them to appear after this one.
*/
EventTriggerCollectSimpleCommand(address, secondaryObject,
parse_tree);
commandCollected = true;
EventTriggerAlterTableEnd();
pgstat_report_waitstatus(oldStatus);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !stmt->isconstraint && !IsConnFromCoord()) {
query_string = ConstructMesageWithMemInfo(query_string, stmt->memUsage);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)) {
ExecUtilityStmtOnNodes(
query_string, exec_nodes, sent_to_remote, stmt->concurrent, EXEC_ON_DATANODES, is_temp);
} else {
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, stmt->concurrent, exec_type, is_temp);
}
}
FreeExecNodes(&exec_nodes);
#endif
} break;
case T_RuleStmt:
{
#ifdef ENABLE_MULTIPLE_NODES
bool isredis_rule = false;
isredis_rule = is_redis_rule((RuleStmt*)parse_tree);
if (!IsInitdb && !u_sess->attr.attr_sql.enable_cluster_resize && !u_sess->exec_cxt.extension_is_valid
&& !IsConnFromCoord() && !isredis_rule)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("RULE is not yet supported.")));
#endif
address = DefineRule((RuleStmt*)parse_tree, query_string);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
RemoteQueryExecType exec_type;
bool is_temp = false;
exec_type = get_nodes_4_rules_utility(((RuleStmt*)parse_tree)->relation, &is_temp);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, exec_type, is_temp);
}
#endif
} break;
case T_CreateSeqStmt: {
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
CreateSeqStmt* stmt = (CreateSeqStmt*)parse_tree;
ExecNodes* exec_nodes = NULL;
char* query_stringWithUUID = gen_hybirdmsg_for_CreateSeqStmt(stmt, query_string);
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!stmt->is_serial) {
bool is_temp = stmt->sequence->relpersistence == RELPERSISTENCE_TEMP;
if (!is_temp) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_stringWithUUID, NULL, sent_to_remote, false, EXEC_ON_COORDS, is_temp, first_exec_node);
}
}
}
address = DefineSequenceWrapper((CreateSeqStmt*)parse_tree);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!stmt->is_serial) {
bool is_temp = stmt->sequence->relpersistence == RELPERSISTENCE_TEMP;
exec_nodes = GetOwnedByNodes((Node*)stmt);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_stringWithUUID,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node);
}
} else {
if (!stmt->is_serial) {
bool is_temp = stmt->sequence->relpersistence == RELPERSISTENCE_TEMP;
exec_nodes = GetOwnedByNodes((Node*)stmt);
ExecUtilityStmtOnNodes(
query_stringWithUUID, exec_nodes, sent_to_remote, false, CHOOSE_EXEC_NODES(is_temp), is_temp);
}
}
#ifdef ENABLE_MULTIPLE_NODES
if (IS_MAIN_COORDINATOR && exec_nodes != NULL &&
exec_nodes->nodeList->length < u_sess->pgxc_cxt.NumDataNodes) {
char* msg = deparse_create_sequence((Node*)parse_tree, true);
exec_remote_query_4_seq(exec_nodes, msg, stmt->uuid);
pfree_ext(msg);
}
#endif
pfree_ext(query_stringWithUUID);
FreeExecNodes(&exec_nodes);
} else {
address = DefineSequenceWrapper((CreateSeqStmt*)parse_tree);
}
#else
address = DefineSequenceWrapper((CreateSeqStmt*)parse_tree);
#endif
ClearCreateSeqStmtUUID((CreateSeqStmt*)parse_tree);
} break;
case T_AlterSeqStmt:
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
AlterSeqStmt* stmt = (AlterSeqStmt*)parse_tree;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
ExecNodes* exec_nodes = NULL;
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (!stmt->is_serial) {
bool is_temp = false;
RemoteQueryExecType exec_type;
Oid rel_id = RangeVarGetRelid(stmt->sequence, NoLock, stmt->missing_ok);
if (!OidIsValid(rel_id))
break;
exec_type = ExecUtilityFindNodes(OBJECT_SEQUENCE, rel_id, &is_temp);
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, is_temp, first_exec_node);
}
}
address = AlterSequenceWrapper((AlterSeqStmt*)parse_tree);
#ifdef ENABLE_MULTIPLE_NODES
if (IS_MAIN_COORDINATOR && !stmt->is_serial) {
bool is_temp = false;
RemoteQueryExecType exec_type;
Oid rel_id = RangeVarGetRelid(stmt->sequence, NoLock, true);
if (!OidIsValid(rel_id))
break;
exec_type = ExecUtilityFindNodes(OBJECT_SEQUENCE, rel_id, &is_temp);
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES) {
exec_nodes = GetOwnedByNodes((Node*)stmt);
alter_sequence_all_nodes(stmt, exec_nodes);
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node);
}
}
#endif
} else {
address = AlterSequenceWrapper((AlterSeqStmt*)parse_tree);
#ifdef ENABLE_MULTIPLE_NODES
if (IS_MAIN_COORDINATOR && !stmt->is_serial) {
bool is_temp = false;
RemoteQueryExecType exec_type;
Oid rel_id = RangeVarGetRelid(stmt->sequence, NoLock, true);
if (!OidIsValid(rel_id))
break;
exec_type = ExecUtilityFindNodes(OBJECT_SEQUENCE, rel_id, &is_temp);
exec_nodes = GetOwnedByNodes((Node*)stmt);
alter_sequence_all_nodes(stmt, exec_nodes);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, false, exec_type, is_temp);
}
#endif
}
FreeExecNodes(&exec_nodes);
} else {
address = AlterSequenceWrapper((AlterSeqStmt*)parse_tree);
}
#else
address = AlterSequenceWrapper((AlterSeqStmt*)parse_tree);
#endif
break;
case T_CreateTableAsStmt: {
CreateTableAsStmt *stmt = (CreateTableAsStmt*)parse_tree;
if (stmt->into->ivm) {
address = ExecCreateMatViewInc((CreateTableAsStmt*)parse_tree, query_string, params);
} else {
address = ExecCreateTableAs((CreateTableAsStmt*)parse_tree, query_string, params, completion_tag);
}
} break;
case T_SecLabelStmt: {
#ifdef PGXC
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("SECURITY LABEL is not yet supported.")));
#endif
address = ExecSecLabelStmt((SecLabelStmt*)parse_tree);
} break;
case T_RefreshMatViewStmt: {
RefreshMatViewStmt *stmt = (RefreshMatViewStmt *)parse_tree;
#ifdef ENABLE_MULTIPLE_NODES
Query *query = NULL;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
Relation matview = NULL;
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_COORDS,
false,
first_exec_node);
matview = heap_openrv(stmt->relation, ExclusiveLock);
query = get_matview_query(matview);
acquire_mativew_tables_lock(query, stmt->incremental);
} else {
matview = heap_openrv(stmt->relation, ExclusiveLock);
query = get_matview_query(matview);
acquire_mativew_tables_lock(query, stmt->incremental);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false);
}
CheckRefreshMatview(matview, is_incremental_matview(matview->rd_id));
ExecNodes *exec_nodes = getRelationExecNodes(matview->rd_id);
ExecUtilityStmtOnNodes(query_string,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
false);
heap_close(matview, NoLock);
} else if (IS_PGXC_COORDINATOR) {
Relation matview = heap_openrv(stmt->relation, ExclusiveLock);
query = get_matview_query(matview);
acquire_mativew_tables_lock(query, stmt->incremental);
heap_close(matview, NoLock);
}
if (IS_PGXC_DATANODE)
#else
Relation matview = HeapOpenrvExtended(stmt->relation,
stmt->incremental ? ExclusiveLock : AccessExclusiveLock,
false, true);
if (!is_incremental_matview(matview->rd_id) &&
!ValidateDependView(matview->rd_id, OBJECT_TYPE_MATVIEW)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("The materialized view %s is invalid, please make it valid before operation.",
RelationGetRelationName(matview)),
errhint("Please re-add missing table fields.")));
}
CheckRefreshMatview(matview, is_incremental_matview(matview->rd_id));
heap_close(matview, NoLock);
#endif
* REFRSH CONCURRENTLY executes some DDL commands internally.
* Inhibit DDL command collection here to avoid those commands
* from showing up in the deparsed command queue. The refresh
* command itself is queued, which is enough.
*/
EventTriggerInhibitCommandCollection();
PG_TRY();
{
if (stmt->incremental) {
address = ExecRefreshMatViewInc(stmt, query_string, params, completion_tag);
}
if (!stmt->incremental) {
address = ExecRefreshMatView(stmt, query_string, params, completion_tag);
}
}
PG_CATCH();
{
EventTriggerUndoInhibitCommandCollection();
PG_RE_THROW();
}
PG_END_TRY();
EventTriggerUndoInhibitCommandCollection();
} break;
case T_CreateMatViewLogStmt:
address = CreateMatViewLog((CreateMatViewLogStmt*)parse_tree);
break;
case T_DropMatViewLogStmt:
DropMatViewLog((DropMatViewLogStmt*)parse_tree);
break;
case T_CreateTrigStmt:
#ifdef DOLPHIN
{
CreateTrigStmt* createTrigStmt = (CreateTrigStmt*)parse_tree;
if (pg_strncasecmp(createTrigStmt->trigname, "user", strlen(createTrigStmt->trigname)) == 0) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s cannot be trigger name",
createTrigStmt->trigname)));
}
#endif
address = CreateTrigger(
(CreateTrigStmt*)parse_tree, query_string, InvalidOid, InvalidOid, InvalidOid, InvalidOid, false);
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
CreateTrigStmt* stmt = (CreateTrigStmt*)parse_tree;
RemoteQueryExecType exec_type;
bool is_temp = false;
Oid rel_id = RangeVarGetRelidExtended(stmt->relation, NoLock, false, false, false, true, NULL, NULL);
exec_type = ExecUtilityFindNodes(OBJECT_TABLE, rel_id, &is_temp);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, exec_type, is_temp, (Node*)stmt);
}
#endif
break;
#ifdef DOLPHIN
}
#endif
case T_CreatePLangStmt:
if (!IsInitdb && strncmp(((CreatePLangStmt*)parse_tree)->plname, "plpython", strlen("plpython")) != 0 &&
strncmp(((CreatePLangStmt*)parse_tree)->plname, "pltsql", strlen("pltsql")) != 0)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("new language is not yet supported.")));
address = CreateProceduralLanguage((CreatePLangStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
* ******************************** DOMAIN statements ****
*/
case T_CreateDomainStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (!IsInitdb && !u_sess->attr.attr_common.IsInplaceUpgrade)
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("domain is not yet supported.")));
#endif
address = DefineDomain((CreateDomainStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreateConversionStmt:
#ifdef PGXC
if (!IsInitdb)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user defined conversion is not yet supported.")));
#endif
address = CreateConversionCommand((CreateConversionStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreateCastStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (!IsInitdb && !u_sess->exec_cxt.extension_is_valid && !u_sess->attr.attr_common.IsInplaceUpgrade)
ereport(
ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user defined cast is not yet supported.")));
#endif
address = CreateCast((CreateCastStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreateOpClassStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !u_sess->exec_cxt.extension_is_valid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("user defined operator is not yet supported.")));
#endif
address = DefineOpClass((CreateOpClassStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
commandCollected = true;
break;
case T_CreateOpFamilyStmt:
address = DefineOpFamily((CreateOpFamilyStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterOpFamilyStmt:
AlterOpFamily((AlterOpFamilyStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
commandCollected = true;
break;
case T_AlterTSDictionaryStmt:
ts_check_feature_disable();
address = AlterTSDictionary((AlterTSDictionaryStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_AlterTSConfigurationStmt:
ts_check_feature_disable();
address = AlterTSConfiguration((AlterTSConfigurationStmt*)parse_tree);
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
* Commands are stashed in MakeConfigurationMapping and
* DropConfigurationMapping, which are called from
* AlterTSConfiguration
*/
commandCollected = true;
break;
case T_DropStmt:
{
ExecDropStmt((DropStmt *) parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
commandCollected = true;
}
break;
case T_RenameStmt:
{
address = doRenameStmt((RenameStmt *) parse_tree, query_string,
#ifdef PGXC
sent_to_remote,
#endif
is_top_level);
} break;
case T_AlterObjectSchemaStmt: {
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parse_tree;
RemoteQueryExecType exec_type;
bool is_temp = false;
CheckObjectInBlackList(stmt->objectType, query_string);
if (stmt->relation) {
* When a relation is defined, it is possible that this object does
* not exist but an IF EXISTS clause might be used. So we do not do
* any error check here but block the access to remote nodes to
* this object as it does not exisy
*/
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
Relation rel = relation_open(rel_id, NoLock);
if ((RelationGetInternalMask(rel) & INTERNAL_MASK_DALTER))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("internal relation doesn't allow ALTER")));
if (rel->rd_rel->relkind == RELKIND_RELATION && RelationIsPAXFormat(rel)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("DFS table doesn't allow ALTER TABLE SET SCHEMA")));
}
relation_close(rel, NoLock);
UnlockRelationOid(rel_id, AccessShareLock);
exec_type = ExecUtilityFindNodes(stmt->objectType, rel_id, &is_temp);
} else
exec_type = EXEC_ON_NONE;
} else {
exec_type = ExecUtilityFindNodes(stmt->objectType, InvalidOid, &is_temp);
}
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_COORDS,
is_temp,
first_exec_node,
(Node*)parse_tree);
}
}
ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt*)parse_tree, NULL);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node,
(Node*)parse_tree);
}
} else {
ExecUtilityStmtOnNodes(
query_string, NULL, sent_to_remote, false, exec_type, is_temp, (Node*)parse_tree);
}
} else {
if (IS_SINGLE_NODE) {
CheckObjectInBlackList(((AlterObjectSchemaStmt*)parse_tree)->objectType, query_string);
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parse_tree;
if (stmt->relation) {
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
Relation rel = relation_open(rel_id, NoLock);
if ((RelationGetInternalMask(rel) & INTERNAL_MASK_DALTER))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("internal relation doesn't allow ALTER")));
relation_close(rel, NoLock);
UnlockRelationOid(rel_id, AccessShareLock);
}
}
}
address = ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt*)parse_tree, NULL);
}
#else
AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parse_tree;
address = ExecAlterObjectSchemaStmt(stmt, NULL),
#endif
} break;
case T_AlterOwnerStmt: {
CheckObjectInBlackList(((AlterOwnerStmt*)parse_tree)->objectType, query_string);
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
ExecNodes* exec_nodes = NULL;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
AlterOwnerStmt* OwnerStmt = (AlterOwnerStmt*)parse_tree;
if (OwnerStmt->objectType == OBJECT_FUNCTION) {
Oid funcid = LookupFuncNameTypeNames(OwnerStmt->object, OwnerStmt->objarg, false);
exec_nodes = GetFunctionNodes(funcid);
}
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, exec_nodes, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
address = ExecAlterOwnerStmt((AlterOwnerStmt*)parse_tree);
}
#else
AlterOwnerStmt *stmt = (AlterOwnerStmt *) parse_tree;
ExecAlterOwnerStmt(stmt);
#endif
} break;
case T_DropOwnedStmt: {
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DropOwnedObjects((DropOwnedStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DropOwnedObjects((DropOwnedStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
DropOwnedObjects((DropOwnedStmt*)parse_tree);
}
#else
DropOwnedObjects((DropOwnedStmt *) parse_tree);
#endif
commandCollected = true;
} break;
case T_AlterDefaultPrivilegesStmt: {
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt*)parse_tree);
}
#else
ExecAlterDefaultPrivilegesStmt((AlterDefaultPrivilegesStmt*)parse_tree);
#endif
EventTriggerCollectAlterDefPrivs((AlterDefaultPrivilegesStmt *) parse_tree);
commandCollected = true;
} break;
case T_DefineStmt: {
DefineStmt* stmt = (DefineStmt*)parse_tree;
switch (stmt->kind) {
case OBJECT_AGGREGATE:
#ifdef ENABLE_MULTIPLE_NODES
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !u_sess->exec_cxt.extension_is_valid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user defined aggregate is not yet supported.")));
#endif
address = DefineAggregate(stmt->defnames, stmt->args, stmt->oldstyle, stmt->definition);
break;
case OBJECT_OPERATOR:
#ifdef ENABLE_MULTIPLE_NODES
if (!u_sess->attr.attr_common.IsInplaceUpgrade && !u_sess->exec_cxt.extension_is_valid)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user defined operator is not yet supported.")));
#endif
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineOperator(stmt->defnames, stmt->definition);
break;
case OBJECT_TYPE:
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineType(stmt->defnames, stmt->definition);
break;
case OBJECT_TSPARSER:
#ifdef PGXC
if (!IsInitdb && !u_sess->attr.attr_common.IsInplaceUpgrade
&& !u_sess->exec_cxt.extension_is_valid) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user-defined text search parser is not yet supported.")));
}
#endif
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineTSParser(stmt->defnames, stmt->definition);
break;
case OBJECT_TSDICTIONARY:
ts_check_feature_disable();
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineTSDictionary(stmt->defnames, stmt->definition);
break;
case OBJECT_TSTEMPLATE:
#ifdef PGXC
* An erroneous text search template definition could confuse or
* even crash the server, so we just forbid user to create a user
* defined text search template definition
*/
if (!IsInitdb) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user-defined text search template is not yet supported.")));
}
#endif
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineTSTemplate(stmt->defnames, stmt->definition);
break;
case OBJECT_TSCONFIGURATION:
ts_check_feature_disable();
address = DefineTSConfiguration(stmt->defnames, stmt->definition, stmt->args);
break;
case OBJECT_COLLATION:
#ifdef PGXC
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("user defined collation is not yet supported.")));
#endif
AssertEreport(stmt->args == NIL, MOD_EXECUTOR, "stmt args is NULL");
address = DefineCollation(stmt->defnames, stmt->definition);
break;
default: {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized define stmt type: %d", (int)stmt->kind)));
} break;
}
}
#ifdef PGXC
if (IS_PGXC_COORDINATOR)
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
#endif
break;
case T_CreatePublicationStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support PUBLICATION yet"),
errdetail("The feature is not currently supported")));
#endif
address = CreatePublication((CreatePublicationStmt *) parse_tree);
break;
case T_AlterPublicationStmt:
AlterPublication((AlterPublicationStmt *) parse_tree);
* AlterPublication calls EventTriggerCollectSimpleCommand
* directly
*/
commandCollected = true;
break;
case T_CreateSubscriptionStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support SUBSCRIPTION yet"),
errdetail("The feature is not currently supported")));
#endif
address = CreateSubscription((CreateSubscriptionStmt *) parse_tree, is_top_level);
break;
case T_AlterSubscriptionStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support SUBSCRIPTION yet"),
errdetail("The feature is not currently supported")));
#endif
address = AlterSubscription((AlterSubscriptionStmt *) parse_tree,is_top_level);
break;
case T_DropSubscriptionStmt:
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("openGauss does not support SUBSCRIPTION yet"),
errdetail("The feature is not currently supported")));
#endif
DropSubscription((DropSubscriptionStmt *) parse_tree, is_top_level);
commandCollected = true;
break;
case T_CreateEventStmt:
address = CreateEventCommand((CreateEventStmt*)parse_tree);
break;
case T_AlterEventStmt:
address = AlterEventCommand((AlterEventStmt*)parse_tree);
break;
case T_DropEventStmt:
DropEventCommand((DropEventStmt*)parse_tree);
break;
case T_TableOfTypeStmt:
{
TableOfTypeStmt* stmt = (TableOfTypeStmt*)parse_tree;
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DefineTableOfType(stmt);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DefineTableOfType(stmt);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
address = DefineTableOfType(stmt);
}
} break;
case T_CreateEnumStmt:
{
#ifdef PGXC
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
DefineEnum((CreateEnumStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
DefineEnum((CreateEnumStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else
#endif
{
address = DefineEnum((CreateEnumStmt*)parse_tree);
}
} break;
case T_GrantDbStmt:
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR) {
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_COORDS, false, first_exec_node);
ExecuteGrantDbStmt((GrantDbStmt*)parse_tree);
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, false, first_exec_node);
} else {
ExecuteGrantDbStmt((GrantDbStmt*)parse_tree);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, false);
}
} else {
ExecuteGrantDbStmt((GrantDbStmt*)parse_tree);
}
#else
ExecuteGrantDbStmt((GrantDbStmt*)parse_tree);
#endif
break;
case T_CreateAmStmt:
address = CreateAccessMethod((CreateAmStmt *) parse_tree);
break;
default:
elog(ERROR, "unrecognized node type: %d",
(int) nodeTag(parse_tree));
break;
}
* Remember the object so that ddl_command_end event triggers have
* access to it.
*/
if (!commandCollected)
EventTriggerCollectSimpleCommand(address, secondaryObject,
parse_tree);
if (isCompleteQuery)
{
EventTriggerSQLDrop(parse_tree);
EventTriggerDDLCommandEnd(parse_tree);
}
}
PG_CATCH();
{
if (needCleanup)
EventTriggerEndCompleteQuery();
PG_RE_THROW();
}
PG_END_TRY();
if (needCleanup)
EventTriggerEndCompleteQuery();
}
* Dispatch function for DropStmt
*/
static void
ExecDropStmt(DropStmt *parse_tree, const char *query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level)
{
CheckObjectInBlackList(parse_tree->removeType, query_string);
* performMultipleDeletions() needs to know is_top_level by
* "DfsDDLIsTopLevelXact" to prevent "drop hdfs table"
* running inside a transaction block.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord())
u_sess->exec_cxt.DfsDDLIsTopLevelXact = is_top_level;
switch (parse_tree->removeType) {
case OBJECT_INDEX:
#ifdef ENABLE_MULTIPLE_NODES
if (parse_tree->concurrent) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PGXC does not support concurrent INDEX yet"),
errdetail("The feature is not currently supported")));
}
#endif
if (parse_tree->concurrent)
PreventTransactionChain(is_top_level, "DROP INDEX CONCURRENTLY");
case OBJECT_FOREIGN_TABLE:
case OBJECT_STREAM:
case OBJECT_MATVIEW:
case OBJECT_TABLE: {
#ifdef PGXC
* For table batch-dropping, we we only support to drop tables
* belonging same nodegroup.
*
* Note: we only have to such kind of check at CN node
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
DropStmt* ds = (DropStmt*)parse_tree;
if (!ObjectsInSameNodeGroup(ds->objects, T_DropStmt)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("NOT-SUPPORT: Not support DROP multiple objects different nodegroup")));
}
}
* Need to let ProcSleep know if we could cancel redistribution transaction which
* locks the table we want to drop. ProcSleep will make sure we only cancel the
* transaction doing redistribution.
*
* need to refactor this part into a common function where all supported cancel-redistribution
* DDL statements sets it
*/
if (IS_PGXC_COORDINATOR && parse_tree->removeType == OBJECT_TABLE) {
u_sess->exec_cxt.could_cancel_redistribution = true;
}
#endif
}
case OBJECT_SEQUENCE:
case OBJECT_LARGE_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
case OBJECT_LARGE_SEQUENCE_GSC:
case OBJECT_VIEW:
case OBJECT_CONTQUERY:
#ifdef PGXC
{
if (parse_tree->removeType == OBJECT_FOREIGN_TABLE ||
parse_tree->removeType == OBJECT_STREAM) {
* In the security mode, the useft privilege of a user must be
* checked before the user creates a foreign table.
*/
if (isSecurityMode && !have_useft_privilege()) {
ereport(ERROR,
(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
errmsg("permission denied to drop foreign table in security mode")));
}
}
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
ObjectAddresses* new_objects = NULL;
* For DROP TABLE/INDEX/VIEW/... IF EXISTS query, only notice is emitted
* if the referred objects are not found. In such case, the atomicity and consistency
* of the query or transaction among local CN and remote nodes can not be guaranteed
* against concurrent CREATE TABLE/INDEX/VIEW/... query.
*
* To ensure such atomicity and consistency, we only refer to local CN about
* the visibility of the objects to be deleted and rewrite the query into tmp_query_string
* without the inivisible objects. Later, if the objects in tmp_query_string are not
* found on remote nodes, which should not happen, just ERROR.
*/
StringInfo tmp_query_string = makeStringInfo();
drop_stmt_pre_treatment((DropStmt*)parse_tree, query_string, sent_to_remote, &is_temp, &exec_type);
char* first_exec_node = NULL;
bool is_first_node = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && u_sess->attr.attr_sql.enable_parallel_ddl) {
if (!is_first_node) {
new_objects = PreCheckforRemoveRelation((DropStmt*)parse_tree, tmp_query_string, &exec_type);
}
}
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = tmp_query_string->data[0] ? tmp_query_string->data : (char*)query_string;
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = false;
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
}
* @NodeGroup Support
*
* Scan for first object from drop-list in DropStmt to find target DNs,
* here for TO-GROUP aware objects, we need pass DropStmt handler into
* ExecUtilityStmtOnNodes() to further evaluate which DNs wend utility.
*/
ExecNodes* exec_nodes = NULL;
Node* reparse = NULL;
ObjectType object_type = ((DropStmt*)parse_tree)->removeType;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(object_type == OBJECT_TABLE || object_type == OBJECT_INDEX ||
object_type == OBJECT_MATVIEW)) {
reparse = (Node*)parse_tree;
ListCell* lc = list_head(((DropStmt*)parse_tree)->objects);
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(lc));
Oid rel_id;
LOCKMODE lockmode = NoLock;
if (object_type == OBJECT_TABLE || object_type == OBJECT_MATVIEW)
lockmode = AccessExclusiveLock;
rel_id = RangeVarGetRelid(rel, lockmode, ((DropStmt*)parse_tree)->missing_ok);
if (OidIsValid(rel_id)) {
Oid check_id = rel_id;
char relkind = get_rel_relkind(rel_id);
if (relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) {
check_id = IndexGetRelation(rel_id, false);
}
Oid group_oid = get_pgxc_class_groupoid(check_id);
char* group_name = get_pgxc_groupname(group_oid);
* Reminding, when supported user-defined node group expansion,
* we need create ExecNodes from target node group.
*
* Notice!!
* In cluster resizing stage we need special processing logics in dropping table as:
* [1]. create table delete_delta ... to group old_group on all DN
* [2]. display pgxc_group.group_members
* [3]. drop table delete_delta ==> drop delete_delta on all DN
*
* So, as normal, when target node group's status is marked as 'installation' or
* 'redistribution', we have to issue a full-DN drop table request, remeber
* pgxc_class.group_members still reflects table's logic distribution to tell pgxc
* planner to build Scan operator in multi_nodegroup way. The reason we have to so is
* to be compatible with current gs_switch_relfilenode() invokation in cluster expand
* and shrunk mechanism.
*/
if (need_full_dn_execution(group_name)) {
exec_nodes = makeNode(ExecNodes);
exec_nodes->nodeList = GetAllDataNodes();
} else {
exec_nodes = RelidGetExecNodes(rel_id);
}
} else {
exec_nodes = RelidGetExecNodes(rel_id);
}
} else if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(object_type == OBJECT_FOREIGN_TABLE ||
object_type == OBJECT_STREAM) &&
in_logic_cluster()) {
ListCell* lc = list_head(((DropStmt*)parse_tree)->objects);
RangeVar* relvar = makeRangeVarFromNameList((List*)lfirst(lc));
Oid rel_id = RangeVarGetRelid(relvar, NoLock, true);
if (OidIsValid(rel_id))
exec_nodes = RelidGetExecNodes(rel_id);
else if (!((DropStmt*)parse_tree)->missing_ok) {
if (relvar->schemaname)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("foreign table \"%s.%s\" does not exist",
relvar->schemaname,
relvar->relname)));
else
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_TABLE),
errmsg("foreign table \"%s\" does not exist", relvar->relname)));
}
}
#ifdef ENABLE_MULTIPLE_NODES
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
drop_sequence_4_node_group((DropStmt*)parse_tree, exec_nodes);
}
#endif
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && u_sess->attr.attr_sql.enable_parallel_ddl) {
if (!is_first_node && new_objects)
RemoveRelationsonMainExecCN((DropStmt*)parse_tree, new_objects);
else
RemoveRelations((DropStmt*)parse_tree, tmp_query_string, &exec_type);
} else
RemoveRelations((DropStmt*)parse_tree, tmp_query_string, &exec_type);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(
tmp_query_string->data[0] ? tmp_query_string->data : query_string,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node,
reparse);
} else {
ExecUtilityStmtOnNodes(tmp_query_string->data[0] ? tmp_query_string->data : query_string,
exec_nodes,
sent_to_remote,
false,
exec_type,
is_temp,
reparse);
}
}
pfree_ext(tmp_query_string->data);
pfree_ext(tmp_query_string);
FreeExecNodes(&exec_nodes);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && new_objects != NULL)
free_object_addresses(new_objects);
#endif
} break;
case OBJECT_SCHEMA:
case OBJECT_FUNCTION: {
#ifdef PGXC
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
ObjectAddresses* new_objects = NULL;
StringInfo tmp_query_string = makeStringInfo();
drop_stmt_pre_treatment((DropStmt*)parse_tree, query_string, sent_to_remote, &is_temp, &exec_type);
char* first_exec_node = NULL;
bool is_first_node = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && u_sess->attr.attr_sql.enable_parallel_ddl) {
new_objects =
PreCheckforRemoveObjects((DropStmt*)parse_tree, tmp_query_string, &exec_type, is_first_node);
}
* @NodeGroup Support
*
* Scan for first object from drop-list in DropStmt to find target DNs.
*/
ExecNodes* exec_nodes = NULL;
ObjectType object_type = ((DropStmt*)parse_tree)->removeType;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && object_type == OBJECT_FUNCTION &&
in_logic_cluster()) {
if (!DropObjectsInSameNodeGroup((DropStmt*)parse_tree)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("NOT-SUPPORT: Not support DROP multiple functions in different nodegroup")));
}
exec_nodes = GetDropFunctionNodes((DropStmt*)parse_tree);
}
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = tmp_query_string->data[0] ? tmp_query_string->data : (char*)query_string;
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = false;
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && u_sess->attr.attr_sql.enable_parallel_ddl && new_objects) {
RemoveObjectsonMainExecCN((DropStmt*)parse_tree, new_objects, is_first_node);
} else {
if (IS_SINGLE_NODE) {
RemoveObjects((DropStmt*)parse_tree, true);
} else {
if (u_sess->attr.attr_sql.enable_parallel_ddl)
RemoveObjects((DropStmt*)parse_tree, false);
else
RemoveObjects((DropStmt*)parse_tree, true);
}
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(
tmp_query_string->data[0] ? tmp_query_string->data : query_string,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node);
} else {
ExecUtilityStmtOnNodes(tmp_query_string->data[0] ? tmp_query_string->data : query_string,
exec_nodes,
sent_to_remote,
false,
exec_type,
is_temp);
}
}
pfree_ext(tmp_query_string->data);
pfree_ext(tmp_query_string);
FreeExecNodes(&exec_nodes);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && new_objects != NULL)
free_object_addresses(new_objects);
#endif
} break;
case OBJECT_PACKAGE:
case OBJECT_PACKAGE_BODY: {
#ifdef ENABLE_MULTIPLE_NODES
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("NOT-SUPPORT: Not support DROP PACKAGE in distributed database")));
#endif
RemoveObjects((DropStmt*)parse_tree, true);
} break;
case OBJECT_GLOBAL_SETTING: {
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
drop_stmt_pre_treatment((DropStmt *) parse_tree, query_string, sent_to_remote,
&is_temp, &exec_type);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
char *FirstExecNode = NULL;
bool isFirstNode = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
FirstExecNode = find_first_exec_cn();
isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
RemoteQuery *step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char *) query_string;
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = false;
ExecRemoteUtility_ParallelDDLMode(step, FirstExecNode);
pfree_ext(step);
}
}
(void)drop_global_settings((DropStmt *)parse_tree);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false,
EXEC_ON_DATANODES, is_temp, FirstExecNode);
} else {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, exec_type, is_temp);
}
}
break;
}
case OBJECT_COLUMN_SETTING: {
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
drop_stmt_pre_treatment((DropStmt *) parse_tree, query_string, sent_to_remote,
&is_temp, &exec_type);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
char *FirstExecNode = NULL;
bool isFirstNode = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
FirstExecNode = find_first_exec_cn();
isFirstNode = (strcmp(FirstExecNode, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
RemoteQuery *step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char *) query_string;
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = false;
ExecRemoteUtility_ParallelDDLMode(step, FirstExecNode);
pfree_ext(step);
}
}
(void)drop_column_settings((DropStmt *)parse_tree);
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !isFirstNode) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string, NULL, sent_to_remote, false,
EXEC_ON_DATANODES, is_temp, FirstExecNode);
} else {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, exec_type, is_temp);
}
}
break;
}
default: {
#ifdef PGXC
bool is_temp = false;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
drop_stmt_pre_treatment((DropStmt*)parse_tree, query_string, sent_to_remote, &is_temp, &exec_type);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
char* first_exec_node = NULL;
bool is_first_node = false;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
first_exec_node = find_first_exec_cn();
is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
}
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->sql_statement = (char*)query_string;
step->exec_type = EXEC_ON_COORDS;
step->exec_nodes = NULL;
step->is_temp = false;
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step);
}
}
#endif
RemoveObjects((DropStmt*)parse_tree, true);
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, EXEC_ON_DATANODES, is_temp, first_exec_node);
} else {
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, exec_type, is_temp);
}
}
#endif
} break;
}
}
static void doGrantStmt(GrantStmt*stmt, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level)
{
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
RemoteQueryExecType remoteExecType = EXEC_ON_ALL_NODES;
bool is_temp = false;
ExecNodes* exec_nodes = NULL;
if ((stmt->objtype == ACL_OBJECT_RELATION && stmt->targtype == ACL_TARGET_OBJECT)) {
* In case object is a relation, differenciate the case
* of a sequence, a view and a table
*/
ListCell* cell = NULL;
bool first = true;
RemoteQueryExecType type_local = remoteExecType;
foreach (cell, stmt->objects) {
RangeVar* relvar = (RangeVar*)lfirst(cell);
Oid rel_id = RangeVarGetRelid(relvar, NoLock, true);
if (!OidIsValid(rel_id))
continue;
remoteExecType = exec_utility_find_nodes_relkind(rel_id, &is_temp);
if (first) {
type_local = remoteExecType;
first = false;
} else {
if (type_local != remoteExecType)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("PGXC does not support GRANT on multiple object types"),
errdetail("Grant VIEW/TABLE with separate queries")));
}
}
} else if (stmt->objtype == ACL_OBJECT_NODEGROUP && stmt->targtype == ACL_TARGET_OBJECT) {
remoteExecType = EXEC_ON_COORDS;
}
if (remoteExecType != EXEC_ON_COORDS &&
(stmt->objtype == ACL_OBJECT_RELATION || stmt->objtype == ACL_OBJECT_SEQUENCE ||
stmt->objtype == ACL_OBJECT_FUNCTION)) {
Oid group_oid = GrantStmtGetNodeGroup(stmt);
if (!OidIsValid(group_oid))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("NOT-SUPPORT: Not support Grant/Revoke privileges"
" to objects in different nodegroup")));
exec_nodes = GetNodeGroupExecNodes(group_oid);
}
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (remoteExecType == EXEC_ON_ALL_NODES || remoteExecType == EXEC_ON_COORDS) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_COORDS,
is_temp,
first_exec_node,
(Node*)stmt);
}
}
ExecuteGrantStmt(stmt);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (remoteExecType == EXEC_ON_ALL_NODES || remoteExecType == EXEC_ON_DATANODES) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
exec_nodes,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node,
(Node*)stmt);
}
} else {
ExecUtilityStmtOnNodes(
query_string, exec_nodes, sent_to_remote, false, remoteExecType, is_temp, (Node*)stmt);
}
} else {
ExecuteGrantStmt(stmt);
}
#else
ExecuteGrantStmt(stmt);
#endif
}
static ObjectAddress doRenameStmt(RenameStmt*parse_tree, const char* query_string,
#ifdef PGXC
bool sent_to_remote,
#endif
bool is_top_level)
{
ObjectAddress address;
#ifdef PGXC
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
RenameStmt* stmt = (RenameStmt*)parse_tree;
RemoteQueryExecType exec_type;
bool is_temp = false;
char* first_exec_node = find_first_exec_cn();
bool is_first_node = (strcmp(first_exec_node, g_instance.attr.attr_common.PGXCNodeName) == 0);
CheckObjectInBlackList(stmt->renameType, query_string);
if (stmt->relation) {
* When a relation is defined, it is possible that this object does
* not exist but an IF EXISTS clause might be used. So we do not do
* any error check here but block the access to remote nodes to
* this object as it does not exisy
*/
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
Relation rel = relation_open(rel_id, NoLock);
if ((RelationGetInternalMask(rel) & INTERNAL_MASK_DALTER))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("internal relation doesn't allow ALTER")));
if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("target table is a foreign table")));
if (rel->rd_rel->relkind == RELKIND_STREAM)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("target table is a stream")));
if (RelationIsPAXFormat(rel)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("RENAME operation is not supported for DFS table.")));
}
#ifdef ENABLE_HTAP
if (RelHasImcs(rel_id)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("RENAME operation is not supported for IMCS table. please unimcs first.")));
}
#endif
relation_close(rel, NoLock);
UnlockRelationOid(rel_id, AccessShareLock);
exec_type = ExecUtilityFindNodes(stmt->renameType, rel_id, &is_temp);
} else
exec_type = EXEC_ON_NONE;
} else {
exec_type = ExecUtilityFindNodes(stmt->renameType, InvalidOid, &is_temp);
}
if (stmt->renameType == OBJECT_DATABASE) {
PreCleanAndCheckConns(stmt->subname, stmt->missing_ok);
} else if (stmt->renameType == OBJECT_USER || stmt->renameType == OBJECT_ROLE) {
PreCleanAndCheckUserConns(stmt->subname, stmt->missing_ok);
}
* If I am the main execute CN but not CCN,
* Notify the CCN to create firstly, and then notify other CNs except me.
*/
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node &&
(exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_COORDS)) {
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_COORDS,
is_temp,
first_exec_node,
(Node*)parse_tree);
}
address = ExecRenameStmt((RenameStmt*)parse_tree);
if (u_sess->attr.attr_sql.enable_parallel_ddl && !is_first_node) {
if (exec_type == EXEC_ON_ALL_NODES || exec_type == EXEC_ON_DATANODES)
ExecUtilityStmtOnNodes_ParallelDDLMode(query_string,
NULL,
sent_to_remote,
false,
EXEC_ON_DATANODES,
is_temp,
first_exec_node,
(Node*)parse_tree);
} else {
ExecUtilityStmtOnNodes(
query_string, NULL, sent_to_remote, false, exec_type, is_temp, (Node*)parse_tree);
}
#ifdef ENABLE_MULTIPLE_NODES
UpdatePartPolicyWhenRenameRelation((RenameStmt*)parse_tree);
#endif
} else {
if (IS_SINGLE_NODE) {
CheckObjectInBlackList(((RenameStmt*)parse_tree)->renameType, query_string);
RenameStmt* stmt = (RenameStmt*)parse_tree;
if (stmt->relation) {
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, true);
if (OidIsValid(rel_id)) {
Relation rel = relation_open(rel_id, NoLock);
if ((RelationGetInternalMask(rel) & INTERNAL_MASK_DALTER))
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("internal relation doesn't allow ALTER")));
#ifdef ENABLE_HTAP
if (RelHasImcs(rel_id)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Un-support feature"),
errdetail("RENAME operation is not supported for IMCS table. please unimcs first.")));
}
#endif
relation_close(rel, NoLock);
UnlockRelationOid(rel_id, AccessShareLock);
}
}
}
address = ExecRenameStmt((RenameStmt*)parse_tree);
}
#else
RenameStmt *stmt = (RenameStmt *) parse_tree;
address = ExecRenameStmt(stmt);
#endif
return address;
}
#ifdef PGXC
* is_stmt_allowed_in_locked_mode
*
* Allow/Disallow a utility command while cluster is locked
* A statement will be disallowed if it makes such changes
* in catalog that are backed up by pg_dump except
* CREATE NODE that has to be allowed because
* a new node has to be created while the cluster is still
* locked for backup
*/
static bool is_stmt_allowed_in_locked_mode(Node* parse_tree, const char* query_string)
{
#define ALLOW 1
#define DISALLOW 0
switch (nodeTag(parse_tree)) {
case T_CreateStmt:
{
CreateStmt* stmt = (CreateStmt*)parse_tree;
if (stmt->relation->relpersistence == RELPERSISTENCE_TEMP &&
!u_sess->attr.attr_sql.enable_online_ddl_waitlock)
return ALLOW;
return DISALLOW;
} break;
case T_CreateGroupStmt: {
CreateGroupStmt* stmt = (CreateGroupStmt*)parse_tree;
if ((in_logic_cluster() && stmt->src_group_name) ||
(u_sess->attr.attr_sql.enable_online_ddl_waitlock && superuser()))
return ALLOW;
return DISALLOW;
} break;
case T_ExecuteStmt:
* Prepared statememts can only have
* SELECT, INSERT, UPDATE, DELETE,
* or VALUES statement, there is no
* point stopping EXECUTE.
*/
case T_CreateNodeStmt:
case T_AlterNodeStmt:
* This has to be allowed so that the new node
* can be created, while the cluster is still
* locked for backup
*/
case T_AlterGroupStmt:
case T_AlterCoordinatorStmt:
case T_DropNodeStmt:
* This has to be allowed so that DROP NODE
* can be issued to drop a node that has crashed.
* Otherwise system would try to acquire a shared
* advisory lock on the crashed node.
*/
case T_TransactionStmt:
case T_PlannedStmt:
case T_ClosePortalStmt:
case T_FetchStmt:
case T_CopyStmt:
case T_PrepareStmt:
* Prepared statememts can only have
* SELECT, INSERT, UPDATE, DELETE,
* or VALUES statement, there is no
* point stopping PREPARE.
*/
case T_DeallocateStmt:
* If prepare is allowed the deallocate should
* be allowed also
*/
case T_DoStmt:
case T_NotifyStmt:
case T_ListenStmt:
case T_UnlistenStmt:
case T_LoadStmt:
case T_ClusterStmt:
case T_ExplainStmt:
case T_VariableSetStmt:
case T_VariableMultiSetStmt:
case T_VariableShowStmt:
case T_DiscardStmt:
case T_LockStmt:
case T_ConstraintsSetStmt:
case T_CheckPointStmt:
case T_BarrierStmt:
case T_ReindexStmt:
case T_RemoteQuery:
case T_CleanConnStmt:
case T_CreateFunctionStmt:
case T_CreateEventStmt:
case T_AlterEventStmt:
case T_DropEventStmt:
case T_ShowEventStmt:
case T_GetDiagStmt:
return ALLOW;
default:
return DISALLOW;
}
}
* ExecUtilityWithMessage:
* Execute the query on remote nodes in a transaction block.
* If this fails on one of the nodes :
* Add a context message containing the failed node names.
* Rethrow the error with the message about the failed nodes.
* If all are successful, just return.
*/
static void ExecUtilityWithMessage(const char* query_string, bool sent_to_remote, bool is_temp)
{
PG_TRY();
{
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, false, EXEC_ON_ALL_NODES, is_temp);
}
PG_CATCH();
{
* Some nodes failed. Add context about what all nodes the query
* failed
*/
ExecNodes* coord_success_nodes = NULL;
ExecNodes* data_success_nodes = NULL;
char* msg_failed_nodes = NULL;
pgxc_all_success_nodes(&data_success_nodes, &coord_success_nodes, &msg_failed_nodes);
if (msg_failed_nodes != NULL)
errcontext("%s", msg_failed_nodes);
PG_RE_THROW();
}
PG_END_TRY();
}
static void exec_utility_with_message_parallel_ddl_mode(
const char* query_string, bool sent_to_remote, bool is_temp, const char* first_exec_node, RemoteQueryExecType exec_type)
{
PG_TRY();
{
ExecUtilityStmtOnNodes_ParallelDDLMode(
query_string, NULL, sent_to_remote, false, exec_type, is_temp, first_exec_node);
}
PG_CATCH();
{
* Some nodes failed. Add context about what all nodes the query
* failed
*/
ExecNodes* coord_success_nodes = NULL;
ExecNodes* data_success_nodes = NULL;
char* msg_failed_nodes = NULL;
pgxc_all_success_nodes(&data_success_nodes, &coord_success_nodes, &msg_failed_nodes);
if (msg_failed_nodes != NULL)
errcontext("%s", msg_failed_nodes);
PG_RE_THROW();
}
PG_END_TRY();
}
* Execute a Utility statement on nodes, including Coordinators
* If the DDL is received from a remote Coordinator,
* it is not possible to push down DDL to Datanodes
* as it is taken in charge by the remote Coordinator.
*/
void ExecUtilityStmtOnNodes(const char* query_string, ExecNodes* nodes, bool sent_to_remote, bool force_auto_commit,
RemoteQueryExecType exec_type, bool is_temp, Node* parse_tree)
{
bool need_free_nodes = false;
* @NodeGroup Support
*
* Binding necessary datanodes under exec_nodes to run utility, we put the logic here to
* re-calculate the exec_nodes for target relation in some statements like VACUUM, GRANT,
* as we don't want to put the similar logic in each swith-case branches in standard_ProcessUtility()
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && parse_tree && nodes == NULL && exec_type != EXEC_ON_COORDS) {
nodes = assign_utility_stmt_exec_nodes(parse_tree);
need_free_nodes = true;
}
if (!IsPostmasterEnvironment)
return;
if (u_sess->attr.attr_common.application_name &&
!strncmp(u_sess->attr.attr_common.application_name, "gs_dump", strlen("gs_dump")))
return;
if (exec_type == EXEC_ON_NONE)
return;
if (sent_to_remote)
return;
* If no Datanodes defined and the remote utility sent to DN, the query cannot
* be launched
*/
if ((exec_type == EXEC_ON_DATANODES || exec_type == EXEC_ON_ALL_NODES) && u_sess->pgxc_cxt.NumDataNodes == 0)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("No Datanode defined in cluster"),
errhint("You need to define at least 1 Datanode with "
"CREATE NODE.")));
#ifdef ENABLE_MULTIPLE_NODES
if (!IsConnFromCoord()) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->exec_nodes = nodes;
step->sql_statement = pstrdup(query_string);
step->force_autocommit = force_auto_commit;
step->exec_type = exec_type;
step->is_temp = is_temp;
ExecRemoteUtility(step);
pfree_ext(step->sql_statement);
pfree_ext(step);
if (need_free_nodes)
FreeExecNodes(&nodes);
}
#endif
}
* Execute a Utility statement on nodes, including Coordinators
* If the DDL is received from a remote Coordinator,
* it is not possible to push down DDL to Datanodes
* as it is taken in charge by the remote Coordinator.
*/
void ExecUtilityStmtOnNodes_ParallelDDLMode(const char* query_string, ExecNodes* nodes, bool sent_to_remote,
bool force_auto_commit, RemoteQueryExecType exec_type, bool is_temp, const char* first_exec_node, Node* parse_tree)
{
* @NodeGroup Support
*
* Binding necessary datanodes under exec_nodes to run utility, we put the logic here to
* re-calculate the exec_nodes for target relation in some statements like VACUUM, GRANT,
* as we don't want to put the similar logic in each swith-case branches in standard_ProcessUtility()
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && parse_tree && nodes == NULL && exec_type != EXEC_ON_COORDS) {
nodes = assign_utility_stmt_exec_nodes(parse_tree);
}
if (!IsPostmasterEnvironment)
return;
if (u_sess->attr.attr_common.application_name &&
!strncmp(u_sess->attr.attr_common.application_name, "gs_dump", strlen("gs_dump")))
return;
if (exec_type == EXEC_ON_NONE)
return;
if (sent_to_remote)
return;
* If no Datanodes defined and the remote utility sent to DN, the query cannot
* be launched
*/
if ((exec_type == EXEC_ON_DATANODES || exec_type == EXEC_ON_ALL_NODES) && u_sess->pgxc_cxt.NumDataNodes == 0)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("No Datanode defined in cluster"),
errhint("You need to define at least 1 Datanode with "
"CREATE NODE.")));
if (!IsConnFromCoord()) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->exec_nodes = nodes;
step->sql_statement = pstrdup(query_string);
step->force_autocommit = force_auto_commit;
step->exec_type = exec_type;
step->is_temp = is_temp;
ExecRemoteUtility_ParallelDDLMode(step, first_exec_node);
pfree_ext(step->sql_statement);
pfree_ext(step);
}
}
static RemoteQueryExecType set_exec_type(Oid object_id, bool* is_temp)
{
RemoteQueryExecType type;
if ((*is_temp = IsTempTable(object_id)))
type = EXEC_ON_DATANODES;
else
type = EXEC_ON_ALL_NODES;
return type;
}
* ExecUtilityFindNodes
*
* Determine the list of nodes to launch query on.
* This depends on temporary nature of object and object type.
* Return also a flag indicating if relation is temporary.
*
* If object is a RULE, the object id sent is that of the object to which the
* rule is applicable.
*/
static RemoteQueryExecType ExecUtilityFindNodes(ObjectType object_type, Oid object_id, bool* is_temp)
{
RemoteQueryExecType exec_type;
switch (object_type) {
case OBJECT_SEQUENCE:
case OBJECT_LARGE_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
case OBJECT_LARGE_SEQUENCE_GSC:
exec_type = set_exec_type(object_id, is_temp);
break;
case OBJECT_TABLE:
case OBJECT_TRIGGER:
exec_type = exec_utility_find_nodes_relkind(object_id, is_temp);
break;
* Views and rules, both permanent or temporary are created
* on Coordinators only.
*/
case OBJECT_RULE:
case OBJECT_CONTQUERY:
case OBJECT_VIEW:
if ((*is_temp = IsTempTable(object_id)))
exec_type = EXEC_ON_NONE;
else
exec_type = u_sess->attr.attr_common.IsInplaceUpgrade ? EXEC_ON_ALL_NODES : EXEC_ON_COORDS;
break;
case OBJECT_INDEX:
case OBJECT_CONSTRAINT:
exec_type = set_exec_type(object_id, is_temp);
break;
case OBJECT_COLUMN:
case OBJECT_INTERNAL:
* 1. temp view column, exec on current CN and exec_type is EXEC_ON_NONE;
* 2. temp table column, exec on current CN and DNs, so exec_type is EXEC_ON_DATANODES;
* 3. normal view column, exec on all CNs, so exec_type is EXEC_ON_COORDS;
* 4. normal table column, exec on all CNs and DNs, so exec_type is EXEC_ON_ALL_NODES;
*/
if ((*is_temp = IsTempTable(object_id))) {
if (IsRelaionView(object_id))
exec_type = EXEC_ON_NONE;
else
exec_type = EXEC_ON_DATANODES;
} else {
if (IsRelaionView(object_id))
exec_type = u_sess->attr.attr_common.IsInplaceUpgrade ? EXEC_ON_ALL_NODES : EXEC_ON_COORDS;
else
exec_type = EXEC_ON_ALL_NODES;
}
break;
default:
*is_temp = false;
exec_type = EXEC_ON_ALL_NODES;
break;
}
return exec_type;
}
* exec_utility_find_nodes_relkind
*
* Get node execution and temporary type
* for given relation depending on its relkind
*/
static RemoteQueryExecType exec_utility_find_nodes_relkind(Oid rel_id, bool* is_temp)
{
char relkind_str = get_rel_relkind(rel_id);
RemoteQueryExecType exec_type;
switch (relkind_str) {
case RELKIND_SEQUENCE:
case RELKIND_LARGE_SEQUENCE:
case RELKIND_SEQUENCE_GSC:
case RELKIND_LARGE_SEQUENCE_GSC:
*is_temp = IsTempTable(rel_id);
exec_type = CHOOSE_EXEC_NODES(*is_temp);
break;
case RELKIND_RELATION:
*is_temp = IsTempTable(rel_id);
exec_type = CHOOSE_EXEC_NODES(*is_temp);
break;
case RELKIND_VIEW:
case RELKIND_CONTQUERY:
if ((*is_temp = IsTempTable(rel_id)))
exec_type = EXEC_ON_NONE;
else
exec_type = u_sess->attr.attr_common.IsInplaceUpgrade ? EXEC_ON_ALL_NODES : EXEC_ON_COORDS;
break;
default:
*is_temp = false;
exec_type = EXEC_ON_ALL_NODES;
break;
}
return exec_type;
}
#endif
* If the DDL is received from a remote Coordinator,
* it is not possible to push down DDL to Datanodes
* as it is taken in charge by the remote Coordinator.
*/
HeapTuple* ExecRemoteVacuumStmt(VacuumStmt* stmt, const char* query_string, bool sent_to_remote, ANALYZE_RQTYPE arq_type,
AnalyzeMode e_analyze_mode, Oid rel_id)
{
ExecNodes* nodes = NULL;
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
if (stmt->relation) {
rel_id = RangeVarGetRelid(stmt->relation, NoLock, false);
}
nodes = RelidGetExecNodes(rel_id);
}
HeapTuple* result = NULL;
if (!IsPostmasterEnvironment) {
FreeExecNodes(&nodes);
return NULL;
}
if (sent_to_remote) {
FreeExecNodes(&nodes);
return NULL;
}
if (u_sess->pgxc_cxt.NumDataNodes == 0)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("No Datanode defined in cluster"),
errhint("You need to define at least 1 Datanode with "
"CREATE NODE.")));
if (!IsConnFromCoord()) {
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_SAME;
step->exec_nodes = nodes;
step->sql_statement = pstrdup(query_string);
step->force_autocommit = true;
step->exec_type = EXEC_ON_DATANODES;
step->is_temp = false;
result = RecvRemoteSampleMessage(stmt, step, arq_type, e_analyze_mode);
pfree_ext(step->sql_statement);
pfree_ext(step);
}
FreeExecNodes(&nodes);
return result;
}
* UtilityReturnsTuples
* Return "true" if this utility statement will send output to the
* destination.
*
* Generally, there should be a case here for each case in ProcessUtility
* where "dest" is passed on.
*/
bool UtilityReturnsTuples(Node* parse_tree)
{
switch (nodeTag(parse_tree)) {
case T_FetchStmt: {
FetchStmt* stmt = (FetchStmt*)parse_tree;
Portal portal;
if (stmt->ismove)
return false;
portal = GetPortalByName(stmt->portalname);
if (!PortalIsValid(portal))
return false;
return portal->tupDesc ? true : false;
}
case T_ExecuteStmt: {
ExecuteStmt* stmt = (ExecuteStmt*)parse_tree;
PreparedStatement *entry = NULL;
entry = FetchPreparedStatement(stmt->name, false, false);
if (entry == NULL)
return false;
if (entry->plansource->resultDesc)
return true;
return false;
}
case T_ExplainStmt:
return true;
case T_VariableShowStmt:
case T_ShowEventStmt:
return true;
default:
return false;
}
}
* UtilityTupleDescriptor
* Fetch the actual output tuple descriptor for a utility statement
* for which UtilityReturnsTuples() previously returned "true".
*
* The returned descriptor is created in (or copied into) the current memory
* context.
*/
TupleDesc UtilityTupleDescriptor(Node* parse_tree)
{
switch (nodeTag(parse_tree)) {
case T_FetchStmt: {
FetchStmt* stmt = (FetchStmt*)parse_tree;
Portal portal;
if (stmt->ismove)
return NULL;
portal = GetPortalByName(stmt->portalname);
if (!PortalIsValid(portal))
return NULL;
return CreateTupleDescCopy(portal->tupDesc);
}
case T_ExecuteStmt: {
ExecuteStmt* stmt = (ExecuteStmt*)parse_tree;
PreparedStatement *entry = NULL;
entry = FetchPreparedStatement(stmt->name, false, true);
if (entry == NULL)
return NULL;
return FetchPreparedStatementResultDesc(entry);
}
case T_ExplainStmt:
return ExplainResultDesc((ExplainStmt*)parse_tree);
case T_VariableShowStmt: {
VariableShowStmt* n = (VariableShowStmt*)parse_tree;
return GetPGVariableResultDesc(n->name);
}
case T_ShowEventStmt:
return GetEventResultDesc();
default:
return NULL;
}
}
* QueryReturnsTuples
* Return "true" if this Query will send output to the destination.
*/
#ifdef NOT_USED
bool QueryReturnsTuples(Query* parse_tree)
{
switch (parse_tree->commandType) {
case CMD_SELECT:
if (parse_tree->utilityStmt == NULL)
return true;
break;
case CMD_MERGE:
return false;
case CMD_INSERT:
case CMD_UPDATE:
case CMD_DELETE:
if (parse_tree->returningList)
return true;
break;
case CMD_UTILITY:
return UtilityReturnsTuples(parse_tree->utilityStmt);
case CMD_UNKNOWN:
case CMD_NOTHING:
break;
default:
break;
}
return false;
}
#endif
* UtilityContainsQuery
* Return the contained Query, or NULL if there is none
*
* Certain utility statements, such as EXPLAIN, contain a plannable Query.
* This function encapsulates knowledge of exactly which ones do.
* We assume it is invoked only on already-parse-analyzed statements
* (else the contained parse_tree isn't a Query yet).
*
* In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO and
* CREATE MATERIALIZED VIEW), potentially Query-containing utility statements
* can be nested. This function will drill down to a non-utility Query, or
* return NULL if none.
*/
Query* UtilityContainsQuery(Node* parse_tree)
{
Query* qry = NULL;
switch (nodeTag(parse_tree)) {
case T_ExplainStmt:
qry = (Query*)((ExplainStmt*)parse_tree)->query;
AssertEreport(IsA(qry, Query), MOD_EXECUTOR, "node type is not query");
if (qry->commandType == CMD_UTILITY)
return UtilityContainsQuery(qry->utilityStmt);
return qry;
case T_CreateTableAsStmt:
qry = (Query*)((CreateTableAsStmt*)parse_tree)->query;
AssertEreport(IsA(qry, Query), MOD_EXECUTOR, "node type is not query");
if (qry->commandType == CMD_UTILITY)
return UtilityContainsQuery(qry->utilityStmt);
return qry;
default:
return NULL;
}
}
* AlterObjectTypeCommandTag
* helper function for CreateCommandTag
*
* This covers most cases where ALTER is used with an ObjectType enum.
*/
static const char* AlterObjectTypeCommandTag(ObjectType obj_type)
{
const char* tag = NULL;
bool is_multiple_nodes = false;
#ifdef ENABLE_MULTIPLE_NODES
is_multiple_nodes = true;
#endif
switch (obj_type) {
case OBJECT_AGGREGATE:
tag = "ALTER AGGREGATE";
break;
case OBJECT_ATTRIBUTE:
tag = "ALTER TYPE";
break;
case OBJECT_CAST:
tag = "ALTER CAST";
break;
case OBJECT_COLLATION:
tag = "ALTER COLLATION";
break;
case OBJECT_COLUMN:
tag = "ALTER TABLE";
break;
case OBJECT_TABCONSTRAINT:
tag = "ALTER TABLE";
break;
case OBJECT_CONVERSION:
tag = "ALTER CONVERSION";
break;
case OBJECT_DATABASE:
tag = "ALTER DATABASE";
break;
case OBJECT_DOMAIN:
tag = "ALTER DOMAIN";
break;
case OBJECT_EVENT_TRIGGER:
tag = "ALTER EVENT TRIGGER";
break;
case OBJECT_EXTENSION:
tag = "ALTER EXTENSION";
break;
case OBJECT_FDW:
tag = "ALTER FOREIGN DATA WRAPPER";
break;
case OBJECT_FOREIGN_SERVER:
tag = "ALTER SERVER";
break;
case OBJECT_FOREIGN_TABLE:
tag = "ALTER FOREIGN TABLE";
break;
case OBJECT_STREAM:
tag = "ALTER STREAM";
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
break;
case OBJECT_FUNCTION:
tag = "ALTER FUNCTION";
break;
case OBJECT_PACKAGE:
tag = "ALTER PACKAGE";
break;
case OBJECT_INDEX:
tag = "ALTER INDEX";
break;
case OBJECT_LANGUAGE:
tag = "ALTER LANGUAGE";
break;
case OBJECT_LARGEOBJECT:
tag = "ALTER LARGE OBJECT";
break;
case OBJECT_OPCLASS:
tag = "ALTER OPERATOR CLASS";
break;
case OBJECT_OPERATOR:
tag = "ALTER OPERATOR";
break;
case OBJECT_OPFAMILY:
tag = "ALTER OPERATOR FAMILY";
break;
case OBJECT_RLSPOLICY:
tag = "ALTER ROW LEVEL SECURITY POLICY";
break;
case OBJECT_PARTITION:
tag = "ALTER TABLE";
break;
case OBJECT_PARTITION_INDEX:
tag = "ALTER INDEX";
break;
case OBJECT_ROLE:
case OBJECT_USER:
tag = "ALTER ROLE";
break;
case OBJECT_RULE:
tag = "ALTER RULE";
break;
case OBJECT_SCHEMA:
tag = "ALTER SCHEMA";
break;
case OBJECT_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
tag = "ALTER SEQUENCE";
break;
case OBJECT_LARGE_SEQUENCE:
case OBJECT_LARGE_SEQUENCE_GSC:
tag = "ALTER LARGE SEQUENCE";
break;
case OBJECT_TABLE:
tag = "ALTER TABLE";
break;
case OBJECT_TABLESPACE:
tag = "ALTER TABLESPACE";
break;
case OBJECT_TRIGGER:
tag = "ALTER TRIGGER";
break;
case OBJECT_TSCONFIGURATION:
tag = "ALTER TEXT SEARCH CONFIGURATION";
break;
case OBJECT_TSDICTIONARY:
tag = "ALTER TEXT SEARCH DICTIONARY";
break;
case OBJECT_TSPARSER:
tag = "ALTER TEXT SEARCH PARSER";
break;
case OBJECT_TSTEMPLATE:
tag = "ALTER TEXT SEARCH TEMPLATE";
break;
case OBJECT_TYPE:
tag = "ALTER TYPE";
break;
case OBJECT_VIEW:
tag = "ALTER VIEW";
break;
case OBJECT_CONTQUERY:
tag = "ALTER CONTVIEW";
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
break;
case OBJECT_MATVIEW:
tag = "ALTER MATERIALIZED VIEW";
break;
case OBJECT_DATA_SOURCE:
tag = "ALTER DATA SOURCE";
break;
case OBJECT_DIRECTORY:
tag = "ALTER DIRECTORY";
break;
case OBJECT_SYNONYM:
tag = "ALTER SYNONYM";
break;
case OBJECT_PUBLICATION:
tag = "ALTER PUBLICATION";
break;
case OBJECT_SUBSCRIPTION:
tag = "ALTER SUBSCRIPTION";
break;
default:
tag = "?\?\?";
break;
}
return tag;
}
static const char* CompileTag(CompileEntry compileItem)
{
const char* tag = NULL;
switch (compileItem) {
case COMPILE_PROCEDURE:
tag = "ALTER PROCEDURE";
break;
case COMPILE_FUNCTION:
tag = "ALTER FUNCTION";
break;
case COMPILE_PACKAGE:
tag = "ALTER PACKAGE";
break;
case COMPILE_PKG_SPECIFICATION:
tag = "ALTER PACKAGE SPECIFICATION";
break;
case COMPILE_PKG_BODY:
tag = "ALTER PACKAGE BODY";
break;
}
return tag;
}
* CreateCommandTag
* utility to get a string representation of the command operation,
* given either a raw (un-analyzed) parse_tree or a planned query.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
*
* NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
* Also, the result must point at a true constant (permanent storage).
*/
const char* CreateCommandTag(Node* parse_tree)
{
const char* tag = NULL;
bool is_multiple_nodes = false;
#ifdef ENABLE_MULTIPLE_NODES
is_multiple_nodes = true;
#endif
switch (nodeTag(parse_tree)) {
case T_InsertStmt:
tag = "INSERT";
break;
case T_DeleteStmt:
tag = "DELETE";
break;
case T_UpdateStmt:
tag = "UPDATE";
break;
case T_MergeStmt:
tag = "MERGE";
break;
case T_SelectStmt:
#ifdef DOLPHIN
if (((SelectStmt *)parse_tree)->intoClause != NULL) {
IntoClause *into = ((SelectStmt *)parse_tree)->intoClause;
if (into->userVarList != NIL || into->filename != NULL) {
tag = "SELECT INTO";
} else {
tag = "CREATE TABLE AS";
}
}
#else
if (((SelectStmt *)parse_tree)->intoClause != NULL &&
((SelectStmt *)parse_tree)->intoClause->userVarList != NIL) {
tag = "SELECT INTO";
}
#endif
else {
tag = "SELECT";
}
break;
case T_TransactionStmt: {
TransactionStmt* stmt = (TransactionStmt*)parse_tree;
switch (stmt->kind) {
case TRANS_STMT_BEGIN:
tag = "BEGIN";
break;
case TRANS_STMT_START:
tag = "START TRANSACTION";
break;
case TRANS_STMT_COMMIT:
tag = "COMMIT";
break;
case TRANS_STMT_ROLLBACK:
case TRANS_STMT_ROLLBACK_TO:
tag = "ROLLBACK";
break;
case TRANS_STMT_SAVEPOINT:
tag = "SAVEPOINT";
break;
case TRANS_STMT_RELEASE:
tag = "RELEASE";
break;
case TRANS_STMT_PREPARE:
tag = "PREPARE TRANSACTION";
break;
case TRANS_STMT_COMMIT_PREPARED:
tag = "COMMIT PREPARED";
break;
case TRANS_STMT_ROLLBACK_PREPARED:
tag = "ROLLBACK PREPARED";
break;
default:
tag = "?\?\?";
break;
}
} break;
case T_DeclareCursorStmt:
tag = "DECLARE CURSOR";
break;
case T_ClosePortalStmt: {
ClosePortalStmt* stmt = (ClosePortalStmt*)parse_tree;
if (stmt->portalname == NULL)
tag = "CLOSE CURSOR ALL";
else
tag = "CLOSE CURSOR";
} break;
case T_FetchStmt: {
FetchStmt* stmt = (FetchStmt*)parse_tree;
tag = (stmt->ismove) ? "MOVE" : "FETCH";
} break;
case T_CreateDomainStmt:
tag = "CREATE DOMAIN";
break;
case T_CreateWeakPasswordDictionaryStmt:
tag = "CREATE WEAK PASSWORD DICTIONARY";
break;
case T_DropWeakPasswordDictionaryStmt:
tag = "DROP WEAK PASSWORD DICTIONARY";
break;
case T_CreateSchemaStmt:
tag = "CREATE SCHEMA";
break;
case T_AlterSchemaStmt:
tag = "ALTER SCHEMA";
break;
case T_CreateSetStmt:
tag = "ALTER SET";
break;
case T_CreateStmt:
tag = "CREATE TABLE";
break;
case T_CreateTableSpaceStmt:
tag = "CREATE TABLESPACE";
break;
case T_DropTableSpaceStmt:
tag = "DROP TABLESPACE";
break;
case T_AlterTableSpaceOptionsStmt:
tag = "ALTER TABLESPACE";
break;
case T_CreateExtensionStmt:
tag = "CREATE EXTENSION";
break;
case T_AlterExtensionStmt:
tag = "ALTER EXTENSION";
break;
case T_AlterExtensionContentsStmt:
tag = "ALTER EXTENSION";
break;
case T_CreateFdwStmt:
tag = "CREATE FOREIGN DATA WRAPPER";
break;
case T_AlterFdwStmt:
tag = "ALTER FOREIGN DATA WRAPPER";
break;
case T_CreateForeignServerStmt:
tag = "CREATE SERVER";
break;
case T_AlterForeignServerStmt:
tag = "ALTER SERVER";
break;
case T_CreateUserMappingStmt:
tag = "CREATE USER MAPPING";
break;
case T_AlterUserMappingStmt:
tag = "ALTER USER MAPPING";
break;
case T_DropUserMappingStmt:
tag = "DROP USER MAPPING";
break;
case T_CreateSynonymStmt:
tag = "CREATE SYNONYM";
break;
case T_DropSynonymStmt:
tag = "DROP SYNONYM";
break;
case T_CreateDataSourceStmt:
tag = "CREATE DATA SOURCE";
break;
case T_AlterDataSourceStmt:
tag = "ALTER DATA SOURCE";
break;
case T_CreateRlsPolicyStmt:
tag = "CREATE ROW LEVEL SECURITY POLICY";
break;
case T_AlterRlsPolicyStmt:
tag = "ALTER ROW LEVEL SECURITY POLICY";
break;
case T_CreateForeignTableStmt:
if (pg_strcasecmp(((CreateForeignTableStmt *)parse_tree)->servername,
STREAMING_SERVER) == 0) {
tag = "CREATE STREAM";
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
} else {
tag = "CREATE FOREIGN TABLE";
}
break;
case T_DropStmt:
switch (((DropStmt*)parse_tree)->removeType) {
case OBJECT_TABLE:
tag = "DROP TABLE";
break;
case OBJECT_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
tag = "DROP SEQUENCE";
break;
case OBJECT_LARGE_SEQUENCE:
case OBJECT_LARGE_SEQUENCE_GSC:
tag = "DROP LARGE SEQUENCE";
break;
case OBJECT_VIEW:
#ifdef ENABLE_MULTIPLE_NODES
if (range_var_list_include_streaming_object(((DropStmt*)parse_tree)->objects))
tag = "DROP CONTVIEW";
else
#endif
tag = "DROP VIEW";
break;
case OBJECT_CONTQUERY:
tag = "DROP CONTVIEW";
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
break;
case OBJECT_MATVIEW:
tag = "DROP MATERIALIZED VIEW";
break;
case OBJECT_INDEX:
tag = "DROP INDEX";
break;
case OBJECT_TYPE:
tag = "DROP TYPE";
break;
case OBJECT_DOMAIN:
tag = "DROP DOMAIN";
break;
case OBJECT_COLLATION:
tag = "DROP COLLATION";
break;
case OBJECT_CONVERSION:
tag = "DROP CONVERSION";
break;
case OBJECT_DB4AI_MODEL:
tag = "DROP MODEL";
break;
case OBJECT_SCHEMA:
tag = "DROP SCHEMA";
break;
case OBJECT_TSPARSER:
tag = "DROP TEXT SEARCH PARSER";
break;
case OBJECT_TSDICTIONARY:
tag = "DROP TEXT SEARCH DICTIONARY";
break;
case OBJECT_TSTEMPLATE:
tag = "DROP TEXT SEARCH TEMPLATE";
break;
case OBJECT_TSCONFIGURATION:
tag = "DROP TEXT SEARCH CONFIGURATION";
break;
case OBJECT_FOREIGN_TABLE:
#ifdef ENABLE_MULTIPLE_NODES
if (range_var_list_include_streaming_object(((DropStmt*)parse_tree)->objects))
tag = "DROP STREAM";
else
#endif
tag = "DROP FOREIGN TABLE";
break;
case OBJECT_STREAM:
tag = "DROP STREAM";
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
break;
case OBJECT_EVENT_TRIGGER:
tag = "DROP EVENT TRIGGER";
break;
case OBJECT_EXTENSION:
tag = "DROP EXTENSION";
break;
case OBJECT_FUNCTION: {
DropStmt* dpStmt = (DropStmt*)parse_tree;
tag = (dpStmt->isProcedure) ? "DROP PROCEDURE" : "DROP FUNCTION";
break;
}
case OBJECT_PACKAGE: {
tag = "DROP PACKAGE";
break;
}
case OBJECT_PACKAGE_BODY: {
tag = "DROP PACKAGE BODY";
break;
}
case OBJECT_AGGREGATE:
tag = "DROP AGGREGATE";
break;
case OBJECT_OPERATOR:
tag = "DROP OPERATOR";
break;
case OBJECT_LANGUAGE:
tag = "DROP LANGUAGE";
break;
case OBJECT_CAST:
tag = "DROP CAST";
break;
case OBJECT_TRIGGER:
tag = "DROP TRIGGER";
break;
case OBJECT_RULE:
tag = "DROP RULE";
break;
case OBJECT_FDW:
tag = "DROP FOREIGN DATA WRAPPER";
break;
case OBJECT_FOREIGN_SERVER:
tag = "DROP SERVER";
break;
case OBJECT_OPCLASS:
tag = "DROP OPERATOR CLASS";
break;
case OBJECT_OPFAMILY:
tag = "DROP OPERATOR FAMILY";
break;
case OBJECT_RLSPOLICY:
tag = "DROP ROW LEVEL SECURITY POLICY";
break;
case OBJECT_DATA_SOURCE:
tag = "DROP DATA SOURCE";
break;
case OBJECT_GLOBAL_SETTING:
tag = "DROP CLIENT MASTER KEY";
break;
case OBJECT_COLUMN_SETTING:
tag = "DROP COLUMN ENCRYPTION KEY";
break;
case OBJECT_PUBLICATION:
tag = "DROP PUBLICATION";
break;
case OBJECT_ACCESS_METHOD:
tag = "DROP ACCESS METHOD";
break;
default:
tag = "?\?\?";
break;
}
break;
case T_TruncateStmt:
tag = "TRUNCATE TABLE";
break;
case T_PurgeStmt:
switch (((PurgeStmt*)parse_tree)->purtype) {
case PURGE_TABLE:
tag = "PURGE TABLE";
break;
case PURGE_INDEX:
tag = "PURGE INDEX";
break;
case PURGE_TABLESPACE:
tag = "PURGE TABLESPACE";
break;
case PURGE_RECYCLEBIN:
tag = "PURGE RECYCLEBIN";
break;
default:
tag = "?\?\?";
break;
}
break;
case T_CommentStmt:
tag = "COMMENT";
break;
case T_SecLabelStmt:
tag = "SECURITY LABEL";
break;
case T_CopyStmt:
tag = "COPY";
break;
case T_RenameStmt: {
ObjectType RenameType = ((RenameStmt*)parse_tree)->renameType == OBJECT_COLUMN ? ((RenameStmt*)parse_tree)->relationType:((RenameStmt*)parse_tree)->renameType;
tag = AlterObjectTypeCommandTag(RenameType);
break;
}
case T_AlterObjectSchemaStmt:
tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt*)parse_tree)->objectType);
break;
case T_AlterOwnerStmt:
tag = AlterObjectTypeCommandTag(((AlterOwnerStmt*)parse_tree)->objectType);
break;
case T_AlterTableStmt:
tag = AlterObjectTypeCommandTag(((AlterTableStmt*)parse_tree)->relkind);
break;
case T_AlterDomainStmt:
tag = "ALTER DOMAIN";
break;
case T_AlterFunctionStmt:
tag = "ALTER FUNCTION";
break;
case T_CompileStmt: {
u_sess->plsql_cxt.during_compile = true;
if (!enable_plpgsql_gsdependency_guc()) {
u_sess->plsql_cxt.during_compile = false;
ereport(ERROR, (errmsg("This operation is not supported.")));
break;
}
CompileStmt* stmt = (CompileStmt*)parse_tree;
tag = CompileTag(stmt->compileItem);
} break;
case T_GrantStmt: {
GrantStmt* stmt = (GrantStmt*)parse_tree;
tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
} break;
case T_GrantRoleStmt: {
GrantRoleStmt* stmt = (GrantRoleStmt*)parse_tree;
tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
} break;
case T_GrantDbStmt: {
GrantDbStmt* stmt = (GrantDbStmt*)parse_tree;
tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
} break;
case T_AlterDefaultPrivilegesStmt:
tag = "ALTER DEFAULT PRIVILEGES";
break;
case T_DefineStmt:
switch (((DefineStmt*)parse_tree)->kind) {
case OBJECT_AGGREGATE:
tag = "CREATE AGGREGATE";
break;
case OBJECT_OPERATOR:
tag = "CREATE OPERATOR";
break;
case OBJECT_TYPE:
tag = "CREATE TYPE";
break;
case OBJECT_TSPARSER:
tag = "CREATE TEXT SEARCH PARSER";
break;
case OBJECT_TSDICTIONARY:
tag = "CREATE TEXT SEARCH DICTIONARY";
break;
case OBJECT_TSTEMPLATE:
tag = "CREATE TEXT SEARCH TEMPLATE";
break;
case OBJECT_TSCONFIGURATION:
tag = "CREATE TEXT SEARCH CONFIGURATION";
break;
case OBJECT_COLLATION:
tag = "CREATE COLLATION";
break;
case OBJECT_ACCESS_METHOD:
tag = "CREATE ACCESS METHOD";
break;
default:
tag = "?\?\?";
break;
}
break;
case T_CompositeTypeStmt:
tag = "CREATE TYPE";
break;
case T_TableOfTypeStmt:
tag = "CREATE TYPE";
break;
case T_CreateEnumStmt:
tag = "CREATE TYPE";
break;
case T_CreateRangeStmt:
tag = "CREATE TYPE";
break;
case T_AlterEnumStmt:
tag = "ALTER TYPE";
break;
case T_ViewStmt:
#ifdef ENABLE_MULTIPLE_NODES
if ((((ViewStmt*)parse_tree)->relkind) == OBJECT_CONTQUERY ||
view_stmt_has_stream((ViewStmt*)parse_tree))
tag = "CREATE CONTVIEW";
else
#endif
tag = "CREATE VIEW";
if (((ViewStmt*)parse_tree)->is_alter) {
tag = "ALTER VIEW";
}
if ((((ViewStmt*)parse_tree)->relkind) == OBJECT_CONTQUERY) {
if (!is_multiple_nodes) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not supported for streaming engine in current version"),
errdetail("You should use the multiple nodes version")));
}
}
break;
case T_CreateFunctionStmt: {
CreateFunctionStmt* cfstmt = (CreateFunctionStmt*)parse_tree;
tag = (cfstmt->isProcedure) ? "CREATE PROCEDURE" : "CREATE FUNCTION";
break;
}
case T_CreatePackageStmt: {
tag = "CREATE PACKAGE";
break;
}
case T_CreatePackageBodyStmt: {
tag = "CREATE PACKAGE BODY";
break;
}
case T_CreateEventStmt: {
tag = "CREATE EVENT";
break;
}
case T_AlterEventStmt: {
tag = "ALTER EVENT";
break;
}
case T_DropEventStmt: {
tag = "DROP EVENT";
break;
}
case T_ShowEventStmt: {
tag = "SHOW";
break;
}
case T_IndexStmt:
tag = "CREATE INDEX";
break;
case T_RuleStmt:
tag = "CREATE RULE";
break;
case T_CreateSeqStmt:
if (((CreateSeqStmt*)parse_tree)->is_large) {
tag = "CREATE LARGE SEQUENCE";
} else {
tag = "CREATE SEQUENCE";
}
break;
case T_AlterSeqStmt:
if (((AlterSeqStmt*)parse_tree)->is_large) {
tag = "ALTER LARGE SEQUENCE";
} else {
tag = "ALTER SEQUENCE";
}
break;
case T_DoStmt:
tag = "ANONYMOUS BLOCK EXECUTE";
break;
case T_CreatedbStmt:
tag = "CREATE DATABASE";
break;
case T_AlterDatabaseStmt:
tag = "ALTER DATABASE";
break;
case T_AlterDatabaseSetStmt:
tag = "ALTER DATABASE";
break;
case T_DropdbStmt:
tag = "DROP DATABASE";
break;
case T_NotifyStmt:
tag = "NOTIFY";
break;
case T_ListenStmt:
tag = "LISTEN";
break;
case T_UnlistenStmt:
tag = "UNLISTEN";
break;
case T_LoadStmt:
tag = "LOAD";
break;
case T_ClusterStmt:
tag = "CLUSTER";
break;
case T_VacuumStmt:
if (((VacuumStmt*)parse_tree)->options & VACOPT_VACUUM) {
tag = "VACUUM";
} else if (((VacuumStmt*)parse_tree)->options & VACOPT_MERGE) {
tag = "DELTA MERGE";
} else if (((VacuumStmt*)parse_tree)->options & VACOPT_VERIFY) {
tag = "ANALYZE VERIFY";
} else {
tag = "ANALYZE";
}
break;
case T_ExplainStmt:
tag = "EXPLAIN";
break;
case T_CreateTableAsStmt:
switch (((CreateTableAsStmt *)parse_tree)->relkind)
{
case OBJECT_TABLE:
if (((CreateTableAsStmt *)parse_tree)->is_select_into)
tag = "SELECT INTO";
else
tag = "CREATE TABLE AS";
break;
case OBJECT_MATVIEW:
tag = "CREATE MATERIALIZED VIEW";
break;
default:
tag = "???";
}
break;
case T_RefreshMatViewStmt:
tag = "REFRESH MATERIALIZED VIEW";
break;
case T_CreateMatViewLogStmt:
tag = "CREATE MATERIALIZED VIEW LOG";
break;
case T_DropMatViewLogStmt:
tag = "DROP MATERIALIZED VIEW LOG";
break;
#ifndef ENABLE_MULTIPLE_NODES
case T_AlterSystemStmt:
tag = "ALTER SYSTEM SET";
break;
#endif
case T_VariableSetStmt:
switch (((VariableSetStmt*)parse_tree)->kind) {
case VAR_SET_VALUE:
case VAR_SET_CURRENT:
case VAR_SET_DEFAULT:
case VAR_SET_MULTI:
case VAR_SET_ROLEPWD:
case VAR_SET_DEFINED:
tag = "SET";
break;
case VAR_RESET:
case VAR_RESET_ALL:
tag = "RESET";
break;
default:
tag = "?\?\?";
break;
}
break;
case T_VariableMultiSetStmt:
{
tag = "SET";
} break;
case T_VariableShowStmt:
tag = "SHOW";
break;
case T_DiscardStmt:
switch (((DiscardStmt*)parse_tree)->target) {
case DISCARD_ALL:
tag = "DISCARD ALL";
break;
case DISCARD_PLANS:
tag = "DISCARD PLANS";
break;
case DISCARD_TEMP:
tag = "DISCARD TEMP";
break;
default:
tag = "?\?\?";
break;
}
break;
case T_CreateTrigStmt:
tag = "CREATE TRIGGER";
break;
case T_CreateEventTrigStmt:
tag = "CREATE EVENT TRIGGER";
break;
case T_AlterEventTrigStmt:
tag = "ALTER EVENT TRIGGER";
break;
case T_CreatePLangStmt:
tag = "CREATE LANGUAGE";
break;
case T_CreateRoleStmt:
tag = "CREATE ROLE";
break;
case T_AlterRoleStmt:
tag = "ALTER ROLE";
break;
case T_AlterRoleSetStmt:
tag = "ALTER ROLE";
break;
case T_DropRoleStmt:
tag = "DROP ROLE";
break;
case T_DropOwnedStmt:
tag = "DROP OWNED";
break;
case T_ReassignOwnedStmt:
tag = "REASSIGN OWNED";
break;
case T_LockStmt:
tag = "LOCK TABLE";
break;
case T_TimeCapsuleStmt:
tag = "TimeCapsule Table";
break;
case T_ConstraintsSetStmt:
tag = "SET CONSTRAINTS";
break;
case T_CheckPointStmt:
tag = "CHECKPOINT";
break;
#ifdef PGXC
case T_BarrierStmt:
tag = "BARRIER";
break;
case T_AlterNodeStmt:
case T_AlterCoordinatorStmt:
tag = "ALTER NODE";
break;
case T_CreateNodeStmt:
tag = "CREATE NODE";
break;
case T_DropNodeStmt:
tag = "DROP NODE";
break;
case T_CreateGroupStmt:
tag = "CREATE NODE GROUP";
break;
case T_AlterGroupStmt:
tag = "ALTER NODE GROUP";
break;
case T_DropGroupStmt:
tag = "DROP NODE GROUP";
break;
case T_CreatePolicyLabelStmt:
tag = "CREATE RESOURCE LABEL";
break;
case T_AlterPolicyLabelStmt:
tag = "ALTER RESOURCE LABEL";
break;
case T_DropPolicyLabelStmt:
tag = "DROP RESOURCE LABEL";
break;
case T_CreateAuditPolicyStmt:
tag = "CREATE AUDIT POLICY";
break;
case T_AlterAuditPolicyStmt:
tag = "ALTER AUDIT POLICY";
break;
case T_DropAuditPolicyStmt:
tag = "DROP AUDIT POLICY";
break;
case T_CreateMaskingPolicyStmt:
tag = "CREATE MASKING POLICY";
break;
case T_AlterMaskingPolicyStmt:
tag = "ALTER MASKING POLICY";
break;
case T_DropMaskingPolicyStmt:
tag = "DROP MASKING POLICY";
break;
case T_CreateResourcePoolStmt:
tag = "CREATE RESOURCE POOL";
break;
case T_AlterResourcePoolStmt:
tag = "ALTER RESOURCE POOL";
break;
case T_DropResourcePoolStmt:
tag = "DROP RESOURCE POOL";
break;
case T_AlterGlobalConfigStmt:
tag = "ALTER GLOBAL CONFIGURATION";
break;
case T_DropGlobalConfigStmt:
tag = "Drop GLOBAL CONFIGURATION";
break;
case T_CreateWorkloadGroupStmt:
tag = "CREATE WORKLOAD GROUP";
break;
case T_AlterWorkloadGroupStmt:
tag = "ALTER WORKLOAD GROUP";
break;
case T_DropWorkloadGroupStmt:
tag = "DROP WORKLOAD GROUP";
break;
case T_CreateAppWorkloadGroupMappingStmt:
tag = "CREATE APP WORKLOAD GROUP MAPPING";
break;
case T_AlterAppWorkloadGroupMappingStmt:
tag = "ALTER APP WORKLOAD GROUP MAPPING";
break;
case T_DropAppWorkloadGroupMappingStmt:
tag = "DROP APP WORKLOAD GROUP MAPPING";
break;
#endif
case T_ReindexStmt:
tag = "REINDEX";
break;
case T_CreateConversionStmt:
tag = "CREATE CONVERSION";
break;
case T_CreateCastStmt:
tag = "CREATE CAST";
break;
case T_CreateOpClassStmt:
tag = "CREATE OPERATOR CLASS";
break;
case T_CreateOpFamilyStmt:
tag = "CREATE OPERATOR FAMILY";
break;
case T_AlterOpFamilyStmt:
tag = "ALTER OPERATOR FAMILY";
break;
case T_AlterTSDictionaryStmt:
tag = "ALTER TEXT SEARCH DICTIONARY";
break;
case T_AlterTSConfigurationStmt:
tag = "ALTER TEXT SEARCH CONFIGURATION";
break;
case T_CreateAmStmt:
tag = "CREATE ACCESS METHOD";
break;
case T_PrepareStmt:
tag = "PREPARE";
break;
case T_ExecuteStmt:
tag = "EXECUTE";
break;
case T_DeallocateStmt: {
DeallocateStmt* stmt = (DeallocateStmt*)parse_tree;
if (stmt->name == NULL)
tag = "DEALLOCATE ALL";
else
tag = "DEALLOCATE";
} break;
case T_PlannedStmt: {
PlannedStmt* stmt = (PlannedStmt*)parse_tree;
switch (stmt->commandType) {
case CMD_SELECT:
* We take a little extra care here so that the result
* will be useful for complaints about read-only
* statements
*/
if (stmt->utilityStmt != NULL) {
AssertEreport(
IsA(stmt->utilityStmt, DeclareCursorStmt), MOD_EXECUTOR, "not a cursor declare stmt");
tag = "DECLARE CURSOR";
} else if (stmt->rowMarks != NIL) {
switch (((PlanRowMark *)linitial(stmt->rowMarks))->markType) {
case ROW_MARK_EXCLUSIVE:
tag = "SELECT FOR UPDATE";
break;
case ROW_MARK_NOKEYEXCLUSIVE:
tag = "SELECT FOR NO KEY UPDATE";
break;
case ROW_MARK_SHARE:
tag = "SELECT FOR SHARE";
break;
case ROW_MARK_KEYSHARE:
tag = "SELECT FOR KEY SHARE";
break;
default:
tag = "SELECT";
break;
}
} else
tag = "SELECT";
break;
case CMD_UPDATE:
tag = "UPDATE";
break;
case CMD_INSERT:
tag = "INSERT";
break;
case CMD_DELETE:
tag = "DELETE";
break;
case CMD_MERGE:
tag = "MERGE";
break;
default:
elog(WARNING, "unrecognized commandType: %d", (int)stmt->commandType);
tag = "?\?\?";
break;
}
} break;
case T_Query: {
Query* stmt = (Query*)parse_tree;
switch (stmt->commandType) {
case CMD_SELECT:
* We take a little extra care here so that the result
* will be useful for complaints about read-only
* statements
*/
if (stmt->utilityStmt != NULL) {
AssertEreport(
IsA(stmt->utilityStmt, DeclareCursorStmt), MOD_EXECUTOR, "not a cursor declare stmt");
tag = "DECLARE CURSOR";
} else if (stmt->rowMarks != NIL) {
switch (((RowMarkClause *)linitial(stmt->rowMarks))->strength) {
case LCS_FORKEYSHARE:
tag = "SELECT FOR KEY SHARE";
break;
case LCS_FORSHARE:
tag = "SELECT FOR SHARE";
break;
case LCS_FORNOKEYUPDATE:
tag = "SELECT FOR NO KEY UPDATE";
break;
case LCS_FORUPDATE:
tag = "SELECT FOR UPDATE";
break;
default:
tag = "?\?\?";
break;
}
} else
tag = "SELECT";
break;
case CMD_UPDATE:
tag = "UPDATE";
break;
case CMD_INSERT:
tag = "INSERT";
break;
case CMD_DELETE:
tag = "DELETE";
break;
case CMD_MERGE:
tag = "MERGE";
break;
case CMD_UTILITY:
tag = CreateCommandTag(stmt->utilityStmt);
break;
default:
elog(WARNING, "unrecognized commandType: %d", (int)stmt->commandType);
tag = "?\?\?";
break;
}
} break;
case T_ExecDirectStmt:
tag = "EXECUTE DIRECT";
break;
case T_CleanConnStmt:
tag = "CLEAN CONNECTION";
break;
case T_CreateDirectoryStmt:
tag = "CREATE DIRECTORY";
break;
case T_DropDirectoryStmt:
tag = "DROP DIRECTORY";
break;
case T_CreateClientLogicGlobal:
tag = "CREATE CLIENT MASTER KEY";
break;
case T_CreateClientLogicColumn:
tag = "CREATE COLUMN ENCRYPTION KEY";
break;
case T_ShutdownStmt:
tag = "SHUTDOWN";
break;
case T_CreateModelStmt:
tag = "CREATE MODEL";
break;
case T_CreatePublicationStmt:
tag = "CREATE PUBLICATION";
break;
case T_AlterPublicationStmt:
tag = "ALTER PUBLICATION";
break;
case T_CreateSubscriptionStmt:
tag = "CREATE SUBSCRIPTION";
break;
case T_AlterSubscriptionStmt:
tag = "ALTER SUBSCRIPTION";
break;
case T_DropSubscriptionStmt:
tag = "DROP SUBSCRIPTION";
break;
case T_ShrinkStmt:
tag = "SHRINK";
break;
case T_GetDiagStmt:
tag = "GET DIAGNOSTICS";
break;
#ifdef DOLPHIN
case T_DolphinCallStmt:
tag = "CALL";
break;
#endif
default:
elog(WARNING, "unrecognized node type: %d", (int)nodeTag(parse_tree));
tag = "?\?\?";
break;
}
return tag;
}
* GetAlterTableCommandTag
* utility to get a string representation of the alter table
* command operation.
*/
const char* CreateAlterTableCommandTag(const AlterTableType subtype)
{
const char* tag = NULL;
switch (subtype) {
case AT_AddColumn:
tag = "ADD COLUMN";
break;
case AT_AddColumnRecurse:
tag = "ADD COLUMN RECURSE";
break;
case AT_AddColumnToView:
tag = "ADD COLUMN TO VIEW";
break;
case AT_AddPartition:
tag = "ADD PARTITION";
break;
case AT_AddSubPartition:
tag = "MODIFY PARTITION ADD SUBPARTITION";
break;
case AT_ColumnDefault:
tag = "COLUMN DEFAULT";
break;
case AT_DropNotNull:
tag = "DROP NOT NULL";
break;
case AT_SetNotNull:
tag = "SET NOT NULL";
break;
case AT_SetStatistics:
tag = "SET STATISTICS";
break;
case AT_SetOptions:
tag = "SET OPTIONS";
break;
case AT_ResetOptions:
tag = "RESET OPTIONS";
break;
case AT_SetStorage:
tag = "SET STORAGE";
break;
case AT_DropColumn:
tag = "DROP COLUMN";
break;
case AT_DropColumnRecurse:
tag = "DROP COLUMN RECURSE";
break;
case AT_DropPartition:
tag = "DROP PARTITION";
break;
case AT_DropSubPartition:
tag = "DROP SUBPARTITION";
break;
case AT_AddIndex:
tag = "ADD INDEX";
break;
case AT_ReAddIndex:
tag = "RE ADD INDEX";
break;
case AT_AddConstraint:
tag = "ADD CONSTRAINT";
break;
case AT_AddConstraintRecurse:
tag = "ADD CONSTRAINT RECURSE";
break;
case AT_ValidateConstraint:
tag = "VALIDATE CONSTRAINT";
break;
case AT_ValidateConstraintRecurse:
tag = "VALIDATE CONSTRAINT RECURSE";
break;
case AT_ProcessedConstraint:
tag = "PROCESSED CONSTRAINT";
break;
case AT_AddIndexConstraint:
tag = "ADD INDEX CONSTRAINT";
break;
case AT_DropConstraint:
tag = "DROP CONSTRAINT";
break;
case AT_DropConstraintRecurse:
tag = "DROP CONSTRAINT RECURSE";
break;
case AT_AlterColumnType:
tag = "ALTER COLUMN TYPE";
break;
case AT_AlterColumnGenericOptions:
tag = "ALTER COLUMN GENERIC OPTIONS";
break;
case AT_ChangeOwner:
tag = "CHANGE OWNER";
break;
case AT_ClusterOn:
tag = "CLUSTER ON";
break;
case AT_DropCluster:
tag = "DROP CLUSTER";
break;
case AT_AddOids:
tag = "ADD OIDS";
break;
case AT_AddOidsRecurse:
tag = "ADD OIDS RECURSE";
break;
case AT_DropOids:
tag = "DROP OIDS";
break;
case AT_SetTableSpace:
tag = "SET TABLE SPACE";
break;
case AT_SetPartitionTableSpace:
tag = "SET PARTITION TABLE SPACE";
break;
case AT_SetRelOptions:
tag = "SET REL OPTIONS";
break;
case AT_ResetRelOptions:
tag = "RESET REL OPTIONS";
break;
case AT_ReplaceRelOptions:
tag = "REPLACE REL OPTIONS";
break;
case AT_UnusableIndex:
tag = "UNUSABLE INDEX";
break;
case AT_UnusableIndexPartition:
tag = "UNUSABLE INDEX PARTITION";
break;
case AT_UnusableAllIndexOnPartition:
tag = "UNUSABLE ALL INDEX ON PARTITION";
break;
case AT_RebuildIndex:
tag = "REBUILD INDEX";
break;
case AT_RebuildIndexPartition:
tag = "REBUILD INDEX PARTITION";
break;
case AT_RebuildAllIndexOnPartition:
tag = "REBUILD ALL INDEX ON PARTITION";
break;
case AT_DisableIndex:
tag = "DISABLE INDEX";
break;
case AT_EnableIndex:
tag = "ENABLE INDEX";
break;
case AT_EnableTrig:
tag = "ENABLE TRIGGER";
break;
case AT_EnableAlwaysTrig:
tag = "ENABLE ALWAYS TRIGGER";
break;
case AT_EnableReplicaTrig:
tag = "ENABLE REPLICA TRIGGER";
break;
case AT_DisableTrig:
tag = "DISABLE TRIGGER";
break;
case AT_EnableTrigAll:
tag = "ENABLE TRIGGER ALL";
break;
case AT_DisableTrigAll:
tag = "DISABLE TRIGGER ALL";
break;
case AT_EnableTrigUser:
tag = "ENABLE TRIGGER USER";
break;
case AT_DisableTrigUser:
tag = "DISABLE TRIGGER USER";
break;
case AT_EnableRule:
tag = "ENABLE RULE";
break;
case AT_EnableAlwaysRule:
tag = "ENABLE ALWAYS RULE";
break;
case AT_EnableReplicaRule:
tag = "ENABLE REPLICA RULE";
break;
case AT_DisableRule:
tag = "DISABLE RULE";
break;
case AT_EnableRls:
tag = "ENABLE ROW LEVEL SECURITY";
break;
case AT_DisableRls:
tag = "DISABLE ROW LEVEL SECURITY";
break;
case AT_ForceRls:
tag = "FORCE ROW LEVEL SECURITY";
break;
case AT_NoForceRls:
tag = "NO FORCE ROW LEVEL SECURITY";
break;
case AT_EncryptionKeyRotation:
tag = "ENCRYPTION KEY ROTATION";
break;
case AT_AddInherit:
tag = "ADD INHERIT";
break;
case AT_DropInherit:
tag = "DROP INHERIT";
break;
case AT_AddOf:
tag = "ADD OF";
break;
case AT_DropOf:
tag = "DROP OF";
break;
case AT_SET_COMPRESS:
tag = "SET COMPRESS";
break;
#ifdef PGXC
case AT_DistributeBy:
tag = "DISTRIBUTE BY";
break;
case AT_SubCluster:
tag = "SUB CLUSTER";
break;
case AT_AddNodeList:
tag = "ADD NODE LIST";
break;
case AT_DeleteNodeList:
tag = "DELETE NODE LIST";
break;
case AT_UpdateSliceLike:
tag = "UPDATE SLICE LIKE";
break;
#endif
case AT_GenericOptions:
tag = "GENERIC OPTIONS";
break;
case AT_EnableRowMoveMent:
tag = "ENABLE ROW MOVE MENT";
break;
case AT_DisableRowMoveMent:
tag = "DISABLE ROW MOVE MENT";
break;
case AT_TruncatePartition:
tag = "TRUNCATE PARTITION";
break;
case AT_ExchangePartition:
tag = "EXCHANGE PARTITION";
break;
case AT_MergePartition:
tag = "MERGE PARTITION";
break;
case AT_SplitPartition:
tag = "SPLIT PARTITION";
break;
#ifdef DOLPHIN
case AT_ReorganizePartition:
tag = "REORGANIZE PARTITION";
break;
#endif
case AT_ReAddConstraint:
tag = "RE ADD CONSTRAINT";
break;
case AT_ResetPartitionno:
tag = "RESET PARTITIONNO";
break;
case AT_ModifyColumn:
tag = "MODIFY COLUMN";
break;
#ifdef DOLPHIN
case AT_DropIndex:
tag = "DROP INDEX";
break;
case AT_DropForeignKey:
tag = "DROP FOREIGN KEY";
break;
case AT_RenameIndex:
tag = "RENAME INDEX";
break;
case AT_SetTableRowFormat:
tag = "SET TABLE ROW_FORMAT";
break;
case AT_SetTableCharset:
tag = "SET TABLE CHARSET";
break;
case AT_SetTableCollate:
tag = "SET TABLE COLLATE";
break;
case AT_CheckPartition:
tag = "CHECK PARTITION";
break;
case AT_RepairPartition:
tag = "REPAIR PARTITION";
break;
case AT_OptimizePartition:
tag = "OPTIMIZE PARTITION";
break;
#endif
default:
tag = "?\?\?";
break;
}
return tag;
}
* GetCommandLogLevel
* utility to get the minimum log_statement level for a command,
* given either a raw (un-analyzed) parse_tree or a planned query.
*
* This must handle all command types, but since the vast majority
* of 'em are utility commands, it seems sensible to keep it here.
*/
LogStmtLevel GetCommandLogLevel(Node* parse_tree)
{
LogStmtLevel lev;
switch (nodeTag(parse_tree)) {
case T_InsertStmt:
case T_DeleteStmt:
case T_UpdateStmt:
case T_MergeStmt:
lev = LOGSTMT_MOD;
break;
case T_SelectStmt:
if (((SelectStmt*)parse_tree)->intoClause)
lev = LOGSTMT_DDL;
else
lev = LOGSTMT_ALL;
break;
case T_TransactionStmt:
lev = LOGSTMT_ALL;
break;
case T_DeclareCursorStmt:
lev = LOGSTMT_ALL;
break;
case T_ClosePortalStmt:
lev = LOGSTMT_ALL;
break;
case T_FetchStmt:
lev = LOGSTMT_ALL;
break;
case T_CreateSchemaStmt:
case T_AlterSchemaStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateWeakPasswordDictionaryStmt:
case T_DropWeakPasswordDictionaryStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateStmt:
case T_CreateForeignTableStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateTableSpaceStmt:
case T_DropTableSpaceStmt:
case T_AlterTableSpaceOptionsStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateEventTrigStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterEventTrigStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateExtensionStmt:
case T_AlterExtensionStmt:
case T_AlterExtensionContentsStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateFdwStmt:
case T_AlterFdwStmt:
case T_CreateForeignServerStmt:
case T_AlterForeignServerStmt:
case T_CreateUserMappingStmt:
case T_AlterUserMappingStmt:
case T_DropUserMappingStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateDataSourceStmt:
case T_AlterDataSourceStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateRlsPolicyStmt:
case T_AlterRlsPolicyStmt:
lev = LOGSTMT_DDL;
break;
case T_DropStmt:
lev = LOGSTMT_DDL;
break;
case T_TruncateStmt:
lev = LOGSTMT_MOD;
break;
case T_CommentStmt:
lev = LOGSTMT_DDL;
break;
case T_SecLabelStmt:
lev = LOGSTMT_DDL;
break;
case T_CopyStmt:
if (((CopyStmt*)parse_tree)->is_from)
lev = LOGSTMT_MOD;
else
lev = LOGSTMT_ALL;
break;
case T_PrepareStmt: {
PrepareStmt* stmt = (PrepareStmt*)parse_tree;
lev = GetCommandLogLevel(stmt->query);
} break;
case T_ExecuteStmt: {
ExecuteStmt* stmt = (ExecuteStmt*)parse_tree;
PreparedStatement *ps = NULL;
ps = FetchPreparedStatement(stmt->name, false, false);
if (ps != NULL)
lev = GetCommandLogLevel(ps->plansource->raw_parse_tree);
else
lev = LOGSTMT_ALL;
} break;
case T_DeallocateStmt:
lev = LOGSTMT_ALL;
break;
case T_RenameStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterObjectSchemaStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterOwnerStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterTableStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterDomainStmt:
lev = LOGSTMT_DDL;
break;
case T_GrantStmt:
lev = LOGSTMT_DDL;
break;
case T_GrantRoleStmt:
lev = LOGSTMT_DDL;
break;
case T_GrantDbStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterDefaultPrivilegesStmt:
lev = LOGSTMT_DDL;
break;
case T_DefineStmt:
lev = LOGSTMT_DDL;
break;
case T_CreatePackageStmt:
lev = LOGSTMT_DDL;
break;
case T_CreatePackageBodyStmt:
lev = LOGSTMT_DDL;
break;
case T_CompositeTypeStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateEnumStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateRangeStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterEnumStmt:
lev = LOGSTMT_DDL;
break;
case T_ViewStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateFunctionStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterFunctionStmt:
case T_CompileStmt:
case T_CreateEventStmt:
case T_AlterEventStmt:
case T_DropEventStmt:
lev = LOGSTMT_DDL;
break;
case T_ShowEventStmt:
lev = LOGSTMT_ALL;
break;
case T_IndexStmt:
lev = LOGSTMT_DDL;
break;
case T_RuleStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateSeqStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterSeqStmt:
lev = LOGSTMT_DDL;
break;
case T_DoStmt:
lev = LOGSTMT_ALL;
break;
case T_CreatedbStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterDatabaseStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterDatabaseSetStmt:
lev = LOGSTMT_DDL;
break;
case T_DropdbStmt:
lev = LOGSTMT_DDL;
break;
case T_NotifyStmt:
lev = LOGSTMT_ALL;
break;
case T_ListenStmt:
lev = LOGSTMT_ALL;
break;
case T_UnlistenStmt:
lev = LOGSTMT_ALL;
break;
case T_LoadStmt:
lev = LOGSTMT_ALL;
break;
case T_ClusterStmt:
lev = LOGSTMT_DDL;
break;
case T_VacuumStmt:
lev = LOGSTMT_ALL;
break;
case T_ExplainStmt: {
ExplainStmt* stmt = (ExplainStmt*)parse_tree;
bool analyze = false;
ListCell* lc = NULL;
foreach (lc, stmt->options) {
DefElem* opt = (DefElem*)lfirst(lc);
if (strcmp(opt->defname, "analyze") == 0)
analyze = defGetBoolean(opt);
}
if (analyze)
return GetCommandLogLevel(stmt->query);
lev = LOGSTMT_ALL;
} break;
case T_CreateTableAsStmt:
lev = LOGSTMT_DDL;
break;
case T_RefreshMatViewStmt:
#ifndef ENABLE_MULTIPLE_NODES
case T_AlterSystemStmt:
#endif
lev = LOGSTMT_DDL;
break;
case T_VariableSetStmt:
lev = LOGSTMT_ALL;
break;
case T_VariableMultiSetStmt:
lev = LOGSTMT_ALL;
break;
case T_UserVar:
lev = LOGSTMT_ALL;
break;
case T_VariableShowStmt:
lev = LOGSTMT_ALL;
break;
case T_DiscardStmt:
lev = LOGSTMT_ALL;
break;
case T_CreateTrigStmt:
lev = LOGSTMT_DDL;
break;
case T_CreatePLangStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateDomainStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateRoleStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterRoleStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterRoleSetStmt:
lev = LOGSTMT_DDL;
break;
case T_DropRoleStmt:
lev = LOGSTMT_DDL;
break;
case T_DropOwnedStmt:
lev = LOGSTMT_DDL;
break;
case T_ReassignOwnedStmt:
lev = LOGSTMT_DDL;
break;
case T_LockStmt:
lev = LOGSTMT_ALL;
break;
case T_TimeCapsuleStmt:
lev = LOGSTMT_ALL;
break;
case T_ConstraintsSetStmt:
lev = LOGSTMT_ALL;
break;
case T_CheckPointStmt:
lev = LOGSTMT_ALL;
break;
case T_ReindexStmt:
lev = LOGSTMT_ALL;
break;
case T_CreateConversionStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateCastStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateOpClassStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateOpFamilyStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterOpFamilyStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterTSDictionaryStmt:
lev = LOGSTMT_DDL;
break;
case T_AlterTSConfigurationStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateAmStmt:
lev = LOGSTMT_DDL;
break;
case T_PlannedStmt: {
PlannedStmt* stmt = (PlannedStmt*)parse_tree;
switch (stmt->commandType) {
case CMD_SELECT:
lev = LOGSTMT_ALL;
break;
case CMD_UPDATE:
case CMD_INSERT:
case CMD_DELETE:
case CMD_MERGE:
lev = LOGSTMT_MOD;
break;
default:
elog(WARNING, "unrecognized commandType: %d", (int)stmt->commandType);
lev = LOGSTMT_ALL;
break;
}
} break;
case T_Query: {
Query* stmt = (Query*)parse_tree;
switch (stmt->commandType) {
case CMD_SELECT:
lev = LOGSTMT_ALL;
break;
case CMD_UPDATE:
case CMD_INSERT:
case CMD_DELETE:
case CMD_MERGE:
lev = LOGSTMT_MOD;
break;
case CMD_UTILITY:
lev = GetCommandLogLevel(stmt->utilityStmt);
break;
default:
elog(WARNING, "unrecognized commandType: %d", (int)stmt->commandType);
lev = LOGSTMT_ALL;
break;
}
} break;
case T_BarrierStmt:
case T_CreateNodeStmt:
case T_AlterNodeStmt:
case T_DropNodeStmt:
case T_CreateGroupStmt:
case T_AlterGroupStmt:
case T_DropGroupStmt:
case T_CreatePolicyLabelStmt:
case T_AlterPolicyLabelStmt:
case T_DropPolicyLabelStmt:
case T_CreateAuditPolicyStmt:
case T_AlterAuditPolicyStmt:
case T_DropAuditPolicyStmt:
case T_CreateMaskingPolicyStmt:
case T_AlterMaskingPolicyStmt:
case T_DropMaskingPolicyStmt:
case T_CreateResourcePoolStmt:
case T_AlterResourcePoolStmt:
case T_DropResourcePoolStmt:
case T_AlterGlobalConfigStmt:
case T_DropGlobalConfigStmt:
case T_CreateWorkloadGroupStmt:
case T_AlterWorkloadGroupStmt:
case T_DropWorkloadGroupStmt:
case T_CreateAppWorkloadGroupMappingStmt:
case T_AlterAppWorkloadGroupMappingStmt:
case T_DropAppWorkloadGroupMappingStmt:
case T_CreatePublicationStmt:
case T_AlterPublicationStmt:
case T_CreateSubscriptionStmt:
case T_AlterSubscriptionStmt:
case T_DropSubscriptionStmt:
lev = LOGSTMT_DDL;
break;
case T_ExecDirectStmt:
lev = LOGSTMT_ALL;
break;
#ifdef PGXC
case T_CleanConnStmt:
lev = LOGSTMT_DDL;
break;
#endif
case T_CreateDirectoryStmt:
case T_DropDirectoryStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateSynonymStmt:
case T_DropSynonymStmt:
lev = LOGSTMT_DDL;
break;
case T_CreateClientLogicGlobal:
case T_CreateClientLogicColumn:
lev = LOGSTMT_DDL;
break;
case T_ShutdownStmt:
lev = LOGSTMT_ALL;
break;
case T_CreateModelStmt:
lev = LOGSTMT_ALL;
break;
case T_GetDiagStmt:
lev = LOGSTMT_ALL;
break;
default:
elog(WARNING, "unrecognized node type: %d", (int)nodeTag(parse_tree));
lev = LOGSTMT_ALL;
break;
}
return lev;
}
#ifdef PGXC
* GetCommentObjectId
* Change to return the nodes to execute the utility on in future.
*
* Return Object ID of object commented
* Note: This function uses portions of the code of CommentObject,
* even if this code is duplicated this is done like this to facilitate
* merges with PostgreSQL head.
*/
static RemoteQueryExecType get_nodes_4_comment_utility(CommentStmt* stmt, bool* is_temp, ExecNodes** exec_nodes)
{
ObjectAddress address;
Relation relation;
RemoteQueryExecType exec_type = EXEC_ON_ALL_NODES;
Oid object_id;
if (exec_nodes != NULL)
*exec_nodes = NULL;
if (stmt->objtype == OBJECT_DATABASE && list_length(stmt->objname) == 1) {
char* database = strVal(linitial(stmt->objname));
if (!OidIsValid(get_database_oid(database, true)))
ereport(WARNING, (errcode(ERRCODE_UNDEFINED_DATABASE), errmsg("database \"%s\" does not exist", database)));
return exec_type;
}
address =
get_object_address(stmt->objtype, stmt->objname, stmt->objargs, &relation, ShareUpdateExclusiveLock, false);
object_id = address.objectId;
* If the object being commented is a rule, the nodes are decided by the
* object to which rule is applicable, so get the that object's oid
*/
if (stmt->objtype == OBJECT_RULE) {
if (!relation || !OidIsValid(relation->rd_id)) {
char* rulename = strVal(llast(stmt->objname));
ereport(WARNING,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("can not find relation for rule \"%s\" does not exist", rulename)));
object_id = InvalidOid;
} else {
object_id = RelationGetRelid(relation);
if (exec_nodes != NULL) {
*exec_nodes = RelidGetExecNodes(object_id, false);
}
}
}
if (stmt->objtype == OBJECT_CONSTRAINT) {
if (!relation || !OidIsValid(relation->rd_id)) {
char* constraintname = strVal(llast(stmt->objname));
ereport(WARNING,
(errcode(ERRCODE_UNDEFINED_OBJECT),
errmsg("can not find relation for constraint \"%s\" does not exist", constraintname)));
object_id = InvalidOid;
} else {
object_id = RelationGetRelid(relation);
if (exec_nodes != NULL) {
*exec_nodes = RelidGetExecNodes(object_id, false);
}
}
}
if (relation != NULL)
relation_close(relation, NoLock);
if (OidIsValid(object_id)) {
exec_type = ExecUtilityFindNodes(stmt->objtype, object_id, is_temp);
if (exec_nodes != NULL && (exec_type == EXEC_ON_DATANODES || exec_type == EXEC_ON_ALL_NODES)) {
switch (stmt->objtype) {
case OBJECT_TABLE:
case OBJECT_FOREIGN_TABLE:
case OBJECT_STREAM:
case OBJECT_INDEX:
case OBJECT_COLUMN:
*exec_nodes = RelidGetExecNodes(object_id, false);
break;
case OBJECT_FUNCTION:
*exec_nodes = GetFunctionNodes(object_id);
break;
default:
break;
}
}
}
return exec_type;
}
* get_nodes_4_rules_utility
* Get the nodes to execute this RULE related utility statement.
* A rule is expanded on Coordinator itself, and does not need any
* existence on Datanode. In fact, if it were to exist on Datanode,
* there is a possibility that it would expand again
*/
static RemoteQueryExecType get_nodes_4_rules_utility(RangeVar* relation, bool* is_temp)
{
Oid rel_id = RangeVarGetRelid(relation, NoLock, true);
RemoteQueryExecType exec_type;
if (!OidIsValid(rel_id))
return EXEC_ON_NONE;
* See if it's a temporary object, do we really need
* to care about temporary objects here? What about the
* temporary objects defined inside the rule?
*/
exec_type = ExecUtilityFindNodes(OBJECT_RULE, rel_id, is_temp);
return exec_type;
}
* TreatDropStmtOnCoord
* Do a pre-treatment of Drop statement on a remote Coordinator
*/
static void drop_stmt_pre_treatment(
DropStmt* stmt, const char* query_string, bool sent_to_remote, bool* is_temp, RemoteQueryExecType* exec_type)
{
bool res_is_temp = false;
RemoteQueryExecType res_exec_type = EXEC_ON_ALL_NODES;
#ifndef ENABLE_MULTIPLE_NODES
switch (stmt->removeType) {
case OBJECT_TABLE: {
* Check whether the tables are in blockchain schema.
*/
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(cell));
Oid rel_id = RangeVarGetRelid(rel, AccessShareLock, true);
if (is_ledger_hist_table(rel_id)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for userchain table.")));
}
if (OidIsValid(rel_id)) {
UnlockRelationOid(rel_id, AccessShareLock);
}
}
} break;
case OBJECT_SCHEMA: {
ListCell* cell = NULL;
foreach (cell, stmt->objects) {
List* objname = (List*)lfirst(cell);
char* name = NameListToString(objname);
if (get_namespace_oid(name, true) == PG_BLOCKCHAIN_NAMESPACE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for blockchain schema.")));
} else if (!u_sess->attr.attr_common.IsInplaceUpgrade &&
get_namespace_oid(name, true) == PG_SQLADVISOR_NAMESPACE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for sqladvisor schema.")));
} else if (!u_sess->attr.attr_common.IsInplaceUpgrade && IsPackageSchemaName(name)) {
ereport(ERROR,
(errmodule(MOD_COMMAND), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for %s schema.", name),
errdetail("Schemas in a package cannot be deleted."),
errcause("The schema in the package does not support object drop."),
erraction("N/A")));
}
}
} break;
default:
break;
}
#endif
if (IS_PGXC_DATANODE || IsConnFromCoord())
return;
* Check for shared-cache-inval messages before trying to access the
* relation. This is needed to cover the case where the name
* identifies a rel that has been dropped and recreated since the
* start of our transaction: if we don't flush the old syscache entry,
* then we'll latch onto that entry and suffer an error later.
*/
AcceptInvalidationMessages();
switch (stmt->removeType) {
case OBJECT_TABLE:
case OBJECT_SEQUENCE:
case OBJECT_LARGE_SEQUENCE:
case OBJECT_SEQUENCE_GSC:
case OBJECT_LARGE_SEQUENCE_GSC:
case OBJECT_VIEW:
case OBJECT_MATVIEW:
case OBJECT_CONTQUERY:
case OBJECT_INDEX: {
* Check the list of objects going to be dropped.
* XC does not allow yet to mix drop of temporary and
* non-temporary objects because this involves to rewrite
* query to process for tables.
*/
ListCell* cell = NULL;
bool is_first = true;
foreach (cell, stmt->objects) {
RangeVar* rel = makeRangeVarFromNameList((List*)lfirst(cell));
Oid rel_id;
* Do not print result at all, error is thrown
* after if necessary
* Notice : need get lock here to forbid parallel drop
*/
rel_id = RangeVarGetRelid(rel, AccessShareLock, true);
* In case this relation ID is incorrect throw
* a correct DROP error.
*/
if (!OidIsValid(rel_id) && !stmt->missing_ok)
DropTableThrowErrorExternal(rel, stmt->removeType, stmt->missing_ok);
if (!OidIsValid(rel_id) && stmt->missing_ok)
continue;
if (is_ledger_hist_table(rel_id)) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for userchain table.")));
}
if (is_first) {
res_exec_type = ExecUtilityFindNodes(stmt->removeType, rel_id, &res_is_temp);
is_first = false;
} else {
RemoteQueryExecType exec_type_loc;
bool is_temp_loc = false;
exec_type_loc = ExecUtilityFindNodes(stmt->removeType, rel_id, &is_temp_loc);
if (exec_type_loc != res_exec_type || is_temp_loc != res_is_temp)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for TEMP and non-TEMP objects"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
UnlockRelationOid(rel_id, AccessShareLock);
}
} break;
case OBJECT_SCHEMA: {
ListCell* cell = NULL;
bool has_temp = false;
bool has_nontemp = false;
foreach (cell, stmt->objects) {
List* objname = (List*)lfirst(cell);
char* name = NameListToString(objname);
if (isTempNamespaceName(name) || isToastTempNamespaceName(name)) {
has_temp = true;
res_exec_type = EXEC_ON_DATANODES;
} else {
has_nontemp = true;
}
if (get_namespace_oid(name, true) == PG_BLOCKCHAIN_NAMESPACE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for blockchain schema.")));
} else if (!u_sess->attr.attr_common.IsInplaceUpgrade &&
get_namespace_oid(name, true) == PG_SQLADVISOR_NAMESPACE) {
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for sqladvisor schema.")));
} else if (!u_sess->attr.attr_common.IsInplaceUpgrade && IsPackageSchemaName(name)) {
ereport(ERROR,
(errmodule(MOD_COMMAND), errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for %s schema.", name),
errdetail("Schemas in a package cannot be deleted."),
errcause("The schema in the package does not support object drop."),
erraction("N/A")));
}
if (has_temp && has_nontemp)
ereport(ERROR,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("DROP not supported for TEMP and non-TEMP objects"),
errdetail("You should separate TEMP and non-TEMP objects")));
}
res_is_temp = has_temp;
if (has_temp)
res_exec_type = EXEC_ON_DATANODES;
else
res_exec_type = EXEC_ON_ALL_NODES;
break;
}
* Those objects are dropped depending on the nature of the relationss
* they are defined on. This evaluation uses the temporary behavior
* and the relkind of the relation used.
*/
case OBJECT_RULE:
case OBJECT_TRIGGER: {
List* objname = (List*)linitial(stmt->objects);
Relation relation = NULL;
get_object_address(stmt->removeType, objname, NIL, &relation, AccessExclusiveLock, stmt->missing_ok);
if (relation && OidIsValid(relation->rd_id))
res_exec_type = ExecUtilityFindNodes(stmt->removeType, relation->rd_id, &res_is_temp);
else
res_exec_type = EXEC_ON_NONE;
if (relation)
relation_close(relation, NoLock);
} break;
case OBJECT_RLSPOLICY:
res_is_temp = false;
res_exec_type = EXEC_ON_COORDS;
break;
default:
res_is_temp = false;
res_exec_type = EXEC_ON_ALL_NODES;
break;
}
*is_temp = res_is_temp;
*exec_type = res_exec_type;
}
#endif
char* VariableBlackList[] = {
"client_encoding"
};
bool IsVariableinBlackList(const char* name)
{
if (name == NULL) {
return false;
}
bool isInBlackList = false;
int blacklistLen = sizeof(VariableBlackList) / sizeof(char*);
for (int i = 0; i < blacklistLen; i++) {
char* blackname = VariableBlackList[i];
AssertEreport(blackname, MOD_EXECUTOR, "the character string is NULL");
if (0 == pg_strncasecmp(blackname, name, strlen(blackname))) {
isInBlackList = true;
break;
}
}
return isInBlackList;
}
* Check if the object is in blacklist, if true, ALTER/DROP operation of the object is disabled.
* Note that only prescribed extensions are able droppable.
*/
void CheckObjectInBlackList(ObjectType obj_type, const char* query_string)
{
const char* tag = NULL;
bool is_not_extended_feature = false;
switch (obj_type) {
case OBJECT_EXTENSION:
return;
#ifdef ENABLE_MULTIPLE_NODES
case OBJECT_AGGREGATE:
tag = "AGGREGATE";
break;
#endif
case OBJECT_OPERATOR:
tag = "OPERATOR";
break;
#ifdef ENABLE_MULTIPLE_NODES
case OBJECT_OPCLASS:
tag = "OPERATOR CLASS";
break;
#endif
case OBJECT_OPFAMILY:
tag = "OPERATOR FAMILY";
break;
case OBJECT_COLLATION:
tag = "COLLATION";
break;
#ifdef ENABLE_MULTIPLE_NODES
case OBJECT_RULE:
tag = "RULE";
break;
#endif
case OBJECT_TSDICTIONARY:
case OBJECT_TSCONFIGURATION:
ts_check_feature_disable();
return;
case OBJECT_TSPARSER:
is_not_extended_feature = true;
tag = "TEXT SEARCH PARSER";
break;
case OBJECT_TSTEMPLATE:
is_not_extended_feature = true;
tag = "TEXT SEARCH TEMPLATE";
break;
case OBJECT_LANGUAGE:
tag = "LANGUAGE";
break;
case OBJECT_DOMAIN:
tag = "DOMAIN";
break;
case OBJECT_CONVERSION:
tag = "CONVERSION";
break;
case OBJECT_FDW:
tag = "FOREIGN DATA WRAPPER";
break;
default:
return;
}
if (!u_sess->attr.attr_common.IsInplaceUpgrade) {
if (is_not_extended_feature && !IsInitdb) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s is not yet supported.", tag)));
} else if ((!g_instance.attr.attr_common.support_extended_features) &&
(!(obj_type == OBJECT_RULE && u_sess->attr.attr_sql.enable_cluster_resize))) {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), errmsg("%s is not yet supported.", tag)));
}
}
}
* CheckObjectInBlackList
*
* Check if the given extension is supported. If hashCheck is true,
* the hash value of the sql script will be checked.
*/
bool CheckExtensionInWhiteList(const char* extension_name, uint32 hash_value, bool hash_check)
{
#ifdef ENABLE_MULTIPLE_NODES
uint32 postgisHashHistory[POSTGIS_VERSION_NUM] = {2902411162, 2959454932};
if (pg_strcasecmp(extension_name, "postgis") == 0) {
if (is_feature_disabled(POSTGIS_DOCKING)) {
return false;
}
if (hash_check && !isSecurityMode) {
for (int i = 0; i < POSTGIS_VERSION_NUM; i++) {
if (hash_value == postgisHashHistory[i]) {
return true;
}
}
} else {
return true;
}
}
return false;
#endif
return true;
}
#ifdef ENABLE_MULTIPLE_NODES
* @hdfs
* notify_other_cn_get_statistics
*
* We send hybridMessage to each other coordinator node. These nodes get statistics from
* predefined data node represented in hybridMessage.
*/
static void notify_other_cn_get_statistics(const char* hybrid_message, bool sent_to_remote)
{
ExecNodes* exec_nodes = NULL;
int node_index;
exec_nodes = makeNode(ExecNodes);
for (node_index = 0; node_index < u_sess->pgxc_cxt.NumCoords; node_index++) {
if (node_index != PGXCNodeGetNodeIdFromName(g_instance.attr.attr_common.PGXCNodeName, PGXC_NODE_COORDINATOR))
exec_nodes->nodeList = lappend_int(exec_nodes->nodeList, node_index);
}
ExecUtilityStmtOnNodes(hybrid_message, exec_nodes, sent_to_remote, true, EXEC_ON_COORDS, false);
FreeExecNodes(&exec_nodes);
* for VACUUM ANALYZE table where options=3, vacuum has set use_own_xacts
* true and already poped ActiveSnapshot, so we don't want to pop again.
*/
if (ActiveSnapshotSet()) {
PopActiveSnapshot();
}
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
}
#endif
* @hdfs
* AssembleHybridMessage
*
* Assembel hybridmessage. Copy query_string and scheduling_message into query_string_with_info.
*/
void AssembleHybridMessage(char** query_string_with_info, const char* query_string, const char* scheduling_message)
{
unsigned int query_string_len = strlen(query_string);
unsigned int scheduling_message_len = strlen(scheduling_message);
unsigned int pos = 0;
unsigned int alloc_len = 0;
errno_t errorno = EOK;
Const* n = makeNode(Const);
n->constvalue = UInt32GetDatum(query_string_len);
n->constisnull = false;
n->constlen = sizeof(uint32);
n->constbyval = true;
char* query_len_const = nodeToString(n);
alloc_len = 2 + strlen(query_len_const) + strlen(query_string) + strlen(scheduling_message);
*query_string_with_info = (char*)MemoryContextAlloc(t_thrd.mem_cxt.msg_mem_cxt, alloc_len);
const size_t head_len = 1;
errorno = memcpy_s(*query_string_with_info + pos, head_len, "h", head_len);
securec_check(errorno, "\0", "\0");
pos += head_len;
errorno = memcpy_s(*query_string_with_info + pos, strlen(query_len_const), query_len_const, strlen(query_len_const));
securec_check(errorno, "\0", "\0");
pos += strlen(query_len_const);
errorno = memcpy_s(*query_string_with_info + pos, query_string_len, query_string, query_string_len);
securec_check(errorno, "\0", "\0");
pos += query_string_len;
errorno = memcpy_s(*query_string_with_info + pos, scheduling_message_len, scheduling_message, scheduling_message_len);
securec_check(errorno, "\0", "\0");
pos += scheduling_message_len;
const size_t end_len = 1;
errorno = memcpy_s(*query_string_with_info + pos, end_len, "\0", end_len);
securec_check(errorno, "\0", "\0");
pfree_ext(n);
pfree_ext(query_len_const);
}
#ifdef ENABLE_MULTIPLE_NODES
* @hdfs
* get_scheduling_message
*
* In this function we call CNSchedulingForAnalyze to get scheduling information. The exact count of file which
* will be analyzed is stored in totalFileCnt. At the same time, CNSchedulingForAnalyze function selects a data
* node to execute analyze operation. The selected datanode number is stored in nodeNo.
*/
static char* get_scheduling_message(const Oid foreign_table_id, VacuumStmt* stmt)
{
HDFSTableAnalyze* hdfs_table_analyze = makeNode(HDFSTableAnalyze);
List* dn_task = NIL;
dn_task = CNSchedulingForAnalyze(&stmt->totalFileCnt, &stmt->DnCnt, foreign_table_id, true);
hdfs_table_analyze->DnCnt = 0;
stmt->nodeNo = 0;
stmt->hdfsforeignMapDnList = NIL;
* There is a risk that dn_task can be null. We mush process this situation
* It means that we call CNSchedulingForAnalyze failed.
*/
if (dn_task != NIL) {
bool first = true;
ListCell* taskCell = NULL;
if (!IS_OBS_CSV_TXT_FOREIGN_TABLE(foreign_table_id)) {
SplitMap* task_map = NULL;
foreach (taskCell, dn_task) {
task_map = (SplitMap*)lfirst(taskCell);
if (task_map->splits != NIL) {
if (first) {
hdfs_table_analyze->DnCnt = stmt->DnCnt;
stmt->nodeNo = task_map->nodeId;
first = false;
}
stmt->hdfsforeignMapDnList = lappend_int(stmt->hdfsforeignMapDnList, task_map->nodeId);
}
}
} else {
DistFdwDataNodeTask* task_map = NULL;
first = true;
foreach (taskCell, dn_task) {
task_map = (DistFdwDataNodeTask*)lfirst(taskCell);
if (NIL != task_map->task) {
if (first) {
hdfs_table_analyze->DnCnt = stmt->DnCnt;
Oid nodeOid = get_pgxc_nodeoid(task_map->dnName);
stmt->nodeNo = PGXCNodeGetNodeId(nodeOid, PGXC_NODE_DATANODE);
first = false;
}
stmt->hdfsforeignMapDnList = lappend_int(stmt->hdfsforeignMapDnList, stmt->nodeNo);
}
}
}
}
hdfs_table_analyze->DnWorkFlow = dn_task;
return nodeToString(hdfs_table_analyze);
}
#endif
* @Description: attatch sample rate to the query_string for global stats
* @out query_string_with_info - the query string with analyze command and schedulingMessag
* @in stmt - the statment for analyze or vacuum command
* @in query_string - the query string for analyze or vacuum command
* @in rel_id - the relation oid for analyze or vacuum
* @in has_var - the flag identify do analyze for one relation or all database
* @in e_analyze_mode - identify hdfs table or normal table
* @in rel_id - the hdfs foreign table oid. others, it is invalid for other tables
* @in foreign_tbl_schedul_message - identify the schedul info for hdfs foreign table
*
* @return: void
*/
static void attatch_global_info(char** query_string_with_info, VacuumStmt* stmt, const char* query_string, bool has_var,
AnalyzeMode e_analyze_mode, Oid rel_id, char* foreign_tbl_schedul_message)
{
SplitMap* task_map = makeNode(SplitMap);
HDFSTableAnalyze* hdfs_table_analyze = NULL;
char* global_info = NULL;
if (!stmt->isForeignTables || stmt->isPgFdwForeignTables) {
hdfs_table_analyze = makeNode(HDFSTableAnalyze);
if (e_analyze_mode == ANALYZENORMAL) {
hdfs_table_analyze->isHdfsStore = false;
hdfs_table_analyze->sampleRate[0] = stmt->pstGlobalStatEx[0].sampleRate;
} else {
hdfs_table_analyze->isHdfsStore = true;
for (int i = 0; i < ANALYZE_MODE_MAX_NUM - 1; i++)
hdfs_table_analyze->sampleRate[i] = stmt->pstGlobalStatEx[i].sampleRate;
}
task_map->locatorType = LOCATOR_TYPE_NONE;
task_map->splits = NIL;
hdfs_table_analyze->DnCnt = 0;
hdfs_table_analyze->DnWorkFlow = lappend(hdfs_table_analyze->DnWorkFlow, task_map);
hdfs_table_analyze->isHdfsForeignTbl = false;
} else {
* Call scheduler to get datanode which will execute analyze operation
* and file list.
*/
if (foreign_tbl_schedul_message != NULL) {
hdfs_table_analyze = (HDFSTableAnalyze*)stringToNode(foreign_tbl_schedul_message);
} else {
hdfs_table_analyze = makeNode(HDFSTableAnalyze);
task_map->locatorType = LOCATOR_TYPE_NONE;
task_map->splits = NIL;
hdfs_table_analyze->DnCnt = 0;
hdfs_table_analyze->DnWorkFlow = lappend(hdfs_table_analyze->DnWorkFlow, task_map);
}
hdfs_table_analyze->isHdfsStore = false;
hdfs_table_analyze->sampleRate[0] = stmt->pstGlobalStatEx[0].sampleRate;
hdfs_table_analyze->isHdfsForeignTbl = true;
}
hdfs_table_analyze->orgCnNodeNo =
PGXCNodeGetNodeIdFromName(g_instance.attr.attr_common.PGXCNodeName, PGXC_NODE_COORDINATOR);
hdfs_table_analyze->sampleTableRequired = stmt->sampleTableRequired;
hdfs_table_analyze->tmpSampleTblNameList = stmt->tmpSampleTblNameList;
hdfs_table_analyze->disttype = stmt->disttype;
hdfs_table_analyze->memUsage.work_mem = stmt->memUsage.work_mem;
hdfs_table_analyze->memUsage.max_mem = stmt->memUsage.max_mem;
global_info = nodeToString(hdfs_table_analyze);
if (has_var) {
AssembleHybridMessage(query_string_with_info, query_string, global_info);
} else {
AssertEreport(query_string != NULL, MOD_EXECUTOR, "Invalid NULL value");
if (!stmt->isForeignTables || stmt->isPgFdwForeignTables) {
StringInfo stringptr = makeStringInfo();
* If the query comes from JDBC connecion, there is without ';'
* at the end of query_string.
*/
const char* position = NULL;
position = strrchr(query_string, ';');
if (position == NULL) {
appendBinaryStringInfo(stringptr, query_string, strlen(query_string));
} else {
bool hasSemicolon = true;
position += 1;
while (*position != '\0') {
if (!((*position == ' ') || (*position == '\n') || (*position == '\t') || (*position == '\r') ||
(*position == '\f') || (*position == '\v'))) {
appendBinaryStringInfo(stringptr, query_string, strlen(query_string));
hasSemicolon = false;
break;
}
++position;
}
if (hasSemicolon) {
appendBinaryStringInfo(stringptr, query_string, strlen(query_string) - 1);
}
}
appendStringInfoSpaces(stringptr, 1);
appendStringInfoString(stringptr, quote_identifier(stmt->relation->schemaname));
appendStringInfoChar(stringptr, '.');
appendStringInfoString(stringptr, quote_identifier(stmt->relation->relname));
appendStringInfoChar(stringptr, ';');
AssembleHybridMessage(query_string_with_info, stringptr->data, global_info);
pfree_ext(stringptr->data);
pfree_ext(stringptr);
} else {
* Assemble ForeignTableDesc and put it into workList.
* We will use items in workList to apply analzye operation
*/
ForeignTableDesc* foreignTableDesc = makeNode(ForeignTableDesc);
foreignTableDesc->tableName = (char*)quote_identifier(stmt->relation->relname);
foreignTableDesc->tableOid = rel_id;
foreignTableDesc->schemaName = (char*)quote_identifier(stmt->relation->schemaname);
*query_string_with_info = get_hybrid_message(foreignTableDesc, stmt, global_info);
pfree_ext(foreignTableDesc);
}
}
pfree_ext(task_map);
pfree_ext(hdfs_table_analyze);
pfree_ext(global_info);
}
* @hdfs
* get_hybrid_message
*
* This function uses parameter table_desc generating hybridmessage which will be sent
* to data node.
*/
static char* get_hybrid_message(ForeignTableDesc* table_desc, VacuumStmt* stmt, char* foreign_tbl_schedul_message)
{
char* table_name = table_desc->tableName;
char* schema_name = table_desc->schemaName;
size_t table_len = strlen(table_name);
size_t schema_len = strlen(schema_name);
const char* analyze_key_words = "analyze \0";
const char* verbose_key_words = "verbose \0";
unsigned int analyze_key_words_len = strlen(analyze_key_words);
unsigned int verbose_key_words_len = strlen(verbose_key_words);
char* query_string = NULL;
int pos = 0;
char* scheduling_message = NULL;
errno_t errorno = EOK;
scheduling_message = foreign_tbl_schedul_message;
* If SQL sentence includes "verbose" command, we write verbose keyword into query_string.
*/
if (stmt->options & VACOPT_VERBOSE) {
query_string =
(char*)palloc(analyze_key_words_len + verbose_key_words_len + table_len + schema_len + 3);
errorno = memcpy_s(query_string, analyze_key_words_len, analyze_key_words, analyze_key_words_len);
securec_check(errorno, "\0", "\0");
pos += analyze_key_words_len;
errorno = memcpy_s(query_string + pos, verbose_key_words_len, verbose_key_words, verbose_key_words_len);
securec_check(errorno, "\0", "\0");
pos += verbose_key_words_len;
errorno = memcpy_s(query_string + pos, schema_len, schema_name, schema_len);
securec_check(errorno, "\0", "\0");
const size_t spilt_len = 1;
pos += schema_len;
errorno = memcpy_s(query_string + pos, spilt_len, ".", spilt_len);
securec_check(errorno, "\0", "\0");
pos += spilt_len;
errorno = memcpy_s(query_string + pos, table_len, table_name, table_len);
securec_check(errorno, "\0", "\0");
pos += table_len;
errorno = memcpy_s(query_string + pos, spilt_len, ";", spilt_len);
securec_check(errorno, "\0", "\0");
pos += spilt_len;
query_string[pos] = '\0';
} else {
query_string = (char*)palloc(analyze_key_words_len + table_len + schema_len + 3);
errorno = memcpy_s(query_string + pos, analyze_key_words_len, analyze_key_words, analyze_key_words_len);
securec_check(errorno, "\0", "\0");
pos += analyze_key_words_len;
errorno = memcpy_s(query_string + pos, schema_len, schema_name, schema_len);
securec_check(errorno, "\0", "\0");
const size_t spilt_len = 1;
pos += schema_len;
errorno = memcpy_s(query_string + pos, spilt_len, ".", spilt_len);
securec_check(errorno, "\0", "\0");
pos += spilt_len;
errorno = memcpy_s(query_string + pos, table_len, table_name, table_len);
securec_check(errorno, "\0", "\0");
pos += table_len;
errorno = memcpy_s(query_string + pos, spilt_len, ";", spilt_len);
securec_check(errorno, "\0", "\0");
pos += spilt_len;
query_string[pos] = '\0';
}
char* query_string_with_info = NULL;
AssembleHybridMessage(&query_string_with_info, query_string, scheduling_message);
pfree_ext(query_string);
return query_string_with_info;
}
* @hdfs
* IsForeignTableAnalyze
*
* A table is foreign table or not. If it is a foreign tabel, does it support analyze operation?
*/
bool IsHDFSForeignTableAnalyzable(VacuumStmt* stmt)
{
Oid foreign_table_id = 0;
bool ret_value = false;
stmt->isPgFdwForeignTables = false;
#ifdef ENABLE_MOT
stmt->isMOTForeignTable = false;
#endif
if (stmt->isForeignTables == true) {
ret_value = true;
} else if (!stmt->isForeignTables && !stmt->relation) {
ret_value = false;
} else {
foreign_table_id = RangeVarGetRelidExtended(stmt->relation, NoLock, false, false, false, true, NULL, NULL);
* A MPP local table or foreign table? If it is a foreign table, support analyze or not?
* False for MPP Local table and foreign table which does not support analyze operation.
*/
ret_value = IsHDFSTableAnalyze(foreign_table_id);
stmt->isForeignTables = ret_value;
if (IsPGFDWTableAnalyze(foreign_table_id)) {
stmt->isPgFdwForeignTables = true;
}
#ifdef ENABLE_MOT
if (IsMOTForeignTable(foreign_table_id)) {
stmt->isMOTForeignTable = true;
}
#endif
}
return ret_value;
}
#ifdef ENABLE_MULTIPLE_NODES
static void cn_do_vacuum_full_mpp_table(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
Oid relation_id = InvalidOid;
ExecNodes* exec_nodes = NULL;
RemoteQueryExecType exec_type = EXEC_ON_DATANODES;
if (stmt->relation) {
relation_id = RangeVarGetRelid(stmt->relation, NoLock, false);
if (!IsTempTable(relation_id))
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, true, EXEC_ON_COORDS, false);
}
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
PushActiveSnapshot(GetTransactionSnapshot());
pgxc_lock_for_utility_stmt(NULL, false);
if (!superuser() && in_logic_cluster()) {
* In logic cluster mode, superuser and system DBA can execute Vacuum Full
* on all nodes; logic cluster users can execute Vacuum Full on its node group;
* other users can't execute Vacuum Full in DN nodes.
*/
Oid curr_group_oid = get_current_lcgroup_oid();
if (OidIsValid(curr_group_oid)) {
exec_nodes = GetNodeGroupExecNodes(curr_group_oid);
} else {
exec_type = EXEC_ON_NONE;
ereport(NOTICE,
(errmsg("Vacuum Full do not run in DNs because User \"%s\" don't "
"attach to any logic cluster.",
GetUserNameFromId(GetUserId()))));
}
}
query_string = ConstructMesageWithMemInfo(query_string, stmt->memUsage);
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, true, exec_type, false, (Node*)stmt);
FreeExecNodes(&exec_nodes);
PopActiveSnapshot();
CommitTransactionCommand();
StartTransactionCommand();
}
* Code below is from ExecUtilityStmtOnNodes(), just difference
* is step->combine_type, means that don't care any result from DN.
*/
static void send_delta_merge_req(const char* query_string)
{
RemoteQuery* step = makeNode(RemoteQuery);
step->combine_type = COMBINE_TYPE_NONE;
step->exec_nodes = NULL;
step->sql_statement = pstrdup(query_string);
step->force_autocommit = true;
step->exec_type = EXEC_ON_DATANODES;
step->is_temp = false;
ExecRemoteUtility(step);
pfree_ext(step->sql_statement);
pfree_ext(step);
}
* if enable_upgrade_merge_lock_mode is true, block all insert/delete/
* update on all CNs when deltamerge the same table under ExclusiveLock.
*/
static void delta_merge_with_exclusive_lock(VacuumStmt* stmt)
{
int i = 0;
const int retry_times = 3;
t_thrd.vacuum_cxt.vac_context = AllocSetContextCreate(t_thrd.mem_cxt.portal_mem_cxt,
"Vacuum Deltamerge",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
List* dfsRels = get_rel_oids(InvalidOid, stmt);
PopActiveSnapshot();
CommitTransactionCommand();
ListCell* lc = NULL;
foreach (lc, dfsRels) {
vacuum_object* vo = NULL;
Relation rel = NULL;
StringInfo query = NULL;
char* schema_name = NULL;
vo = (vacuum_object*)lfirst(lc);
if (vacuumSubParent(vo->flags)) {
continue;
}
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
LOCKMODE lockmode = vo->is_tsdb_deltamerge ? ShareUpdateExclusiveLock : AccessExclusiveLock;
* try to lock table on local CN with no long wait for three times,
* if the lock is unavailable, just skip the current relation.
*/
for (i = 0; i < retry_times; i++) {
if (ConditionalLockRelationOid(vo->tab_oid, lockmode)) {
rel = try_relation_open(vo->tab_oid, NoLock);
break;
}
* except the last time, we sleep for a while(30s, 60s) and then try to
* lock the relation again.
*/
if (i + 1 < retry_times)
sleep((i + 1) * 30);
}
if (rel == NULL) {
elog(LOG, "delta merge skip relation %d, because the relation can not be open.", vo->tab_oid);
PopActiveSnapshot();
CommitTransactionCommand();
continue;
}
schema_name = get_namespace_name(rel->rd_rel->relnamespace);
if (schema_name == NULL) {
ereport(ERROR,
(errcode(ERRCODE_INVALID_SCHEMA_NAME), errmsg("Invalid schema oid: %u", rel->rd_rel->relnamespace)));
}
query = makeStringInfo();
appendStringInfo(query, "vacuum deltamerge \"%s\".\"%s\"", schema_name, rel->rd_rel->relname.data);
elog(DEBUG1, "%s under ExclusiveLock mode.", query->data);
* "deltamerge" on other remote CNs do nothing except locking table.
* NOTE: deltamerge on all CNs is in ONE transaction. it will block
* insert/delete/update on all CNs.
*/
ExecUtilityStmtOnNodes(query->data, NULL, false, false, EXEC_ON_COORDS, false);
* do real job on all DNs.
* NOTE: deltamerge on DNs is in local transaction.
*/
send_delta_merge_req(query->data);
relation_close(rel, NoLock);
* Be carefull to use this function, only in the following condition:
* 1. the relation can not be in opening state;
* 2. the pointer of the rel cache can not be referenced by other place;
*/
RelationForgetRelation(RelationGetRelid(rel));
pfree_ext(query->data);
pfree_ext(query);
PopActiveSnapshot();
CommitTransactionCommand();
}
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
MemoryContextDelete(t_thrd.vacuum_cxt.vac_context);
t_thrd.vacuum_cxt.vac_context = NULL;
}
#endif
* @Description: Check whether only one temp relation for analyze
* @Return true if analyze one temp relation or false
*/
bool IsAnalyzeTempRel(VacuumStmt* stmt)
{
bool is_local_tmp_table = false;
if (!MemoryContextIsValid(t_thrd.vacuum_cxt.vac_context))
t_thrd.vacuum_cxt.vac_context = NULL;
if (stmt->options & VACOPT_ANALYZE) {
if (stmt->relation) {
Oid rel_id = RangeVarGetRelid(stmt->relation, AccessShareLock, false);
Relation rel = relation_open(rel_id, AccessShareLock);
if (RelationIsLocalTemp(rel)) {
is_local_tmp_table = true;
}
relation_close(rel, NoLock);
}
}
return is_local_tmp_table;
}
#ifdef ENABLE_MULTIPLE_NODES
static void cn_do_vacuum_mpp_table(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
ExecNodes* exec_nodes = NULL;
RemoteQueryExecType exec_type = EXEC_ON_DATANODES;
bool is_vacuum = false;
if (stmt->relation == NULL && !superuser() && in_logic_cluster()) {
* In logic cluster mode, superuser and system DBA can execute Vacuum Full
* on all nodes; logic cluster users can execute Vacuum on its node group;
* other users can't execute Vacuum in DN nodes.
*/
Oid curr_group_oid = get_current_lcgroup_oid();
if (OidIsValid(curr_group_oid)) {
exec_nodes = GetNodeGroupExecNodes(curr_group_oid);
} else {
exec_type = EXEC_ON_NONE;
ereport(NOTICE,
(errmsg("Vacuum do not run in DNs because User \"%s\" don't "
"attach to any logic cluster.",
GetUserNameFromId(GetUserId()))));
}
}
is_vacuum = (stmt->options & VACOPT_VACUUM) && (!(stmt->options & VACOPT_FULL));
if (stmt->relation == NULL || (!ISMATMAP(stmt->relation->relname) && !ISMLOG(stmt->relation->relname))) {
ExecUtilityStmtOnNodes(query_string, exec_nodes, sent_to_remote, true, exec_type, false, (Node*)stmt);
}
FreeExecNodes(&exec_nodes);
if (!IS_ONLY_ANALYZE_TMPTABLE) {
PopActiveSnapshot();
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
} else {
CommandCounterIncrement();
}
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
}
static List* get_analyzable_matviews(Relation pg_class)
{
Oid rel_oid;
TableScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
List* oid_list = NIL;
ScanKeyInit(&key, Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_MATVIEW));
scan = tableam_scan_begin(pg_class, SnapshotNow, 1, &key);
while ((tuple = (HeapTuple) tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
Form_pg_class class_form = (Form_pg_class)GETSTRUCT(tuple);
if (class_form->relkind == RELKIND_MATVIEW) {
rel_oid = HeapTupleGetOid(tuple);
if (class_form->relnamespace == CSTORE_NAMESPACE || IsToastNamespace(class_form->relnamespace) ||
isOtherTempNamespace(class_form->relnamespace) ||
(isAnyTempNamespace(class_form->relnamespace) && !checkGroup(rel_oid, true))) {
continue;
}
if (rel_oid < FirstNormalObjectId || IsInheritor(rel_oid)) {
continue;
}
if (!superuser() && in_logic_cluster()) {
Oid curr_group_oid = get_current_lcgroup_oid();
Oid table_group_oid = get_pgxc_class_groupoid(rel_oid);
if (curr_group_oid != table_group_oid)
continue;
}
if (ISMATMAP(class_form->relname.data) || ISMLOG(class_form->relname.data)) {
continue;
}
oid_list = lappend_oid(oid_list, rel_oid);
}
}
tableam_scan_end(scan);
return oid_list;
}
* The SQL command is just "analyze" without table specified, so
* we must find all the plain tables from pg_class.
*/
static List* get_analyzable_relations(bool is_foreign_tables)
{
Relation pgclass;
TableScanDesc scan;
HeapTuple tuple;
ScanKeyData key;
Oid rel_oid;
List* oid_list = NIL;
pgclass = heap_open(RelationRelationId, AccessShareLock);
if (!is_foreign_tables) {
ScanKeyInit(&key, Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_RELATION));
scan = tableam_scan_begin(pgclass, SnapshotNow, 1, &key);
} else {
ScanKeyInit(&key, Anum_pg_class_relkind, BTEqualStrategyNumber, F_CHAREQ, CharGetDatum(RELKIND_FOREIGN_TABLE));
scan = tableam_scan_begin(pgclass, SnapshotNow, 1, &key);
}
while ((tuple = (HeapTuple)tableam_scan_getnexttuple(scan, ForwardScanDirection)) != NULL) {
Form_pg_class class_form = (Form_pg_class)GETSTRUCT(tuple);
if ((!is_foreign_tables && (class_form->relkind == RELKIND_RELATION || class_form->relkind == RELKIND_MATVIEW)) ||
(is_foreign_tables && (class_form->relkind == RELKIND_FOREIGN_TABLE))) {
rel_oid = HeapTupleGetOid(tuple);
if (!is_foreign_tables) {
we skip some collect of relations, for example in CSTORE namespace,
skip other temp namespace, and our own invalid temp namespace. */
if (class_form->relnamespace == CSTORE_NAMESPACE || IsToastNamespace(class_form->relnamespace) ||
isOtherTempNamespace(class_form->relnamespace) ||
(isAnyTempNamespace(class_form->relnamespace) && !checkGroup(rel_oid, true))) {
continue;
}
if (rel_oid < FirstNormalObjectId || IsInheritor(rel_oid))
continue;
} else {
* we should be sure every foreign table in pg_class is hdfs foreign table
* if we do analyze command for hdfs foreign table.
*/
if (!IsHDFSTableAnalyze(rel_oid) && !IsPGFDWTableAnalyze(rel_oid))
continue;
}
if (!superuser() && in_logic_cluster()) {
Oid curr_group_oid = get_current_lcgroup_oid();
Oid table_group_oid = get_pgxc_class_groupoid(rel_oid);
if (curr_group_oid != table_group_oid)
continue;
}
if ((strncmp(class_form->relname.data, MATMAPNAME, MATMAPLEN) == 0) ||
(strncmp(class_form->relname.data, MLOGNAME, MLOGLEN) == 0)) {
continue;
}
oid_list = lappend_oid(oid_list, rel_oid);
}
}
tableam_scan_end(scan);
List *matview_oids = get_analyzable_matviews(pgclass);
oid_list = list_concat_unique_oid(oid_list, matview_oids);
heap_close(pgclass, AccessShareLock);
return oid_list;
}
static void do_global_analyze_pgfdw_Rel(
VacuumStmt* stmt, Oid rel_id, const char* query_string, bool has_var, bool sent_to_remote, bool is_top_level)
{
Assert(!IS_PGXC_DATANODE);
FdwRoutine* fdw_routine = GetFdwRoutineByRelId(rel_id);
bool ret_value = false;
PGFDWTableAnalyze* info = (PGFDWTableAnalyze*)palloc0(sizeof(PGFDWTableAnalyze));
info->relid = rel_id;
volatile bool use_own_xacts = true;
if (use_own_xacts) {
if (ActiveSnapshotSet())
PopActiveSnapshot();
CommitTransactionCommand();
}
if (use_own_xacts) {
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
}
if (NULL != fdw_routine->AnalyzeForeignTable) {
ret_value = fdw_routine->AnalyzeForeignTable(NULL, NULL, NULL, (void*)info, false);
}
FetchGlobalPgfdwStatistics(stmt, has_var, info);
if (use_own_xacts) {
PopActiveSnapshot();
CommitTransactionCommand();
}
if (use_own_xacts) {
* This matches the CommitTransaction waiting for us in
* PostgresMain().
*/
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
}
if (has_var) {
char* query_string_with_info = NULL;
stmt->orgCnNodeNo = PGXCNodeGetNodeIdFromName(g_instance.attr.attr_common.PGXCNodeName, PGXC_NODE_COORDINATOR);
attatch_global_info(&query_string_with_info, stmt, query_string, has_var, ANALYZENORMAL, rel_id, NULL);
if (!IsConnFromCoord()) {
notify_other_cn_get_statistics(query_string_with_info, sent_to_remote);
}
if (query_string_with_info != NULL) {
pfree_ext(query_string_with_info);
query_string_with_info = NULL;
}
}
}
* @global stats
* @Description: do analyze for one rel
* @in stmt - the statment for analyze or vacuum command
* @in rel_id - the relation oid for analyze or vacuum
* @in query_string - the query string for analyze or vacuum command
* @in is_replication - the flag identify the relation which analyze or vacuum is replication or not
* @in has_var - the flag identify do analyze for one relation or all database
* @in attnum - how many attribute num of the relation
* @in sent_to_remote - identify if this statement has been sent to the nodes
* @in is_top_level - is_top_level should be passed down from ProcessUtility
* @return: void
*
* step 1: get estimate total rows from DNs
* step 2: compute sample rate
* step 3: get sample rows from DNs
* step 4: get real total tuples from pg_class in DNs except replication table
* step 5: get stadndistinct from DN1
* step 6: compute statistics and update in pg_statistics
*/
static void do_global_analyze_rel(VacuumStmt* stmt, Oid rel_id, const char* query_string, bool is_replication, bool has_var,
int attnum, bool sent_to_remote, bool is_top_level, int* table_num)
{
char* query_string_with_info = NULL;
char* foreign_tbl_schedul_message = NULL;
stmt->tableidx = ANALYZENORMAL;
int one_tuple_size = 0;
if (!check_analyze_permission(rel_id)) {
(*table_num)--;
return;
}
if (*table_num == 1) {
Relation rel = relation_open(rel_id, AccessShareLock);
one_tuple_size = GetOneTupleSize(stmt, rel);
relation_close(rel, AccessShareLock);
}
* if it is a REPLICATION table, we set stmt->totalRowCnts = 0, then the sample rate=0, DNs send 0 rows to CN.
* then CN do not compute stats and just fetch stats from DN1. same for System Relation
*/
if (is_replication) {
stmt->nodeNo = stmt->isForeignTables ? stmt->nodeNo : 0;
stmt->pstGlobalStatEx[ANALYZENORMAL].totalRowCnts = 0;
stmt->pstGlobalStatEx[ANALYZENORMAL].topRowCnts = 0;
stmt->pstGlobalStatEx[ANALYZENORMAL].topMemSize = DEFAULT_SAMPLE_ROWCNT * one_tuple_size / 1024;
stmt->num_samples = 0;
stmt->pstGlobalStatEx[ANALYZENORMAL].isReplication = true;
} else {
global_stats_set_samplerate(ANALYZENORMAL, stmt, NULL);
stmt->pstGlobalStatEx[ANALYZENORMAL].isReplication = false;
stmt->pstGlobalStatEx[ANALYZENORMAL].exec_query = true;
stmt->sampleTableRequired = false;
analyze_tmptbl_debug_cn(rel_id, InvalidOid, stmt, true);
attatch_global_info(
&query_string_with_info, stmt, query_string, has_var, ANALYZENORMAL, rel_id, foreign_tbl_schedul_message);
* Step 1: get estimated total rows from DNs
*
* We set sampleRate to -1 identify the action.
* if sampleRate more or equal 0, it means CN should get sample rows from DN.
*/
DEBUG_START_TIMER;
(void)ExecRemoteVacuumStmt(stmt, query_string_with_info, sent_to_remote, ARQ_TYPE_TOTALROWCNTS, ANALYZENORMAL);
DEBUG_STOP_TIMER("Fetch estimate totalRowCount for table: %s", stmt->relation->relname);
elog(ES_LOGLEVEL,
"Step 1 > table: %s, estimated total row count: [%.2lf]",
stmt->relation->relname,
stmt->pstGlobalStatEx[ANALYZENORMAL].totalRowCnts);
if (query_string_with_info != NULL) {
pfree_ext(query_string_with_info);
query_string_with_info = NULL;
}
}
if (*table_num > 0) {
UtilityDesc desc;
errno_t rc = memset_s(&desc, sizeof(UtilityDesc), 0, sizeof(UtilityDesc));
securec_check(rc, "\0", "\0");
AdaptMem* mem_usage = &stmt->memUsage;
bool need_sort = false;
int cn_row_cnts = (int)Min(DEFAULT_SAMPLE_ROWCNT, stmt->pstGlobalStatEx[ANALYZENORMAL].totalRowCnts);
int top_mem_size = stmt->pstGlobalStatEx[ANALYZENORMAL].topMemSize;
bool use_tenant = false;
int available_mem = 0;
int max_mem = 0;
dywlm_client_get_memory_info(&max_mem, &available_mem, &use_tenant);
if (stmt->options & VACOPT_FULL) {
Relation rel = relation_open(rel_id, AccessShareLock);
if (rel->rd_rel->relhasclusterkey) {
SetSortMemInfo(&desc, cn_row_cnts, one_tuple_size, true, false, u_sess->attr.attr_memory.work_mem);
need_sort = true;
}
List* index_ids = RelationGetIndexList(rel);
if (index_ids != NIL) {
if (desc.assigned_mem == 0) {
desc.assigned_mem = max_mem;
}
desc.cost = g_instance.cost_cxt.disable_cost;
desc.query_mem[0] = Max(STATEMENT_MIN_MEM * 1024, desc.query_mem[0]);
need_sort = true;
list_free_ext(index_ids);
}
relation_close(rel, AccessShareLock);
}
top_mem_size = Max(top_mem_size, cn_row_cnts * one_tuple_size / 1024);
desc.query_mem[0] = (int)Max(BIG_MEM_RATIO * top_mem_size, desc.query_mem[0]);
desc.assigned_mem = Max(Max(desc.query_mem[0], STATEMENT_MIN_MEM * 1024L), desc.assigned_mem);
desc.cost = Max((double)(desc.query_mem[0] / PAGE_SIZE) * u_sess->attr.attr_sql.seq_page_cost, desc.cost);
if (*table_num > 1) {
desc.query_mem[0] = Max(desc.query_mem[0], STATEMENT_MIN_MEM * 1024L);
desc.assigned_mem = Max(Max(desc.query_mem[0], STATEMENT_MIN_MEM * 1024L), desc.assigned_mem);
desc.cost = g_instance.cost_cxt.disable_cost;
}
if (VALID_QUERY_MEM()) {
desc.query_mem[0] = Max(desc.query_mem[0], max_mem);
desc.query_mem[1] = Max(desc.query_mem[1], max_mem);
}
if (desc.query_mem[1] == 0)
desc.query_mem[1] = desc.query_mem[0];
WLMInitQueryPlan((QueryDesc*)&desc, false);
dywlm_client_manager((QueryDesc*)&desc, false);
if (need_sort)
AdjustIdxMemInfo(mem_usage, &desc);
*table_num = 0;
}
set_dndistinct_coors(stmt, attnum);
(void)compute_sample_size(stmt, 0, NULL, rel_id, ANALYZENORMAL);
elog(DEBUG1,
"Step 2: Compute sample rate[%.12f] for normal table: %s.",
stmt->pstGlobalStatEx[0].sampleRate,
stmt->relation->relname);
elog(ES_LOGLEVEL,
"Step 2 > table: %s, compute sample rate [%.12f]",
stmt->relation->relname,
stmt->pstGlobalStatEx[ANALYZENORMAL].sampleRate);
attatch_global_info(&query_string_with_info, stmt, query_string, has_var, ANALYZENORMAL, rel_id, foreign_tbl_schedul_message);
elog(DEBUG1,
"Analyzing [%s], oid is [%d], the messege is [%s]",
stmt->relation->relname,
rel_id,
query_string_with_info);
* We only send query string to all datanodes in two cases:
* 1. for replication table;
* 2. there is no split map of all datanodes for hdfs foreign tables.
*/
if (stmt->pstGlobalStatEx[stmt->tableidx].isReplication ||
(stmt->isForeignTables && (stmt->hdfsforeignMapDnList == NIL))) {
bool analyze_force_autocommit = IS_ONLY_ANALYZE_TMPTABLE ? false : true;
ExecUtilityStmtOnNodes(
query_string_with_info, NULL, sent_to_remote, analyze_force_autocommit, EXEC_ON_DATANODES, false, (Node*)stmt);
} else {
DEBUG_START_TIMER;
stmt->sampleRows =
ExecRemoteVacuumStmt(stmt, query_string_with_info, sent_to_remote, ARQ_TYPE_SAMPLE, ANALYZENORMAL);
DEBUG_STOP_TIMER("Get sample rows from all DNs for table: %s", stmt->relation->relname);
}
if (stmt->isForeignTables && !stmt->isPgFdwForeignTables)
pfree_ext(foreign_tbl_schedul_message);
if (!IS_ONLY_ANALYZE_TMPTABLE) {
if (ActiveSnapshotSet())
PopActiveSnapshot();
* It will switch CurrentMemoryContext to t_thrd.top_mem_cxt after call CommitTransactionCommand(),
* and switch to u_sess->top_transaction_mem_cxt after call StartTransactionCommand().
*/
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
} else
CommandCounterIncrement();
stmt->pstGlobalStatEx[stmt->tableidx].eAnalyzeMode = ANALYZENORMAL;
DEBUG_START_TIMER;
FetchGlobalStatistics(stmt, rel_id, NULL, is_replication);
DEBUG_STOP_TIMER("Fetch statistics from DN1 for table: %s", stmt->relation->relname);
* Both FetchGlobalStatistics and vacuum refreshes relpages.
* To prevent inconsistent update, we add CommandCounterIncrement in between.
* Refactor it if necessary.
*/
CommandCounterIncrement();
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
if (!IS_ONLY_ANALYZE_TMPTABLE) {
* for VACUUM ANALYZE table where options=3, vacuum has set use_own_xacts
* true and already poped ActiveSnapshot, so we don't want to pop again.
*/
if (ActiveSnapshotSet())
PopActiveSnapshot();
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
} else
CommandCounterIncrement();
if (stmt->tmpSampleTblNameList && log_min_messages > DEBUG1) {
dropSampleTable(get_sample_tblname(ANALYZENORMAL, stmt->tmpSampleTblNameList));
* Drop table is twophase-transaction, if dn fault at this time,
* current transaction don't be cleanned immadiately. So we should commit the transaction and
* Start a new transaction.
*/
PopActiveSnapshot();
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
}
if (has_var && !IsTempTable(rel_id)) {
notify_other_cn_get_statistics(query_string_with_info, sent_to_remote);
}
if (query_string_with_info != NULL) {
pfree_ext(query_string_with_info);
query_string_with_info = NULL;
}
* Other CNs have already gotten statistics. Now, We can resume the time counter which was stoped before
* update statistics on local CN.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord() && IsAutoVacuumWorkerProcess())
resume_sig_alarm(true);
return;
}
static void do_global_analyze_mpp_table(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
List* oid_list = NIL;
ListCell* cur = NULL;
Oid rel_oid = InvalidOid;
bool has_var = false;
char* query_string_with_info = NULL;
MemoryContext old_context = NULL;
MemoryContext global_stats_context = NULL;
PgxcNodeGetOids(NULL, NULL, NULL, (int*)&stmt->DnCnt, false);
if (stmt->relation == NULL) {
has_var = false;
oid_list = get_analyzable_relations(stmt->isForeignTables);
} else {
rel_oid = RangeVarGetRelid(stmt->relation, NoLock, false);
if (ISMATMAP(stmt->relation->relname) || ISMLOG(stmt->relation->relname)) {
ereport(WARNING,
(errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("Not support Analyze Matmap or Mlog table.")));
} else {
oid_list = lappend_oid(oid_list, rel_oid);
}
has_var = true;
}
global_stats_context = AllocSetContextCreate(t_thrd.mem_cxt.msg_mem_cxt,
"global_stats_context",
ALLOCSET_DEFAULT_MINSIZE,
ALLOCSET_DEFAULT_INITSIZE,
ALLOCSET_DEFAULT_MAXSIZE);
* Switch to global_stats_context because it only used to get sample from all datanodes
* and we want to put this memory into this memory context.
*/
old_context = MemoryContextSwitchTo(global_stats_context);
int table_num = 0;
if (IS_PGXC_COORDINATOR && ENABLE_WORKLOAD_CONTROL && !IsAnyAutoVacuumProcess())
table_num = list_length(oid_list);
foreach (cur, oid_list) {
* Check if the table is REPLICATION for global analyze,
* if it is a REPLICATION table, just fetch stats from DN1
*/
int attnum = 0;
bool is_replication = false;
Oid rel_id = lfirst_oid(cur);
DistributeBy* distributeby = NULL;
Relation rel = try_relation_open(rel_id, AccessShareLock);
if (!rel) {
table_num--;
elog(DEBUG1, "Skip analyze table for cannot get AccessShareLock");
continue;
}
if (RelationIsView(rel)) {
elog(LOG, "View is an unanalyzable object");
relation_close(rel, AccessShareLock);
continue;
}
if (RelationIsContquery(rel)) {
elog(LOG, "Contquery is an unanalyzable object");
relation_close(rel, AccessShareLock);
continue;
}
if (RelationIsSequnce(rel)) {
elog(LOG, "Sequnce is an unanalyzable object");
relation_close(rel, AccessShareLock);
continue;
}
attnum = rel->rd_att->natts;
if (rel_id < FirstNormalObjectId ||
(rel->rd_rel->relkind != RELKIND_RELATION && rel->rd_rel->relkind != RELKIND_TOASTVALUE &&
rel->rd_rel->relkind != RELKIND_FOREIGN_TABLE && rel->rd_rel->relkind != RELKIND_MATVIEW)) {
if (table_num > 0) {
UtilityDesc desc;
errno_t rc = memset_s(&desc, sizeof(UtilityDesc), 0, sizeof(UtilityDesc));
securec_check(rc, "\0", "\0");
if (table_num == 1) {
int one_tuple_size = GetOneTupleSize(stmt, rel);
desc.query_mem[0] = (int)(BIG_MEM_RATIO * DEFAULT_SAMPLE_ROWCNT * one_tuple_size / 1024L);
desc.cost = (double)(desc.query_mem[0] / PAGE_SIZE) * u_sess->attr.attr_sql.seq_page_cost;
} else {
desc.query_mem[0] = STATEMENT_MIN_MEM * 1024L;
desc.cost = g_instance.cost_cxt.disable_cost;
}
relation_close(rel, AccessShareLock);
desc.query_mem[1] = desc.query_mem[0];
WLMInitQueryPlan((QueryDesc*)&desc, false);
dywlm_client_manager((QueryDesc*)&desc, false);
table_num = 0;
} else
relation_close(rel, AccessShareLock);
stmt->tableidx = ANALYZENORMAL;
set_dndistinct_coors(stmt, attnum);
ExecUtilityStmtOnNodes(query_string, NULL, sent_to_remote, true, EXEC_ON_ALL_NODES, false, (Node*)stmt);
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
continue;
}
if (!IsSystemRelation(rel)) {
distributeby = getTableDistribution(rel_id);
stmt->disttype = distributeby->disttype;
is_replication = (distributeby->disttype == DISTTYPE_REPLICATION);
}
if (!has_var) {
stmt->relation = makeNode(RangeVar);
stmt->relation->relname = pstrdup(RelationGetRelationName(rel));
}
PG_TRY();
{
stmt->relation->schemaname = get_namespace_name(RelationGetNamespace(rel));
if (IsPGFDWTableAnalyze(rel_id)) {
relation_close(rel, AccessShareLock);
do_global_analyze_pgfdw_Rel(stmt, rel_id, query_string, has_var, sent_to_remote, is_top_level);
} else {
relation_close(rel, AccessShareLock);
do_global_analyze_rel(
stmt, rel_id, query_string, is_replication, has_var, attnum, sent_to_remote, is_top_level, &table_num);
}
stmt->relation->schemaname = NULL;
}
PG_CATCH();
{
stmt->relation->schemaname = NULL;
PG_RE_THROW();
}
PG_END_TRY();
* Analyzing a big table or analyzing database may consume a lot of memeory,
* so we reset the memory after analyze each relation in order to avoid memory increase.
*/
MemoryContextReset(global_stats_context);
* We have finished analyze for one relation, so we should switch to global_stats_context
* in order to do analyze for next relation.
*/
(void)MemoryContextSwitchTo(global_stats_context);
stmt->tmpSampleTblNameList = NIL;
}
if (!has_var) {
* We should construct the new query string include the PGXCNodeId of the current CN,
* in order to tell other CN get statistic, because other CN need to know which CN is orignal.
* the AnalyzeMode is no use because it will send analyze command with no table name to other CN.
*/
attatch_global_info(&query_string_with_info, stmt, query_string, true, ANALYZENORMAL, InvalidOid);
notify_other_cn_get_statistics(query_string_with_info, sent_to_remote);
if (query_string_with_info != NULL) {
pfree_ext(query_string_with_info);
query_string_with_info = NULL;
}
}
(void)MemoryContextSwitchTo(old_context);
MemoryContextDelete(global_stats_context);
}
* do_vacuum_mpp_table_local_cn
*
* @Description: do vacuum/analyze where come from client in local coordinator.
* @in stmt - the statment for analyze or vacuum command
* @in query_string - the query string for analyze or vacuum command
* @in is_top_level - true if executing a "top level" (interactively issued) command
* @in sent_to_remote - identify if this statement has been sent to the nodes
*/
static void do_vacuum_mpp_table_local_cn(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
if (stmt->options & VACOPT_HDFSDIRECTORY) {
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
return;
}
* Now VACUUM FULL statement has different executing order from
* normal VACUUM statement. Degrading lock level results in this different.
*/
if (stmt->options & VACOPT_VERIFY) {
if (stmt->relation != NULL) {
DoGlobalVerifyMppTable(stmt, query_string, sent_to_remote);
} else {
DoGlobalVerifyDatabase(stmt, query_string, sent_to_remote);
}
} else if (stmt->options & VACOPT_ANALYZE)
do_global_analyze_mpp_table(stmt, query_string, is_top_level, sent_to_remote);
else if (stmt->options & VACOPT_FULL)
cn_do_vacuum_full_mpp_table(stmt, query_string, is_top_level, sent_to_remote);
else if (stmt->options & VACOPT_MERGE)
delta_merge_with_exclusive_lock(stmt);
else
cn_do_vacuum_mpp_table(stmt, query_string, is_top_level, sent_to_remote);
}
#endif
* DoVacuumMppTableOtherCN
*
* @Description: do vacuum/analyze where come from original cn in other coordinator.
* @in stmt - the statment for analyze or vacuum command
* @in query_string - the query string for analyze or vacuum command
* @in is_top_level - true if executing a "top level" (interactively issued) command
* @in sent_to_remote - identify if this statement has been sent to the nodes
*/
static void do_vacuum_mpp_table_other_node(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
if (stmt->options & VACOPT_HDFSDIRECTORY)
return;
if (stmt->options & VACOPT_MERGE) {
#ifdef ENABLE_MULTIPLE_NODES
if (Tsdb::IsDeltaMergeStmt(stmt)) {
if (!g_instance.attr.attr_common.enable_tsdb) {
ereport(ERROR,
(errcode(ERRCODE_LOG),
errmsg("Can't DoDeltaMerge when 'enable_tsdb' is off"),
errdetail("When the guc is off, it is forbidden to DoDeltaMerge"),
errcause("Unexpected error"),
erraction("Turn on the 'enable_tsdb'."),
errmodule(MOD_VACUUM)));
}
Tsdb::DoDeltaMerge(stmt->relation);
} else {
begin_delta_merge(stmt);
}
#else
begin_delta_merge(stmt);
#endif
return;
}
if (stmt->options & VACOPT_VERIFY) {
DoVerifyTableOtherNode(stmt, sent_to_remote);
return;
}
if (IS_PGXC_COORDINATOR && (stmt->options & VACOPT_ANALYZE))
FetchGlobalStatistics(stmt, InvalidOid, NULL);
if (NEED_EST_TOTAL_ROWS_DN(stmt))
stmt->options &= ~(VACOPT_VACUUM | VACOPT_FULL | VACOPT_FREEZE);
if (!stmt->isPgFdwForeignTables) {
vacuum(stmt, InvalidOid, true, NULL, is_top_level);
}
}
void ClearVacuumStmt(VacuumStmt* stmt)
{
if (stmt->relation && (stmt->options & VACOPT_ANALYZE)) {
stmt->relation->schemaname = NULL;
}
}
void ClearCreateSeqStmtUUID(CreateSeqStmt* stmt)
{
stmt->uuid = INVALIDSEQUUID;
}
void ClearCreateStmtUUIDS(CreateStmt* stmt)
{
stmt->uuids = NIL;
}
* @hdfs
* DoVacuumMappTable
*
* We capsule orignal VacuumStmt processing code in this function.
* Processe MPP local table/tables here.
*/
void DoVacuumMppTable(VacuumStmt* stmt, const char* query_string, bool is_top_level, bool sent_to_remote)
{
elog_node_display(ES_LOGLEVEL, "[DoVacuumMppTable]", stmt->va_cols, true);
char* cmdname = NULL;
if (stmt->options & VACOPT_MERGE) {
cmdname = "DELTA MERGE";
} else if (stmt->options & VACOPT_VACUUM) {
cmdname = "VACUUM";
} else if (stmt->options & VACOPT_VERIFY) {
cmdname = "VERIFY";
} else {
cmdname = "ANALYZE";
}
if (stmt->relation != NULL && (stmt->isForeignTables || stmt->isPgFdwForeignTables)
&& (stmt->options & VACOPT_VACUUM)) {
ereport(WARNING, (errmsg("skipping \"%s\" --- cannot vacuum a foreign table", stmt->relation->relname)));
return;
}
if (stmt->relation != NULL && stmt->relation->catalogname != NULL &&
strcmp(stmt->relation->catalogname, "template0") == 0) {
ereport(WARNING,
(errmsg("skipping \"%s\" --- cannot vacuum database template0",
stmt->relation->catalogname)));
return;
}
if (stmt->relation != NULL && (stmt->options & VACOPT_FULL)){
Oid relId = RangeVarGetRelid(stmt->relation, NoLock, false);
Relation targRel = heap_open(relId, AccessShareLock);
bool isSegmentTable = targRel->storage_type == SEGMENT_PAGE;
heap_close(targRel, AccessShareLock);
if (isSegmentTable) {
ereport(INFO, (errmsg("skipping segment table \"%s\" --- please use gs_space_shrink "
"to recycle segment space.", stmt->relation->relname)));
return;
}
}
PreventCommandDuringRecovery(cmdname);
#ifdef PGXC
* We have to run the command on nodes before Coordinator
* because vacuum() pops active snapshot and we can not
* send it to nodes.
*/
if (IS_PGXC_COORDINATOR && !IsConnFromCoord()) {
#ifdef ENABLE_MULTIPLE_NODES
stmt->isAnalyzeTmpTable = IsAnalyzeTempRel(stmt);
if ((!IS_ONLY_ANALYZE_TMPTABLE) && !Tsdb::IsDeltaMergeStmt(stmt)) {
PreventTransactionChain(is_top_level, cmdname);
* With jdbc terminal, the cid of vacuum statement should be zero;
* Cause CN will set the vacuum statement blockstate to TBLOCK_STARTED(running single-query transaction)
*/
if (GetCurrentCommandId(false) > 0 && ((stmt)->options & VACOPT_VACUUM))
ereport(ERROR,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("%s can not run inside a transaction block", cmdname)));
}
do_vacuum_mpp_table_local_cn(stmt, query_string, is_top_level, sent_to_remote);
#endif
} else {
if (IS_SINGLE_NODE) {
stmt->isAnalyzeTmpTable = IsAnalyzeTempRel(stmt);
}
if (IS_SINGLE_NODE && !IS_ONLY_ANALYZE_TMPTABLE) {
PreventTransactionChain(is_top_level, cmdname);
if (GetCurrentCommandId(false) > 0 && ((stmt)->options & VACOPT_VACUUM))
ereport(ERROR,
(errcode(ERRCODE_ACTIVE_SQL_TRANSACTION),
errmsg("%s can not run inside a transaction block", cmdname)));
}
do_vacuum_mpp_table_other_node(stmt, query_string, is_top_level, sent_to_remote);
* There is plan cache in store procedure, some data structure
* must be rest, because memcontext has been reset. We can't use
* those pointer when get stmt from plan cache.
*/
ClearVacuumStmt(stmt);
}
#endif
}
* @hdfs
* IsSupportForeignTableAnalyze
*
* Use foreign_table_id to judge if a foreign table support analysis operation.
*/
bool IsHDFSTableAnalyze(Oid foreign_table_id)
{
HeapTuple tuple = NULL;
Form_pg_class class_form = NULL;
bool ret_value = false;
* Find the tuple in pg_class, using syscache for the lookup.
*/
tuple = SearchSysCacheCopyWithLogLevel(RELOID, ObjectIdGetDatum(foreign_table_id), LOG);
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", foreign_table_id)));
}
class_form = (Form_pg_class)GETSTRUCT(tuple);
* Judge if the relation is a foreign table
*/
if (RELKIND_FOREIGN_TABLE != class_form->relkind) {
ret_value = false;
} else {
* Judge if a foreign supports analysis opertation. If a foreign table
* implements AnalyzeForeignTable, we think it supports foreign table
* analyze operation.
*/
FdwRoutine* table_fdw_routine = GetFdwRoutineByRelId(foreign_table_id);
if (table_fdw_routine->AnalyzeForeignTable == NULL) {
* Foreign table does not support analyze operation.
*/
ereport(WARNING, (errmsg("The operation foreign table doesn't support analyze command.")));
ret_value = false;
} else {
if (isObsOrHdfsTableFormTblOid(foreign_table_id) || IS_OBS_CSV_TXT_FOREIGN_TABLE(foreign_table_id))
ret_value = true;
else if (IS_LOGFDW_FOREIGN_TABLE(foreign_table_id)) {
ret_value = true;
} else
ret_value = false;
}
}
return ret_value;
}
* @pgfdw
* IsPGFDWTableAnalyze
*
* Use foreignTableId to judge if is a gc_fdw foreign table support analysis operation.
*/
bool IsPGFDWTableAnalyze(Oid foreignTableId)
{
HeapTuple tuple = NULL;
Form_pg_class classForm = NULL;
* Find the tuple in pg_class, using syscache for the lookup.
*/
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(foreignTableId));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for relation %u", foreignTableId)));
}
classForm = (Form_pg_class)GETSTRUCT(tuple);
* Judge if the relation is a foreign table
*/
if (RELKIND_FOREIGN_TABLE == classForm->relkind && IS_POSTGRESFDW_FOREIGN_TABLE(foreignTableId)) {
return true;
}
return false;
}
#ifdef ENABLE_MOT
bool IsMOTForeignTable(Oid foreignTableId)
{
HeapTuple tuple = NULL;
Form_pg_class classForm = NULL;
* Find the tuple in pg_class, using syscache for the lookup.
*/
tuple = SearchSysCacheCopy1(RELOID, ObjectIdGetDatum(foreignTableId));
if (!HeapTupleIsValid(tuple)) {
ereport(ERROR,
(errcode(ERRCODE_CACHE_LOOKUP_FAILED),
errmsg("cache lookup failed for relation %u", foreignTableId)));
}
classForm = (Form_pg_class) GETSTRUCT(tuple);
* Judge if the relation is a foreign table
*/
if (RELKIND_FOREIGN_TABLE == classForm->relkind && isMOTFromTblOid(foreignTableId)) {
return true;
}
return false;
}
#endif
* @global stats
* @Description: set sample rate in local which compute by total row count.
* @in e_analyze_mode - identify which type the table, normal talbe/dfs table/delta table
* @in stmt - the statment for analyze or vacuum command
* @in newSampleRate - the estimate total row count which get from dn
* @return: void
*/
void global_stats_set_samplerate(AnalyzeMode e_analyze_mode, VacuumStmt* stmt, const double* newSampleRate)
{
if (e_analyze_mode == ANALYZENORMAL) {
stmt->pstGlobalStatEx[0].sampleRate = ((NULL == newSampleRate) ? GET_ESTTOTALROWCNTS_FLAG : newSampleRate[0]);
} else {
for (int i = 0; i < ANALYZE_MODE_MAX_NUM - 1; i++) {
stmt->pstGlobalStatEx[i].sampleRate =
((NULL == newSampleRate) ? GET_ESTTOTALROWCNTS_FLAG : newSampleRate[i]);
}
}
}
#ifdef ENABLE_MULTIPLE_NODES
* @global stats
* @Description: initial stadndistinct and coorelations which using for get the value from dn1 and set in local's
* pg_statistic.
* @in stmt - the statment for analyze or vacuum command
* @in attnum - the num of attribute for current table which do analyze
* @return: void
*/
static void set_dndistinct_coors(VacuumStmt* stmt, int attnum)
{
stmt->pstGlobalStatEx[stmt->tableidx].attnum = attnum;
stmt->pstGlobalStatEx[stmt->tableidx].dndistinct = (double*)palloc0(sizeof(double) * attnum);
stmt->pstGlobalStatEx[stmt->tableidx].correlations = (double*)palloc0(sizeof(double) * attnum);
for (int i = 0; i < attnum; i++) {
stmt->pstGlobalStatEx[stmt->tableidx].dndistinct[i] = 1;
stmt->pstGlobalStatEx[stmt->tableidx].correlations[i] = 1;
}
}
#endif
* @global stats
* @Description: for a relation to be analyzed, check the permission. If no permisision, skip it.
* @in rel_id - oid of the relation to be checked
* @return: whether has permission
*/
bool check_analyze_permission(Oid rel_id)
{
Relation rel = try_relation_open(rel_id, ShareUpdateExclusiveLock);
if (!rel) {
elog(DEBUG1, "Skip analyze table for cannot get ShareUpdateExclusiveLock");
return false;
}
* If rel is in read only mode(none redistribution scenario), we skip analyze
* the relation.
*/
if (!u_sess->attr.attr_sql.enable_cluster_resize && RelationInClusterResizingReadOnly(rel)) {
ereport(WARNING,
(errmsg(
"skipping \"%s\" --- only none read-only mode can do analyze command", RelationGetRelationName(rel))));
relation_close(rel, ShareUpdateExclusiveLock);
return false;
}
AclResult aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), ACL_VACUUM);
if (aclresult != ACLCHECK_OK && !(pg_class_ownercheck(RelationGetRelid(rel), GetUserId()) ||
(pg_database_ownercheck(u_sess->proc_cxt.MyDatabaseId, GetUserId()) && !rel->rd_rel->relisshared) ||
(isOperatoradmin(GetUserId()) && u_sess->attr.attr_security.operation_mode))) {
* we may have checked the first two situations before, just double check to
* make sure no miss in every cases.
*/
if (rel->rd_rel->relisshared)
ereport(WARNING,
(errmsg("skipping \"%s\" --- only system admin can analyze it", RelationGetRelationName(rel))));
else if (rel->rd_rel->relnamespace == PG_CATALOG_NAMESPACE)
ereport(WARNING,
(errmsg("skipping \"%s\" --- only system admin or database owner can analyze it",
RelationGetRelationName(rel))));
else
ereport(WARNING,
(errmsg(
"skipping \"%s\" --- only table or database owner can analyze it", RelationGetRelationName(rel))));
relation_close(rel, ShareUpdateExclusiveLock);
return false;
}
relation_close(rel, ShareUpdateExclusiveLock);
return true;
}
* @NodeGroup Support
* @Description: Find ExecNodes for given rel_id(for index case) we return the base
* table's execnodes
*
* @Return: return the rel_id's execnodes
*/
ExecNodes* RelidGetExecNodes(Oid rel_id, bool isutility)
{
ExecNodes* exec_nodes = makeNode(ExecNodes);
int nmembers = 0;
Oid* members = NULL;
char relkind = get_rel_relkind(rel_id);
Oid group_oid = InvalidOid;
if (rel_id > FirstNormalObjectId) {
if (relkind == RELKIND_RELATION || relkind == RELKIND_MATVIEW) {
nmembers = get_pgxc_classnodes(rel_id, &members);
group_oid = get_pgxc_class_groupoid(rel_id);
} else if (relkind == RELKIND_INDEX || relkind == RELKIND_GLOBAL_INDEX) {
* For index, there is enry in pgxc_class, so we first get index's
* base rel_id and then fetch group list from pgxc_class
*/
Oid base_relid = IndexGetRelation(rel_id, false);
nmembers = get_pgxc_classnodes(base_relid, &members);
group_oid = get_pgxc_class_groupoid(base_relid);
} else if (relkind == RELKIND_FOREIGN_TABLE || relkind == RELKIND_STREAM) {
if (in_logic_cluster() && is_pgxc_class_table(rel_id)) {
nmembers = get_pgxc_classnodes(rel_id, &members);
group_oid = get_pgxc_class_groupoid(rel_id);
}
}
} else {
group_oid = ng_get_installation_group_oid();
nmembers = get_pgxc_groupmembers(group_oid, &members);
AssertEreport(nmembers > 0 && members != NULL, MOD_EXECUTOR, "Invalid group member value");
}
* Notice!!
* In cluster resizing stage we need special processing logics in utility statement.
*
* So, as normal, when target node group's status is marked as 'installation' or
* 'redistribution', we have to issue a full-DN request.
*/
if (isutility && OidIsValid(group_oid)) {
char* group_name = get_pgxc_groupname(group_oid);
if (need_full_dn_execution(group_name)) {
exec_nodes->nodeList = GetAllDataNodes();
} else {
if (IsLogicClusterRedistributed(group_name) &&
((relkind == RELKIND_FOREIGN_TABLE || relkind == RELKIND_STREAM)
|| !RelationIsDeleteDeltaTable(get_rel_name(rel_id)))) {
int dst_nmembers = 0;
Oid* dst_members = NULL;
Oid destgroup_oid = PgxcGroupGetRedistDestGroupOid();
if (OidIsValid(destgroup_oid))
dst_nmembers = get_pgxc_groupmembers(destgroup_oid, &dst_members);
if (dst_nmembers > nmembers) {
nmembers = dst_nmembers;
pfree_ext(members);
members = dst_members;
} else if (dst_members != NULL) {
pfree_ext(dst_members);
}
}
exec_nodes->nodeList = GetNodeGroupNodeList(members, nmembers);
}
} else {
exec_nodes->nodeList = GetNodeGroupNodeList(members, nmembers);
}
Distribution* distribution = ng_convert_to_distribution(exec_nodes->nodeList);
distribution->group_oid = group_oid;
ng_set_distribution(&exec_nodes->distribution, distribution);
pfree_ext(members);
return exec_nodes;
}
* @NodeGroup Support
* @Description: Determine the list objects is in same node group
*
* @Return: 'true' in same group, 'false' in different group
*/
bool ObjectsInSameNodeGroup(List* objects, NodeTag stmttype)
{
ListCell* cell = NULL;
Oid first_ngroup_oid = InvalidOid;
Oid table_oid = InvalidOid;
Oid group_oid = InvalidOid;
if (objects == NIL || list_length(objects) == 1) {
return true;
}
foreach (cell, objects) {
RangeVar* rel = NULL;
if (stmttype == T_DropStmt) {
rel = makeRangeVarFromNameList((List*)lfirst(cell));
} else if (stmttype == T_TruncateStmt) {
rel = (RangeVar*)lfirst(cell);
}
table_oid = RangeVarGetRelid(rel, NoLock, true);
if (table_oid == InvalidOid) {
continue;
}
* Only check relation and materialized view, for index, view is not associated with
* nodegroup they belongs to its base table
*/
char relkind = get_rel_relkind(table_oid);
if ((relkind != RELKIND_RELATION) && (relkind != RELKIND_MATVIEW)) {
continue;
}
group_oid = get_pgxc_class_groupoid(table_oid);
AssertEreport(group_oid != InvalidOid, MOD_EXECUTOR, "group OID is invalid");
if (first_ngroup_oid == InvalidOid) {
first_ngroup_oid = group_oid;
* In 1st round of dropping list iteration we don't have
* to process following verification.
*/
continue;
}
if (first_ngroup_oid != group_oid) {
return false;
}
}
return true;
}
* @NodeGroup Support
* @Description: Return correct ExecNodes with given parser node
*
* @Return: execnodes of target table in ReIndex, Grant, Lock statements
*/
static ExecNodes* assign_utility_stmt_exec_nodes(Node* parse_tree)
{
ExecNodes* nodes = NULL;
Oid rel_id = InvalidOid;
AssertEreport(parse_tree, MOD_EXECUTOR, "parser tree is NULL");
AssertEreport(IS_PGXC_COORDINATOR && !IsConnFromCoord(), MOD_EXECUTOR, "the node is not a CN node");
switch (nodeTag(parse_tree)) {
case T_ReindexStmt: {
ReindexStmt* stmt = (ReindexStmt*)parse_tree;
if (stmt->relation) {
rel_id = RangeVarGetRelid(stmt->relation, NoLock, false);
nodes = RelidGetExecNodes(rel_id);
}
break;
}
case T_GrantStmt: {
GrantStmt* stmt = (GrantStmt*)parse_tree;
if (stmt->objects && stmt->objtype == ACL_OBJECT_RELATION && stmt->targtype == ACL_TARGET_OBJECT) {
RangeVar* relvar = (RangeVar*)list_nth(stmt->objects, 0);
Oid relation_id = RangeVarGetRelid(relvar, NoLock, false);
nodes = RelidGetExecNodes(relation_id);
}
break;
}
case T_ClusterStmt: {
ClusterStmt* stmt = (ClusterStmt*)parse_tree;
if (stmt->relation) {
Oid relation_id = RangeVarGetRelid(stmt->relation, NoLock, false);
nodes = RelidGetExecNodes(relation_id);
}
break;
}
case T_LockStmt: {
LockStmt* stmt = (LockStmt*)parse_tree;
if (stmt->relations) {
RangeVar* relvar = (RangeVar*)list_nth(stmt->relations, 0);
Oid relation_id = RangeVarGetRelid(relvar, NoLock, false);
nodes = RelidGetExecNodes(relation_id);
}
break;
}
case T_VacuumStmt: {
VacuumStmt* stmt = (VacuumStmt*)parse_tree;
if (stmt->relation) {
Oid relation_id = RangeVarGetRelid(stmt->relation, NoLock, false);
nodes = RelidGetExecNodes(relation_id);
} else if (stmt->options & VACOPT_VERIFY) {
nodes = RelidGetExecNodes(stmt->curVerifyRel);
}
break;
}
case T_RenameStmt: {
RenameStmt* stmt = (RenameStmt*)parse_tree;
if (stmt->relation) {
Oid relation_id = RangeVarGetRelid(stmt->relation, NoLock, stmt->missing_ok);
nodes = RelidGetExecNodes(relation_id);
} else if (stmt->renameType == OBJECT_FUNCTION) {
Oid funcid = LookupFuncNameTypeNames(stmt->object, stmt->objarg, false);
nodes = GetFunctionNodes(funcid);
}
break;
}
case T_AlterObjectSchemaStmt: {
AlterObjectSchemaStmt* stmt = (AlterObjectSchemaStmt*)parse_tree;
if (stmt->relation) {
rel_id = RangeVarGetRelid(stmt->relation, NoLock, true);
nodes = RelidGetExecNodes(rel_id);
} else if (stmt->objectType == OBJECT_FUNCTION) {
Oid funcid = LookupFuncNameTypeNames(stmt->object, stmt->objarg, false);
nodes = GetFunctionNodes(funcid);
}
break;
}
case T_CreateTrigStmt: {
CreateTrigStmt* stmt = (CreateTrigStmt*)parse_tree;
if (stmt->relation) {
* Notice : When support create or replace trigger in future,
* we may need adjust the missing_ok parameter here.
*/
rel_id = RangeVarGetRelidExtended(stmt->relation, NoLock, false, false, false, true, NULL, NULL);
nodes = RelidGetExecNodes(rel_id);
}
break;
}
default: {
ereport(ERROR,
(errcode(ERRCODE_UNRECOGNIZED_NODE_TYPE),
errmsg("unrecognized nodes %s in node group utility execution", nodeTagToString(parse_tree->type))));
}
}
return nodes;
}
#ifdef ENABLE_MULTIPLE_NODES
* Description: Create temp table on coordinator for analyze with relative-estimation or
* absolute-estimate with debuging.
*
* Parameters:
* @in relid: relation oid
* @in stmt: the statment for analyze or vacuum command
* @in iscommit: commit and start a new transaction if non-temp table or delta table.
*
* Returns: void
*/
static void analyze_tmptbl_debug_cn(Oid relid, Oid main_relid, VacuumStmt* stmt, bool iscommit)
{
* Don't create temp sample table for u_sess->cmd_cxt.default_statistics_target is absolute-estimate
* or analyze for hdfs foreign table or temp table.
*/
if (NULL == stmt || stmt->isAnalyzeTmpTable || DISTTYPE_HASH != stmt->disttype ||
(default_statistics_target >= 0 && log_min_messages > DEBUG1)) {
return;
}
MemoryContext oldcontext = CurrentMemoryContext;
char* tableName = buildTempSampleTable(relid, main_relid, TempSmpleTblType_Table);
if (tableName != NULL) {
stmt->tmpSampleTblNameList = lappend(stmt->tmpSampleTblNameList, makeString(tableName));
}
if (iscommit) {
PopActiveSnapshot();
CommitTransactionCommand();
StartTransactionCommand();
PushActiveSnapshot(GetTransactionSnapshot());
} else {
CommandCounterIncrement();
}
(void)MemoryContextSwitchTo(oldcontext);
}
#endif
* Return if we need a full_datanodes execution with given group,
* for example group_name is installation group or redistribution group
* in cluster resizing stage.
*/
static bool need_full_dn_execution(const char* group_name)
{
if (in_logic_cluster()) {
return false;
}
const char* redistribution_group_name = PgxcGroupGetInRedistributionGroup();
const char* installation_group_name = PgxcGroupGetInstallationGroup();
if ((installation_group_name && strcmp(group_name, installation_group_name) == 0) ||
(redistribution_group_name && strcmp(group_name, redistribution_group_name) == 0)) {
return true;
}
Oid group_oid = get_pgxc_groupoid(group_name, false);
const char* group_parent_name = get_pgxc_groupparent(group_oid);
if (group_parent_name != NULL) {
if ((installation_group_name && strcmp(group_parent_name, installation_group_name) == 0) ||
(redistribution_group_name && strcmp(group_parent_name, redistribution_group_name) == 0)) {
return true;
}
}
return false;
}
* EstIdxMemInfo
* According to resource usage and cardinality estimation, assign query
* max/min mem, assigned statement mem in utilityDesc struct
*
* We use this function to evaluate mem in three cases:
* 1. index case: create index/ reindex
* 2. cluster case: now only btree is needed for sort
* 3. vacuum full case: now only cstore table with pck is needed for sort
*
* Parameters:
* @in rel: input relation
* @in relation: parser struct of relation, containing nspname and relname
* @out desc: set meminfo in this struct
* @in info: index info, or NULL when we do cluster operation
* @in accessMethod: access method of index, or NULL when we do cluster operation
*
* Returns: void
*/
void EstIdxMemInfo(Relation rel, RangeVar* relation, UtilityDesc* desc, IndexInfo* info, const char* access_method)
{
VacuumStmt* stmt = makeNode(VacuumStmt);
char* query_string_with_info = NULL;
StringInfoData s;
bool vectorized = false;
AnalyzeMode mode = ANALYZENORMAL;
if (rel->rd_id < FirstNormalObjectId)
return;
if (info != NULL && access_method && strncmp(access_method, "btree", strlen("btree")) &&
strncmp(access_method, "cbtree", strlen("cbtree")) && strncmp(access_method, "hash", strlen("hash")) &&
strncmp(access_method, "psort", strlen("psort")))
return;
global_stats_set_samplerate(mode, stmt, NULL);
if (mode == ANALYZENORMAL) {
stmt->pstGlobalStatEx[ANALYZENORMAL].isReplication = false;
stmt->pstGlobalStatEx[ANALYZENORMAL].exec_query = true;
}
stmt->sampleTableRequired = false;
const char* schemaname = NULL;
const char* relname = NULL;
if (relation != NULL) {
if (relation->schemaname != NULL)
schemaname = quote_identifier((const char*)relation->schemaname);
else
schemaname = quote_identifier((const char*)get_namespace_name(get_rel_namespace(rel->rd_id)));
relname = quote_identifier((const char*)relation->relname);
} else {
schemaname = quote_identifier((const char*)get_namespace_name(get_rel_namespace(rel->rd_id)));
relname = quote_identifier((const char*)get_rel_name(rel->rd_id));
}
initStringInfo(&s);
appendStringInfo(&s, "analyze %s.%s;", schemaname, relname);
attatch_global_info(&query_string_with_info, stmt, s.data, true, mode, rel->rd_id, NULL);
(void)ExecRemoteVacuumStmt(stmt, query_string_with_info, false, ARQ_TYPE_TOTALROWCNTS, mode, rel->rd_id);
double maxRowCnt = stmt->pstGlobalStatEx[ANALYZENORMAL].topRowCnts;
double sortRowCnt = maxRowCnt;
int32 width = 0;
if (RELATION_IS_PARTITIONED(rel)) {
if (rel->partMap && rel->partMap->type == PART_TYPE_RANGE) {
int sumtotal = getPartitionNumber(rel->partMap);
if (sumtotal > 0) {
sortRowCnt /= sumtotal;
}
}
}
if ((access_method != NULL && strncmp(access_method, "psort", strlen("psort")) == 0) ||
rel->rd_rel->relhasclusterkey) {
sortRowCnt = Min(sortRowCnt, RelationGetPartialClusterRows(rel));
vectorized = true;
}
if (info != NULL) {
if (access_method != NULL && strncmp(access_method, "hash", strlen("hash")) == 0)
width = sizeof(Datum);
else
width = getIdxDataWidth(rel, info, vectorized);
} else
width = get_relation_data_width(rel->rd_id, InvalidOid, NULL, rel->rd_rel->relhasclusterkey);
SetSortMemInfo(
desc, (int)sortRowCnt, width, vectorized, (info != NULL), u_sess->attr.attr_memory.maintenance_work_mem);
}
* SetSortMemInfo
* According to resource usage and cardinality estimation, assign query
* max/min mem, assigned statement mem in utilityDesc struct
*
* Parameters:
* @out desc: set meminfo in this struct
* @in row_count: estimated sort row count
* @in width: estimated tuple width
* @in vectorized: whether vertorized sort will be used for sort
* @in index_sort: if sort tuple is from index
* @in default_size: default size used when resource management is unavailable
* @in copy_rel: input relation for copy, or NULL for non-copy case
*
* Returns: void
*/
void SetSortMemInfo(
UtilityDesc* desc, int row_count, int width, bool vectorized, bool index_sort, Size default_size, Relation copy_rel)
{
Path sort_path;
OpMemInfo mem_info;
bool use_tenant = false;
int max_mem = 0;
int available_mem = 0;
dywlm_client_get_memory_info(&max_mem, &available_mem, &use_tenant);
* When database is startup, the require of resource mem will be deferred,
* so give a default size in case no resource info available
*/
if (max_mem == 0)
max_mem = available_mem = default_size;
if (copy_rel != NULL) {
Oid rel_oid = copy_rel->rd_id;
cost_insert(&sort_path, vectorized, 0.0, row_count, width, 0.0, available_mem, 1, rel_oid, &mem_info);
} else {
cost_sort(&sort_path, NIL, 0.0, row_count, width, 0.0, available_mem, -1.0, vectorized, 1, &mem_info, index_sort);
}
desc->cost = sort_path.total_cost + u_sess->attr.attr_sql.seq_page_cost * cost_page_size(row_count, width);
desc->query_mem[0] = (int)ceil(Min(mem_info.maxMem, MAX_OP_MEM * 2));
if (desc->query_mem[0] > STATEMENT_MIN_MEM * 1024L) {
desc->query_mem[1] = (int)Max(STATEMENT_MIN_MEM * 1024L, desc->query_mem[0] * DECREASED_MIN_CMP_GAP);
} else
desc->query_mem[1] = desc->query_mem[0];
if (VALID_QUERY_MEM()) {
desc->query_mem[0] = Max(desc->query_mem[0], max_mem);
desc->query_mem[1] = Max(desc->query_mem[1], max_mem);
}
desc->min_mem = (int)(mem_info.minMem);
desc->assigned_mem = max_mem;
if (desc->query_mem[0] > desc->assigned_mem)
desc->query_mem[0] = desc->assigned_mem;
if (desc->query_mem[1] > desc->assigned_mem)
desc->query_mem[1] = desc->assigned_mem;
}
* AdjustIdxMemInfo
* Assign the final decided mem info into IndexStmt, so it can then pass
* down to datanode for memory control
*
* Parameters:
* @out mem_info: adaptive mem struct to assign mem info
* @in desc: Utility desc struct that has mem info to assign
*
* Returns: void
*/
void AdjustIdxMemInfo(AdaptMem* mem_info, UtilityDesc* desc)
{
mem_info->work_mem = Min(desc->query_mem[0], desc->assigned_mem);
mem_info->max_mem = desc->assigned_mem;
if (mem_info->work_mem < MIN_OP_MEM)
mem_info->work_mem = MIN_OP_MEM;
else if (mem_info->work_mem < mem_info->max_mem)
mem_info->work_mem = (int)Min(mem_info->work_mem * BIG_MEM_RATIO, mem_info->max_mem);
else if (mem_info->work_mem == desc->min_mem)
mem_info->work_mem = (int)(mem_info->work_mem * SMALL_MEM_RATIO);
desc->query_mem[0] = 0;
desc->query_mem[1] = 0;
}
* constructMesageWithMemInfo
* if index mem info is adaptive, we should pass it down to datanode.
* This function is used to combine mem info to the message.
*
* Parameters:
* @in query_string: original query string
* @in mem_info: adapt mem struct that contains mem info
*
* Returns: combined message
*/
char* ConstructMesageWithMemInfo(const char* query_string, AdaptMem mem_info)
{
char* final_string = (char*)query_string;
if (mem_info.work_mem > 0) {
List* l = NIL;
l = lappend_int(l, mem_info.work_mem);
l = lappend_int(l, mem_info.max_mem);
char* op_string = nodeToString(l);
AssembleHybridMessage(&final_string, query_string, op_string);
pfree_ext(op_string);
list_free_ext(l);
query_string = final_string;
}
return final_string;
}
static bool IsAllTempObjectsInVacuumStmt(Node* parsetree)
{
bool isTemp = false;
VacuumStmt* stmt = (VacuumStmt*)parsetree;
if (((stmt->options & VACOPT_ANALYZE) || (stmt->options & VACOPT_VACUUM)) && stmt->relation != NULL) {
Oid relationid = RangeVarGetRelid(stmt->relation, NoLock, false);
LOCKMODE lockmode = NEED_EST_TOTAL_ROWS_DN(stmt) ? AccessShareLock : ShareUpdateExclusiveLock;
Relation onerel = try_relation_open(relationid, lockmode);
if (onerel != NULL && onerel->rd_rel != NULL && onerel->rd_rel->relpersistence == RELPERSISTENCE_TEMP) {
isTemp = true;
}
if (onerel != NULL) {
relation_close(onerel, lockmode);
}
}
return isTemp;
}
static void SendCopySqlToClient(StringInfo copy_sql)
{
StringInfoData buf;
pq_beginmessage(&buf, 'T');
pq_sendint16(&buf, 1);
pq_sendstring(&buf, "LOAD TRANSFORM TO COPY RESULT");
pq_sendint32(&buf, 0);
pq_sendint16(&buf, 0);
pq_sendint32(&buf, TEXTOID);
pq_sendint16(&buf, UINT16_MAX);
pq_sendint32(&buf, 0);
pq_sendint16(&buf, 0);
pq_endmessage_noblock(&buf);
pq_beginmessage(&buf, 'D');
pq_sendint16(&buf, 1);
pq_sendint32(&buf, copy_sql->len);
pq_sendbytes(&buf, (char *)copy_sql->data, copy_sql->len);
pq_endmessage_noblock(&buf);
EndCommand_noblock("LOAD", DestTuplestore);
}
static void TransformLoadGetRelation(LoadStmt* stmt, StringInfo buf)
{
RangeVar* relation = stmt->relation;
if (relation->schemaname && relation->schemaname[0]) {
appendStringInfo(buf, "%s.", quote_identifier(relation->schemaname));
}
appendStringInfo(buf, "%s ", quote_identifier(relation->relname));
}
static void TransformLoadGetFiledList(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
ListCell* field_option = NULL;
int special_filed = 0;
foreach (option, stmt->rel_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "fields_list") == 0) {
List* field_list = (List *)def->arg;
appendStringInfo(buf, "(");
foreach (field_option, field_list) {
SqlLoadColExpr* coltem = (SqlLoadColExpr *)lfirst(field_option);
special_filed += ((coltem->const_info != NULL) || (coltem->sequence_info != NULL) ||
(coltem->is_filler == true) ||
((coltem->scalar_spec != NULL) &&
((SqlLoadScalarSpec *)coltem->scalar_spec)->position_info != NULL))
? 1
: 0;
appendStringInfo(buf, " %s,", coltem->colname);
}
buf->data[buf->len - 1] = ' ';
appendStringInfo(buf, ") ");
stmt->is_only_special_filed = (special_filed == list_length(field_list));
break;
}
}
}
static void TransformLoadType(LoadStmt* stmt, StringInfo buf)
{
switch (stmt->load_type) {
case LOAD_DATA_APPEND: {
break;
}
case LOAD_DATA_REPLACE:
case LOAD_DATA_TRUNCATE: {
appendStringInfo(buf, "TRUNCATE TABLE ");
TransformLoadGetRelation(stmt, buf);
appendStringInfo(buf, "; ");
break;
}
case LOAD_DATA_INSERT: {
appendStringInfo(buf, "SELECT 'has_data_in_table' FROM ");
TransformLoadGetRelation(stmt, buf);
appendStringInfo(buf, "LIMIT 1; ");
break;
}
default: {
elog(ERROR, "load type(%d) is not supported", stmt->load_type);
break;
}
}
}
static char * TransformPreLoadOptionsData(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
foreach (option, stmt->pre_load_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "data") == 0) {
char* data = defGetString(def);
return data;
}
}
return NULL;
}
static void TransformLoadDatafile(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
char* filename = NULL;
appendStringInfo(buf, "FROM ");
filename = TransformPreLoadOptionsData(stmt, buf);
if (filename == NULL) {
foreach (option, stmt->load_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "infile") == 0) {
filename = defGetString(def);
break;
}
}
}
if (filename != NULL) {
appendStringInfo(buf, "\'%s\' LOAD ", filename);
}
else {
appendStringInfo(buf, "STDIN LOAD ");
}
}
static void TransformFieldsListScalar(SqlLoadColExpr* coltem, StringInfo transformbuf, StringInfo formatterbuf)
{
SqlLoadScalarSpec* scalarSpec = (SqlLoadScalarSpec *)coltem->scalar_spec;
if (scalarSpec->nullif_col != NULL) {
appendStringInfo(transformbuf, "%s AS nullif(trim(%s), \'\'),", quote_identifier(coltem->colname),
quote_identifier(scalarSpec->nullif_col));
}
if (scalarSpec->sqlstr != NULL) {
appendStringInfo(transformbuf, "%s", quote_identifier(coltem->colname));
A_Const* colexprVal = (A_Const *)scalarSpec->sqlstr;
appendStringInfo(transformbuf, " AS %s,", colexprVal->val.val.str);
}
if (scalarSpec->position_info) {
appendStringInfo(formatterbuf, "%s", quote_identifier(coltem->colname));
SqlLoadColPosInfo* posInfo = (SqlLoadColPosInfo *)scalarSpec->position_info;
int offset = posInfo->start - 1;
int length = posInfo->end - posInfo->start + 1;
appendStringInfo(formatterbuf, "(%d, %d),", offset, length);
}
}
static void TransformFieldsListSeque(const char *schemaname, const char *relname,
SqlLoadColExpr* coltem, StringInfo sequencebuf)
{
SqlLoadSequInfo* sequenceInfo = (SqlLoadSequInfo *)coltem->sequence_info;
int64 start_num = sequenceInfo->start;
int64 step_num = sequenceInfo->step;
if (start_num == LOADER_SEQUENCE_COUNT_FLAG) {
start_num = getCopySequenceCountval(schemaname, relname) + step_num;
} else if (start_num == LOADER_SEQUENCE_MAX_FLAG) {
start_num = getCopySequenceMaxval(schemaname, relname, coltem->colname) + step_num;
}
appendStringInfo(sequencebuf, "%s", quote_identifier(coltem->colname));
appendStringInfo(sequencebuf, "(%ld, %ld),", start_num, step_num);
}
static void TransformFieldsListAppInfo(StringInfo fieldDataBuf, int dataBufCnt, StringInfo buf)
{
if (dataBufCnt < LOADER_COL_BUF_CNT) {
return;
}
StringInfo constantbuf = &fieldDataBuf[0];
StringInfo formatterbuf = &fieldDataBuf[1];
StringInfo transformbuf = &fieldDataBuf[2];
StringInfo sequencebuf = &fieldDataBuf[3];
StringInfo fillerbuf = &fieldDataBuf[4];
if (constantbuf->len) {
constantbuf->data[constantbuf->len - 1] = '\0';
appendStringInfo(buf, "CONSTANT(%s) ", constantbuf->data);
}
if (formatterbuf->len) {
formatterbuf->data[formatterbuf->len - 1] = '\0';
appendStringInfo(buf, "fixed FORMATTER(%s) ", formatterbuf->data);
}
if (sequencebuf->len) {
sequencebuf->data[sequencebuf->len - 1] = '\0';
appendStringInfo(buf, "SEQUENCE(%s) ", sequencebuf->data);
}
if (fillerbuf->len) {
fillerbuf->data[fillerbuf->len - 1] = '\0';
appendStringInfo(buf, "FILLER(%s) ", fillerbuf->data);
}
if (transformbuf->len) {
transformbuf->data[transformbuf->len - 1] = '\0';
appendStringInfo(buf, "TRANSFORM(%s) ", transformbuf->data);
}
}
static void TransformFieldsListOpt(LoadStmt* stmt, DefElem* def, StringInfo buf)
{
List* field_list = (List *)def->arg;
ListCell* option = NULL;
StringInfoData fieldDataBuf[LOADER_COL_BUF_CNT];
RangeVar* relation = stmt->relation;
const char *schemaname = NULL;
const char *relname = quote_identifier(relation->relname);
if (relation->schemaname && relation->schemaname[0]) {
schemaname = quote_identifier(relation->schemaname);
}
for (int i = 0; i < LOADER_COL_BUF_CNT; i++) {
initStringInfo(&fieldDataBuf[i]);
}
StringInfo constantbuf = &fieldDataBuf[0];
StringInfo formatterbuf = &fieldDataBuf[1];
StringInfo transformbuf = &fieldDataBuf[2];
StringInfo sequencebuf = &fieldDataBuf[3];
StringInfo fillerbuf = &fieldDataBuf[4];
foreach (option, field_list) {
SqlLoadColExpr* coltem = (SqlLoadColExpr *)lfirst(option);
if (coltem->const_info != NULL) {
appendStringInfo(constantbuf, "%s", quote_identifier(coltem->colname));
A_Const* constVal = (A_Const *)coltem->const_info;
appendStringInfo(constantbuf, " \'%s\',", constVal->val.val.str);
}
else if (coltem->sequence_info != NULL) {
TransformFieldsListSeque(schemaname, relname, coltem, sequencebuf);
}else if (coltem->is_filler == true) {
appendStringInfo(fillerbuf, " %s,", quote_identifier(coltem->colname));
}
else if (coltem->scalar_spec != NULL) {
TransformFieldsListScalar(coltem, transformbuf, formatterbuf);
}
}
TransformFieldsListAppInfo(fieldDataBuf, LOADER_COL_BUF_CNT, buf);
}
static void TransformLoadOptions(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
foreach (option, stmt->load_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "characterset") == 0) {
char* encoding = defGetString(def);
if (pg_strcasecmp(encoding, "AL32UTF8") == 0) {
encoding = "utf8";
}
if (pg_strcasecmp(encoding, "zhs16gbk") == 0) {
encoding = "gbk";
}
if (pg_strcasecmp(encoding, "zhs32gb18030") == 0) {
encoding = "gb18030";
}
appendStringInfo(buf, "ENCODING \'%s\' ", encoding);
break;
}
}
}
static void TransformLoadRelationOptionsWhen(List *when_list, StringInfo buf)
{
ListCell* lc = NULL;
bool is_frist = true;
foreach (lc, when_list) {
LoadWhenExpr *when = (LoadWhenExpr *)lfirst(lc);
CheckCopyWhenExprOptions(when);
if (is_frist == true) {
appendStringInfo(buf, "WHEN ");
is_frist = false;
} else {
appendStringInfo(buf, "AND ");
}
if (when->whentype == 0) {
appendStringInfo(buf, "(%d-%d) ", when->start, when->end);
appendStringInfo(buf, "%s ", when->oper);
appendStringInfo(buf, "\'%s\' ", when->val);
} else {
appendStringInfo(buf, "%s ", quote_identifier(when->attname));
appendStringInfo(buf, "%s ", when->oper);
appendStringInfo(buf, "\'%s\' ", when->val);
}
}
}
static void TransformLoadRelationOptions(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
bool fields_csv = false;
bool optionally_enclosed_by = false;
foreach (option, stmt->rel_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "when_expr") == 0) {
List *when_list = (List*)(def->arg);
TransformLoadRelationOptionsWhen(when_list, buf);
}
else if (strcmp(def->defname, "fields_csv") == 0) {
if (stmt->is_only_special_filed == false) {
appendStringInfo(buf, "csv ");
fields_csv = true;
}
} else if (strcmp(def->defname, "trailing_nullcols") == 0) {
appendStringInfo(buf, "FILL_MISSING_FIELDS 'multi' ");
} else if (strcmp(def->defname, "fields_terminated_by") == 0) {
if (stmt->is_only_special_filed == false) {
char* terminated_by = defGetString(def);
appendStringInfo(buf, "DELIMITER \'%s\' ", terminated_by);
}
} else if (strcmp(def->defname, "optionally_enclosed_by") == 0) {
if (stmt->is_only_special_filed == false) {
char *quote = defGetString(def);
appendStringInfo(buf, "QUOTE \'%s\' ", quote);
optionally_enclosed_by = true;
}
} else if (strcmp(def->defname, "fields_list") == 0) {
TransformFieldsListOpt(stmt, def, buf);
} else {
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("option \"%s\" not recognized", def->defname)));
}
}
if (optionally_enclosed_by && !fields_csv && stmt->is_only_special_filed == false) {
appendStringInfo(buf, "csv ");
}
}
static bool LoadDataHasWhenExpr(LoadStmt* stmt)
{
ListCell* option = NULL;
foreach (option, stmt->rel_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "when_expr") == 0) {
return list_length((List*)(def->arg)) != 0;
}
}
return false;
}
static void TransformPreLoadOptions(LoadStmt* stmt, StringInfo buf)
{
ListCell* option = NULL;
int64 errors = 0;
foreach (option, stmt->pre_load_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "errors") == 0) {
errors = defGetInt64(def);
if (errors < 0) {
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("ERRORS=%ld in OPTIONS should be >= 0", errors)));
}
else if (errors > 0) {
appendStringInfo(buf, "LOG ERRORS DATA REJECT LIMIT \'%ld\' ", errors);
}
}
}
if (errors == 0 && LoadDataHasWhenExpr(stmt)) {
appendStringInfo(buf, "LOG ERRORS DATA ");
}
foreach (option, stmt->pre_load_options) {
DefElem* def = (DefElem*)lfirst(option);
if (strcmp(def->defname, "skip") == 0) {
int64 skip = defGetInt64(def);
if (skip < 0) {
ereport(ERROR, (errcode(ERRCODE_SYNTAX_ERROR), errmsg("SKIP=%ld in OPTIONS should be >= 0", skip)));
}
appendStringInfo(buf, "SKIP %ld ", skip);
}
}
}
static void TransformLoadDataToCopy(LoadStmt* stmt)
{
StringInfoData bufData;
StringInfo buf = &bufData;
initStringInfo(buf);
TransformLoadType(stmt, buf);
appendStringInfo(buf, "\\COPY ");
TransformLoadGetRelation(stmt, buf);
TransformLoadGetFiledList(stmt, buf);
TransformLoadDatafile(stmt, buf);
TransformPreLoadOptions(stmt, buf);
TransformLoadOptions(stmt, buf);
TransformLoadRelationOptions(stmt, buf);
appendStringInfo(buf, "IGNORE_EXTRA_DATA;");
elog(LOG, "load data is recv type: %d tablename:%s\ncopy sql:%s",
stmt->type, stmt->relation->relname, buf->data);
SendCopySqlToClient(buf);
}
static int64 getCopySequenceCountval(const char *nspname, const char *relname)
{
StringInfoData buf;
int ret;
bool isnull;
Datum attval;
if (relname == NULL) {
return 0;
}
initStringInfo(&buf);
if (nspname == NULL) {
appendStringInfo(&buf, "select cast(count(*) as bigint) from %s", quote_identifier(relname));
} else {
appendStringInfo(&buf, "select cast(count(*) as bigint) from %s.%s", quote_identifier(nspname),
quote_identifier(relname));
}
if ((ret = SPI_connect()) < 0) {
ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI connect failure - returned %d", ret)));
}
ret = SPI_execute(buf.data, true, INT_MAX);
if (ret != SPI_OK_SELECT)
ereport(ERROR, (errcode(ERRCODE_SPI_EXECUTE_FAILURE), errmsg("SPI_execute failed: error code %d", ret)));
attval = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
if (isnull) {
attval = 0;
}
SPI_finish();
return DatumGetInt64(attval);
}
static int64 getCopySequenceMaxval(const char *nspname, const char *relname, const char *colname)
{
StringInfoData buf;
int ret;
bool isnull;
Datum attval;
if (relname == NULL || colname == NULL) {
return 0;
}
initStringInfo(&buf);
if (nspname == NULL) {
appendStringInfo(&buf, "select cast(nvl(max(%s),0) as bigint) from %s ", quote_identifier(colname),
quote_identifier(relname));
} else {
appendStringInfo(&buf, "select cast(nvl(max(%s),0) as bigint) from %s.%s ", quote_identifier(colname),
quote_identifier(nspname), quote_identifier(relname));
}
if ((ret = SPI_connect()) < 0) {
ereport(ERROR, (errcode(ERRCODE_SPI_CONNECTION_FAILURE), errmsg("SPI connect failure - returned %d", ret)));
}
ret = SPI_execute(buf.data, true, INT_MAX);
if (ret != SPI_OK_SELECT)
ereport(ERROR, (errcode(ERRCODE_SPI_EXECUTE_FAILURE), errmsg("SPI_execute failed: error code %d", ret)));
attval = SPI_getbinval(SPI_tuptable->vals[0], SPI_tuptable->tupdesc, 1, &isnull);
if (isnull) {
attval = 0;
}
SPI_finish();
return DatumGetInt64(attval);
}
struct OndemandParseInfo {
NodeTag parseType;
ObjectType objectType;
RangeVar* relationRangeVar;
const char* dbName;
};
static List* AppendItemToOndemandParseList(List* ondemandParseList, NodeTag parseType, ObjectType objectType, RangeVar* relationRangeVar, const char* dbName = NULL) {
OndemandParseInfo* info = (OndemandParseInfo*)palloc(sizeof(OndemandParseInfo));
info->parseType = parseType;
info->objectType = objectType;
info->relationRangeVar = relationRangeVar;
info->dbName = dbName;
ondemandParseList = lappend(ondemandParseList, info);
return ondemandParseList;
}
* @brief Get the table by index rangeVar, where the index is on,
* and pre redo the table.
*
* @param ondemandParseInfo
*/
static void PreRedoRelationByIndexRangeVar(OndemandParseInfo* ondemandParseInfo) {
Oid relId = RangeVarGetRelid(ondemandParseInfo->relationRangeVar, NoLock, true);
if (!OidIsValid(relId)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.",
ondemandParseInfo->relationRangeVar->relname)));
return;
}
Oid relationOid = IndexGetRelation(relId, true);
if (OidIsValid(relationOid)) {
PreRedoTableInOndemandRecovery(relationOid);
} else {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for index %u", relId)));
}
}
* @brief pre redo in ondemand redo phase before execute ddl.
*
* @param parseTree
*/
static void PreRedoInOndemandRecovery(Node* parseTree) {
if (!ENABLE_ONDEMAND_RECOVERY || !SS_IN_ONDEMAND_RECOVERY) {
return;
}
List* ondemandParseList = NULL;
switch (nodeTag(parseTree)) {
case T_CompileStmt: {
CompileStmt* compileStmt = (CompileStmt*) parseTree;
switch (compileStmt->compileItem) {
case COMPILE_PROCEDURE: {
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
parseTree->type, compileStmt->compileItem)));
break;
}
}
break;
}
case T_AlterTableStmt: {
AlterTableStmt* alterTableStmt = (AlterTableStmt*) parseTree;
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_AlterTableStmt,
alterTableStmt->relkind, alterTableStmt->relation);
break;
}
case T_DropStmt: {
DropStmt* dropStmt = (DropStmt*) parseTree;
ListCell* cell = NULL;
foreach (cell, dropStmt->objects) {
RangeVar* relationRangeVar = makeRangeVarFromNameList((List*)lfirst(cell));
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_DropStmt,
dropStmt->removeType, relationRangeVar);
}
break;
}
case T_IndexStmt: {
IndexStmt* indexStmt = (IndexStmt*) parseTree;
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_IndexStmt,
OBJECT_INDEX, indexStmt->relation);
break;
}
case T_RenameStmt: {
RenameStmt* renameStmt = (RenameStmt*) parseTree;
if (renameStmt->renameType == OBJECT_PARTITION || renameStmt->renameType == OBJECT_COLUMN) {
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_RenameStmt,
renameStmt->relationType, renameStmt->relation);
} else {
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_RenameStmt,
renameStmt->renameType, renameStmt->relation);
}
break;
}
case T_ReindexStmt: {
ReindexStmt* reindexStmt = (ReindexStmt*) parseTree;
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_ReindexStmt,
reindexStmt->kind, reindexStmt->relation,
reindexStmt->kind == OBJECT_DATABASE ? reindexStmt->name : NULL);
break;
}
case T_AlterObjectSchemaStmt: {
AlterObjectSchemaStmt* alterObjectSchemaStmt = (AlterObjectSchemaStmt*) parseTree;
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_AlterObjectSchemaStmt,
alterObjectSchemaStmt->objectType, alterObjectSchemaStmt->relation);
break;
}
case T_AlterOwnerStmt: {
AlterOwnerStmt* alterOwnerStmt = (AlterOwnerStmt*) parseTree;
ondemandParseList = AppendItemToOndemandParseList(ondemandParseList, T_AlterObjectSchemaStmt,
alterOwnerStmt->objectType, NULL);
break;
}
* Some syntax types no need to adapt, in ondemand reovery phase.
* See PreventCommandDuringSSOndemandRedo to get all the support syntax type.
*/
default:
break;
}
ListCell* cell = NULL;
foreach (cell, ondemandParseList) {
OndemandParseInfo* ondemandParseInfo = (OndemandParseInfo*)lfirst(cell);
switch (ondemandParseInfo->objectType) {
case OBJECT_DATABASE: {
switch (ondemandParseInfo->parseType) {
case T_RenameStmt:
case T_AlterOwnerStmt: {
break;
}
case T_ReindexStmt: {
const char* dbName = ondemandParseInfo->dbName;
Oid dbOid = get_database_oid_by_name(dbName);
if (!OidIsValid(dbOid)) {
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_OBJECT), errmsg("Database \"%s\" does not exist.", dbName)));
break;
}
RedoDatabaseForOndemandExtremeRTO(dbOid);
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_FUNCTION: {
switch (ondemandParseInfo->parseType) {
case T_DropStmt:
case T_RenameStmt:
case T_AlterObjectSchemaStmt:
case T_AlterOwnerStmt: {
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_INDEX: {
switch (ondemandParseInfo->parseType) {
case T_AlterTableStmt:
case T_DropStmt: {
Oid relId = RangeVarGetRelid(ondemandParseInfo->relationRangeVar, NoLock, true);
if (!OidIsValid(relId)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.", ondemandParseInfo->relationRangeVar->relname)));
break;
}
PreRedoIndexInOndemandRecovery(relId);
break;
}
case T_IndexStmt: {
RangeVar* relationRangeVar = ondemandParseInfo->relationRangeVar;
char* relname = relationRangeVar->relname;
Oid namespaceId = RangeVarGetCreationNamespace(relationRangeVar);
HeapTuple tuple = SearchSysCache2(RELNAMENSP, PointerGetDatum(relationRangeVar->relname), ObjectIdGetDatum(namespaceId));
if (!HeapTupleIsValid(tuple)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.",
relname)));
break;
}
Oid relId = HeapTupleGetOid(tuple);
ReleaseSysCache(tuple);
* Pre-redo the relation which this index is on before create index.
* We will pre-redo all the relation and partition associated with the relation.
*/
PreRedoTableInOndemandRecovery(relId);
break;
}
case T_ReindexStmt:
case T_RenameStmt: {
PreRedoRelationByIndexRangeVar(ondemandParseInfo);
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_INDEX_PARTITION: {
switch (ondemandParseInfo->parseType) {
case T_ReindexStmt: {
PreRedoRelationByIndexRangeVar(ondemandParseInfo);
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_SCHEMA: {
switch (ondemandParseInfo->parseType) {
case T_DropStmt: {
RangeVar* schemaRangeVar = ondemandParseInfo->relationRangeVar;
char* schemaName = schemaRangeVar->relname;
Oid schemaOid = get_namespace_oid(schemaName, true);
if (!OidIsValid(schemaOid)) {
ereport(LOG,
(errmsg("[On-demand] The schema doesn't exists, no need to redo, schemaname: %s.", schemaName)));
break;
}
RedoDatabaseForOndemandExtremeRTO(u_sess->proc_cxt.MyDatabaseId);
break;
}
case T_RenameStmt:
case T_AlterOwnerStmt: {
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_PARTITION_INDEX: {
switch (ondemandParseInfo->parseType) {
case T_RenameStmt: {
Oid relId = RangeVarGetRelid(ondemandParseInfo->relationRangeVar, NoLock, true);
if (!OidIsValid(relId)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.", ondemandParseInfo->relationRangeVar->relname)));
break;
}
Oid relationOid = IndexGetRelation(relId, true);
if (OidIsValid(relationOid)) {
PreRedoTableInOndemandRecovery(relationOid);
} else {
ereport(ERROR, (errcode(ERRCODE_CACHE_LOOKUP_FAILED), errmsg("cache lookup failed for index %u", relId)));
}
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_TABLE: {
switch (ondemandParseInfo->parseType) {
case T_AlterTableStmt:
case T_DropStmt:
case T_RenameStmt:
case T_ReindexStmt:
case T_AlterObjectSchemaStmt: {
Oid relId = RangeVarGetRelid(ondemandParseInfo->relationRangeVar, NoLock, true);
if (!OidIsValid(relId)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.", ondemandParseInfo->relationRangeVar->relname)));
break;
}
PreRedoTableInOndemandRecovery(relId);
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_TABLE_PARTITION: {
switch (ondemandParseInfo->parseType) {
case T_ReindexStmt: {
Oid relId = RangeVarGetRelid(ondemandParseInfo->relationRangeVar, NoLock, true);
if (!OidIsValid(relId)) {
ereport(LOG,
(errmsg("[On-demand] The relation doesn't exists, no need to redo, relname: %s.", ondemandParseInfo->relationRangeVar->relname)));
break;
}
PreRedoTableInOndemandRecovery(relId);
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
case OBJECT_VIEW: {
switch (ondemandParseInfo->parseType) {
case T_AlterTableStmt:
case T_DropStmt:
case T_RenameStmt:
case T_AlterObjectSchemaStmt: {
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
break;
}
default: {
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand] Not support this sql in ondemand redo phase, nodeType: %d, relKind: %d.",
ondemandParseInfo->parseType, ondemandParseInfo->objectType)));
break;
}
}
}
list_free_deep(ondemandParseList);
}
* @brief Find out all the index and the partition of inex on of target relation, and pre
* redo the before execute ddl.
*
* @param relation target relation
* @return int
*/
int PreRedoIndexByRelation(Relation relation) {
Assert(RelationIsValid(relation));
int indexNum = 0;
List* indexList = RelationGetIndexList(relation);
ListCell* indexCell = NULL;
if (indexList == NULL || indexList->length == 0) {
return 0;
}
foreach (indexCell, indexList) {
Oid indexOid = lfirst_oid(indexCell);
Relation indexRelation;
indexRelation = index_open(indexOid, ExclusiveLock);
if (indexRelation->rd_rel->relkind != RELKIND_INDEX || RelationIsNonpartitioned(indexRelation)) {
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(indexRelation);
}
indexNum++;
} else {
List *partitionTupleList = NULL;
Partition partition = NULL;
partitionTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_INDEX_PARTITION, indexRelation->rd_id);
Assert(PointerIsValid(partitionTupleList));
ListCell *partitionCell = NULL;
foreach (partitionCell, partitionTupleList) {
HeapTuple tuple = (HeapTuple)lfirst(partitionCell);
Oid partitionId = HeapTupleGetOid(tuple);
partition = partitionOpen(indexRelation, partitionId, ExclusiveLock);
Relation partitionRelation = partitionGetRelation(indexRelation, partition);
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(partitionRelation);
}
releaseDummyRelation(&partitionRelation);
partitionClose(indexRelation, partition, ExclusiveLock);
}
list_free_deep(partitionTupleList);
}
index_close(indexRelation, ExclusiveLock);
}
list_free(indexList);
return indexNum;
}
* @brief Find out all the partition or relation of target relation, and pre
* redo the before execute ddl.
*
* @param relation
* @return int
*/
int PreRedoRelationByRelation(Relation relation) {
Assert(RelationIsValid(relation));
int partitionNum = 0;
if (!RELATION_IS_PARTITIONED(relation)) {
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(relation);
}
partitionNum++;
return partitionNum;
}
List* partitionList = NULL;
if (RelationIsCommonPartitioned(relation)) {
partitionList = relationGetPartitionList(relation, ExclusiveLock);
} else {
partitionList = RelationGetSubPartitionList(relation, ExclusiveLock);
}
ListCell* partitionCell = NULL;
if (partitionList == NULL) {
return partitionNum;
}
foreach (partitionCell, partitionList) {
Partition partition = (Partition)lfirst(partitionCell);
Relation partitionRelation = partitionGetRelation(relation, partition);
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(partitionRelation);
}
releaseDummyRelation(&partitionRelation);
partitionNum++;
}
releasePartitionList(relation, &partitionList, ExclusiveLock);
return partitionNum;
}
* @brief Find out all the toast table of taget relation, and pre redo them before execute ddl.
*
* @param relation target relation
* @return int the num of toast table
*/
int PreRedoToastByRelation(Relation relation) {
Assert(RelationIsValid(relation));
int toastNum = 0;
if (!RELATION_IS_PARTITIONED(relation)) {
Relation toastRelation = try_relation_open(relation->rd_rel->reltoastrelid, ExclusiveLock);
if (RelationIsValid(toastRelation)) {
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(toastRelation);
}
toastNum++;
heap_close(toastRelation, ExclusiveLock);
}
return toastNum;
}
List* partitionList = NULL;
if (RelationIsCommonPartitioned(relation)) {
partitionList = relationGetPartitionList(relation, ExclusiveLock);
} else {
partitionList = RelationGetSubPartitionList(relation, ExclusiveLock);
}
if (partitionList == NULL) {
return toastNum;
}
ListCell* partitionCell = NULL;
foreach (partitionCell, partitionList) {
Partition partition = (Partition)lfirst(partitionCell);
Relation partitionRelation = partitionGetRelation(relation, partition);
if (!RelationIsValid(partitionRelation) || !OidIsValid(partitionRelation->rd_rel->reltoastrelid)) {
releaseDummyRelation(&partitionRelation);
continue;
}
Relation toastRelation = heap_open(partitionRelation->rd_rel->reltoastrelid, ExclusiveLock);
if (!RelationIsValid(toastRelation)) {
releaseDummyRelation(&toastRelation);
continue;
}
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(toastRelation);
}
heap_close(toastRelation, ExclusiveLock);
releaseDummyRelation(&partitionRelation);
toastNum++;
}
releasePartitionList(relation, &partitionList, ExclusiveLock);
return toastNum;
}
* @brief Redo for index In ondemand recvery by relId, before execute ddl.
*
* @param indexId oid of index.
*/
static void PreRedoIndexInOndemandRecovery(Oid indexId) {
int indexNum = 0;
if (!SS_IN_ONDEMAND_RECOVERY) {
return;
}
Relation indexRelation = index_open(indexId, ExclusiveLock);
if (!RelationIsValid(indexRelation)) {
return;
}
if (indexRelation->rd_rel->relkind != RELKIND_INDEX || RelationIsNonpartitioned(indexRelation)) {
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(indexRelation);
}
indexNum++;
} else {
List *partitionTupleList = NULL;
Partition partition = NULL;
partitionTupleList = searchPgPartitionByParentId(PART_OBJ_TYPE_INDEX_PARTITION, indexRelation->rd_id);
Assert(PointerIsValid(partitionTupleList));
ListCell *partitionCell = NULL;
foreach (partitionCell, partitionTupleList) {
HeapTuple tuple = (HeapTuple)lfirst(partitionCell);
Oid partitionId = HeapTupleGetOid(tuple);
partition = partitionOpen(indexRelation, partitionId, ExclusiveLock);
Relation partitionRelation = partitionGetRelation(indexRelation, partition);
if (SS_IN_ONDEMAND_RECOVERY) {
RedoRelationForOndemandExtremeRTO(partitionRelation);
}
releaseDummyRelation(&partitionRelation);
partitionClose(indexRelation, partition, ExclusiveLock);
}
list_free_deep(partitionTupleList);
}
index_close(indexRelation, ExclusiveLock);
}
* @brief Redo for table In ondemand recvery by relId, before execute ddl.
*
* @param relId Oid of relation
*/
static void PreRedoTableInOndemandRecovery(Oid relId) {
if (!SS_IN_ONDEMAND_RECOVERY) {
return;
}
Relation relation = heap_open(relId, ExclusiveLock);
if (!RelationIsValid(relation)) {
return;
}
if ((relation->rd_rel->relkind == RELKIND_RELATION && IsSegmentPhysicalRelNode(relation->rd_node)) ||
RelationIsTableAccessMethodUStoreType(relation->rd_options) ||
RelationIsCUFormat(relation) ||
RowRelationIsCompressed(relation)) {
if (SS_IN_ONDEMAND_RECOVERY) {
heap_close(relation, ExclusiveLock);
ereport(ERROR, (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
errmsg("[On-demand]Only support segment storage type and ASTORE while DMS and DSS enable.\n"
"Compression is not supported.")));
}
}
int indexNum = 0;
indexNum = PreRedoIndexByRelation(relation);
int partitionNum = 0;
partitionNum = PreRedoRelationByRelation(relation);
int toastNum = 0;
toastNum = PreRedoToastByRelation(relation);
ereport(DEBUG1,
(errmsg("[On-demand] pre redo relation befor execute ddl, relId: %u, indexNum: %d, partitionNum/relationNum: %d, toastNum: %d.",
relId, indexNum, partitionNum, toastNum)));
heap_close(relation, ExclusiveLock);
}