1 /*-------------------------------------------------------------------------
2  *
3  * utility.c
4  *	  Contains functions which control the execution of the POSTGRES utility
5  *	  commands.  At one time acted as an interface between the Lisp and C
6  *	  systems.
7  *
8  * Portions Copyright (c) 1996-2019, PostgreSQL Global Development Group
9  * Portions Copyright (c) 1994, Regents of the University of California
10  *
11  *
12  * IDENTIFICATION
13  *	  src/backend/tcop/utility.c
14  *
15  *-------------------------------------------------------------------------
16  */
17 #include "postgres.h"
18 
19 #include "access/htup_details.h"
20 #include "access/reloptions.h"
21 #include "access/twophase.h"
22 #include "access/xact.h"
23 #include "access/xlog.h"
24 #include "catalog/catalog.h"
25 #include "catalog/namespace.h"
26 #include "catalog/pg_inherits.h"
27 #include "catalog/toasting.h"
28 #include "commands/alter.h"
29 #include "commands/async.h"
30 #include "commands/cluster.h"
31 #include "commands/comment.h"
32 #include "commands/collationcmds.h"
33 #include "commands/conversioncmds.h"
34 #include "commands/copy.h"
35 #include "commands/createas.h"
36 #include "commands/dbcommands.h"
37 #include "commands/defrem.h"
38 #include "commands/discard.h"
39 #include "commands/event_trigger.h"
40 #include "commands/explain.h"
41 #include "commands/extension.h"
42 #include "commands/matview.h"
43 #include "commands/lockcmds.h"
44 #include "commands/policy.h"
45 #include "commands/portalcmds.h"
46 #include "commands/prepare.h"
47 #include "commands/proclang.h"
48 #include "commands/publicationcmds.h"
49 #include "commands/schemacmds.h"
50 #include "commands/seclabel.h"
51 #include "commands/sequence.h"
52 #include "commands/subscriptioncmds.h"
53 #include "commands/tablecmds.h"
54 #include "commands/tablespace.h"
55 #include "commands/trigger.h"
56 #include "commands/typecmds.h"
57 #include "commands/user.h"
58 #include "commands/vacuum.h"
59 #include "commands/view.h"
60 #include "miscadmin.h"
61 #include "parser/parse_utilcmd.h"
62 #include "postmaster/bgwriter.h"
63 #include "rewrite/rewriteDefine.h"
64 #include "rewrite/rewriteRemove.h"
65 #include "storage/fd.h"
66 #include "tcop/pquery.h"
67 #include "tcop/utility.h"
68 #include "utils/acl.h"
69 #include "utils/guc.h"
70 #include "utils/lsyscache.h"
71 #include "utils/syscache.h"
72 #include "utils/rel.h"
73 
74 
75 /* Hook for plugins to get control in ProcessUtility() */
76 ProcessUtility_hook_type ProcessUtility_hook = NULL;
77 
78 /* local function declarations */
79 static void ProcessUtilitySlow(ParseState *pstate,
80 							   PlannedStmt *pstmt,
81 							   const char *queryString,
82 							   ProcessUtilityContext context,
83 							   ParamListInfo params,
84 							   QueryEnvironment *queryEnv,
85 							   DestReceiver *dest,
86 							   char *completionTag);
87 static void ExecDropStmt(DropStmt *stmt, bool isTopLevel);
88 
89 
90 /*
91  * CommandIsReadOnly: is an executable query read-only?
92  *
93  * This is a much stricter test than we apply for XactReadOnly mode;
94  * the query must be *in truth* read-only, because the caller wishes
95  * not to do CommandCounterIncrement for it.
96  *
97  * Note: currently no need to support raw or analyzed queries here
98  */
99 bool
CommandIsReadOnly(PlannedStmt * pstmt)100 CommandIsReadOnly(PlannedStmt *pstmt)
101 {
102 	Assert(IsA(pstmt, PlannedStmt));
103 	switch (pstmt->commandType)
104 	{
105 		case CMD_SELECT:
106 			if (pstmt->rowMarks != NIL)
107 				return false;	/* SELECT FOR [KEY] UPDATE/SHARE */
108 			else if (pstmt->hasModifyingCTE)
109 				return false;	/* data-modifying CTE */
110 			else
111 				return true;
112 		case CMD_UPDATE:
113 		case CMD_INSERT:
114 		case CMD_DELETE:
115 			return false;
116 		case CMD_UTILITY:
117 			/* For now, treat all utility commands as read/write */
118 			return false;
119 		default:
120 			elog(WARNING, "unrecognized commandType: %d",
121 				 (int) pstmt->commandType);
122 			break;
123 	}
124 	return false;
125 }
126 
127 /*
128  * check_xact_readonly: is a utility command read-only?
129  *
130  * Here we use the loose rules of XactReadOnly mode: no permanent effects
131  * on the database are allowed.
132  */
133 static void
check_xact_readonly(Node * parsetree)134 check_xact_readonly(Node *parsetree)
135 {
136 	/* Only perform the check if we have a reason to do so. */
137 	if (!XactReadOnly && !IsInParallelMode())
138 		return;
139 
140 	/*
141 	 * Note: Commands that need to do more complicated checking are handled
142 	 * elsewhere, in particular COPY and plannable statements do their own
143 	 * checking.  However they should all call PreventCommandIfReadOnly or
144 	 * PreventCommandIfParallelMode to actually throw the error.
145 	 */
146 
147 	switch (nodeTag(parsetree))
148 	{
149 		case T_AlterDatabaseStmt:
150 		case T_AlterDatabaseSetStmt:
151 		case T_AlterDomainStmt:
152 		case T_AlterFunctionStmt:
153 		case T_AlterRoleStmt:
154 		case T_AlterRoleSetStmt:
155 		case T_AlterObjectDependsStmt:
156 		case T_AlterObjectSchemaStmt:
157 		case T_AlterOwnerStmt:
158 		case T_AlterOperatorStmt:
159 		case T_AlterSeqStmt:
160 		case T_AlterTableMoveAllStmt:
161 		case T_AlterTableStmt:
162 		case T_RenameStmt:
163 		case T_CommentStmt:
164 		case T_DefineStmt:
165 		case T_CreateCastStmt:
166 		case T_CreateEventTrigStmt:
167 		case T_AlterEventTrigStmt:
168 		case T_CreateConversionStmt:
169 		case T_CreatedbStmt:
170 		case T_CreateDomainStmt:
171 		case T_CreateFunctionStmt:
172 		case T_CreateRoleStmt:
173 		case T_IndexStmt:
174 		case T_CreatePLangStmt:
175 		case T_CreateOpClassStmt:
176 		case T_CreateOpFamilyStmt:
177 		case T_AlterOpFamilyStmt:
178 		case T_RuleStmt:
179 		case T_CreateSchemaStmt:
180 		case T_CreateSeqStmt:
181 		case T_CreateStmt:
182 		case T_CreateTableAsStmt:
183 		case T_RefreshMatViewStmt:
184 		case T_CreateTableSpaceStmt:
185 		case T_CreateTransformStmt:
186 		case T_CreateTrigStmt:
187 		case T_CompositeTypeStmt:
188 		case T_CreateEnumStmt:
189 		case T_CreateRangeStmt:
190 		case T_AlterEnumStmt:
191 		case T_ViewStmt:
192 		case T_DropStmt:
193 		case T_DropdbStmt:
194 		case T_DropTableSpaceStmt:
195 		case T_DropRoleStmt:
196 		case T_GrantStmt:
197 		case T_GrantRoleStmt:
198 		case T_AlterDefaultPrivilegesStmt:
199 		case T_TruncateStmt:
200 		case T_DropOwnedStmt:
201 		case T_ReassignOwnedStmt:
202 		case T_AlterTSDictionaryStmt:
203 		case T_AlterTSConfigurationStmt:
204 		case T_CreateExtensionStmt:
205 		case T_AlterExtensionStmt:
206 		case T_AlterExtensionContentsStmt:
207 		case T_CreateFdwStmt:
208 		case T_AlterFdwStmt:
209 		case T_CreateForeignServerStmt:
210 		case T_AlterForeignServerStmt:
211 		case T_CreateUserMappingStmt:
212 		case T_AlterUserMappingStmt:
213 		case T_DropUserMappingStmt:
214 		case T_AlterTableSpaceOptionsStmt:
215 		case T_CreateForeignTableStmt:
216 		case T_ImportForeignSchemaStmt:
217 		case T_SecLabelStmt:
218 		case T_CreatePublicationStmt:
219 		case T_AlterPublicationStmt:
220 		case T_CreateSubscriptionStmt:
221 		case T_AlterSubscriptionStmt:
222 		case T_DropSubscriptionStmt:
223 			PreventCommandIfReadOnly(CreateCommandTag(parsetree));
224 			PreventCommandIfParallelMode(CreateCommandTag(parsetree));
225 			break;
226 		default:
227 			/* do nothing */
228 			break;
229 	}
230 }
231 
232 /*
233  * PreventCommandIfReadOnly: throw error if XactReadOnly
234  *
235  * This is useful mainly to ensure consistency of the error message wording;
236  * most callers have checked XactReadOnly for themselves.
237  */
238 void
PreventCommandIfReadOnly(const char * cmdname)239 PreventCommandIfReadOnly(const char *cmdname)
240 {
241 	if (XactReadOnly)
242 		ereport(ERROR,
243 				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
244 		/* translator: %s is name of a SQL command, eg CREATE */
245 				 errmsg("cannot execute %s in a read-only transaction",
246 						cmdname)));
247 }
248 
249 /*
250  * PreventCommandIfParallelMode: throw error if current (sub)transaction is
251  * in parallel mode.
252  *
253  * This is useful mainly to ensure consistency of the error message wording;
254  * most callers have checked IsInParallelMode() for themselves.
255  */
256 void
PreventCommandIfParallelMode(const char * cmdname)257 PreventCommandIfParallelMode(const char *cmdname)
258 {
259 	if (IsInParallelMode())
260 		ereport(ERROR,
261 				(errcode(ERRCODE_INVALID_TRANSACTION_STATE),
262 		/* translator: %s is name of a SQL command, eg CREATE */
263 				 errmsg("cannot execute %s during a parallel operation",
264 						cmdname)));
265 }
266 
267 /*
268  * PreventCommandDuringRecovery: throw error if RecoveryInProgress
269  *
270  * The majority of operations that are unsafe in a Hot Standby
271  * will be rejected by XactReadOnly tests.  However there are a few
272  * commands that are allowed in "read-only" xacts but cannot be allowed
273  * in Hot Standby mode.  Those commands should call this function.
274  */
275 void
PreventCommandDuringRecovery(const char * cmdname)276 PreventCommandDuringRecovery(const char *cmdname)
277 {
278 	if (RecoveryInProgress())
279 		ereport(ERROR,
280 				(errcode(ERRCODE_READ_ONLY_SQL_TRANSACTION),
281 		/* translator: %s is name of a SQL command, eg CREATE */
282 				 errmsg("cannot execute %s during recovery",
283 						cmdname)));
284 }
285 
286 /*
287  * CheckRestrictedOperation: throw error for hazardous command if we're
288  * inside a security restriction context.
289  *
290  * This is needed to protect session-local state for which there is not any
291  * better-defined protection mechanism, such as ownership.
292  */
293 static void
CheckRestrictedOperation(const char * cmdname)294 CheckRestrictedOperation(const char *cmdname)
295 {
296 	if (InSecurityRestrictedOperation())
297 		ereport(ERROR,
298 				(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
299 		/* translator: %s is name of a SQL command, eg PREPARE */
300 				 errmsg("cannot execute %s within security-restricted operation",
301 						cmdname)));
302 }
303 
304 
305 /*
306  * ProcessUtility
307  *		general utility function invoker
308  *
309  *	pstmt: PlannedStmt wrapper for the utility statement
310  *	queryString: original source text of command
311  *	context: identifies source of statement (toplevel client command,
312  *		non-toplevel client command, subcommand of a larger utility command)
313  *	params: parameters to use during execution
314  *	queryEnv: environment for parse through execution (e.g., ephemeral named
315  *		tables like trigger transition tables).  May be NULL.
316  *	dest: where to send results
317  *	completionTag: points to a buffer of size COMPLETION_TAG_BUFSIZE
318  *		in which to store a command completion status string.
319  *
320  * Caller MUST supply a queryString; it is not allowed (anymore) to pass NULL.
321  * If you really don't have source text, you can pass a constant string,
322  * perhaps "(query not available)".
323  *
324  * completionTag is only set nonempty if we want to return a nondefault status.
325  *
326  * completionTag may be NULL if caller doesn't want a status string.
327  *
328  * Note for users of ProcessUtility_hook: the same queryString may be passed
329  * to multiple invocations of ProcessUtility when processing a query string
330  * containing multiple semicolon-separated statements.  One should use
331  * pstmt->stmt_location and pstmt->stmt_len to identify the substring
332  * containing the current statement.  Keep in mind also that some utility
333  * statements (e.g., CREATE SCHEMA) will recurse to ProcessUtility to process
334  * sub-statements, often passing down the same queryString, stmt_location,
335  * and stmt_len that were given for the whole statement.
336  */
337 void
ProcessUtility(PlannedStmt * pstmt,const char * queryString,ProcessUtilityContext context,ParamListInfo params,QueryEnvironment * queryEnv,DestReceiver * dest,char * completionTag)338 ProcessUtility(PlannedStmt *pstmt,
339 			   const char *queryString,
340 			   ProcessUtilityContext context,
341 			   ParamListInfo params,
342 			   QueryEnvironment *queryEnv,
343 			   DestReceiver *dest,
344 			   char *completionTag)
345 {
346 	Assert(IsA(pstmt, PlannedStmt));
347 	Assert(pstmt->commandType == CMD_UTILITY);
348 	Assert(queryString != NULL);	/* required as of 8.4 */
349 
350 	/*
351 	 * We provide a function hook variable that lets loadable plugins get
352 	 * control when ProcessUtility is called.  Such a plugin would normally
353 	 * call standard_ProcessUtility().
354 	 */
355 	if (ProcessUtility_hook)
356 		(*ProcessUtility_hook) (pstmt, queryString,
357 								context, params, queryEnv,
358 								dest, completionTag);
359 	else
360 		standard_ProcessUtility(pstmt, queryString,
361 								context, params, queryEnv,
362 								dest, completionTag);
363 }
364 
365 /*
366  * standard_ProcessUtility itself deals only with utility commands for
367  * which we do not provide event trigger support.  Commands that do have
368  * such support are passed down to ProcessUtilitySlow, which contains the
369  * necessary infrastructure for such triggers.
370  *
371  * This division is not just for performance: it's critical that the
372  * event trigger code not be invoked when doing START TRANSACTION for
373  * example, because we might need to refresh the event trigger cache,
374  * which requires being in a valid transaction.
375  */
376 void
standard_ProcessUtility(PlannedStmt * pstmt,const char * queryString,ProcessUtilityContext context,ParamListInfo params,QueryEnvironment * queryEnv,DestReceiver * dest,char * completionTag)377 standard_ProcessUtility(PlannedStmt *pstmt,
378 						const char *queryString,
379 						ProcessUtilityContext context,
380 						ParamListInfo params,
381 						QueryEnvironment *queryEnv,
382 						DestReceiver *dest,
383 						char *completionTag)
384 {
385 	Node	   *parsetree = pstmt->utilityStmt;
386 	bool		isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
387 	bool		isAtomicContext = (!(context == PROCESS_UTILITY_TOPLEVEL || context == PROCESS_UTILITY_QUERY_NONATOMIC) || IsTransactionBlock());
388 	ParseState *pstate;
389 
390 	/* This can recurse, so check for excessive recursion */
391 	check_stack_depth();
392 
393 	check_xact_readonly(parsetree);
394 
395 	if (completionTag)
396 		completionTag[0] = '\0';
397 
398 	pstate = make_parsestate(NULL);
399 	pstate->p_sourcetext = queryString;
400 
401 	switch (nodeTag(parsetree))
402 	{
403 			/*
404 			 * ******************** transactions ********************
405 			 */
406 		case T_TransactionStmt:
407 			{
408 				TransactionStmt *stmt = (TransactionStmt *) parsetree;
409 
410 				switch (stmt->kind)
411 				{
412 						/*
413 						 * START TRANSACTION, as defined by SQL99: Identical
414 						 * to BEGIN.  Same code for both.
415 						 */
416 					case TRANS_STMT_BEGIN:
417 					case TRANS_STMT_START:
418 						{
419 							ListCell   *lc;
420 
421 							BeginTransactionBlock();
422 							foreach(lc, stmt->options)
423 							{
424 								DefElem    *item = (DefElem *) lfirst(lc);
425 
426 								if (strcmp(item->defname, "transaction_isolation") == 0)
427 									SetPGVariable("transaction_isolation",
428 												  list_make1(item->arg),
429 												  true);
430 								else if (strcmp(item->defname, "transaction_read_only") == 0)
431 									SetPGVariable("transaction_read_only",
432 												  list_make1(item->arg),
433 												  true);
434 								else if (strcmp(item->defname, "transaction_deferrable") == 0)
435 									SetPGVariable("transaction_deferrable",
436 												  list_make1(item->arg),
437 												  true);
438 							}
439 						}
440 						break;
441 
442 					case TRANS_STMT_COMMIT:
443 						if (!EndTransactionBlock(stmt->chain))
444 						{
445 							/* report unsuccessful commit in completionTag */
446 							if (completionTag)
447 								strcpy(completionTag, "ROLLBACK");
448 						}
449 						break;
450 
451 					case TRANS_STMT_PREPARE:
452 						PreventCommandDuringRecovery("PREPARE TRANSACTION");
453 						if (!PrepareTransactionBlock(stmt->gid))
454 						{
455 							/* report unsuccessful commit in completionTag */
456 							if (completionTag)
457 								strcpy(completionTag, "ROLLBACK");
458 						}
459 						break;
460 
461 					case TRANS_STMT_COMMIT_PREPARED:
462 						PreventInTransactionBlock(isTopLevel, "COMMIT PREPARED");
463 						PreventCommandDuringRecovery("COMMIT PREPARED");
464 						FinishPreparedTransaction(stmt->gid, true);
465 						break;
466 
467 					case TRANS_STMT_ROLLBACK_PREPARED:
468 						PreventInTransactionBlock(isTopLevel, "ROLLBACK PREPARED");
469 						PreventCommandDuringRecovery("ROLLBACK PREPARED");
470 						FinishPreparedTransaction(stmt->gid, false);
471 						break;
472 
473 					case TRANS_STMT_ROLLBACK:
474 						UserAbortTransactionBlock(stmt->chain);
475 						break;
476 
477 					case TRANS_STMT_SAVEPOINT:
478 						RequireTransactionBlock(isTopLevel, "SAVEPOINT");
479 						DefineSavepoint(stmt->savepoint_name);
480 						break;
481 
482 					case TRANS_STMT_RELEASE:
483 						RequireTransactionBlock(isTopLevel, "RELEASE SAVEPOINT");
484 						ReleaseSavepoint(stmt->savepoint_name);
485 						break;
486 
487 					case TRANS_STMT_ROLLBACK_TO:
488 						RequireTransactionBlock(isTopLevel, "ROLLBACK TO SAVEPOINT");
489 						RollbackToSavepoint(stmt->savepoint_name);
490 
491 						/*
492 						 * CommitTransactionCommand is in charge of
493 						 * re-defining the savepoint again
494 						 */
495 						break;
496 				}
497 			}
498 			break;
499 
500 			/*
501 			 * Portal (cursor) manipulation
502 			 */
503 		case T_DeclareCursorStmt:
504 			PerformCursorOpen((DeclareCursorStmt *) parsetree, params,
505 							  queryString, isTopLevel);
506 			break;
507 
508 		case T_ClosePortalStmt:
509 			{
510 				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
511 
512 				CheckRestrictedOperation("CLOSE");
513 				PerformPortalClose(stmt->portalname);
514 			}
515 			break;
516 
517 		case T_FetchStmt:
518 			PerformPortalFetch((FetchStmt *) parsetree, dest,
519 							   completionTag);
520 			break;
521 
522 		case T_DoStmt:
523 			ExecuteDoStmt((DoStmt *) parsetree, isAtomicContext);
524 			break;
525 
526 		case T_CreateTableSpaceStmt:
527 			/* no event triggers for global objects */
528 			PreventInTransactionBlock(isTopLevel, "CREATE TABLESPACE");
529 			CreateTableSpace((CreateTableSpaceStmt *) parsetree);
530 			break;
531 
532 		case T_DropTableSpaceStmt:
533 			/* no event triggers for global objects */
534 			PreventInTransactionBlock(isTopLevel, "DROP TABLESPACE");
535 			DropTableSpace((DropTableSpaceStmt *) parsetree);
536 			break;
537 
538 		case T_AlterTableSpaceOptionsStmt:
539 			/* no event triggers for global objects */
540 			AlterTableSpaceOptions((AlterTableSpaceOptionsStmt *) parsetree);
541 			break;
542 
543 		case T_TruncateStmt:
544 			ExecuteTruncate((TruncateStmt *) parsetree);
545 			break;
546 
547 		case T_CopyStmt:
548 			{
549 				uint64		processed;
550 
551 				DoCopy(pstate, (CopyStmt *) parsetree,
552 					   pstmt->stmt_location, pstmt->stmt_len,
553 					   &processed);
554 				if (completionTag)
555 					snprintf(completionTag, COMPLETION_TAG_BUFSIZE,
556 							 "COPY " UINT64_FORMAT, processed);
557 			}
558 			break;
559 
560 		case T_PrepareStmt:
561 			CheckRestrictedOperation("PREPARE");
562 			PrepareQuery((PrepareStmt *) parsetree, queryString,
563 						 pstmt->stmt_location, pstmt->stmt_len);
564 			break;
565 
566 		case T_ExecuteStmt:
567 			ExecuteQuery((ExecuteStmt *) parsetree, NULL,
568 						 queryString, params,
569 						 dest, completionTag);
570 			break;
571 
572 		case T_DeallocateStmt:
573 			CheckRestrictedOperation("DEALLOCATE");
574 			DeallocateQuery((DeallocateStmt *) parsetree);
575 			break;
576 
577 		case T_GrantRoleStmt:
578 			/* no event triggers for global objects */
579 			GrantRole((GrantRoleStmt *) parsetree);
580 			break;
581 
582 		case T_CreatedbStmt:
583 			/* no event triggers for global objects */
584 			PreventInTransactionBlock(isTopLevel, "CREATE DATABASE");
585 			createdb(pstate, (CreatedbStmt *) parsetree);
586 			break;
587 
588 		case T_AlterDatabaseStmt:
589 			/* no event triggers for global objects */
590 			AlterDatabase(pstate, (AlterDatabaseStmt *) parsetree, isTopLevel);
591 			break;
592 
593 		case T_AlterDatabaseSetStmt:
594 			/* no event triggers for global objects */
595 			AlterDatabaseSet((AlterDatabaseSetStmt *) parsetree);
596 			break;
597 
598 		case T_DropdbStmt:
599 			{
600 				DropdbStmt *stmt = (DropdbStmt *) parsetree;
601 
602 				/* no event triggers for global objects */
603 				PreventInTransactionBlock(isTopLevel, "DROP DATABASE");
604 				dropdb(stmt->dbname, stmt->missing_ok);
605 			}
606 			break;
607 
608 			/* Query-level asynchronous notification */
609 		case T_NotifyStmt:
610 			{
611 				NotifyStmt *stmt = (NotifyStmt *) parsetree;
612 
613 				PreventCommandDuringRecovery("NOTIFY");
614 				Async_Notify(stmt->conditionname, stmt->payload);
615 			}
616 			break;
617 
618 		case T_ListenStmt:
619 			{
620 				ListenStmt *stmt = (ListenStmt *) parsetree;
621 
622 				PreventCommandDuringRecovery("LISTEN");
623 				CheckRestrictedOperation("LISTEN");
624 				Async_Listen(stmt->conditionname);
625 			}
626 			break;
627 
628 		case T_UnlistenStmt:
629 			{
630 				UnlistenStmt *stmt = (UnlistenStmt *) parsetree;
631 
632 				/* we allow UNLISTEN during recovery, as it's a noop */
633 				CheckRestrictedOperation("UNLISTEN");
634 				if (stmt->conditionname)
635 					Async_Unlisten(stmt->conditionname);
636 				else
637 					Async_UnlistenAll();
638 			}
639 			break;
640 
641 		case T_LoadStmt:
642 			{
643 				LoadStmt   *stmt = (LoadStmt *) parsetree;
644 
645 				closeAllVfds(); /* probably not necessary... */
646 				/* Allowed names are restricted if you're not superuser */
647 				load_file(stmt->filename, !superuser());
648 			}
649 			break;
650 
651 		case T_CallStmt:
652 			ExecuteCallStmt(castNode(CallStmt, parsetree), params, isAtomicContext, dest);
653 			break;
654 
655 		case T_ClusterStmt:
656 			/* we choose to allow this during "read only" transactions */
657 			PreventCommandDuringRecovery("CLUSTER");
658 			/* forbidden in parallel mode due to CommandIsReadOnly */
659 			cluster((ClusterStmt *) parsetree, isTopLevel);
660 			break;
661 
662 		case T_VacuumStmt:
663 			{
664 				VacuumStmt *stmt = (VacuumStmt *) parsetree;
665 
666 				/* we choose to allow this during "read only" transactions */
667 				PreventCommandDuringRecovery(stmt->is_vacuumcmd ?
668 											 "VACUUM" : "ANALYZE");
669 				/* forbidden in parallel mode due to CommandIsReadOnly */
670 				ExecVacuum(pstate, stmt, isTopLevel);
671 			}
672 			break;
673 
674 		case T_ExplainStmt:
675 			ExplainQuery(pstate, (ExplainStmt *) parsetree, queryString, params,
676 						 queryEnv, dest);
677 			break;
678 
679 		case T_AlterSystemStmt:
680 			PreventInTransactionBlock(isTopLevel, "ALTER SYSTEM");
681 			AlterSystemSetConfigFile((AlterSystemStmt *) parsetree);
682 			break;
683 
684 		case T_VariableSetStmt:
685 			ExecSetVariableStmt((VariableSetStmt *) parsetree, isTopLevel);
686 			break;
687 
688 		case T_VariableShowStmt:
689 			{
690 				VariableShowStmt *n = (VariableShowStmt *) parsetree;
691 
692 				GetPGVariable(n->name, dest);
693 			}
694 			break;
695 
696 		case T_DiscardStmt:
697 			/* should we allow DISCARD PLANS? */
698 			CheckRestrictedOperation("DISCARD");
699 			DiscardCommand((DiscardStmt *) parsetree, isTopLevel);
700 			break;
701 
702 		case T_CreateEventTrigStmt:
703 			/* no event triggers on event triggers */
704 			CreateEventTrigger((CreateEventTrigStmt *) parsetree);
705 			break;
706 
707 		case T_AlterEventTrigStmt:
708 			/* no event triggers on event triggers */
709 			AlterEventTrigger((AlterEventTrigStmt *) parsetree);
710 			break;
711 
712 			/*
713 			 * ******************************** ROLE statements ****
714 			 */
715 		case T_CreateRoleStmt:
716 			/* no event triggers for global objects */
717 			CreateRole(pstate, (CreateRoleStmt *) parsetree);
718 			break;
719 
720 		case T_AlterRoleStmt:
721 			/* no event triggers for global objects */
722 			AlterRole((AlterRoleStmt *) parsetree);
723 			break;
724 
725 		case T_AlterRoleSetStmt:
726 			/* no event triggers for global objects */
727 			AlterRoleSet((AlterRoleSetStmt *) parsetree);
728 			break;
729 
730 		case T_DropRoleStmt:
731 			/* no event triggers for global objects */
732 			DropRole((DropRoleStmt *) parsetree);
733 			break;
734 
735 		case T_ReassignOwnedStmt:
736 			/* no event triggers for global objects */
737 			ReassignOwnedObjects((ReassignOwnedStmt *) parsetree);
738 			break;
739 
740 		case T_LockStmt:
741 
742 			/*
743 			 * Since the lock would just get dropped immediately, LOCK TABLE
744 			 * outside a transaction block is presumed to be user error.
745 			 */
746 			RequireTransactionBlock(isTopLevel, "LOCK TABLE");
747 			/* forbidden in parallel mode due to CommandIsReadOnly */
748 			LockTableCommand((LockStmt *) parsetree);
749 			break;
750 
751 		case T_ConstraintsSetStmt:
752 			WarnNoTransactionBlock(isTopLevel, "SET CONSTRAINTS");
753 			AfterTriggerSetState((ConstraintsSetStmt *) parsetree);
754 			break;
755 
756 		case T_CheckPointStmt:
757 			if (!superuser())
758 				ereport(ERROR,
759 						(errcode(ERRCODE_INSUFFICIENT_PRIVILEGE),
760 						 errmsg("must be superuser to do CHECKPOINT")));
761 
762 			/*
763 			 * You might think we should have a PreventCommandDuringRecovery()
764 			 * here, but we interpret a CHECKPOINT command during recovery as
765 			 * a request for a restartpoint instead. We allow this since it
766 			 * can be a useful way of reducing switchover time when using
767 			 * various forms of replication.
768 			 */
769 			RequestCheckpoint(CHECKPOINT_IMMEDIATE | CHECKPOINT_WAIT |
770 							  (RecoveryInProgress() ? 0 : CHECKPOINT_FORCE));
771 			break;
772 
773 		case T_ReindexStmt:
774 			{
775 				ReindexStmt *stmt = (ReindexStmt *) parsetree;
776 
777 				if (stmt->concurrent)
778 					PreventInTransactionBlock(isTopLevel,
779 											  "REINDEX CONCURRENTLY");
780 
781 				/* we choose to allow this during "read only" transactions */
782 				PreventCommandDuringRecovery("REINDEX");
783 				/* forbidden in parallel mode due to CommandIsReadOnly */
784 				switch (stmt->kind)
785 				{
786 					case REINDEX_OBJECT_INDEX:
787 						ReindexIndex(stmt->relation, stmt->options, stmt->concurrent);
788 						break;
789 					case REINDEX_OBJECT_TABLE:
790 						ReindexTable(stmt->relation, stmt->options, stmt->concurrent);
791 						break;
792 					case REINDEX_OBJECT_SCHEMA:
793 					case REINDEX_OBJECT_SYSTEM:
794 					case REINDEX_OBJECT_DATABASE:
795 
796 						/*
797 						 * This cannot run inside a user transaction block; if
798 						 * we were inside a transaction, then its commit- and
799 						 * start-transaction-command calls would not have the
800 						 * intended effect!
801 						 */
802 						PreventInTransactionBlock(isTopLevel,
803 												  (stmt->kind == REINDEX_OBJECT_SCHEMA) ? "REINDEX SCHEMA" :
804 												  (stmt->kind == REINDEX_OBJECT_SYSTEM) ? "REINDEX SYSTEM" :
805 												  "REINDEX DATABASE");
806 						ReindexMultipleTables(stmt->name, stmt->kind, stmt->options, stmt->concurrent);
807 						break;
808 					default:
809 						elog(ERROR, "unrecognized object type: %d",
810 							 (int) stmt->kind);
811 						break;
812 				}
813 			}
814 			break;
815 
816 			/*
817 			 * The following statements are supported by Event Triggers only
818 			 * in some cases, so we "fast path" them in the other cases.
819 			 */
820 
821 		case T_GrantStmt:
822 			{
823 				GrantStmt  *stmt = (GrantStmt *) parsetree;
824 
825 				if (EventTriggerSupportsObjectType(stmt->objtype))
826 					ProcessUtilitySlow(pstate, pstmt, queryString,
827 									   context, params, queryEnv,
828 									   dest, completionTag);
829 				else
830 					ExecuteGrantStmt(stmt);
831 			}
832 			break;
833 
834 		case T_DropStmt:
835 			{
836 				DropStmt   *stmt = (DropStmt *) parsetree;
837 
838 				if (EventTriggerSupportsObjectType(stmt->removeType))
839 					ProcessUtilitySlow(pstate, pstmt, queryString,
840 									   context, params, queryEnv,
841 									   dest, completionTag);
842 				else
843 					ExecDropStmt(stmt, isTopLevel);
844 			}
845 			break;
846 
847 		case T_RenameStmt:
848 			{
849 				RenameStmt *stmt = (RenameStmt *) parsetree;
850 
851 				if (EventTriggerSupportsObjectType(stmt->renameType))
852 					ProcessUtilitySlow(pstate, pstmt, queryString,
853 									   context, params, queryEnv,
854 									   dest, completionTag);
855 				else
856 					ExecRenameStmt(stmt);
857 			}
858 			break;
859 
860 		case T_AlterObjectDependsStmt:
861 			{
862 				AlterObjectDependsStmt *stmt = (AlterObjectDependsStmt *) parsetree;
863 
864 				if (EventTriggerSupportsObjectType(stmt->objectType))
865 					ProcessUtilitySlow(pstate, pstmt, queryString,
866 									   context, params, queryEnv,
867 									   dest, completionTag);
868 				else
869 					ExecAlterObjectDependsStmt(stmt, NULL);
870 			}
871 			break;
872 
873 		case T_AlterObjectSchemaStmt:
874 			{
875 				AlterObjectSchemaStmt *stmt = (AlterObjectSchemaStmt *) parsetree;
876 
877 				if (EventTriggerSupportsObjectType(stmt->objectType))
878 					ProcessUtilitySlow(pstate, pstmt, queryString,
879 									   context, params, queryEnv,
880 									   dest, completionTag);
881 				else
882 					ExecAlterObjectSchemaStmt(stmt, NULL);
883 			}
884 			break;
885 
886 		case T_AlterOwnerStmt:
887 			{
888 				AlterOwnerStmt *stmt = (AlterOwnerStmt *) parsetree;
889 
890 				if (EventTriggerSupportsObjectType(stmt->objectType))
891 					ProcessUtilitySlow(pstate, pstmt, queryString,
892 									   context, params, queryEnv,
893 									   dest, completionTag);
894 				else
895 					ExecAlterOwnerStmt(stmt);
896 			}
897 			break;
898 
899 		case T_CommentStmt:
900 			{
901 				CommentStmt *stmt = (CommentStmt *) parsetree;
902 
903 				if (EventTriggerSupportsObjectType(stmt->objtype))
904 					ProcessUtilitySlow(pstate, pstmt, queryString,
905 									   context, params, queryEnv,
906 									   dest, completionTag);
907 				else
908 					CommentObject(stmt);
909 				break;
910 			}
911 
912 		case T_SecLabelStmt:
913 			{
914 				SecLabelStmt *stmt = (SecLabelStmt *) parsetree;
915 
916 				if (EventTriggerSupportsObjectType(stmt->objtype))
917 					ProcessUtilitySlow(pstate, pstmt, queryString,
918 									   context, params, queryEnv,
919 									   dest, completionTag);
920 				else
921 					ExecSecLabelStmt(stmt);
922 				break;
923 			}
924 
925 		default:
926 			/* All other statement types have event trigger support */
927 			ProcessUtilitySlow(pstate, pstmt, queryString,
928 							   context, params, queryEnv,
929 							   dest, completionTag);
930 			break;
931 	}
932 
933 	free_parsestate(pstate);
934 
935 	/*
936 	 * Make effects of commands visible, for instance so that
937 	 * PreCommit_on_commit_actions() can see them (see for example bug
938 	 * #15631).
939 	 */
940 	CommandCounterIncrement();
941 }
942 
943 /*
944  * The "Slow" variant of ProcessUtility should only receive statements
945  * supported by the event triggers facility.  Therefore, we always
946  * perform the trigger support calls if the context allows it.
947  */
948 static void
ProcessUtilitySlow(ParseState * pstate,PlannedStmt * pstmt,const char * queryString,ProcessUtilityContext context,ParamListInfo params,QueryEnvironment * queryEnv,DestReceiver * dest,char * completionTag)949 ProcessUtilitySlow(ParseState *pstate,
950 				   PlannedStmt *pstmt,
951 				   const char *queryString,
952 				   ProcessUtilityContext context,
953 				   ParamListInfo params,
954 				   QueryEnvironment *queryEnv,
955 				   DestReceiver *dest,
956 				   char *completionTag)
957 {
958 	Node	   *parsetree = pstmt->utilityStmt;
959 	bool		isTopLevel = (context == PROCESS_UTILITY_TOPLEVEL);
960 	bool		isCompleteQuery = (context != PROCESS_UTILITY_SUBCOMMAND);
961 	bool		needCleanup;
962 	bool		commandCollected = false;
963 	ObjectAddress address;
964 	ObjectAddress secondaryObject = InvalidObjectAddress;
965 
966 	/* All event trigger calls are done only when isCompleteQuery is true */
967 	needCleanup = isCompleteQuery && EventTriggerBeginCompleteQuery();
968 
969 	/* PG_TRY block is to ensure we call EventTriggerEndCompleteQuery */
970 	PG_TRY();
971 	{
972 		if (isCompleteQuery)
973 			EventTriggerDDLCommandStart(parsetree);
974 
975 		switch (nodeTag(parsetree))
976 		{
977 				/*
978 				 * relation and attribute manipulation
979 				 */
980 			case T_CreateSchemaStmt:
981 				CreateSchemaCommand((CreateSchemaStmt *) parsetree,
982 									queryString,
983 									pstmt->stmt_location,
984 									pstmt->stmt_len);
985 
986 				/*
987 				 * EventTriggerCollectSimpleCommand called by
988 				 * CreateSchemaCommand
989 				 */
990 				commandCollected = true;
991 				break;
992 
993 			case T_CreateStmt:
994 			case T_CreateForeignTableStmt:
995 				{
996 					List	   *stmts;
997 					RangeVar   *table_rv = NULL;
998 
999 					/* Run parse analysis ... */
1000 					stmts = transformCreateStmt((CreateStmt *) parsetree,
1001 												queryString);
1002 
1003 					/*
1004 					 * ... and do it.  We can't use foreach() because we may
1005 					 * modify the list midway through, so pick off the
1006 					 * elements one at a time, the hard way.
1007 					 */
1008 					while (stmts != NIL)
1009 					{
1010 						Node	   *stmt = (Node *) linitial(stmts);
1011 
1012 						stmts = list_delete_first(stmts);
1013 
1014 						if (IsA(stmt, CreateStmt))
1015 						{
1016 							CreateStmt *cstmt = (CreateStmt *) stmt;
1017 							Datum		toast_options;
1018 							static char *validnsps[] = HEAP_RELOPT_NAMESPACES;
1019 
1020 							/* Remember transformed RangeVar for LIKE */
1021 							table_rv = cstmt->relation;
1022 
1023 							/* Create the table itself */
1024 							address = DefineRelation(cstmt,
1025 													 RELKIND_RELATION,
1026 													 InvalidOid, NULL,
1027 													 queryString);
1028 							EventTriggerCollectSimpleCommand(address,
1029 															 secondaryObject,
1030 															 stmt);
1031 
1032 							/*
1033 							 * Let NewRelationCreateToastTable decide if this
1034 							 * one needs a secondary relation too.
1035 							 */
1036 							CommandCounterIncrement();
1037 
1038 							/*
1039 							 * parse and validate reloptions for the toast
1040 							 * table
1041 							 */
1042 							toast_options = transformRelOptions((Datum) 0,
1043 																cstmt->options,
1044 																"toast",
1045 																validnsps,
1046 																true,
1047 																false);
1048 							(void) heap_reloptions(RELKIND_TOASTVALUE,
1049 												   toast_options,
1050 												   true);
1051 
1052 							NewRelationCreateToastTable(address.objectId,
1053 														toast_options);
1054 						}
1055 						else if (IsA(stmt, CreateForeignTableStmt))
1056 						{
1057 							CreateForeignTableStmt *cstmt = (CreateForeignTableStmt *) stmt;
1058 
1059 							/* Remember transformed RangeVar for LIKE */
1060 							table_rv = cstmt->base.relation;
1061 
1062 							/* Create the table itself */
1063 							address = DefineRelation(&cstmt->base,
1064 													 RELKIND_FOREIGN_TABLE,
1065 													 InvalidOid, NULL,
1066 													 queryString);
1067 							CreateForeignTable(cstmt,
1068 											   address.objectId);
1069 							EventTriggerCollectSimpleCommand(address,
1070 															 secondaryObject,
1071 															 stmt);
1072 						}
1073 						else if (IsA(stmt, TableLikeClause))
1074 						{
1075 							/*
1076 							 * Do delayed processing of LIKE options.  This
1077 							 * will result in additional sub-statements for us
1078 							 * to process.  Those should get done before any
1079 							 * remaining actions, so prepend them to "stmts".
1080 							 */
1081 							TableLikeClause *like = (TableLikeClause *) stmt;
1082 							List	   *morestmts;
1083 
1084 							Assert(table_rv != NULL);
1085 
1086 							morestmts = expandTableLikeClause(table_rv, like);
1087 							stmts = list_concat(morestmts, stmts);
1088 						}
1089 						else
1090 						{
1091 							/*
1092 							 * Recurse for anything else.  Note the recursive
1093 							 * call will stash the objects so created into our
1094 							 * event trigger context.
1095 							 */
1096 							PlannedStmt *wrapper;
1097 
1098 							wrapper = makeNode(PlannedStmt);
1099 							wrapper->commandType = CMD_UTILITY;
1100 							wrapper->canSetTag = false;
1101 							wrapper->utilityStmt = stmt;
1102 							wrapper->stmt_location = pstmt->stmt_location;
1103 							wrapper->stmt_len = pstmt->stmt_len;
1104 
1105 							ProcessUtility(wrapper,
1106 										   queryString,
1107 										   PROCESS_UTILITY_SUBCOMMAND,
1108 										   params,
1109 										   NULL,
1110 										   None_Receiver,
1111 										   NULL);
1112 						}
1113 
1114 						/* Need CCI between commands */
1115 						if (stmts != NIL)
1116 							CommandCounterIncrement();
1117 					}
1118 
1119 					/*
1120 					 * The multiple commands generated here are stashed
1121 					 * individually, so disable collection below.
1122 					 */
1123 					commandCollected = true;
1124 				}
1125 				break;
1126 
1127 			case T_AlterTableStmt:
1128 				{
1129 					AlterTableStmt *atstmt = (AlterTableStmt *) parsetree;
1130 					Oid			relid;
1131 					List	   *stmts;
1132 					ListCell   *l;
1133 					LOCKMODE	lockmode;
1134 
1135 					/*
1136 					 * Figure out lock mode, and acquire lock.  This also does
1137 					 * basic permissions checks, so that we won't wait for a
1138 					 * lock on (for example) a relation on which we have no
1139 					 * permissions.
1140 					 */
1141 					lockmode = AlterTableGetLockLevel(atstmt->cmds);
1142 					relid = AlterTableLookupRelation(atstmt, lockmode);
1143 
1144 					if (OidIsValid(relid))
1145 					{
1146 						/* Run parse analysis ... */
1147 						stmts = transformAlterTableStmt(relid, atstmt,
1148 														queryString);
1149 
1150 						/* ... ensure we have an event trigger context ... */
1151 						EventTriggerAlterTableStart(parsetree);
1152 						EventTriggerAlterTableRelid(relid);
1153 
1154 						/* ... and do it */
1155 						foreach(l, stmts)
1156 						{
1157 							Node	   *stmt = (Node *) lfirst(l);
1158 
1159 							if (IsA(stmt, AlterTableStmt))
1160 							{
1161 								/* Do the table alteration proper */
1162 								AlterTable(relid, lockmode,
1163 										   (AlterTableStmt *) stmt);
1164 							}
1165 							else
1166 							{
1167 								/*
1168 								 * Recurse for anything else.  If we need to
1169 								 * do so, "close" the current complex-command
1170 								 * set, and start a new one at the bottom;
1171 								 * this is needed to ensure the ordering of
1172 								 * queued commands is consistent with the way
1173 								 * they are executed here.
1174 								 */
1175 								PlannedStmt *wrapper;
1176 
1177 								EventTriggerAlterTableEnd();
1178 								wrapper = makeNode(PlannedStmt);
1179 								wrapper->commandType = CMD_UTILITY;
1180 								wrapper->canSetTag = false;
1181 								wrapper->utilityStmt = stmt;
1182 								wrapper->stmt_location = pstmt->stmt_location;
1183 								wrapper->stmt_len = pstmt->stmt_len;
1184 								ProcessUtility(wrapper,
1185 											   queryString,
1186 											   PROCESS_UTILITY_SUBCOMMAND,
1187 											   params,
1188 											   NULL,
1189 											   None_Receiver,
1190 											   NULL);
1191 								EventTriggerAlterTableStart(parsetree);
1192 								EventTriggerAlterTableRelid(relid);
1193 							}
1194 
1195 							/* Need CCI between commands */
1196 							if (lnext(l) != NULL)
1197 								CommandCounterIncrement();
1198 						}
1199 
1200 						/* done */
1201 						EventTriggerAlterTableEnd();
1202 					}
1203 					else
1204 						ereport(NOTICE,
1205 								(errmsg("relation \"%s\" does not exist, skipping",
1206 										atstmt->relation->relname)));
1207 				}
1208 
1209 				/* ALTER TABLE stashes commands internally */
1210 				commandCollected = true;
1211 				break;
1212 
1213 			case T_AlterDomainStmt:
1214 				{
1215 					AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree;
1216 
1217 					/*
1218 					 * Some or all of these functions are recursive to cover
1219 					 * inherited things, so permission checks are done there.
1220 					 */
1221 					switch (stmt->subtype)
1222 					{
1223 						case 'T':	/* ALTER DOMAIN DEFAULT */
1224 
1225 							/*
1226 							 * Recursively alter column default for table and,
1227 							 * if requested, for descendants
1228 							 */
1229 							address =
1230 								AlterDomainDefault(stmt->typeName,
1231 												   stmt->def);
1232 							break;
1233 						case 'N':	/* ALTER DOMAIN DROP NOT NULL */
1234 							address =
1235 								AlterDomainNotNull(stmt->typeName,
1236 												   false);
1237 							break;
1238 						case 'O':	/* ALTER DOMAIN SET NOT NULL */
1239 							address =
1240 								AlterDomainNotNull(stmt->typeName,
1241 												   true);
1242 							break;
1243 						case 'C':	/* ADD CONSTRAINT */
1244 							address =
1245 								AlterDomainAddConstraint(stmt->typeName,
1246 														 stmt->def,
1247 														 &secondaryObject);
1248 							break;
1249 						case 'X':	/* DROP CONSTRAINT */
1250 							address =
1251 								AlterDomainDropConstraint(stmt->typeName,
1252 														  stmt->name,
1253 														  stmt->behavior,
1254 														  stmt->missing_ok);
1255 							break;
1256 						case 'V':	/* VALIDATE CONSTRAINT */
1257 							address =
1258 								AlterDomainValidateConstraint(stmt->typeName,
1259 															  stmt->name);
1260 							break;
1261 						default:	/* oops */
1262 							elog(ERROR, "unrecognized alter domain type: %d",
1263 								 (int) stmt->subtype);
1264 							break;
1265 					}
1266 				}
1267 				break;
1268 
1269 				/*
1270 				 * ************* object creation / destruction **************
1271 				 */
1272 			case T_DefineStmt:
1273 				{
1274 					DefineStmt *stmt = (DefineStmt *) parsetree;
1275 
1276 					switch (stmt->kind)
1277 					{
1278 						case OBJECT_AGGREGATE:
1279 							address =
1280 								DefineAggregate(pstate, stmt->defnames, stmt->args,
1281 												stmt->oldstyle,
1282 												stmt->definition,
1283 												stmt->replace);
1284 							break;
1285 						case OBJECT_OPERATOR:
1286 							Assert(stmt->args == NIL);
1287 							address = DefineOperator(stmt->defnames,
1288 													 stmt->definition);
1289 							break;
1290 						case OBJECT_TYPE:
1291 							Assert(stmt->args == NIL);
1292 							address = DefineType(pstate,
1293 												 stmt->defnames,
1294 												 stmt->definition);
1295 							break;
1296 						case OBJECT_TSPARSER:
1297 							Assert(stmt->args == NIL);
1298 							address = DefineTSParser(stmt->defnames,
1299 													 stmt->definition);
1300 							break;
1301 						case OBJECT_TSDICTIONARY:
1302 							Assert(stmt->args == NIL);
1303 							address = DefineTSDictionary(stmt->defnames,
1304 														 stmt->definition);
1305 							break;
1306 						case OBJECT_TSTEMPLATE:
1307 							Assert(stmt->args == NIL);
1308 							address = DefineTSTemplate(stmt->defnames,
1309 													   stmt->definition);
1310 							break;
1311 						case OBJECT_TSCONFIGURATION:
1312 							Assert(stmt->args == NIL);
1313 							address = DefineTSConfiguration(stmt->defnames,
1314 															stmt->definition,
1315 															&secondaryObject);
1316 							break;
1317 						case OBJECT_COLLATION:
1318 							Assert(stmt->args == NIL);
1319 							address = DefineCollation(pstate,
1320 													  stmt->defnames,
1321 													  stmt->definition,
1322 													  stmt->if_not_exists);
1323 							break;
1324 						default:
1325 							elog(ERROR, "unrecognized define stmt type: %d",
1326 								 (int) stmt->kind);
1327 							break;
1328 					}
1329 				}
1330 				break;
1331 
1332 			case T_IndexStmt:	/* CREATE INDEX */
1333 				{
1334 					IndexStmt  *stmt = (IndexStmt *) parsetree;
1335 					Oid			relid;
1336 					LOCKMODE	lockmode;
1337 					bool		is_alter_table;
1338 
1339 					if (stmt->concurrent)
1340 						PreventInTransactionBlock(isTopLevel,
1341 												  "CREATE INDEX CONCURRENTLY");
1342 
1343 					/*
1344 					 * Look up the relation OID just once, right here at the
1345 					 * beginning, so that we don't end up repeating the name
1346 					 * lookup later and latching onto a different relation
1347 					 * partway through.  To avoid lock upgrade hazards, it's
1348 					 * important that we take the strongest lock that will
1349 					 * eventually be needed here, so the lockmode calculation
1350 					 * needs to match what DefineIndex() does.
1351 					 */
1352 					lockmode = stmt->concurrent ? ShareUpdateExclusiveLock
1353 						: ShareLock;
1354 					relid =
1355 						RangeVarGetRelidExtended(stmt->relation, lockmode,
1356 												 0,
1357 												 RangeVarCallbackOwnsRelation,
1358 												 NULL);
1359 
1360 					/*
1361 					 * CREATE INDEX on partitioned tables (but not regular
1362 					 * inherited tables) recurses to partitions, so we must
1363 					 * acquire locks early to avoid deadlocks.
1364 					 *
1365 					 * We also take the opportunity to verify that all
1366 					 * partitions are something we can put an index on, to
1367 					 * avoid building some indexes only to fail later.
1368 					 */
1369 					if (stmt->relation->inh &&
1370 						get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE)
1371 					{
1372 						ListCell   *lc;
1373 						List	   *inheritors = NIL;
1374 
1375 						inheritors = find_all_inheritors(relid, lockmode, NULL);
1376 						foreach(lc, inheritors)
1377 						{
1378 							char		relkind = get_rel_relkind(lfirst_oid(lc));
1379 
1380 							if (relkind != RELKIND_RELATION &&
1381 								relkind != RELKIND_MATVIEW &&
1382 								relkind != RELKIND_PARTITIONED_TABLE &&
1383 								relkind != RELKIND_FOREIGN_TABLE)
1384 								elog(ERROR, "unexpected relkind \"%c\" on partition \"%s\"",
1385 									 relkind, stmt->relation->relname);
1386 
1387 							if (relkind == RELKIND_FOREIGN_TABLE &&
1388 								(stmt->unique || stmt->primary))
1389 								ereport(ERROR,
1390 										(errcode(ERRCODE_WRONG_OBJECT_TYPE),
1391 										 errmsg("cannot create unique index on partitioned table \"%s\"",
1392 												stmt->relation->relname),
1393 										 errdetail("Table \"%s\" contains partitions that are foreign tables.",
1394 												   stmt->relation->relname)));
1395 						}
1396 						list_free(inheritors);
1397 					}
1398 
1399 					/*
1400 					 * If the IndexStmt is already transformed, it must have
1401 					 * come from generateClonedIndexStmt, which in current
1402 					 * usage means it came from expandTableLikeClause rather
1403 					 * than from original parse analysis.  And that means we
1404 					 * must treat it like ALTER TABLE ADD INDEX, not CREATE.
1405 					 * (This is a bit grotty, but currently it doesn't seem
1406 					 * worth adding a separate bool field for the purpose.)
1407 					 */
1408 					is_alter_table = stmt->transformed;
1409 
1410 					/* Run parse analysis ... */
1411 					stmt = transformIndexStmt(relid, stmt, queryString);
1412 
1413 					/* ... and do it */
1414 					EventTriggerAlterTableStart(parsetree);
1415 					address =
1416 						DefineIndex(relid,	/* OID of heap relation */
1417 									stmt,
1418 									InvalidOid, /* no predefined OID */
1419 									InvalidOid, /* no parent index */
1420 									InvalidOid, /* no parent constraint */
1421 									is_alter_table,
1422 									true,	/* check_rights */
1423 									true,	/* check_not_in_use */
1424 									false,	/* skip_build */
1425 									false); /* quiet */
1426 
1427 					/*
1428 					 * Add the CREATE INDEX node itself to stash right away;
1429 					 * if there were any commands stashed in the ALTER TABLE
1430 					 * code, we need them to appear after this one.
1431 					 */
1432 					EventTriggerCollectSimpleCommand(address, secondaryObject,
1433 													 parsetree);
1434 					commandCollected = true;
1435 					EventTriggerAlterTableEnd();
1436 				}
1437 				break;
1438 
1439 			case T_CreateExtensionStmt:
1440 				address = CreateExtension(pstate, (CreateExtensionStmt *) parsetree);
1441 				break;
1442 
1443 			case T_AlterExtensionStmt:
1444 				address = ExecAlterExtensionStmt(pstate, (AlterExtensionStmt *) parsetree);
1445 				break;
1446 
1447 			case T_AlterExtensionContentsStmt:
1448 				address = ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree,
1449 														 &secondaryObject);
1450 				break;
1451 
1452 			case T_CreateFdwStmt:
1453 				address = CreateForeignDataWrapper((CreateFdwStmt *) parsetree);
1454 				break;
1455 
1456 			case T_AlterFdwStmt:
1457 				address = AlterForeignDataWrapper((AlterFdwStmt *) parsetree);
1458 				break;
1459 
1460 			case T_CreateForeignServerStmt:
1461 				address = CreateForeignServer((CreateForeignServerStmt *) parsetree);
1462 				break;
1463 
1464 			case T_AlterForeignServerStmt:
1465 				address = AlterForeignServer((AlterForeignServerStmt *) parsetree);
1466 				break;
1467 
1468 			case T_CreateUserMappingStmt:
1469 				address = CreateUserMapping((CreateUserMappingStmt *) parsetree);
1470 				break;
1471 
1472 			case T_AlterUserMappingStmt:
1473 				address = AlterUserMapping((AlterUserMappingStmt *) parsetree);
1474 				break;
1475 
1476 			case T_DropUserMappingStmt:
1477 				RemoveUserMapping((DropUserMappingStmt *) parsetree);
1478 				/* no commands stashed for DROP */
1479 				commandCollected = true;
1480 				break;
1481 
1482 			case T_ImportForeignSchemaStmt:
1483 				ImportForeignSchema((ImportForeignSchemaStmt *) parsetree);
1484 				/* commands are stashed inside ImportForeignSchema */
1485 				commandCollected = true;
1486 				break;
1487 
1488 			case T_CompositeTypeStmt:	/* CREATE TYPE (composite) */
1489 				{
1490 					CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree;
1491 
1492 					address = DefineCompositeType(stmt->typevar,
1493 												  stmt->coldeflist);
1494 				}
1495 				break;
1496 
1497 			case T_CreateEnumStmt:	/* CREATE TYPE AS ENUM */
1498 				address = DefineEnum((CreateEnumStmt *) parsetree);
1499 				break;
1500 
1501 			case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */
1502 				address = DefineRange((CreateRangeStmt *) parsetree);
1503 				break;
1504 
1505 			case T_AlterEnumStmt:	/* ALTER TYPE (enum) */
1506 				address = AlterEnum((AlterEnumStmt *) parsetree);
1507 				break;
1508 
1509 			case T_ViewStmt:	/* CREATE VIEW */
1510 				EventTriggerAlterTableStart(parsetree);
1511 				address = DefineView((ViewStmt *) parsetree, queryString,
1512 									 pstmt->stmt_location, pstmt->stmt_len);
1513 				EventTriggerCollectSimpleCommand(address, secondaryObject,
1514 												 parsetree);
1515 				/* stashed internally */
1516 				commandCollected = true;
1517 				EventTriggerAlterTableEnd();
1518 				break;
1519 
1520 			case T_CreateFunctionStmt:	/* CREATE FUNCTION */
1521 				address = CreateFunction(pstate, (CreateFunctionStmt *) parsetree);
1522 				break;
1523 
1524 			case T_AlterFunctionStmt:	/* ALTER FUNCTION */
1525 				address = AlterFunction(pstate, (AlterFunctionStmt *) parsetree);
1526 				break;
1527 
1528 			case T_RuleStmt:	/* CREATE RULE */
1529 				address = DefineRule((RuleStmt *) parsetree, queryString);
1530 				break;
1531 
1532 			case T_CreateSeqStmt:
1533 				address = DefineSequence(pstate, (CreateSeqStmt *) parsetree);
1534 				break;
1535 
1536 			case T_AlterSeqStmt:
1537 				address = AlterSequence(pstate, (AlterSeqStmt *) parsetree);
1538 				break;
1539 
1540 			case T_CreateTableAsStmt:
1541 				address = ExecCreateTableAs((CreateTableAsStmt *) parsetree,
1542 											queryString, params, queryEnv,
1543 											completionTag);
1544 				break;
1545 
1546 			case T_RefreshMatViewStmt:
1547 
1548 				/*
1549 				 * REFRESH CONCURRENTLY executes some DDL commands internally.
1550 				 * Inhibit DDL command collection here to avoid those commands
1551 				 * from showing up in the deparsed command queue.  The refresh
1552 				 * command itself is queued, which is enough.
1553 				 */
1554 				EventTriggerInhibitCommandCollection();
1555 				PG_TRY();
1556 				{
1557 					address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree,
1558 												 queryString, params, completionTag);
1559 				}
1560 				PG_CATCH();
1561 				{
1562 					EventTriggerUndoInhibitCommandCollection();
1563 					PG_RE_THROW();
1564 				}
1565 				PG_END_TRY();
1566 				EventTriggerUndoInhibitCommandCollection();
1567 				break;
1568 
1569 			case T_CreateTrigStmt:
1570 				address = CreateTrigger((CreateTrigStmt *) parsetree,
1571 										queryString, InvalidOid, InvalidOid,
1572 										InvalidOid, InvalidOid, InvalidOid,
1573 										InvalidOid, NULL, false, false);
1574 				break;
1575 
1576 			case T_CreatePLangStmt:
1577 				address = CreateProceduralLanguage((CreatePLangStmt *) parsetree);
1578 				break;
1579 
1580 			case T_CreateDomainStmt:
1581 				address = DefineDomain((CreateDomainStmt *) parsetree);
1582 				break;
1583 
1584 			case T_CreateConversionStmt:
1585 				address = CreateConversionCommand((CreateConversionStmt *) parsetree);
1586 				break;
1587 
1588 			case T_CreateCastStmt:
1589 				address = CreateCast((CreateCastStmt *) parsetree);
1590 				break;
1591 
1592 			case T_CreateOpClassStmt:
1593 				DefineOpClass((CreateOpClassStmt *) parsetree);
1594 				/* command is stashed in DefineOpClass */
1595 				commandCollected = true;
1596 				break;
1597 
1598 			case T_CreateOpFamilyStmt:
1599 				address = DefineOpFamily((CreateOpFamilyStmt *) parsetree);
1600 				break;
1601 
1602 			case T_CreateTransformStmt:
1603 				address = CreateTransform((CreateTransformStmt *) parsetree);
1604 				break;
1605 
1606 			case T_AlterOpFamilyStmt:
1607 				AlterOpFamily((AlterOpFamilyStmt *) parsetree);
1608 				/* commands are stashed in AlterOpFamily */
1609 				commandCollected = true;
1610 				break;
1611 
1612 			case T_AlterTSDictionaryStmt:
1613 				address = AlterTSDictionary((AlterTSDictionaryStmt *) parsetree);
1614 				break;
1615 
1616 			case T_AlterTSConfigurationStmt:
1617 				AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree);
1618 
1619 				/*
1620 				 * Commands are stashed in MakeConfigurationMapping and
1621 				 * DropConfigurationMapping, which are called from
1622 				 * AlterTSConfiguration
1623 				 */
1624 				commandCollected = true;
1625 				break;
1626 
1627 			case T_AlterTableMoveAllStmt:
1628 				AlterTableMoveAll((AlterTableMoveAllStmt *) parsetree);
1629 				/* commands are stashed in AlterTableMoveAll */
1630 				commandCollected = true;
1631 				break;
1632 
1633 			case T_DropStmt:
1634 				ExecDropStmt((DropStmt *) parsetree, isTopLevel);
1635 				/* no commands stashed for DROP */
1636 				commandCollected = true;
1637 				break;
1638 
1639 			case T_RenameStmt:
1640 				address = ExecRenameStmt((RenameStmt *) parsetree);
1641 				break;
1642 
1643 			case T_AlterObjectDependsStmt:
1644 				address =
1645 					ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree,
1646 											   &secondaryObject);
1647 				break;
1648 
1649 			case T_AlterObjectSchemaStmt:
1650 				address =
1651 					ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree,
1652 											  &secondaryObject);
1653 				break;
1654 
1655 			case T_AlterOwnerStmt:
1656 				address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree);
1657 				break;
1658 
1659 			case T_AlterOperatorStmt:
1660 				address = AlterOperator((AlterOperatorStmt *) parsetree);
1661 				break;
1662 
1663 			case T_CommentStmt:
1664 				address = CommentObject((CommentStmt *) parsetree);
1665 				break;
1666 
1667 			case T_GrantStmt:
1668 				ExecuteGrantStmt((GrantStmt *) parsetree);
1669 				/* commands are stashed in ExecGrantStmt_oids */
1670 				commandCollected = true;
1671 				break;
1672 
1673 			case T_DropOwnedStmt:
1674 				DropOwnedObjects((DropOwnedStmt *) parsetree);
1675 				/* no commands stashed for DROP */
1676 				commandCollected = true;
1677 				break;
1678 
1679 			case T_AlterDefaultPrivilegesStmt:
1680 				ExecAlterDefaultPrivilegesStmt(pstate, (AlterDefaultPrivilegesStmt *) parsetree);
1681 				EventTriggerCollectAlterDefPrivs((AlterDefaultPrivilegesStmt *) parsetree);
1682 				commandCollected = true;
1683 				break;
1684 
1685 			case T_CreatePolicyStmt:	/* CREATE POLICY */
1686 				address = CreatePolicy((CreatePolicyStmt *) parsetree);
1687 				break;
1688 
1689 			case T_AlterPolicyStmt: /* ALTER POLICY */
1690 				address = AlterPolicy((AlterPolicyStmt *) parsetree);
1691 				break;
1692 
1693 			case T_SecLabelStmt:
1694 				address = ExecSecLabelStmt((SecLabelStmt *) parsetree);
1695 				break;
1696 
1697 			case T_CreateAmStmt:
1698 				address = CreateAccessMethod((CreateAmStmt *) parsetree);
1699 				break;
1700 
1701 			case T_CreatePublicationStmt:
1702 				address = CreatePublication((CreatePublicationStmt *) parsetree);
1703 				break;
1704 
1705 			case T_AlterPublicationStmt:
1706 				AlterPublication((AlterPublicationStmt *) parsetree);
1707 
1708 				/*
1709 				 * AlterPublication calls EventTriggerCollectSimpleCommand
1710 				 * directly
1711 				 */
1712 				commandCollected = true;
1713 				break;
1714 
1715 			case T_CreateSubscriptionStmt:
1716 				address = CreateSubscription((CreateSubscriptionStmt *) parsetree,
1717 											 isTopLevel);
1718 				break;
1719 
1720 			case T_AlterSubscriptionStmt:
1721 				address = AlterSubscription((AlterSubscriptionStmt *) parsetree);
1722 				break;
1723 
1724 			case T_DropSubscriptionStmt:
1725 				DropSubscription((DropSubscriptionStmt *) parsetree, isTopLevel);
1726 				/* no commands stashed for DROP */
1727 				commandCollected = true;
1728 				break;
1729 
1730 			case T_CreateStatsStmt:
1731 				address = CreateStatistics((CreateStatsStmt *) parsetree);
1732 				break;
1733 
1734 			case T_AlterCollationStmt:
1735 				address = AlterCollation((AlterCollationStmt *) parsetree);
1736 				break;
1737 
1738 			default:
1739 				elog(ERROR, "unrecognized node type: %d",
1740 					 (int) nodeTag(parsetree));
1741 				break;
1742 		}
1743 
1744 		/*
1745 		 * Remember the object so that ddl_command_end event triggers have
1746 		 * access to it.
1747 		 */
1748 		if (!commandCollected)
1749 			EventTriggerCollectSimpleCommand(address, secondaryObject,
1750 											 parsetree);
1751 
1752 		if (isCompleteQuery)
1753 		{
1754 			EventTriggerSQLDrop(parsetree);
1755 			EventTriggerDDLCommandEnd(parsetree);
1756 		}
1757 	}
1758 	PG_CATCH();
1759 	{
1760 		if (needCleanup)
1761 			EventTriggerEndCompleteQuery();
1762 		PG_RE_THROW();
1763 	}
1764 	PG_END_TRY();
1765 
1766 	if (needCleanup)
1767 		EventTriggerEndCompleteQuery();
1768 }
1769 
1770 /*
1771  * Dispatch function for DropStmt
1772  */
1773 static void
ExecDropStmt(DropStmt * stmt,bool isTopLevel)1774 ExecDropStmt(DropStmt *stmt, bool isTopLevel)
1775 {
1776 	switch (stmt->removeType)
1777 	{
1778 		case OBJECT_INDEX:
1779 			if (stmt->concurrent)
1780 				PreventInTransactionBlock(isTopLevel,
1781 										  "DROP INDEX CONCURRENTLY");
1782 			/* fall through */
1783 
1784 		case OBJECT_TABLE:
1785 		case OBJECT_SEQUENCE:
1786 		case OBJECT_VIEW:
1787 		case OBJECT_MATVIEW:
1788 		case OBJECT_FOREIGN_TABLE:
1789 			RemoveRelations(stmt);
1790 			break;
1791 		default:
1792 			RemoveObjects(stmt);
1793 			break;
1794 	}
1795 }
1796 
1797 
1798 /*
1799  * UtilityReturnsTuples
1800  *		Return "true" if this utility statement will send output to the
1801  *		destination.
1802  *
1803  * Generally, there should be a case here for each case in ProcessUtility
1804  * where "dest" is passed on.
1805  */
1806 bool
UtilityReturnsTuples(Node * parsetree)1807 UtilityReturnsTuples(Node *parsetree)
1808 {
1809 	switch (nodeTag(parsetree))
1810 	{
1811 		case T_CallStmt:
1812 			{
1813 				CallStmt   *stmt = (CallStmt *) parsetree;
1814 
1815 				return (stmt->funcexpr->funcresulttype == RECORDOID);
1816 			}
1817 		case T_FetchStmt:
1818 			{
1819 				FetchStmt  *stmt = (FetchStmt *) parsetree;
1820 				Portal		portal;
1821 
1822 				if (stmt->ismove)
1823 					return false;
1824 				portal = GetPortalByName(stmt->portalname);
1825 				if (!PortalIsValid(portal))
1826 					return false;	/* not our business to raise error */
1827 				return portal->tupDesc ? true : false;
1828 			}
1829 
1830 		case T_ExecuteStmt:
1831 			{
1832 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
1833 				PreparedStatement *entry;
1834 
1835 				entry = FetchPreparedStatement(stmt->name, false);
1836 				if (!entry)
1837 					return false;	/* not our business to raise error */
1838 				if (entry->plansource->resultDesc)
1839 					return true;
1840 				return false;
1841 			}
1842 
1843 		case T_ExplainStmt:
1844 			return true;
1845 
1846 		case T_VariableShowStmt:
1847 			return true;
1848 
1849 		default:
1850 			return false;
1851 	}
1852 }
1853 
1854 /*
1855  * UtilityTupleDescriptor
1856  *		Fetch the actual output tuple descriptor for a utility statement
1857  *		for which UtilityReturnsTuples() previously returned "true".
1858  *
1859  * The returned descriptor is created in (or copied into) the current memory
1860  * context.
1861  */
1862 TupleDesc
UtilityTupleDescriptor(Node * parsetree)1863 UtilityTupleDescriptor(Node *parsetree)
1864 {
1865 	switch (nodeTag(parsetree))
1866 	{
1867 		case T_CallStmt:
1868 			return CallStmtResultDesc((CallStmt *) parsetree);
1869 
1870 		case T_FetchStmt:
1871 			{
1872 				FetchStmt  *stmt = (FetchStmt *) parsetree;
1873 				Portal		portal;
1874 
1875 				if (stmt->ismove)
1876 					return NULL;
1877 				portal = GetPortalByName(stmt->portalname);
1878 				if (!PortalIsValid(portal))
1879 					return NULL;	/* not our business to raise error */
1880 				return CreateTupleDescCopy(portal->tupDesc);
1881 			}
1882 
1883 		case T_ExecuteStmt:
1884 			{
1885 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
1886 				PreparedStatement *entry;
1887 
1888 				entry = FetchPreparedStatement(stmt->name, false);
1889 				if (!entry)
1890 					return NULL;	/* not our business to raise error */
1891 				return FetchPreparedStatementResultDesc(entry);
1892 			}
1893 
1894 		case T_ExplainStmt:
1895 			return ExplainResultDesc((ExplainStmt *) parsetree);
1896 
1897 		case T_VariableShowStmt:
1898 			{
1899 				VariableShowStmt *n = (VariableShowStmt *) parsetree;
1900 
1901 				return GetPGVariableResultDesc(n->name);
1902 			}
1903 
1904 		default:
1905 			return NULL;
1906 	}
1907 }
1908 
1909 
1910 /*
1911  * QueryReturnsTuples
1912  *		Return "true" if this Query will send output to the destination.
1913  */
1914 #ifdef NOT_USED
1915 bool
QueryReturnsTuples(Query * parsetree)1916 QueryReturnsTuples(Query *parsetree)
1917 {
1918 	switch (parsetree->commandType)
1919 	{
1920 		case CMD_SELECT:
1921 			/* returns tuples */
1922 			return true;
1923 		case CMD_INSERT:
1924 		case CMD_UPDATE:
1925 		case CMD_DELETE:
1926 			/* the forms with RETURNING return tuples */
1927 			if (parsetree->returningList)
1928 				return true;
1929 			break;
1930 		case CMD_UTILITY:
1931 			return UtilityReturnsTuples(parsetree->utilityStmt);
1932 		case CMD_UNKNOWN:
1933 		case CMD_NOTHING:
1934 			/* probably shouldn't get here */
1935 			break;
1936 	}
1937 	return false;				/* default */
1938 }
1939 #endif
1940 
1941 
1942 /*
1943  * UtilityContainsQuery
1944  *		Return the contained Query, or NULL if there is none
1945  *
1946  * Certain utility statements, such as EXPLAIN, contain a plannable Query.
1947  * This function encapsulates knowledge of exactly which ones do.
1948  * We assume it is invoked only on already-parse-analyzed statements
1949  * (else the contained parsetree isn't a Query yet).
1950  *
1951  * In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO and
1952  * CREATE MATERIALIZED VIEW), potentially Query-containing utility statements
1953  * can be nested.  This function will drill down to a non-utility Query, or
1954  * return NULL if none.
1955  */
1956 Query *
UtilityContainsQuery(Node * parsetree)1957 UtilityContainsQuery(Node *parsetree)
1958 {
1959 	Query	   *qry;
1960 
1961 	switch (nodeTag(parsetree))
1962 	{
1963 		case T_DeclareCursorStmt:
1964 			qry = castNode(Query, ((DeclareCursorStmt *) parsetree)->query);
1965 			if (qry->commandType == CMD_UTILITY)
1966 				return UtilityContainsQuery(qry->utilityStmt);
1967 			return qry;
1968 
1969 		case T_ExplainStmt:
1970 			qry = castNode(Query, ((ExplainStmt *) parsetree)->query);
1971 			if (qry->commandType == CMD_UTILITY)
1972 				return UtilityContainsQuery(qry->utilityStmt);
1973 			return qry;
1974 
1975 		case T_CreateTableAsStmt:
1976 			qry = castNode(Query, ((CreateTableAsStmt *) parsetree)->query);
1977 			if (qry->commandType == CMD_UTILITY)
1978 				return UtilityContainsQuery(qry->utilityStmt);
1979 			return qry;
1980 
1981 		default:
1982 			return NULL;
1983 	}
1984 }
1985 
1986 
1987 /*
1988  * AlterObjectTypeCommandTag
1989  *		helper function for CreateCommandTag
1990  *
1991  * This covers most cases where ALTER is used with an ObjectType enum.
1992  */
1993 static const char *
AlterObjectTypeCommandTag(ObjectType objtype)1994 AlterObjectTypeCommandTag(ObjectType objtype)
1995 {
1996 	const char *tag;
1997 
1998 	switch (objtype)
1999 	{
2000 		case OBJECT_AGGREGATE:
2001 			tag = "ALTER AGGREGATE";
2002 			break;
2003 		case OBJECT_ATTRIBUTE:
2004 			tag = "ALTER TYPE";
2005 			break;
2006 		case OBJECT_CAST:
2007 			tag = "ALTER CAST";
2008 			break;
2009 		case OBJECT_COLLATION:
2010 			tag = "ALTER COLLATION";
2011 			break;
2012 		case OBJECT_COLUMN:
2013 			tag = "ALTER TABLE";
2014 			break;
2015 		case OBJECT_CONVERSION:
2016 			tag = "ALTER CONVERSION";
2017 			break;
2018 		case OBJECT_DATABASE:
2019 			tag = "ALTER DATABASE";
2020 			break;
2021 		case OBJECT_DOMAIN:
2022 		case OBJECT_DOMCONSTRAINT:
2023 			tag = "ALTER DOMAIN";
2024 			break;
2025 		case OBJECT_EXTENSION:
2026 			tag = "ALTER EXTENSION";
2027 			break;
2028 		case OBJECT_FDW:
2029 			tag = "ALTER FOREIGN DATA WRAPPER";
2030 			break;
2031 		case OBJECT_FOREIGN_SERVER:
2032 			tag = "ALTER SERVER";
2033 			break;
2034 		case OBJECT_FOREIGN_TABLE:
2035 			tag = "ALTER FOREIGN TABLE";
2036 			break;
2037 		case OBJECT_FUNCTION:
2038 			tag = "ALTER FUNCTION";
2039 			break;
2040 		case OBJECT_INDEX:
2041 			tag = "ALTER INDEX";
2042 			break;
2043 		case OBJECT_LANGUAGE:
2044 			tag = "ALTER LANGUAGE";
2045 			break;
2046 		case OBJECT_LARGEOBJECT:
2047 			tag = "ALTER LARGE OBJECT";
2048 			break;
2049 		case OBJECT_OPCLASS:
2050 			tag = "ALTER OPERATOR CLASS";
2051 			break;
2052 		case OBJECT_OPERATOR:
2053 			tag = "ALTER OPERATOR";
2054 			break;
2055 		case OBJECT_OPFAMILY:
2056 			tag = "ALTER OPERATOR FAMILY";
2057 			break;
2058 		case OBJECT_POLICY:
2059 			tag = "ALTER POLICY";
2060 			break;
2061 		case OBJECT_PROCEDURE:
2062 			tag = "ALTER PROCEDURE";
2063 			break;
2064 		case OBJECT_ROLE:
2065 			tag = "ALTER ROLE";
2066 			break;
2067 		case OBJECT_ROUTINE:
2068 			tag = "ALTER ROUTINE";
2069 			break;
2070 		case OBJECT_RULE:
2071 			tag = "ALTER RULE";
2072 			break;
2073 		case OBJECT_SCHEMA:
2074 			tag = "ALTER SCHEMA";
2075 			break;
2076 		case OBJECT_SEQUENCE:
2077 			tag = "ALTER SEQUENCE";
2078 			break;
2079 		case OBJECT_TABLE:
2080 		case OBJECT_TABCONSTRAINT:
2081 			tag = "ALTER TABLE";
2082 			break;
2083 		case OBJECT_TABLESPACE:
2084 			tag = "ALTER TABLESPACE";
2085 			break;
2086 		case OBJECT_TRIGGER:
2087 			tag = "ALTER TRIGGER";
2088 			break;
2089 		case OBJECT_EVENT_TRIGGER:
2090 			tag = "ALTER EVENT TRIGGER";
2091 			break;
2092 		case OBJECT_TSCONFIGURATION:
2093 			tag = "ALTER TEXT SEARCH CONFIGURATION";
2094 			break;
2095 		case OBJECT_TSDICTIONARY:
2096 			tag = "ALTER TEXT SEARCH DICTIONARY";
2097 			break;
2098 		case OBJECT_TSPARSER:
2099 			tag = "ALTER TEXT SEARCH PARSER";
2100 			break;
2101 		case OBJECT_TSTEMPLATE:
2102 			tag = "ALTER TEXT SEARCH TEMPLATE";
2103 			break;
2104 		case OBJECT_TYPE:
2105 			tag = "ALTER TYPE";
2106 			break;
2107 		case OBJECT_VIEW:
2108 			tag = "ALTER VIEW";
2109 			break;
2110 		case OBJECT_MATVIEW:
2111 			tag = "ALTER MATERIALIZED VIEW";
2112 			break;
2113 		case OBJECT_PUBLICATION:
2114 			tag = "ALTER PUBLICATION";
2115 			break;
2116 		case OBJECT_SUBSCRIPTION:
2117 			tag = "ALTER SUBSCRIPTION";
2118 			break;
2119 		case OBJECT_STATISTIC_EXT:
2120 			tag = "ALTER STATISTICS";
2121 			break;
2122 		default:
2123 			tag = "???";
2124 			break;
2125 	}
2126 
2127 	return tag;
2128 }
2129 
2130 /*
2131  * CreateCommandTag
2132  *		utility to get a string representation of the command operation,
2133  *		given either a raw (un-analyzed) parsetree, an analyzed Query,
2134  *		or a PlannedStmt.
2135  *
2136  * This must handle all command types, but since the vast majority
2137  * of 'em are utility commands, it seems sensible to keep it here.
2138  *
2139  * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE.
2140  * Also, the result must point at a true constant (permanent storage).
2141  */
2142 const char *
CreateCommandTag(Node * parsetree)2143 CreateCommandTag(Node *parsetree)
2144 {
2145 	const char *tag;
2146 
2147 	switch (nodeTag(parsetree))
2148 	{
2149 			/* recurse if we're given a RawStmt */
2150 		case T_RawStmt:
2151 			tag = CreateCommandTag(((RawStmt *) parsetree)->stmt);
2152 			break;
2153 
2154 			/* raw plannable queries */
2155 		case T_InsertStmt:
2156 			tag = "INSERT";
2157 			break;
2158 
2159 		case T_DeleteStmt:
2160 			tag = "DELETE";
2161 			break;
2162 
2163 		case T_UpdateStmt:
2164 			tag = "UPDATE";
2165 			break;
2166 
2167 		case T_SelectStmt:
2168 			tag = "SELECT";
2169 			break;
2170 
2171 			/* utility statements --- same whether raw or cooked */
2172 		case T_TransactionStmt:
2173 			{
2174 				TransactionStmt *stmt = (TransactionStmt *) parsetree;
2175 
2176 				switch (stmt->kind)
2177 				{
2178 					case TRANS_STMT_BEGIN:
2179 						tag = "BEGIN";
2180 						break;
2181 
2182 					case TRANS_STMT_START:
2183 						tag = "START TRANSACTION";
2184 						break;
2185 
2186 					case TRANS_STMT_COMMIT:
2187 						tag = "COMMIT";
2188 						break;
2189 
2190 					case TRANS_STMT_ROLLBACK:
2191 					case TRANS_STMT_ROLLBACK_TO:
2192 						tag = "ROLLBACK";
2193 						break;
2194 
2195 					case TRANS_STMT_SAVEPOINT:
2196 						tag = "SAVEPOINT";
2197 						break;
2198 
2199 					case TRANS_STMT_RELEASE:
2200 						tag = "RELEASE";
2201 						break;
2202 
2203 					case TRANS_STMT_PREPARE:
2204 						tag = "PREPARE TRANSACTION";
2205 						break;
2206 
2207 					case TRANS_STMT_COMMIT_PREPARED:
2208 						tag = "COMMIT PREPARED";
2209 						break;
2210 
2211 					case TRANS_STMT_ROLLBACK_PREPARED:
2212 						tag = "ROLLBACK PREPARED";
2213 						break;
2214 
2215 					default:
2216 						tag = "???";
2217 						break;
2218 				}
2219 			}
2220 			break;
2221 
2222 		case T_DeclareCursorStmt:
2223 			tag = "DECLARE CURSOR";
2224 			break;
2225 
2226 		case T_ClosePortalStmt:
2227 			{
2228 				ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree;
2229 
2230 				if (stmt->portalname == NULL)
2231 					tag = "CLOSE CURSOR ALL";
2232 				else
2233 					tag = "CLOSE CURSOR";
2234 			}
2235 			break;
2236 
2237 		case T_FetchStmt:
2238 			{
2239 				FetchStmt  *stmt = (FetchStmt *) parsetree;
2240 
2241 				tag = (stmt->ismove) ? "MOVE" : "FETCH";
2242 			}
2243 			break;
2244 
2245 		case T_CreateDomainStmt:
2246 			tag = "CREATE DOMAIN";
2247 			break;
2248 
2249 		case T_CreateSchemaStmt:
2250 			tag = "CREATE SCHEMA";
2251 			break;
2252 
2253 		case T_CreateStmt:
2254 			tag = "CREATE TABLE";
2255 			break;
2256 
2257 		case T_CreateTableSpaceStmt:
2258 			tag = "CREATE TABLESPACE";
2259 			break;
2260 
2261 		case T_DropTableSpaceStmt:
2262 			tag = "DROP TABLESPACE";
2263 			break;
2264 
2265 		case T_AlterTableSpaceOptionsStmt:
2266 			tag = "ALTER TABLESPACE";
2267 			break;
2268 
2269 		case T_CreateExtensionStmt:
2270 			tag = "CREATE EXTENSION";
2271 			break;
2272 
2273 		case T_AlterExtensionStmt:
2274 			tag = "ALTER EXTENSION";
2275 			break;
2276 
2277 		case T_AlterExtensionContentsStmt:
2278 			tag = "ALTER EXTENSION";
2279 			break;
2280 
2281 		case T_CreateFdwStmt:
2282 			tag = "CREATE FOREIGN DATA WRAPPER";
2283 			break;
2284 
2285 		case T_AlterFdwStmt:
2286 			tag = "ALTER FOREIGN DATA WRAPPER";
2287 			break;
2288 
2289 		case T_CreateForeignServerStmt:
2290 			tag = "CREATE SERVER";
2291 			break;
2292 
2293 		case T_AlterForeignServerStmt:
2294 			tag = "ALTER SERVER";
2295 			break;
2296 
2297 		case T_CreateUserMappingStmt:
2298 			tag = "CREATE USER MAPPING";
2299 			break;
2300 
2301 		case T_AlterUserMappingStmt:
2302 			tag = "ALTER USER MAPPING";
2303 			break;
2304 
2305 		case T_DropUserMappingStmt:
2306 			tag = "DROP USER MAPPING";
2307 			break;
2308 
2309 		case T_CreateForeignTableStmt:
2310 			tag = "CREATE FOREIGN TABLE";
2311 			break;
2312 
2313 		case T_ImportForeignSchemaStmt:
2314 			tag = "IMPORT FOREIGN SCHEMA";
2315 			break;
2316 
2317 		case T_DropStmt:
2318 			switch (((DropStmt *) parsetree)->removeType)
2319 			{
2320 				case OBJECT_TABLE:
2321 					tag = "DROP TABLE";
2322 					break;
2323 				case OBJECT_SEQUENCE:
2324 					tag = "DROP SEQUENCE";
2325 					break;
2326 				case OBJECT_VIEW:
2327 					tag = "DROP VIEW";
2328 					break;
2329 				case OBJECT_MATVIEW:
2330 					tag = "DROP MATERIALIZED VIEW";
2331 					break;
2332 				case OBJECT_INDEX:
2333 					tag = "DROP INDEX";
2334 					break;
2335 				case OBJECT_TYPE:
2336 					tag = "DROP TYPE";
2337 					break;
2338 				case OBJECT_DOMAIN:
2339 					tag = "DROP DOMAIN";
2340 					break;
2341 				case OBJECT_COLLATION:
2342 					tag = "DROP COLLATION";
2343 					break;
2344 				case OBJECT_CONVERSION:
2345 					tag = "DROP CONVERSION";
2346 					break;
2347 				case OBJECT_SCHEMA:
2348 					tag = "DROP SCHEMA";
2349 					break;
2350 				case OBJECT_TSPARSER:
2351 					tag = "DROP TEXT SEARCH PARSER";
2352 					break;
2353 				case OBJECT_TSDICTIONARY:
2354 					tag = "DROP TEXT SEARCH DICTIONARY";
2355 					break;
2356 				case OBJECT_TSTEMPLATE:
2357 					tag = "DROP TEXT SEARCH TEMPLATE";
2358 					break;
2359 				case OBJECT_TSCONFIGURATION:
2360 					tag = "DROP TEXT SEARCH CONFIGURATION";
2361 					break;
2362 				case OBJECT_FOREIGN_TABLE:
2363 					tag = "DROP FOREIGN TABLE";
2364 					break;
2365 				case OBJECT_EXTENSION:
2366 					tag = "DROP EXTENSION";
2367 					break;
2368 				case OBJECT_FUNCTION:
2369 					tag = "DROP FUNCTION";
2370 					break;
2371 				case OBJECT_PROCEDURE:
2372 					tag = "DROP PROCEDURE";
2373 					break;
2374 				case OBJECT_ROUTINE:
2375 					tag = "DROP ROUTINE";
2376 					break;
2377 				case OBJECT_AGGREGATE:
2378 					tag = "DROP AGGREGATE";
2379 					break;
2380 				case OBJECT_OPERATOR:
2381 					tag = "DROP OPERATOR";
2382 					break;
2383 				case OBJECT_LANGUAGE:
2384 					tag = "DROP LANGUAGE";
2385 					break;
2386 				case OBJECT_CAST:
2387 					tag = "DROP CAST";
2388 					break;
2389 				case OBJECT_TRIGGER:
2390 					tag = "DROP TRIGGER";
2391 					break;
2392 				case OBJECT_EVENT_TRIGGER:
2393 					tag = "DROP EVENT TRIGGER";
2394 					break;
2395 				case OBJECT_RULE:
2396 					tag = "DROP RULE";
2397 					break;
2398 				case OBJECT_FDW:
2399 					tag = "DROP FOREIGN DATA WRAPPER";
2400 					break;
2401 				case OBJECT_FOREIGN_SERVER:
2402 					tag = "DROP SERVER";
2403 					break;
2404 				case OBJECT_OPCLASS:
2405 					tag = "DROP OPERATOR CLASS";
2406 					break;
2407 				case OBJECT_OPFAMILY:
2408 					tag = "DROP OPERATOR FAMILY";
2409 					break;
2410 				case OBJECT_POLICY:
2411 					tag = "DROP POLICY";
2412 					break;
2413 				case OBJECT_TRANSFORM:
2414 					tag = "DROP TRANSFORM";
2415 					break;
2416 				case OBJECT_ACCESS_METHOD:
2417 					tag = "DROP ACCESS METHOD";
2418 					break;
2419 				case OBJECT_PUBLICATION:
2420 					tag = "DROP PUBLICATION";
2421 					break;
2422 				case OBJECT_STATISTIC_EXT:
2423 					tag = "DROP STATISTICS";
2424 					break;
2425 				default:
2426 					tag = "???";
2427 			}
2428 			break;
2429 
2430 		case T_TruncateStmt:
2431 			tag = "TRUNCATE TABLE";
2432 			break;
2433 
2434 		case T_CommentStmt:
2435 			tag = "COMMENT";
2436 			break;
2437 
2438 		case T_SecLabelStmt:
2439 			tag = "SECURITY LABEL";
2440 			break;
2441 
2442 		case T_CopyStmt:
2443 			tag = "COPY";
2444 			break;
2445 
2446 		case T_RenameStmt:
2447 			tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType);
2448 			break;
2449 
2450 		case T_AlterObjectDependsStmt:
2451 			tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType);
2452 			break;
2453 
2454 		case T_AlterObjectSchemaStmt:
2455 			tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType);
2456 			break;
2457 
2458 		case T_AlterOwnerStmt:
2459 			tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType);
2460 			break;
2461 
2462 		case T_AlterTableMoveAllStmt:
2463 			tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype);
2464 			break;
2465 
2466 		case T_AlterTableStmt:
2467 			tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind);
2468 			break;
2469 
2470 		case T_AlterDomainStmt:
2471 			tag = "ALTER DOMAIN";
2472 			break;
2473 
2474 		case T_AlterFunctionStmt:
2475 			switch (((AlterFunctionStmt *) parsetree)->objtype)
2476 			{
2477 				case OBJECT_FUNCTION:
2478 					tag = "ALTER FUNCTION";
2479 					break;
2480 				case OBJECT_PROCEDURE:
2481 					tag = "ALTER PROCEDURE";
2482 					break;
2483 				case OBJECT_ROUTINE:
2484 					tag = "ALTER ROUTINE";
2485 					break;
2486 				default:
2487 					tag = "???";
2488 			}
2489 			break;
2490 
2491 		case T_GrantStmt:
2492 			{
2493 				GrantStmt  *stmt = (GrantStmt *) parsetree;
2494 
2495 				tag = (stmt->is_grant) ? "GRANT" : "REVOKE";
2496 			}
2497 			break;
2498 
2499 		case T_GrantRoleStmt:
2500 			{
2501 				GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree;
2502 
2503 				tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE";
2504 			}
2505 			break;
2506 
2507 		case T_AlterDefaultPrivilegesStmt:
2508 			tag = "ALTER DEFAULT PRIVILEGES";
2509 			break;
2510 
2511 		case T_DefineStmt:
2512 			switch (((DefineStmt *) parsetree)->kind)
2513 			{
2514 				case OBJECT_AGGREGATE:
2515 					tag = "CREATE AGGREGATE";
2516 					break;
2517 				case OBJECT_OPERATOR:
2518 					tag = "CREATE OPERATOR";
2519 					break;
2520 				case OBJECT_TYPE:
2521 					tag = "CREATE TYPE";
2522 					break;
2523 				case OBJECT_TSPARSER:
2524 					tag = "CREATE TEXT SEARCH PARSER";
2525 					break;
2526 				case OBJECT_TSDICTIONARY:
2527 					tag = "CREATE TEXT SEARCH DICTIONARY";
2528 					break;
2529 				case OBJECT_TSTEMPLATE:
2530 					tag = "CREATE TEXT SEARCH TEMPLATE";
2531 					break;
2532 				case OBJECT_TSCONFIGURATION:
2533 					tag = "CREATE TEXT SEARCH CONFIGURATION";
2534 					break;
2535 				case OBJECT_COLLATION:
2536 					tag = "CREATE COLLATION";
2537 					break;
2538 				case OBJECT_ACCESS_METHOD:
2539 					tag = "CREATE ACCESS METHOD";
2540 					break;
2541 				default:
2542 					tag = "???";
2543 			}
2544 			break;
2545 
2546 		case T_CompositeTypeStmt:
2547 			tag = "CREATE TYPE";
2548 			break;
2549 
2550 		case T_CreateEnumStmt:
2551 			tag = "CREATE TYPE";
2552 			break;
2553 
2554 		case T_CreateRangeStmt:
2555 			tag = "CREATE TYPE";
2556 			break;
2557 
2558 		case T_AlterEnumStmt:
2559 			tag = "ALTER TYPE";
2560 			break;
2561 
2562 		case T_ViewStmt:
2563 			tag = "CREATE VIEW";
2564 			break;
2565 
2566 		case T_CreateFunctionStmt:
2567 			if (((CreateFunctionStmt *) parsetree)->is_procedure)
2568 				tag = "CREATE PROCEDURE";
2569 			else
2570 				tag = "CREATE FUNCTION";
2571 			break;
2572 
2573 		case T_IndexStmt:
2574 			tag = "CREATE INDEX";
2575 			break;
2576 
2577 		case T_RuleStmt:
2578 			tag = "CREATE RULE";
2579 			break;
2580 
2581 		case T_CreateSeqStmt:
2582 			tag = "CREATE SEQUENCE";
2583 			break;
2584 
2585 		case T_AlterSeqStmt:
2586 			tag = "ALTER SEQUENCE";
2587 			break;
2588 
2589 		case T_DoStmt:
2590 			tag = "DO";
2591 			break;
2592 
2593 		case T_CreatedbStmt:
2594 			tag = "CREATE DATABASE";
2595 			break;
2596 
2597 		case T_AlterDatabaseStmt:
2598 			tag = "ALTER DATABASE";
2599 			break;
2600 
2601 		case T_AlterDatabaseSetStmt:
2602 			tag = "ALTER DATABASE";
2603 			break;
2604 
2605 		case T_DropdbStmt:
2606 			tag = "DROP DATABASE";
2607 			break;
2608 
2609 		case T_NotifyStmt:
2610 			tag = "NOTIFY";
2611 			break;
2612 
2613 		case T_ListenStmt:
2614 			tag = "LISTEN";
2615 			break;
2616 
2617 		case T_UnlistenStmt:
2618 			tag = "UNLISTEN";
2619 			break;
2620 
2621 		case T_LoadStmt:
2622 			tag = "LOAD";
2623 			break;
2624 
2625 		case T_CallStmt:
2626 			tag = "CALL";
2627 			break;
2628 
2629 		case T_ClusterStmt:
2630 			tag = "CLUSTER";
2631 			break;
2632 
2633 		case T_VacuumStmt:
2634 			if (((VacuumStmt *) parsetree)->is_vacuumcmd)
2635 				tag = "VACUUM";
2636 			else
2637 				tag = "ANALYZE";
2638 			break;
2639 
2640 		case T_ExplainStmt:
2641 			tag = "EXPLAIN";
2642 			break;
2643 
2644 		case T_CreateTableAsStmt:
2645 			switch (((CreateTableAsStmt *) parsetree)->relkind)
2646 			{
2647 				case OBJECT_TABLE:
2648 					if (((CreateTableAsStmt *) parsetree)->is_select_into)
2649 						tag = "SELECT INTO";
2650 					else
2651 						tag = "CREATE TABLE AS";
2652 					break;
2653 				case OBJECT_MATVIEW:
2654 					tag = "CREATE MATERIALIZED VIEW";
2655 					break;
2656 				default:
2657 					tag = "???";
2658 			}
2659 			break;
2660 
2661 		case T_RefreshMatViewStmt:
2662 			tag = "REFRESH MATERIALIZED VIEW";
2663 			break;
2664 
2665 		case T_AlterSystemStmt:
2666 			tag = "ALTER SYSTEM";
2667 			break;
2668 
2669 		case T_VariableSetStmt:
2670 			switch (((VariableSetStmt *) parsetree)->kind)
2671 			{
2672 				case VAR_SET_VALUE:
2673 				case VAR_SET_CURRENT:
2674 				case VAR_SET_DEFAULT:
2675 				case VAR_SET_MULTI:
2676 					tag = "SET";
2677 					break;
2678 				case VAR_RESET:
2679 				case VAR_RESET_ALL:
2680 					tag = "RESET";
2681 					break;
2682 				default:
2683 					tag = "???";
2684 			}
2685 			break;
2686 
2687 		case T_VariableShowStmt:
2688 			tag = "SHOW";
2689 			break;
2690 
2691 		case T_DiscardStmt:
2692 			switch (((DiscardStmt *) parsetree)->target)
2693 			{
2694 				case DISCARD_ALL:
2695 					tag = "DISCARD ALL";
2696 					break;
2697 				case DISCARD_PLANS:
2698 					tag = "DISCARD PLANS";
2699 					break;
2700 				case DISCARD_TEMP:
2701 					tag = "DISCARD TEMP";
2702 					break;
2703 				case DISCARD_SEQUENCES:
2704 					tag = "DISCARD SEQUENCES";
2705 					break;
2706 				default:
2707 					tag = "???";
2708 			}
2709 			break;
2710 
2711 		case T_CreateTransformStmt:
2712 			tag = "CREATE TRANSFORM";
2713 			break;
2714 
2715 		case T_CreateTrigStmt:
2716 			tag = "CREATE TRIGGER";
2717 			break;
2718 
2719 		case T_CreateEventTrigStmt:
2720 			tag = "CREATE EVENT TRIGGER";
2721 			break;
2722 
2723 		case T_AlterEventTrigStmt:
2724 			tag = "ALTER EVENT TRIGGER";
2725 			break;
2726 
2727 		case T_CreatePLangStmt:
2728 			tag = "CREATE LANGUAGE";
2729 			break;
2730 
2731 		case T_CreateRoleStmt:
2732 			tag = "CREATE ROLE";
2733 			break;
2734 
2735 		case T_AlterRoleStmt:
2736 			tag = "ALTER ROLE";
2737 			break;
2738 
2739 		case T_AlterRoleSetStmt:
2740 			tag = "ALTER ROLE";
2741 			break;
2742 
2743 		case T_DropRoleStmt:
2744 			tag = "DROP ROLE";
2745 			break;
2746 
2747 		case T_DropOwnedStmt:
2748 			tag = "DROP OWNED";
2749 			break;
2750 
2751 		case T_ReassignOwnedStmt:
2752 			tag = "REASSIGN OWNED";
2753 			break;
2754 
2755 		case T_LockStmt:
2756 			tag = "LOCK TABLE";
2757 			break;
2758 
2759 		case T_ConstraintsSetStmt:
2760 			tag = "SET CONSTRAINTS";
2761 			break;
2762 
2763 		case T_CheckPointStmt:
2764 			tag = "CHECKPOINT";
2765 			break;
2766 
2767 		case T_ReindexStmt:
2768 			tag = "REINDEX";
2769 			break;
2770 
2771 		case T_CreateConversionStmt:
2772 			tag = "CREATE CONVERSION";
2773 			break;
2774 
2775 		case T_CreateCastStmt:
2776 			tag = "CREATE CAST";
2777 			break;
2778 
2779 		case T_CreateOpClassStmt:
2780 			tag = "CREATE OPERATOR CLASS";
2781 			break;
2782 
2783 		case T_CreateOpFamilyStmt:
2784 			tag = "CREATE OPERATOR FAMILY";
2785 			break;
2786 
2787 		case T_AlterOpFamilyStmt:
2788 			tag = "ALTER OPERATOR FAMILY";
2789 			break;
2790 
2791 		case T_AlterOperatorStmt:
2792 			tag = "ALTER OPERATOR";
2793 			break;
2794 
2795 		case T_AlterTSDictionaryStmt:
2796 			tag = "ALTER TEXT SEARCH DICTIONARY";
2797 			break;
2798 
2799 		case T_AlterTSConfigurationStmt:
2800 			tag = "ALTER TEXT SEARCH CONFIGURATION";
2801 			break;
2802 
2803 		case T_CreatePolicyStmt:
2804 			tag = "CREATE POLICY";
2805 			break;
2806 
2807 		case T_AlterPolicyStmt:
2808 			tag = "ALTER POLICY";
2809 			break;
2810 
2811 		case T_CreateAmStmt:
2812 			tag = "CREATE ACCESS METHOD";
2813 			break;
2814 
2815 		case T_CreatePublicationStmt:
2816 			tag = "CREATE PUBLICATION";
2817 			break;
2818 
2819 		case T_AlterPublicationStmt:
2820 			tag = "ALTER PUBLICATION";
2821 			break;
2822 
2823 		case T_CreateSubscriptionStmt:
2824 			tag = "CREATE SUBSCRIPTION";
2825 			break;
2826 
2827 		case T_AlterSubscriptionStmt:
2828 			tag = "ALTER SUBSCRIPTION";
2829 			break;
2830 
2831 		case T_DropSubscriptionStmt:
2832 			tag = "DROP SUBSCRIPTION";
2833 			break;
2834 
2835 		case T_AlterCollationStmt:
2836 			tag = "ALTER COLLATION";
2837 			break;
2838 
2839 		case T_PrepareStmt:
2840 			tag = "PREPARE";
2841 			break;
2842 
2843 		case T_ExecuteStmt:
2844 			tag = "EXECUTE";
2845 			break;
2846 
2847 		case T_CreateStatsStmt:
2848 			tag = "CREATE STATISTICS";
2849 			break;
2850 
2851 		case T_DeallocateStmt:
2852 			{
2853 				DeallocateStmt *stmt = (DeallocateStmt *) parsetree;
2854 
2855 				if (stmt->name == NULL)
2856 					tag = "DEALLOCATE ALL";
2857 				else
2858 					tag = "DEALLOCATE";
2859 			}
2860 			break;
2861 
2862 			/* already-planned queries */
2863 		case T_PlannedStmt:
2864 			{
2865 				PlannedStmt *stmt = (PlannedStmt *) parsetree;
2866 
2867 				switch (stmt->commandType)
2868 				{
2869 					case CMD_SELECT:
2870 
2871 						/*
2872 						 * We take a little extra care here so that the result
2873 						 * will be useful for complaints about read-only
2874 						 * statements
2875 						 */
2876 						if (stmt->rowMarks != NIL)
2877 						{
2878 							/* not 100% but probably close enough */
2879 							switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength)
2880 							{
2881 								case LCS_FORKEYSHARE:
2882 									tag = "SELECT FOR KEY SHARE";
2883 									break;
2884 								case LCS_FORSHARE:
2885 									tag = "SELECT FOR SHARE";
2886 									break;
2887 								case LCS_FORNOKEYUPDATE:
2888 									tag = "SELECT FOR NO KEY UPDATE";
2889 									break;
2890 								case LCS_FORUPDATE:
2891 									tag = "SELECT FOR UPDATE";
2892 									break;
2893 								default:
2894 									tag = "SELECT";
2895 									break;
2896 							}
2897 						}
2898 						else
2899 							tag = "SELECT";
2900 						break;
2901 					case CMD_UPDATE:
2902 						tag = "UPDATE";
2903 						break;
2904 					case CMD_INSERT:
2905 						tag = "INSERT";
2906 						break;
2907 					case CMD_DELETE:
2908 						tag = "DELETE";
2909 						break;
2910 					case CMD_UTILITY:
2911 						tag = CreateCommandTag(stmt->utilityStmt);
2912 						break;
2913 					default:
2914 						elog(WARNING, "unrecognized commandType: %d",
2915 							 (int) stmt->commandType);
2916 						tag = "???";
2917 						break;
2918 				}
2919 			}
2920 			break;
2921 
2922 			/* parsed-and-rewritten-but-not-planned queries */
2923 		case T_Query:
2924 			{
2925 				Query	   *stmt = (Query *) parsetree;
2926 
2927 				switch (stmt->commandType)
2928 				{
2929 					case CMD_SELECT:
2930 
2931 						/*
2932 						 * We take a little extra care here so that the result
2933 						 * will be useful for complaints about read-only
2934 						 * statements
2935 						 */
2936 						if (stmt->rowMarks != NIL)
2937 						{
2938 							/* not 100% but probably close enough */
2939 							switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength)
2940 							{
2941 								case LCS_FORKEYSHARE:
2942 									tag = "SELECT FOR KEY SHARE";
2943 									break;
2944 								case LCS_FORSHARE:
2945 									tag = "SELECT FOR SHARE";
2946 									break;
2947 								case LCS_FORNOKEYUPDATE:
2948 									tag = "SELECT FOR NO KEY UPDATE";
2949 									break;
2950 								case LCS_FORUPDATE:
2951 									tag = "SELECT FOR UPDATE";
2952 									break;
2953 								default:
2954 									tag = "???";
2955 									break;
2956 							}
2957 						}
2958 						else
2959 							tag = "SELECT";
2960 						break;
2961 					case CMD_UPDATE:
2962 						tag = "UPDATE";
2963 						break;
2964 					case CMD_INSERT:
2965 						tag = "INSERT";
2966 						break;
2967 					case CMD_DELETE:
2968 						tag = "DELETE";
2969 						break;
2970 					case CMD_UTILITY:
2971 						tag = CreateCommandTag(stmt->utilityStmt);
2972 						break;
2973 					default:
2974 						elog(WARNING, "unrecognized commandType: %d",
2975 							 (int) stmt->commandType);
2976 						tag = "???";
2977 						break;
2978 				}
2979 			}
2980 			break;
2981 
2982 		default:
2983 			elog(WARNING, "unrecognized node type: %d",
2984 				 (int) nodeTag(parsetree));
2985 			tag = "???";
2986 			break;
2987 	}
2988 
2989 	return tag;
2990 }
2991 
2992 
2993 /*
2994  * GetCommandLogLevel
2995  *		utility to get the minimum log_statement level for a command,
2996  *		given either a raw (un-analyzed) parsetree, an analyzed Query,
2997  *		or a PlannedStmt.
2998  *
2999  * This must handle all command types, but since the vast majority
3000  * of 'em are utility commands, it seems sensible to keep it here.
3001  */
3002 LogStmtLevel
GetCommandLogLevel(Node * parsetree)3003 GetCommandLogLevel(Node *parsetree)
3004 {
3005 	LogStmtLevel lev;
3006 
3007 	switch (nodeTag(parsetree))
3008 	{
3009 			/* recurse if we're given a RawStmt */
3010 		case T_RawStmt:
3011 			lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt);
3012 			break;
3013 
3014 			/* raw plannable queries */
3015 		case T_InsertStmt:
3016 		case T_DeleteStmt:
3017 		case T_UpdateStmt:
3018 			lev = LOGSTMT_MOD;
3019 			break;
3020 
3021 		case T_SelectStmt:
3022 			if (((SelectStmt *) parsetree)->intoClause)
3023 				lev = LOGSTMT_DDL;	/* SELECT INTO */
3024 			else
3025 				lev = LOGSTMT_ALL;
3026 			break;
3027 
3028 			/* utility statements --- same whether raw or cooked */
3029 		case T_TransactionStmt:
3030 			lev = LOGSTMT_ALL;
3031 			break;
3032 
3033 		case T_DeclareCursorStmt:
3034 			lev = LOGSTMT_ALL;
3035 			break;
3036 
3037 		case T_ClosePortalStmt:
3038 			lev = LOGSTMT_ALL;
3039 			break;
3040 
3041 		case T_FetchStmt:
3042 			lev = LOGSTMT_ALL;
3043 			break;
3044 
3045 		case T_CreateSchemaStmt:
3046 			lev = LOGSTMT_DDL;
3047 			break;
3048 
3049 		case T_CreateStmt:
3050 		case T_CreateForeignTableStmt:
3051 			lev = LOGSTMT_DDL;
3052 			break;
3053 
3054 		case T_CreateTableSpaceStmt:
3055 		case T_DropTableSpaceStmt:
3056 		case T_AlterTableSpaceOptionsStmt:
3057 			lev = LOGSTMT_DDL;
3058 			break;
3059 
3060 		case T_CreateExtensionStmt:
3061 		case T_AlterExtensionStmt:
3062 		case T_AlterExtensionContentsStmt:
3063 			lev = LOGSTMT_DDL;
3064 			break;
3065 
3066 		case T_CreateFdwStmt:
3067 		case T_AlterFdwStmt:
3068 		case T_CreateForeignServerStmt:
3069 		case T_AlterForeignServerStmt:
3070 		case T_CreateUserMappingStmt:
3071 		case T_AlterUserMappingStmt:
3072 		case T_DropUserMappingStmt:
3073 		case T_ImportForeignSchemaStmt:
3074 			lev = LOGSTMT_DDL;
3075 			break;
3076 
3077 		case T_DropStmt:
3078 			lev = LOGSTMT_DDL;
3079 			break;
3080 
3081 		case T_TruncateStmt:
3082 			lev = LOGSTMT_MOD;
3083 			break;
3084 
3085 		case T_CommentStmt:
3086 			lev = LOGSTMT_DDL;
3087 			break;
3088 
3089 		case T_SecLabelStmt:
3090 			lev = LOGSTMT_DDL;
3091 			break;
3092 
3093 		case T_CopyStmt:
3094 			if (((CopyStmt *) parsetree)->is_from)
3095 				lev = LOGSTMT_MOD;
3096 			else
3097 				lev = LOGSTMT_ALL;
3098 			break;
3099 
3100 		case T_PrepareStmt:
3101 			{
3102 				PrepareStmt *stmt = (PrepareStmt *) parsetree;
3103 
3104 				/* Look through a PREPARE to the contained stmt */
3105 				lev = GetCommandLogLevel(stmt->query);
3106 			}
3107 			break;
3108 
3109 		case T_ExecuteStmt:
3110 			{
3111 				ExecuteStmt *stmt = (ExecuteStmt *) parsetree;
3112 				PreparedStatement *ps;
3113 
3114 				/* Look through an EXECUTE to the referenced stmt */
3115 				ps = FetchPreparedStatement(stmt->name, false);
3116 				if (ps && ps->plansource->raw_parse_tree)
3117 					lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt);
3118 				else
3119 					lev = LOGSTMT_ALL;
3120 			}
3121 			break;
3122 
3123 		case T_DeallocateStmt:
3124 			lev = LOGSTMT_ALL;
3125 			break;
3126 
3127 		case T_RenameStmt:
3128 			lev = LOGSTMT_DDL;
3129 			break;
3130 
3131 		case T_AlterObjectDependsStmt:
3132 			lev = LOGSTMT_DDL;
3133 			break;
3134 
3135 		case T_AlterObjectSchemaStmt:
3136 			lev = LOGSTMT_DDL;
3137 			break;
3138 
3139 		case T_AlterOwnerStmt:
3140 			lev = LOGSTMT_DDL;
3141 			break;
3142 
3143 		case T_AlterOperatorStmt:
3144 			lev = LOGSTMT_DDL;
3145 			break;
3146 
3147 		case T_AlterTableMoveAllStmt:
3148 		case T_AlterTableStmt:
3149 			lev = LOGSTMT_DDL;
3150 			break;
3151 
3152 		case T_AlterDomainStmt:
3153 			lev = LOGSTMT_DDL;
3154 			break;
3155 
3156 		case T_GrantStmt:
3157 			lev = LOGSTMT_DDL;
3158 			break;
3159 
3160 		case T_GrantRoleStmt:
3161 			lev = LOGSTMT_DDL;
3162 			break;
3163 
3164 		case T_AlterDefaultPrivilegesStmt:
3165 			lev = LOGSTMT_DDL;
3166 			break;
3167 
3168 		case T_DefineStmt:
3169 			lev = LOGSTMT_DDL;
3170 			break;
3171 
3172 		case T_CompositeTypeStmt:
3173 			lev = LOGSTMT_DDL;
3174 			break;
3175 
3176 		case T_CreateEnumStmt:
3177 			lev = LOGSTMT_DDL;
3178 			break;
3179 
3180 		case T_CreateRangeStmt:
3181 			lev = LOGSTMT_DDL;
3182 			break;
3183 
3184 		case T_AlterEnumStmt:
3185 			lev = LOGSTMT_DDL;
3186 			break;
3187 
3188 		case T_ViewStmt:
3189 			lev = LOGSTMT_DDL;
3190 			break;
3191 
3192 		case T_CreateFunctionStmt:
3193 			lev = LOGSTMT_DDL;
3194 			break;
3195 
3196 		case T_AlterFunctionStmt:
3197 			lev = LOGSTMT_DDL;
3198 			break;
3199 
3200 		case T_IndexStmt:
3201 			lev = LOGSTMT_DDL;
3202 			break;
3203 
3204 		case T_RuleStmt:
3205 			lev = LOGSTMT_DDL;
3206 			break;
3207 
3208 		case T_CreateSeqStmt:
3209 			lev = LOGSTMT_DDL;
3210 			break;
3211 
3212 		case T_AlterSeqStmt:
3213 			lev = LOGSTMT_DDL;
3214 			break;
3215 
3216 		case T_DoStmt:
3217 			lev = LOGSTMT_ALL;
3218 			break;
3219 
3220 		case T_CreatedbStmt:
3221 			lev = LOGSTMT_DDL;
3222 			break;
3223 
3224 		case T_AlterDatabaseStmt:
3225 			lev = LOGSTMT_DDL;
3226 			break;
3227 
3228 		case T_AlterDatabaseSetStmt:
3229 			lev = LOGSTMT_DDL;
3230 			break;
3231 
3232 		case T_DropdbStmt:
3233 			lev = LOGSTMT_DDL;
3234 			break;
3235 
3236 		case T_NotifyStmt:
3237 			lev = LOGSTMT_ALL;
3238 			break;
3239 
3240 		case T_ListenStmt:
3241 			lev = LOGSTMT_ALL;
3242 			break;
3243 
3244 		case T_UnlistenStmt:
3245 			lev = LOGSTMT_ALL;
3246 			break;
3247 
3248 		case T_LoadStmt:
3249 			lev = LOGSTMT_ALL;
3250 			break;
3251 
3252 		case T_CallStmt:
3253 			lev = LOGSTMT_ALL;
3254 			break;
3255 
3256 		case T_ClusterStmt:
3257 			lev = LOGSTMT_DDL;
3258 			break;
3259 
3260 		case T_VacuumStmt:
3261 			lev = LOGSTMT_ALL;
3262 			break;
3263 
3264 		case T_ExplainStmt:
3265 			{
3266 				ExplainStmt *stmt = (ExplainStmt *) parsetree;
3267 				bool		analyze = false;
3268 				ListCell   *lc;
3269 
3270 				/* Look through an EXPLAIN ANALYZE to the contained stmt */
3271 				foreach(lc, stmt->options)
3272 				{
3273 					DefElem    *opt = (DefElem *) lfirst(lc);
3274 
3275 					if (strcmp(opt->defname, "analyze") == 0)
3276 						analyze = defGetBoolean(opt);
3277 					/* don't "break", as explain.c will use the last value */
3278 				}
3279 				if (analyze)
3280 					return GetCommandLogLevel(stmt->query);
3281 
3282 				/* Plain EXPLAIN isn't so interesting */
3283 				lev = LOGSTMT_ALL;
3284 			}
3285 			break;
3286 
3287 		case T_CreateTableAsStmt:
3288 			lev = LOGSTMT_DDL;
3289 			break;
3290 
3291 		case T_RefreshMatViewStmt:
3292 			lev = LOGSTMT_DDL;
3293 			break;
3294 
3295 		case T_AlterSystemStmt:
3296 			lev = LOGSTMT_DDL;
3297 			break;
3298 
3299 		case T_VariableSetStmt:
3300 			lev = LOGSTMT_ALL;
3301 			break;
3302 
3303 		case T_VariableShowStmt:
3304 			lev = LOGSTMT_ALL;
3305 			break;
3306 
3307 		case T_DiscardStmt:
3308 			lev = LOGSTMT_ALL;
3309 			break;
3310 
3311 		case T_CreateTrigStmt:
3312 			lev = LOGSTMT_DDL;
3313 			break;
3314 
3315 		case T_CreateEventTrigStmt:
3316 			lev = LOGSTMT_DDL;
3317 			break;
3318 
3319 		case T_AlterEventTrigStmt:
3320 			lev = LOGSTMT_DDL;
3321 			break;
3322 
3323 		case T_CreatePLangStmt:
3324 			lev = LOGSTMT_DDL;
3325 			break;
3326 
3327 		case T_CreateDomainStmt:
3328 			lev = LOGSTMT_DDL;
3329 			break;
3330 
3331 		case T_CreateRoleStmt:
3332 			lev = LOGSTMT_DDL;
3333 			break;
3334 
3335 		case T_AlterRoleStmt:
3336 			lev = LOGSTMT_DDL;
3337 			break;
3338 
3339 		case T_AlterRoleSetStmt:
3340 			lev = LOGSTMT_DDL;
3341 			break;
3342 
3343 		case T_DropRoleStmt:
3344 			lev = LOGSTMT_DDL;
3345 			break;
3346 
3347 		case T_DropOwnedStmt:
3348 			lev = LOGSTMT_DDL;
3349 			break;
3350 
3351 		case T_ReassignOwnedStmt:
3352 			lev = LOGSTMT_DDL;
3353 			break;
3354 
3355 		case T_LockStmt:
3356 			lev = LOGSTMT_ALL;
3357 			break;
3358 
3359 		case T_ConstraintsSetStmt:
3360 			lev = LOGSTMT_ALL;
3361 			break;
3362 
3363 		case T_CheckPointStmt:
3364 			lev = LOGSTMT_ALL;
3365 			break;
3366 
3367 		case T_ReindexStmt:
3368 			lev = LOGSTMT_ALL;	/* should this be DDL? */
3369 			break;
3370 
3371 		case T_CreateConversionStmt:
3372 			lev = LOGSTMT_DDL;
3373 			break;
3374 
3375 		case T_CreateCastStmt:
3376 			lev = LOGSTMT_DDL;
3377 			break;
3378 
3379 		case T_CreateOpClassStmt:
3380 			lev = LOGSTMT_DDL;
3381 			break;
3382 
3383 		case T_CreateOpFamilyStmt:
3384 			lev = LOGSTMT_DDL;
3385 			break;
3386 
3387 		case T_CreateTransformStmt:
3388 			lev = LOGSTMT_DDL;
3389 			break;
3390 
3391 		case T_AlterOpFamilyStmt:
3392 			lev = LOGSTMT_DDL;
3393 			break;
3394 
3395 		case T_CreatePolicyStmt:
3396 			lev = LOGSTMT_DDL;
3397 			break;
3398 
3399 		case T_AlterPolicyStmt:
3400 			lev = LOGSTMT_DDL;
3401 			break;
3402 
3403 		case T_AlterTSDictionaryStmt:
3404 			lev = LOGSTMT_DDL;
3405 			break;
3406 
3407 		case T_AlterTSConfigurationStmt:
3408 			lev = LOGSTMT_DDL;
3409 			break;
3410 
3411 		case T_CreateAmStmt:
3412 			lev = LOGSTMT_DDL;
3413 			break;
3414 
3415 		case T_CreatePublicationStmt:
3416 			lev = LOGSTMT_DDL;
3417 			break;
3418 
3419 		case T_AlterPublicationStmt:
3420 			lev = LOGSTMT_DDL;
3421 			break;
3422 
3423 		case T_CreateSubscriptionStmt:
3424 			lev = LOGSTMT_DDL;
3425 			break;
3426 
3427 		case T_AlterSubscriptionStmt:
3428 			lev = LOGSTMT_DDL;
3429 			break;
3430 
3431 		case T_DropSubscriptionStmt:
3432 			lev = LOGSTMT_DDL;
3433 			break;
3434 
3435 		case T_CreateStatsStmt:
3436 			lev = LOGSTMT_DDL;
3437 			break;
3438 
3439 		case T_AlterCollationStmt:
3440 			lev = LOGSTMT_DDL;
3441 			break;
3442 
3443 			/* already-planned queries */
3444 		case T_PlannedStmt:
3445 			{
3446 				PlannedStmt *stmt = (PlannedStmt *) parsetree;
3447 
3448 				switch (stmt->commandType)
3449 				{
3450 					case CMD_SELECT:
3451 						lev = LOGSTMT_ALL;
3452 						break;
3453 
3454 					case CMD_UPDATE:
3455 					case CMD_INSERT:
3456 					case CMD_DELETE:
3457 						lev = LOGSTMT_MOD;
3458 						break;
3459 
3460 					case CMD_UTILITY:
3461 						lev = GetCommandLogLevel(stmt->utilityStmt);
3462 						break;
3463 
3464 					default:
3465 						elog(WARNING, "unrecognized commandType: %d",
3466 							 (int) stmt->commandType);
3467 						lev = LOGSTMT_ALL;
3468 						break;
3469 				}
3470 			}
3471 			break;
3472 
3473 			/* parsed-and-rewritten-but-not-planned queries */
3474 		case T_Query:
3475 			{
3476 				Query	   *stmt = (Query *) parsetree;
3477 
3478 				switch (stmt->commandType)
3479 				{
3480 					case CMD_SELECT:
3481 						lev = LOGSTMT_ALL;
3482 						break;
3483 
3484 					case CMD_UPDATE:
3485 					case CMD_INSERT:
3486 					case CMD_DELETE:
3487 						lev = LOGSTMT_MOD;
3488 						break;
3489 
3490 					case CMD_UTILITY:
3491 						lev = GetCommandLogLevel(stmt->utilityStmt);
3492 						break;
3493 
3494 					default:
3495 						elog(WARNING, "unrecognized commandType: %d",
3496 							 (int) stmt->commandType);
3497 						lev = LOGSTMT_ALL;
3498 						break;
3499 				}
3500 
3501 			}
3502 			break;
3503 
3504 		default:
3505 			elog(WARNING, "unrecognized node type: %d",
3506 				 (int) nodeTag(parsetree));
3507 			lev = LOGSTMT_ALL;
3508 			break;
3509 	}
3510 
3511 	return lev;
3512 }
3513