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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 |
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 | ListCell *l; |
998 | |
999 | /* Run parse analysis ... */ |
1000 | stmts = transformCreateStmt((CreateStmt *) parsetree, |
1001 | queryString); |
1002 | |
1003 | /* ... and do it */ |
1004 | foreach(l, stmts) |
1005 | { |
1006 | Node *stmt = (Node *) lfirst(l); |
1007 | |
1008 | if (IsA(stmt, CreateStmt)) |
1009 | { |
1010 | Datum toast_options; |
1011 | static char *validnsps[] = HEAP_RELOPT_NAMESPACES; |
1012 | |
1013 | /* Create the table itself */ |
1014 | address = DefineRelation((CreateStmt *) stmt, |
1015 | RELKIND_RELATION, |
1016 | InvalidOid, NULL, |
1017 | queryString); |
1018 | EventTriggerCollectSimpleCommand(address, |
1019 | secondaryObject, |
1020 | stmt); |
1021 | |
1022 | /* |
1023 | * Let NewRelationCreateToastTable decide if this |
1024 | * one needs a secondary relation too. |
1025 | */ |
1026 | CommandCounterIncrement(); |
1027 | |
1028 | /* |
1029 | * parse and validate reloptions for the toast |
1030 | * table |
1031 | */ |
1032 | toast_options = transformRelOptions((Datum) 0, |
1033 | ((CreateStmt *) stmt)->options, |
1034 | "toast" , |
1035 | validnsps, |
1036 | true, |
1037 | false); |
1038 | (void) heap_reloptions(RELKIND_TOASTVALUE, |
1039 | toast_options, |
1040 | true); |
1041 | |
1042 | NewRelationCreateToastTable(address.objectId, |
1043 | toast_options); |
1044 | } |
1045 | else if (IsA(stmt, CreateForeignTableStmt)) |
1046 | { |
1047 | /* Create the table itself */ |
1048 | address = DefineRelation((CreateStmt *) stmt, |
1049 | RELKIND_FOREIGN_TABLE, |
1050 | InvalidOid, NULL, |
1051 | queryString); |
1052 | CreateForeignTable((CreateForeignTableStmt *) stmt, |
1053 | address.objectId); |
1054 | EventTriggerCollectSimpleCommand(address, |
1055 | secondaryObject, |
1056 | stmt); |
1057 | } |
1058 | else |
1059 | { |
1060 | /* |
1061 | * Recurse for anything else. Note the recursive |
1062 | * call will stash the objects so created into our |
1063 | * event trigger context. |
1064 | */ |
1065 | PlannedStmt *wrapper; |
1066 | |
1067 | wrapper = makeNode(PlannedStmt); |
1068 | wrapper->commandType = CMD_UTILITY; |
1069 | wrapper->canSetTag = false; |
1070 | wrapper->utilityStmt = stmt; |
1071 | wrapper->stmt_location = pstmt->stmt_location; |
1072 | wrapper->stmt_len = pstmt->stmt_len; |
1073 | |
1074 | ProcessUtility(wrapper, |
1075 | queryString, |
1076 | PROCESS_UTILITY_SUBCOMMAND, |
1077 | params, |
1078 | NULL, |
1079 | None_Receiver, |
1080 | NULL); |
1081 | } |
1082 | |
1083 | /* Need CCI between commands */ |
1084 | if (lnext(l) != NULL) |
1085 | CommandCounterIncrement(); |
1086 | } |
1087 | |
1088 | /* |
1089 | * The multiple commands generated here are stashed |
1090 | * individually, so disable collection below. |
1091 | */ |
1092 | commandCollected = true; |
1093 | } |
1094 | break; |
1095 | |
1096 | case T_AlterTableStmt: |
1097 | { |
1098 | AlterTableStmt *atstmt = (AlterTableStmt *) parsetree; |
1099 | Oid relid; |
1100 | List *stmts; |
1101 | ListCell *l; |
1102 | LOCKMODE lockmode; |
1103 | |
1104 | /* |
1105 | * Figure out lock mode, and acquire lock. This also does |
1106 | * basic permissions checks, so that we won't wait for a |
1107 | * lock on (for example) a relation on which we have no |
1108 | * permissions. |
1109 | */ |
1110 | lockmode = AlterTableGetLockLevel(atstmt->cmds); |
1111 | relid = AlterTableLookupRelation(atstmt, lockmode); |
1112 | |
1113 | if (OidIsValid(relid)) |
1114 | { |
1115 | /* Run parse analysis ... */ |
1116 | stmts = transformAlterTableStmt(relid, atstmt, |
1117 | queryString); |
1118 | |
1119 | /* ... ensure we have an event trigger context ... */ |
1120 | EventTriggerAlterTableStart(parsetree); |
1121 | EventTriggerAlterTableRelid(relid); |
1122 | |
1123 | /* ... and do it */ |
1124 | foreach(l, stmts) |
1125 | { |
1126 | Node *stmt = (Node *) lfirst(l); |
1127 | |
1128 | if (IsA(stmt, AlterTableStmt)) |
1129 | { |
1130 | /* Do the table alteration proper */ |
1131 | AlterTable(relid, lockmode, |
1132 | (AlterTableStmt *) stmt); |
1133 | } |
1134 | else |
1135 | { |
1136 | /* |
1137 | * Recurse for anything else. If we need to |
1138 | * do so, "close" the current complex-command |
1139 | * set, and start a new one at the bottom; |
1140 | * this is needed to ensure the ordering of |
1141 | * queued commands is consistent with the way |
1142 | * they are executed here. |
1143 | */ |
1144 | PlannedStmt *wrapper; |
1145 | |
1146 | EventTriggerAlterTableEnd(); |
1147 | wrapper = makeNode(PlannedStmt); |
1148 | wrapper->commandType = CMD_UTILITY; |
1149 | wrapper->canSetTag = false; |
1150 | wrapper->utilityStmt = stmt; |
1151 | wrapper->stmt_location = pstmt->stmt_location; |
1152 | wrapper->stmt_len = pstmt->stmt_len; |
1153 | ProcessUtility(wrapper, |
1154 | queryString, |
1155 | PROCESS_UTILITY_SUBCOMMAND, |
1156 | params, |
1157 | NULL, |
1158 | None_Receiver, |
1159 | NULL); |
1160 | EventTriggerAlterTableStart(parsetree); |
1161 | EventTriggerAlterTableRelid(relid); |
1162 | } |
1163 | |
1164 | /* Need CCI between commands */ |
1165 | if (lnext(l) != NULL) |
1166 | CommandCounterIncrement(); |
1167 | } |
1168 | |
1169 | /* done */ |
1170 | EventTriggerAlterTableEnd(); |
1171 | } |
1172 | else |
1173 | ereport(NOTICE, |
1174 | (errmsg("relation \"%s\" does not exist, skipping" , |
1175 | atstmt->relation->relname))); |
1176 | } |
1177 | |
1178 | /* ALTER TABLE stashes commands internally */ |
1179 | commandCollected = true; |
1180 | break; |
1181 | |
1182 | case T_AlterDomainStmt: |
1183 | { |
1184 | AlterDomainStmt *stmt = (AlterDomainStmt *) parsetree; |
1185 | |
1186 | /* |
1187 | * Some or all of these functions are recursive to cover |
1188 | * inherited things, so permission checks are done there. |
1189 | */ |
1190 | switch (stmt->subtype) |
1191 | { |
1192 | case 'T': /* ALTER DOMAIN DEFAULT */ |
1193 | |
1194 | /* |
1195 | * Recursively alter column default for table and, |
1196 | * if requested, for descendants |
1197 | */ |
1198 | address = |
1199 | AlterDomainDefault(stmt->typeName, |
1200 | stmt->def); |
1201 | break; |
1202 | case 'N': /* ALTER DOMAIN DROP NOT NULL */ |
1203 | address = |
1204 | AlterDomainNotNull(stmt->typeName, |
1205 | false); |
1206 | break; |
1207 | case 'O': /* ALTER DOMAIN SET NOT NULL */ |
1208 | address = |
1209 | AlterDomainNotNull(stmt->typeName, |
1210 | true); |
1211 | break; |
1212 | case 'C': /* ADD CONSTRAINT */ |
1213 | address = |
1214 | AlterDomainAddConstraint(stmt->typeName, |
1215 | stmt->def, |
1216 | &secondaryObject); |
1217 | break; |
1218 | case 'X': /* DROP CONSTRAINT */ |
1219 | address = |
1220 | AlterDomainDropConstraint(stmt->typeName, |
1221 | stmt->name, |
1222 | stmt->behavior, |
1223 | stmt->missing_ok); |
1224 | break; |
1225 | case 'V': /* VALIDATE CONSTRAINT */ |
1226 | address = |
1227 | AlterDomainValidateConstraint(stmt->typeName, |
1228 | stmt->name); |
1229 | break; |
1230 | default: /* oops */ |
1231 | elog(ERROR, "unrecognized alter domain type: %d" , |
1232 | (int) stmt->subtype); |
1233 | break; |
1234 | } |
1235 | } |
1236 | break; |
1237 | |
1238 | /* |
1239 | * ************* object creation / destruction ************** |
1240 | */ |
1241 | case T_DefineStmt: |
1242 | { |
1243 | DefineStmt *stmt = (DefineStmt *) parsetree; |
1244 | |
1245 | switch (stmt->kind) |
1246 | { |
1247 | case OBJECT_AGGREGATE: |
1248 | address = |
1249 | DefineAggregate(pstate, stmt->defnames, stmt->args, |
1250 | stmt->oldstyle, |
1251 | stmt->definition, |
1252 | stmt->replace); |
1253 | break; |
1254 | case OBJECT_OPERATOR: |
1255 | Assert(stmt->args == NIL); |
1256 | address = DefineOperator(stmt->defnames, |
1257 | stmt->definition); |
1258 | break; |
1259 | case OBJECT_TYPE: |
1260 | Assert(stmt->args == NIL); |
1261 | address = DefineType(pstate, |
1262 | stmt->defnames, |
1263 | stmt->definition); |
1264 | break; |
1265 | case OBJECT_TSPARSER: |
1266 | Assert(stmt->args == NIL); |
1267 | address = DefineTSParser(stmt->defnames, |
1268 | stmt->definition); |
1269 | break; |
1270 | case OBJECT_TSDICTIONARY: |
1271 | Assert(stmt->args == NIL); |
1272 | address = DefineTSDictionary(stmt->defnames, |
1273 | stmt->definition); |
1274 | break; |
1275 | case OBJECT_TSTEMPLATE: |
1276 | Assert(stmt->args == NIL); |
1277 | address = DefineTSTemplate(stmt->defnames, |
1278 | stmt->definition); |
1279 | break; |
1280 | case OBJECT_TSCONFIGURATION: |
1281 | Assert(stmt->args == NIL); |
1282 | address = DefineTSConfiguration(stmt->defnames, |
1283 | stmt->definition, |
1284 | &secondaryObject); |
1285 | break; |
1286 | case OBJECT_COLLATION: |
1287 | Assert(stmt->args == NIL); |
1288 | address = DefineCollation(pstate, |
1289 | stmt->defnames, |
1290 | stmt->definition, |
1291 | stmt->if_not_exists); |
1292 | break; |
1293 | default: |
1294 | elog(ERROR, "unrecognized define stmt type: %d" , |
1295 | (int) stmt->kind); |
1296 | break; |
1297 | } |
1298 | } |
1299 | break; |
1300 | |
1301 | case T_IndexStmt: /* CREATE INDEX */ |
1302 | { |
1303 | IndexStmt *stmt = (IndexStmt *) parsetree; |
1304 | Oid relid; |
1305 | LOCKMODE lockmode; |
1306 | |
1307 | if (stmt->concurrent) |
1308 | PreventInTransactionBlock(isTopLevel, |
1309 | "CREATE INDEX CONCURRENTLY" ); |
1310 | |
1311 | /* |
1312 | * Look up the relation OID just once, right here at the |
1313 | * beginning, so that we don't end up repeating the name |
1314 | * lookup later and latching onto a different relation |
1315 | * partway through. To avoid lock upgrade hazards, it's |
1316 | * important that we take the strongest lock that will |
1317 | * eventually be needed here, so the lockmode calculation |
1318 | * needs to match what DefineIndex() does. |
1319 | */ |
1320 | lockmode = stmt->concurrent ? ShareUpdateExclusiveLock |
1321 | : ShareLock; |
1322 | relid = |
1323 | RangeVarGetRelidExtended(stmt->relation, lockmode, |
1324 | 0, |
1325 | RangeVarCallbackOwnsRelation, |
1326 | NULL); |
1327 | |
1328 | /* |
1329 | * CREATE INDEX on partitioned tables (but not regular |
1330 | * inherited tables) recurses to partitions, so we must |
1331 | * acquire locks early to avoid deadlocks. |
1332 | * |
1333 | * We also take the opportunity to verify that all |
1334 | * partitions are something we can put an index on, to |
1335 | * avoid building some indexes only to fail later. |
1336 | */ |
1337 | if (stmt->relation->inh && |
1338 | get_rel_relkind(relid) == RELKIND_PARTITIONED_TABLE) |
1339 | { |
1340 | ListCell *lc; |
1341 | List *inheritors = NIL; |
1342 | |
1343 | inheritors = find_all_inheritors(relid, lockmode, NULL); |
1344 | foreach(lc, inheritors) |
1345 | { |
1346 | char relkind = get_rel_relkind(lfirst_oid(lc)); |
1347 | |
1348 | if (relkind != RELKIND_RELATION && |
1349 | relkind != RELKIND_MATVIEW && |
1350 | relkind != RELKIND_PARTITIONED_TABLE && |
1351 | relkind != RELKIND_FOREIGN_TABLE) |
1352 | elog(ERROR, "unexpected relkind \"%c\" on partition \"%s\"" , |
1353 | relkind, stmt->relation->relname); |
1354 | |
1355 | if (relkind == RELKIND_FOREIGN_TABLE && |
1356 | (stmt->unique || stmt->primary)) |
1357 | ereport(ERROR, |
1358 | (errcode(ERRCODE_WRONG_OBJECT_TYPE), |
1359 | errmsg("cannot create unique index on partitioned table \"%s\"" , |
1360 | stmt->relation->relname), |
1361 | errdetail("Table \"%s\" contains partitions that are foreign tables." , |
1362 | stmt->relation->relname))); |
1363 | } |
1364 | list_free(inheritors); |
1365 | } |
1366 | |
1367 | /* Run parse analysis ... */ |
1368 | stmt = transformIndexStmt(relid, stmt, queryString); |
1369 | |
1370 | /* ... and do it */ |
1371 | EventTriggerAlterTableStart(parsetree); |
1372 | address = |
1373 | DefineIndex(relid, /* OID of heap relation */ |
1374 | stmt, |
1375 | InvalidOid, /* no predefined OID */ |
1376 | InvalidOid, /* no parent index */ |
1377 | InvalidOid, /* no parent constraint */ |
1378 | false, /* is_alter_table */ |
1379 | true, /* check_rights */ |
1380 | true, /* check_not_in_use */ |
1381 | false, /* skip_build */ |
1382 | false); /* quiet */ |
1383 | |
1384 | /* |
1385 | * Add the CREATE INDEX node itself to stash right away; |
1386 | * if there were any commands stashed in the ALTER TABLE |
1387 | * code, we need them to appear after this one. |
1388 | */ |
1389 | EventTriggerCollectSimpleCommand(address, secondaryObject, |
1390 | parsetree); |
1391 | commandCollected = true; |
1392 | EventTriggerAlterTableEnd(); |
1393 | } |
1394 | break; |
1395 | |
1396 | case T_CreateExtensionStmt: |
1397 | address = CreateExtension(pstate, (CreateExtensionStmt *) parsetree); |
1398 | break; |
1399 | |
1400 | case T_AlterExtensionStmt: |
1401 | address = ExecAlterExtensionStmt(pstate, (AlterExtensionStmt *) parsetree); |
1402 | break; |
1403 | |
1404 | case T_AlterExtensionContentsStmt: |
1405 | address = ExecAlterExtensionContentsStmt((AlterExtensionContentsStmt *) parsetree, |
1406 | &secondaryObject); |
1407 | break; |
1408 | |
1409 | case T_CreateFdwStmt: |
1410 | address = CreateForeignDataWrapper((CreateFdwStmt *) parsetree); |
1411 | break; |
1412 | |
1413 | case T_AlterFdwStmt: |
1414 | address = AlterForeignDataWrapper((AlterFdwStmt *) parsetree); |
1415 | break; |
1416 | |
1417 | case T_CreateForeignServerStmt: |
1418 | address = CreateForeignServer((CreateForeignServerStmt *) parsetree); |
1419 | break; |
1420 | |
1421 | case T_AlterForeignServerStmt: |
1422 | address = AlterForeignServer((AlterForeignServerStmt *) parsetree); |
1423 | break; |
1424 | |
1425 | case T_CreateUserMappingStmt: |
1426 | address = CreateUserMapping((CreateUserMappingStmt *) parsetree); |
1427 | break; |
1428 | |
1429 | case T_AlterUserMappingStmt: |
1430 | address = AlterUserMapping((AlterUserMappingStmt *) parsetree); |
1431 | break; |
1432 | |
1433 | case T_DropUserMappingStmt: |
1434 | RemoveUserMapping((DropUserMappingStmt *) parsetree); |
1435 | /* no commands stashed for DROP */ |
1436 | commandCollected = true; |
1437 | break; |
1438 | |
1439 | case T_ImportForeignSchemaStmt: |
1440 | ImportForeignSchema((ImportForeignSchemaStmt *) parsetree); |
1441 | /* commands are stashed inside ImportForeignSchema */ |
1442 | commandCollected = true; |
1443 | break; |
1444 | |
1445 | case T_CompositeTypeStmt: /* CREATE TYPE (composite) */ |
1446 | { |
1447 | CompositeTypeStmt *stmt = (CompositeTypeStmt *) parsetree; |
1448 | |
1449 | address = DefineCompositeType(stmt->typevar, |
1450 | stmt->coldeflist); |
1451 | } |
1452 | break; |
1453 | |
1454 | case T_CreateEnumStmt: /* CREATE TYPE AS ENUM */ |
1455 | address = DefineEnum((CreateEnumStmt *) parsetree); |
1456 | break; |
1457 | |
1458 | case T_CreateRangeStmt: /* CREATE TYPE AS RANGE */ |
1459 | address = DefineRange((CreateRangeStmt *) parsetree); |
1460 | break; |
1461 | |
1462 | case T_AlterEnumStmt: /* ALTER TYPE (enum) */ |
1463 | address = AlterEnum((AlterEnumStmt *) parsetree); |
1464 | break; |
1465 | |
1466 | case T_ViewStmt: /* CREATE VIEW */ |
1467 | EventTriggerAlterTableStart(parsetree); |
1468 | address = DefineView((ViewStmt *) parsetree, queryString, |
1469 | pstmt->stmt_location, pstmt->stmt_len); |
1470 | EventTriggerCollectSimpleCommand(address, secondaryObject, |
1471 | parsetree); |
1472 | /* stashed internally */ |
1473 | commandCollected = true; |
1474 | EventTriggerAlterTableEnd(); |
1475 | break; |
1476 | |
1477 | case T_CreateFunctionStmt: /* CREATE FUNCTION */ |
1478 | address = CreateFunction(pstate, (CreateFunctionStmt *) parsetree); |
1479 | break; |
1480 | |
1481 | case T_AlterFunctionStmt: /* ALTER FUNCTION */ |
1482 | address = AlterFunction(pstate, (AlterFunctionStmt *) parsetree); |
1483 | break; |
1484 | |
1485 | case T_RuleStmt: /* CREATE RULE */ |
1486 | address = DefineRule((RuleStmt *) parsetree, queryString); |
1487 | break; |
1488 | |
1489 | case T_CreateSeqStmt: |
1490 | address = DefineSequence(pstate, (CreateSeqStmt *) parsetree); |
1491 | break; |
1492 | |
1493 | case T_AlterSeqStmt: |
1494 | address = AlterSequence(pstate, (AlterSeqStmt *) parsetree); |
1495 | break; |
1496 | |
1497 | case T_CreateTableAsStmt: |
1498 | address = ExecCreateTableAs((CreateTableAsStmt *) parsetree, |
1499 | queryString, params, queryEnv, |
1500 | completionTag); |
1501 | break; |
1502 | |
1503 | case T_RefreshMatViewStmt: |
1504 | |
1505 | /* |
1506 | * REFRESH CONCURRENTLY executes some DDL commands internally. |
1507 | * Inhibit DDL command collection here to avoid those commands |
1508 | * from showing up in the deparsed command queue. The refresh |
1509 | * command itself is queued, which is enough. |
1510 | */ |
1511 | EventTriggerInhibitCommandCollection(); |
1512 | PG_TRY(); |
1513 | { |
1514 | address = ExecRefreshMatView((RefreshMatViewStmt *) parsetree, |
1515 | queryString, params, completionTag); |
1516 | } |
1517 | PG_CATCH(); |
1518 | { |
1519 | EventTriggerUndoInhibitCommandCollection(); |
1520 | PG_RE_THROW(); |
1521 | } |
1522 | PG_END_TRY(); |
1523 | EventTriggerUndoInhibitCommandCollection(); |
1524 | break; |
1525 | |
1526 | case T_CreateTrigStmt: |
1527 | address = CreateTrigger((CreateTrigStmt *) parsetree, |
1528 | queryString, InvalidOid, InvalidOid, |
1529 | InvalidOid, InvalidOid, InvalidOid, |
1530 | InvalidOid, NULL, false, false); |
1531 | break; |
1532 | |
1533 | case T_CreatePLangStmt: |
1534 | address = CreateProceduralLanguage((CreatePLangStmt *) parsetree); |
1535 | break; |
1536 | |
1537 | case T_CreateDomainStmt: |
1538 | address = DefineDomain((CreateDomainStmt *) parsetree); |
1539 | break; |
1540 | |
1541 | case T_CreateConversionStmt: |
1542 | address = CreateConversionCommand((CreateConversionStmt *) parsetree); |
1543 | break; |
1544 | |
1545 | case T_CreateCastStmt: |
1546 | address = CreateCast((CreateCastStmt *) parsetree); |
1547 | break; |
1548 | |
1549 | case T_CreateOpClassStmt: |
1550 | DefineOpClass((CreateOpClassStmt *) parsetree); |
1551 | /* command is stashed in DefineOpClass */ |
1552 | commandCollected = true; |
1553 | break; |
1554 | |
1555 | case T_CreateOpFamilyStmt: |
1556 | address = DefineOpFamily((CreateOpFamilyStmt *) parsetree); |
1557 | break; |
1558 | |
1559 | case T_CreateTransformStmt: |
1560 | address = CreateTransform((CreateTransformStmt *) parsetree); |
1561 | break; |
1562 | |
1563 | case T_AlterOpFamilyStmt: |
1564 | AlterOpFamily((AlterOpFamilyStmt *) parsetree); |
1565 | /* commands are stashed in AlterOpFamily */ |
1566 | commandCollected = true; |
1567 | break; |
1568 | |
1569 | case T_AlterTSDictionaryStmt: |
1570 | address = AlterTSDictionary((AlterTSDictionaryStmt *) parsetree); |
1571 | break; |
1572 | |
1573 | case T_AlterTSConfigurationStmt: |
1574 | AlterTSConfiguration((AlterTSConfigurationStmt *) parsetree); |
1575 | |
1576 | /* |
1577 | * Commands are stashed in MakeConfigurationMapping and |
1578 | * DropConfigurationMapping, which are called from |
1579 | * AlterTSConfiguration |
1580 | */ |
1581 | commandCollected = true; |
1582 | break; |
1583 | |
1584 | case T_AlterTableMoveAllStmt: |
1585 | AlterTableMoveAll((AlterTableMoveAllStmt *) parsetree); |
1586 | /* commands are stashed in AlterTableMoveAll */ |
1587 | commandCollected = true; |
1588 | break; |
1589 | |
1590 | case T_DropStmt: |
1591 | ExecDropStmt((DropStmt *) parsetree, isTopLevel); |
1592 | /* no commands stashed for DROP */ |
1593 | commandCollected = true; |
1594 | break; |
1595 | |
1596 | case T_RenameStmt: |
1597 | address = ExecRenameStmt((RenameStmt *) parsetree); |
1598 | break; |
1599 | |
1600 | case T_AlterObjectDependsStmt: |
1601 | address = |
1602 | ExecAlterObjectDependsStmt((AlterObjectDependsStmt *) parsetree, |
1603 | &secondaryObject); |
1604 | break; |
1605 | |
1606 | case T_AlterObjectSchemaStmt: |
1607 | address = |
1608 | ExecAlterObjectSchemaStmt((AlterObjectSchemaStmt *) parsetree, |
1609 | &secondaryObject); |
1610 | break; |
1611 | |
1612 | case T_AlterOwnerStmt: |
1613 | address = ExecAlterOwnerStmt((AlterOwnerStmt *) parsetree); |
1614 | break; |
1615 | |
1616 | case T_AlterOperatorStmt: |
1617 | address = AlterOperator((AlterOperatorStmt *) parsetree); |
1618 | break; |
1619 | |
1620 | case T_CommentStmt: |
1621 | address = CommentObject((CommentStmt *) parsetree); |
1622 | break; |
1623 | |
1624 | case T_GrantStmt: |
1625 | ExecuteGrantStmt((GrantStmt *) parsetree); |
1626 | /* commands are stashed in ExecGrantStmt_oids */ |
1627 | commandCollected = true; |
1628 | break; |
1629 | |
1630 | case T_DropOwnedStmt: |
1631 | DropOwnedObjects((DropOwnedStmt *) parsetree); |
1632 | /* no commands stashed for DROP */ |
1633 | commandCollected = true; |
1634 | break; |
1635 | |
1636 | case T_AlterDefaultPrivilegesStmt: |
1637 | ExecAlterDefaultPrivilegesStmt(pstate, (AlterDefaultPrivilegesStmt *) parsetree); |
1638 | EventTriggerCollectAlterDefPrivs((AlterDefaultPrivilegesStmt *) parsetree); |
1639 | commandCollected = true; |
1640 | break; |
1641 | |
1642 | case T_CreatePolicyStmt: /* CREATE POLICY */ |
1643 | address = CreatePolicy((CreatePolicyStmt *) parsetree); |
1644 | break; |
1645 | |
1646 | case T_AlterPolicyStmt: /* ALTER POLICY */ |
1647 | address = AlterPolicy((AlterPolicyStmt *) parsetree); |
1648 | break; |
1649 | |
1650 | case T_SecLabelStmt: |
1651 | address = ExecSecLabelStmt((SecLabelStmt *) parsetree); |
1652 | break; |
1653 | |
1654 | case T_CreateAmStmt: |
1655 | address = CreateAccessMethod((CreateAmStmt *) parsetree); |
1656 | break; |
1657 | |
1658 | case T_CreatePublicationStmt: |
1659 | address = CreatePublication((CreatePublicationStmt *) parsetree); |
1660 | break; |
1661 | |
1662 | case T_AlterPublicationStmt: |
1663 | AlterPublication((AlterPublicationStmt *) parsetree); |
1664 | |
1665 | /* |
1666 | * AlterPublication calls EventTriggerCollectSimpleCommand |
1667 | * directly |
1668 | */ |
1669 | commandCollected = true; |
1670 | break; |
1671 | |
1672 | case T_CreateSubscriptionStmt: |
1673 | address = CreateSubscription((CreateSubscriptionStmt *) parsetree, |
1674 | isTopLevel); |
1675 | break; |
1676 | |
1677 | case T_AlterSubscriptionStmt: |
1678 | address = AlterSubscription((AlterSubscriptionStmt *) parsetree); |
1679 | break; |
1680 | |
1681 | case T_DropSubscriptionStmt: |
1682 | DropSubscription((DropSubscriptionStmt *) parsetree, isTopLevel); |
1683 | /* no commands stashed for DROP */ |
1684 | commandCollected = true; |
1685 | break; |
1686 | |
1687 | case T_CreateStatsStmt: |
1688 | address = CreateStatistics((CreateStatsStmt *) parsetree); |
1689 | break; |
1690 | |
1691 | case T_AlterCollationStmt: |
1692 | address = AlterCollation((AlterCollationStmt *) parsetree); |
1693 | break; |
1694 | |
1695 | default: |
1696 | elog(ERROR, "unrecognized node type: %d" , |
1697 | (int) nodeTag(parsetree)); |
1698 | break; |
1699 | } |
1700 | |
1701 | /* |
1702 | * Remember the object so that ddl_command_end event triggers have |
1703 | * access to it. |
1704 | */ |
1705 | if (!commandCollected) |
1706 | EventTriggerCollectSimpleCommand(address, secondaryObject, |
1707 | parsetree); |
1708 | |
1709 | if (isCompleteQuery) |
1710 | { |
1711 | EventTriggerSQLDrop(parsetree); |
1712 | EventTriggerDDLCommandEnd(parsetree); |
1713 | } |
1714 | } |
1715 | PG_CATCH(); |
1716 | { |
1717 | if (needCleanup) |
1718 | EventTriggerEndCompleteQuery(); |
1719 | PG_RE_THROW(); |
1720 | } |
1721 | PG_END_TRY(); |
1722 | |
1723 | if (needCleanup) |
1724 | EventTriggerEndCompleteQuery(); |
1725 | } |
1726 | |
1727 | /* |
1728 | * Dispatch function for DropStmt |
1729 | */ |
1730 | static void |
1731 | ExecDropStmt(DropStmt *stmt, bool isTopLevel) |
1732 | { |
1733 | switch (stmt->removeType) |
1734 | { |
1735 | case OBJECT_INDEX: |
1736 | if (stmt->concurrent) |
1737 | PreventInTransactionBlock(isTopLevel, |
1738 | "DROP INDEX CONCURRENTLY" ); |
1739 | /* fall through */ |
1740 | |
1741 | case OBJECT_TABLE: |
1742 | case OBJECT_SEQUENCE: |
1743 | case OBJECT_VIEW: |
1744 | case OBJECT_MATVIEW: |
1745 | case OBJECT_FOREIGN_TABLE: |
1746 | RemoveRelations(stmt); |
1747 | break; |
1748 | default: |
1749 | RemoveObjects(stmt); |
1750 | break; |
1751 | } |
1752 | } |
1753 | |
1754 | |
1755 | /* |
1756 | * UtilityReturnsTuples |
1757 | * Return "true" if this utility statement will send output to the |
1758 | * destination. |
1759 | * |
1760 | * Generally, there should be a case here for each case in ProcessUtility |
1761 | * where "dest" is passed on. |
1762 | */ |
1763 | bool |
1764 | UtilityReturnsTuples(Node *parsetree) |
1765 | { |
1766 | switch (nodeTag(parsetree)) |
1767 | { |
1768 | case T_CallStmt: |
1769 | { |
1770 | CallStmt *stmt = (CallStmt *) parsetree; |
1771 | |
1772 | return (stmt->funcexpr->funcresulttype == RECORDOID); |
1773 | } |
1774 | case T_FetchStmt: |
1775 | { |
1776 | FetchStmt *stmt = (FetchStmt *) parsetree; |
1777 | Portal portal; |
1778 | |
1779 | if (stmt->ismove) |
1780 | return false; |
1781 | portal = GetPortalByName(stmt->portalname); |
1782 | if (!PortalIsValid(portal)) |
1783 | return false; /* not our business to raise error */ |
1784 | return portal->tupDesc ? true : false; |
1785 | } |
1786 | |
1787 | case T_ExecuteStmt: |
1788 | { |
1789 | ExecuteStmt *stmt = (ExecuteStmt *) parsetree; |
1790 | PreparedStatement *entry; |
1791 | |
1792 | entry = FetchPreparedStatement(stmt->name, false); |
1793 | if (!entry) |
1794 | return false; /* not our business to raise error */ |
1795 | if (entry->plansource->resultDesc) |
1796 | return true; |
1797 | return false; |
1798 | } |
1799 | |
1800 | case T_ExplainStmt: |
1801 | return true; |
1802 | |
1803 | case T_VariableShowStmt: |
1804 | return true; |
1805 | |
1806 | default: |
1807 | return false; |
1808 | } |
1809 | } |
1810 | |
1811 | /* |
1812 | * UtilityTupleDescriptor |
1813 | * Fetch the actual output tuple descriptor for a utility statement |
1814 | * for which UtilityReturnsTuples() previously returned "true". |
1815 | * |
1816 | * The returned descriptor is created in (or copied into) the current memory |
1817 | * context. |
1818 | */ |
1819 | TupleDesc |
1820 | UtilityTupleDescriptor(Node *parsetree) |
1821 | { |
1822 | switch (nodeTag(parsetree)) |
1823 | { |
1824 | case T_CallStmt: |
1825 | return CallStmtResultDesc((CallStmt *) parsetree); |
1826 | |
1827 | case T_FetchStmt: |
1828 | { |
1829 | FetchStmt *stmt = (FetchStmt *) parsetree; |
1830 | Portal portal; |
1831 | |
1832 | if (stmt->ismove) |
1833 | return NULL; |
1834 | portal = GetPortalByName(stmt->portalname); |
1835 | if (!PortalIsValid(portal)) |
1836 | return NULL; /* not our business to raise error */ |
1837 | return CreateTupleDescCopy(portal->tupDesc); |
1838 | } |
1839 | |
1840 | case T_ExecuteStmt: |
1841 | { |
1842 | ExecuteStmt *stmt = (ExecuteStmt *) parsetree; |
1843 | PreparedStatement *entry; |
1844 | |
1845 | entry = FetchPreparedStatement(stmt->name, false); |
1846 | if (!entry) |
1847 | return NULL; /* not our business to raise error */ |
1848 | return FetchPreparedStatementResultDesc(entry); |
1849 | } |
1850 | |
1851 | case T_ExplainStmt: |
1852 | return ExplainResultDesc((ExplainStmt *) parsetree); |
1853 | |
1854 | case T_VariableShowStmt: |
1855 | { |
1856 | VariableShowStmt *n = (VariableShowStmt *) parsetree; |
1857 | |
1858 | return GetPGVariableResultDesc(n->name); |
1859 | } |
1860 | |
1861 | default: |
1862 | return NULL; |
1863 | } |
1864 | } |
1865 | |
1866 | |
1867 | /* |
1868 | * QueryReturnsTuples |
1869 | * Return "true" if this Query will send output to the destination. |
1870 | */ |
1871 | #ifdef NOT_USED |
1872 | bool |
1873 | QueryReturnsTuples(Query *parsetree) |
1874 | { |
1875 | switch (parsetree->commandType) |
1876 | { |
1877 | case CMD_SELECT: |
1878 | /* returns tuples */ |
1879 | return true; |
1880 | case CMD_INSERT: |
1881 | case CMD_UPDATE: |
1882 | case CMD_DELETE: |
1883 | /* the forms with RETURNING return tuples */ |
1884 | if (parsetree->returningList) |
1885 | return true; |
1886 | break; |
1887 | case CMD_UTILITY: |
1888 | return UtilityReturnsTuples(parsetree->utilityStmt); |
1889 | case CMD_UNKNOWN: |
1890 | case CMD_NOTHING: |
1891 | /* probably shouldn't get here */ |
1892 | break; |
1893 | } |
1894 | return false; /* default */ |
1895 | } |
1896 | #endif |
1897 | |
1898 | |
1899 | /* |
1900 | * UtilityContainsQuery |
1901 | * Return the contained Query, or NULL if there is none |
1902 | * |
1903 | * Certain utility statements, such as EXPLAIN, contain a plannable Query. |
1904 | * This function encapsulates knowledge of exactly which ones do. |
1905 | * We assume it is invoked only on already-parse-analyzed statements |
1906 | * (else the contained parsetree isn't a Query yet). |
1907 | * |
1908 | * In some cases (currently, only EXPLAIN of CREATE TABLE AS/SELECT INTO and |
1909 | * CREATE MATERIALIZED VIEW), potentially Query-containing utility statements |
1910 | * can be nested. This function will drill down to a non-utility Query, or |
1911 | * return NULL if none. |
1912 | */ |
1913 | Query * |
1914 | UtilityContainsQuery(Node *parsetree) |
1915 | { |
1916 | Query *qry; |
1917 | |
1918 | switch (nodeTag(parsetree)) |
1919 | { |
1920 | case T_DeclareCursorStmt: |
1921 | qry = castNode(Query, ((DeclareCursorStmt *) parsetree)->query); |
1922 | if (qry->commandType == CMD_UTILITY) |
1923 | return UtilityContainsQuery(qry->utilityStmt); |
1924 | return qry; |
1925 | |
1926 | case T_ExplainStmt: |
1927 | qry = castNode(Query, ((ExplainStmt *) parsetree)->query); |
1928 | if (qry->commandType == CMD_UTILITY) |
1929 | return UtilityContainsQuery(qry->utilityStmt); |
1930 | return qry; |
1931 | |
1932 | case T_CreateTableAsStmt: |
1933 | qry = castNode(Query, ((CreateTableAsStmt *) parsetree)->query); |
1934 | if (qry->commandType == CMD_UTILITY) |
1935 | return UtilityContainsQuery(qry->utilityStmt); |
1936 | return qry; |
1937 | |
1938 | default: |
1939 | return NULL; |
1940 | } |
1941 | } |
1942 | |
1943 | |
1944 | /* |
1945 | * AlterObjectTypeCommandTag |
1946 | * helper function for CreateCommandTag |
1947 | * |
1948 | * This covers most cases where ALTER is used with an ObjectType enum. |
1949 | */ |
1950 | static const char * |
1951 | AlterObjectTypeCommandTag(ObjectType objtype) |
1952 | { |
1953 | const char *tag; |
1954 | |
1955 | switch (objtype) |
1956 | { |
1957 | case OBJECT_AGGREGATE: |
1958 | tag = "ALTER AGGREGATE" ; |
1959 | break; |
1960 | case OBJECT_ATTRIBUTE: |
1961 | tag = "ALTER TYPE" ; |
1962 | break; |
1963 | case OBJECT_CAST: |
1964 | tag = "ALTER CAST" ; |
1965 | break; |
1966 | case OBJECT_COLLATION: |
1967 | tag = "ALTER COLLATION" ; |
1968 | break; |
1969 | case OBJECT_COLUMN: |
1970 | tag = "ALTER TABLE" ; |
1971 | break; |
1972 | case OBJECT_CONVERSION: |
1973 | tag = "ALTER CONVERSION" ; |
1974 | break; |
1975 | case OBJECT_DATABASE: |
1976 | tag = "ALTER DATABASE" ; |
1977 | break; |
1978 | case OBJECT_DOMAIN: |
1979 | case OBJECT_DOMCONSTRAINT: |
1980 | tag = "ALTER DOMAIN" ; |
1981 | break; |
1982 | case OBJECT_EXTENSION: |
1983 | tag = "ALTER EXTENSION" ; |
1984 | break; |
1985 | case OBJECT_FDW: |
1986 | tag = "ALTER FOREIGN DATA WRAPPER" ; |
1987 | break; |
1988 | case OBJECT_FOREIGN_SERVER: |
1989 | tag = "ALTER SERVER" ; |
1990 | break; |
1991 | case OBJECT_FOREIGN_TABLE: |
1992 | tag = "ALTER FOREIGN TABLE" ; |
1993 | break; |
1994 | case OBJECT_FUNCTION: |
1995 | tag = "ALTER FUNCTION" ; |
1996 | break; |
1997 | case OBJECT_INDEX: |
1998 | tag = "ALTER INDEX" ; |
1999 | break; |
2000 | case OBJECT_LANGUAGE: |
2001 | tag = "ALTER LANGUAGE" ; |
2002 | break; |
2003 | case OBJECT_LARGEOBJECT: |
2004 | tag = "ALTER LARGE OBJECT" ; |
2005 | break; |
2006 | case OBJECT_OPCLASS: |
2007 | tag = "ALTER OPERATOR CLASS" ; |
2008 | break; |
2009 | case OBJECT_OPERATOR: |
2010 | tag = "ALTER OPERATOR" ; |
2011 | break; |
2012 | case OBJECT_OPFAMILY: |
2013 | tag = "ALTER OPERATOR FAMILY" ; |
2014 | break; |
2015 | case OBJECT_POLICY: |
2016 | tag = "ALTER POLICY" ; |
2017 | break; |
2018 | case OBJECT_PROCEDURE: |
2019 | tag = "ALTER PROCEDURE" ; |
2020 | break; |
2021 | case OBJECT_ROLE: |
2022 | tag = "ALTER ROLE" ; |
2023 | break; |
2024 | case OBJECT_ROUTINE: |
2025 | tag = "ALTER ROUTINE" ; |
2026 | break; |
2027 | case OBJECT_RULE: |
2028 | tag = "ALTER RULE" ; |
2029 | break; |
2030 | case OBJECT_SCHEMA: |
2031 | tag = "ALTER SCHEMA" ; |
2032 | break; |
2033 | case OBJECT_SEQUENCE: |
2034 | tag = "ALTER SEQUENCE" ; |
2035 | break; |
2036 | case OBJECT_TABLE: |
2037 | case OBJECT_TABCONSTRAINT: |
2038 | tag = "ALTER TABLE" ; |
2039 | break; |
2040 | case OBJECT_TABLESPACE: |
2041 | tag = "ALTER TABLESPACE" ; |
2042 | break; |
2043 | case OBJECT_TRIGGER: |
2044 | tag = "ALTER TRIGGER" ; |
2045 | break; |
2046 | case OBJECT_EVENT_TRIGGER: |
2047 | tag = "ALTER EVENT TRIGGER" ; |
2048 | break; |
2049 | case OBJECT_TSCONFIGURATION: |
2050 | tag = "ALTER TEXT SEARCH CONFIGURATION" ; |
2051 | break; |
2052 | case OBJECT_TSDICTIONARY: |
2053 | tag = "ALTER TEXT SEARCH DICTIONARY" ; |
2054 | break; |
2055 | case OBJECT_TSPARSER: |
2056 | tag = "ALTER TEXT SEARCH PARSER" ; |
2057 | break; |
2058 | case OBJECT_TSTEMPLATE: |
2059 | tag = "ALTER TEXT SEARCH TEMPLATE" ; |
2060 | break; |
2061 | case OBJECT_TYPE: |
2062 | tag = "ALTER TYPE" ; |
2063 | break; |
2064 | case OBJECT_VIEW: |
2065 | tag = "ALTER VIEW" ; |
2066 | break; |
2067 | case OBJECT_MATVIEW: |
2068 | tag = "ALTER MATERIALIZED VIEW" ; |
2069 | break; |
2070 | case OBJECT_PUBLICATION: |
2071 | tag = "ALTER PUBLICATION" ; |
2072 | break; |
2073 | case OBJECT_SUBSCRIPTION: |
2074 | tag = "ALTER SUBSCRIPTION" ; |
2075 | break; |
2076 | case OBJECT_STATISTIC_EXT: |
2077 | tag = "ALTER STATISTICS" ; |
2078 | break; |
2079 | default: |
2080 | tag = "???" ; |
2081 | break; |
2082 | } |
2083 | |
2084 | return tag; |
2085 | } |
2086 | |
2087 | /* |
2088 | * CreateCommandTag |
2089 | * utility to get a string representation of the command operation, |
2090 | * given either a raw (un-analyzed) parsetree, an analyzed Query, |
2091 | * or a PlannedStmt. |
2092 | * |
2093 | * This must handle all command types, but since the vast majority |
2094 | * of 'em are utility commands, it seems sensible to keep it here. |
2095 | * |
2096 | * NB: all result strings must be shorter than COMPLETION_TAG_BUFSIZE. |
2097 | * Also, the result must point at a true constant (permanent storage). |
2098 | */ |
2099 | const char * |
2100 | CreateCommandTag(Node *parsetree) |
2101 | { |
2102 | const char *tag; |
2103 | |
2104 | switch (nodeTag(parsetree)) |
2105 | { |
2106 | /* recurse if we're given a RawStmt */ |
2107 | case T_RawStmt: |
2108 | tag = CreateCommandTag(((RawStmt *) parsetree)->stmt); |
2109 | break; |
2110 | |
2111 | /* raw plannable queries */ |
2112 | case T_InsertStmt: |
2113 | tag = "INSERT" ; |
2114 | break; |
2115 | |
2116 | case T_DeleteStmt: |
2117 | tag = "DELETE" ; |
2118 | break; |
2119 | |
2120 | case T_UpdateStmt: |
2121 | tag = "UPDATE" ; |
2122 | break; |
2123 | |
2124 | case T_SelectStmt: |
2125 | tag = "SELECT" ; |
2126 | break; |
2127 | |
2128 | /* utility statements --- same whether raw or cooked */ |
2129 | case T_TransactionStmt: |
2130 | { |
2131 | TransactionStmt *stmt = (TransactionStmt *) parsetree; |
2132 | |
2133 | switch (stmt->kind) |
2134 | { |
2135 | case TRANS_STMT_BEGIN: |
2136 | tag = "BEGIN" ; |
2137 | break; |
2138 | |
2139 | case TRANS_STMT_START: |
2140 | tag = "START TRANSACTION" ; |
2141 | break; |
2142 | |
2143 | case TRANS_STMT_COMMIT: |
2144 | tag = "COMMIT" ; |
2145 | break; |
2146 | |
2147 | case TRANS_STMT_ROLLBACK: |
2148 | case TRANS_STMT_ROLLBACK_TO: |
2149 | tag = "ROLLBACK" ; |
2150 | break; |
2151 | |
2152 | case TRANS_STMT_SAVEPOINT: |
2153 | tag = "SAVEPOINT" ; |
2154 | break; |
2155 | |
2156 | case TRANS_STMT_RELEASE: |
2157 | tag = "RELEASE" ; |
2158 | break; |
2159 | |
2160 | case TRANS_STMT_PREPARE: |
2161 | tag = "PREPARE TRANSACTION" ; |
2162 | break; |
2163 | |
2164 | case TRANS_STMT_COMMIT_PREPARED: |
2165 | tag = "COMMIT PREPARED" ; |
2166 | break; |
2167 | |
2168 | case TRANS_STMT_ROLLBACK_PREPARED: |
2169 | tag = "ROLLBACK PREPARED" ; |
2170 | break; |
2171 | |
2172 | default: |
2173 | tag = "???" ; |
2174 | break; |
2175 | } |
2176 | } |
2177 | break; |
2178 | |
2179 | case T_DeclareCursorStmt: |
2180 | tag = "DECLARE CURSOR" ; |
2181 | break; |
2182 | |
2183 | case T_ClosePortalStmt: |
2184 | { |
2185 | ClosePortalStmt *stmt = (ClosePortalStmt *) parsetree; |
2186 | |
2187 | if (stmt->portalname == NULL) |
2188 | tag = "CLOSE CURSOR ALL" ; |
2189 | else |
2190 | tag = "CLOSE CURSOR" ; |
2191 | } |
2192 | break; |
2193 | |
2194 | case T_FetchStmt: |
2195 | { |
2196 | FetchStmt *stmt = (FetchStmt *) parsetree; |
2197 | |
2198 | tag = (stmt->ismove) ? "MOVE" : "FETCH" ; |
2199 | } |
2200 | break; |
2201 | |
2202 | case T_CreateDomainStmt: |
2203 | tag = "CREATE DOMAIN" ; |
2204 | break; |
2205 | |
2206 | case T_CreateSchemaStmt: |
2207 | tag = "CREATE SCHEMA" ; |
2208 | break; |
2209 | |
2210 | case T_CreateStmt: |
2211 | tag = "CREATE TABLE" ; |
2212 | break; |
2213 | |
2214 | case T_CreateTableSpaceStmt: |
2215 | tag = "CREATE TABLESPACE" ; |
2216 | break; |
2217 | |
2218 | case T_DropTableSpaceStmt: |
2219 | tag = "DROP TABLESPACE" ; |
2220 | break; |
2221 | |
2222 | case T_AlterTableSpaceOptionsStmt: |
2223 | tag = "ALTER TABLESPACE" ; |
2224 | break; |
2225 | |
2226 | case T_CreateExtensionStmt: |
2227 | tag = "CREATE EXTENSION" ; |
2228 | break; |
2229 | |
2230 | case T_AlterExtensionStmt: |
2231 | tag = "ALTER EXTENSION" ; |
2232 | break; |
2233 | |
2234 | case T_AlterExtensionContentsStmt: |
2235 | tag = "ALTER EXTENSION" ; |
2236 | break; |
2237 | |
2238 | case T_CreateFdwStmt: |
2239 | tag = "CREATE FOREIGN DATA WRAPPER" ; |
2240 | break; |
2241 | |
2242 | case T_AlterFdwStmt: |
2243 | tag = "ALTER FOREIGN DATA WRAPPER" ; |
2244 | break; |
2245 | |
2246 | case T_CreateForeignServerStmt: |
2247 | tag = "CREATE SERVER" ; |
2248 | break; |
2249 | |
2250 | case T_AlterForeignServerStmt: |
2251 | tag = "ALTER SERVER" ; |
2252 | break; |
2253 | |
2254 | case T_CreateUserMappingStmt: |
2255 | tag = "CREATE USER MAPPING" ; |
2256 | break; |
2257 | |
2258 | case T_AlterUserMappingStmt: |
2259 | tag = "ALTER USER MAPPING" ; |
2260 | break; |
2261 | |
2262 | case T_DropUserMappingStmt: |
2263 | tag = "DROP USER MAPPING" ; |
2264 | break; |
2265 | |
2266 | case T_CreateForeignTableStmt: |
2267 | tag = "CREATE FOREIGN TABLE" ; |
2268 | break; |
2269 | |
2270 | case T_ImportForeignSchemaStmt: |
2271 | tag = "IMPORT FOREIGN SCHEMA" ; |
2272 | break; |
2273 | |
2274 | case T_DropStmt: |
2275 | switch (((DropStmt *) parsetree)->removeType) |
2276 | { |
2277 | case OBJECT_TABLE: |
2278 | tag = "DROP TABLE" ; |
2279 | break; |
2280 | case OBJECT_SEQUENCE: |
2281 | tag = "DROP SEQUENCE" ; |
2282 | break; |
2283 | case OBJECT_VIEW: |
2284 | tag = "DROP VIEW" ; |
2285 | break; |
2286 | case OBJECT_MATVIEW: |
2287 | tag = "DROP MATERIALIZED VIEW" ; |
2288 | break; |
2289 | case OBJECT_INDEX: |
2290 | tag = "DROP INDEX" ; |
2291 | break; |
2292 | case OBJECT_TYPE: |
2293 | tag = "DROP TYPE" ; |
2294 | break; |
2295 | case OBJECT_DOMAIN: |
2296 | tag = "DROP DOMAIN" ; |
2297 | break; |
2298 | case OBJECT_COLLATION: |
2299 | tag = "DROP COLLATION" ; |
2300 | break; |
2301 | case OBJECT_CONVERSION: |
2302 | tag = "DROP CONVERSION" ; |
2303 | break; |
2304 | case OBJECT_SCHEMA: |
2305 | tag = "DROP SCHEMA" ; |
2306 | break; |
2307 | case OBJECT_TSPARSER: |
2308 | tag = "DROP TEXT SEARCH PARSER" ; |
2309 | break; |
2310 | case OBJECT_TSDICTIONARY: |
2311 | tag = "DROP TEXT SEARCH DICTIONARY" ; |
2312 | break; |
2313 | case OBJECT_TSTEMPLATE: |
2314 | tag = "DROP TEXT SEARCH TEMPLATE" ; |
2315 | break; |
2316 | case OBJECT_TSCONFIGURATION: |
2317 | tag = "DROP TEXT SEARCH CONFIGURATION" ; |
2318 | break; |
2319 | case OBJECT_FOREIGN_TABLE: |
2320 | tag = "DROP FOREIGN TABLE" ; |
2321 | break; |
2322 | case OBJECT_EXTENSION: |
2323 | tag = "DROP EXTENSION" ; |
2324 | break; |
2325 | case OBJECT_FUNCTION: |
2326 | tag = "DROP FUNCTION" ; |
2327 | break; |
2328 | case OBJECT_PROCEDURE: |
2329 | tag = "DROP PROCEDURE" ; |
2330 | break; |
2331 | case OBJECT_ROUTINE: |
2332 | tag = "DROP ROUTINE" ; |
2333 | break; |
2334 | case OBJECT_AGGREGATE: |
2335 | tag = "DROP AGGREGATE" ; |
2336 | break; |
2337 | case OBJECT_OPERATOR: |
2338 | tag = "DROP OPERATOR" ; |
2339 | break; |
2340 | case OBJECT_LANGUAGE: |
2341 | tag = "DROP LANGUAGE" ; |
2342 | break; |
2343 | case OBJECT_CAST: |
2344 | tag = "DROP CAST" ; |
2345 | break; |
2346 | case OBJECT_TRIGGER: |
2347 | tag = "DROP TRIGGER" ; |
2348 | break; |
2349 | case OBJECT_EVENT_TRIGGER: |
2350 | tag = "DROP EVENT TRIGGER" ; |
2351 | break; |
2352 | case OBJECT_RULE: |
2353 | tag = "DROP RULE" ; |
2354 | break; |
2355 | case OBJECT_FDW: |
2356 | tag = "DROP FOREIGN DATA WRAPPER" ; |
2357 | break; |
2358 | case OBJECT_FOREIGN_SERVER: |
2359 | tag = "DROP SERVER" ; |
2360 | break; |
2361 | case OBJECT_OPCLASS: |
2362 | tag = "DROP OPERATOR CLASS" ; |
2363 | break; |
2364 | case OBJECT_OPFAMILY: |
2365 | tag = "DROP OPERATOR FAMILY" ; |
2366 | break; |
2367 | case OBJECT_POLICY: |
2368 | tag = "DROP POLICY" ; |
2369 | break; |
2370 | case OBJECT_TRANSFORM: |
2371 | tag = "DROP TRANSFORM" ; |
2372 | break; |
2373 | case OBJECT_ACCESS_METHOD: |
2374 | tag = "DROP ACCESS METHOD" ; |
2375 | break; |
2376 | case OBJECT_PUBLICATION: |
2377 | tag = "DROP PUBLICATION" ; |
2378 | break; |
2379 | case OBJECT_STATISTIC_EXT: |
2380 | tag = "DROP STATISTICS" ; |
2381 | break; |
2382 | default: |
2383 | tag = "???" ; |
2384 | } |
2385 | break; |
2386 | |
2387 | case T_TruncateStmt: |
2388 | tag = "TRUNCATE TABLE" ; |
2389 | break; |
2390 | |
2391 | case T_CommentStmt: |
2392 | tag = "COMMENT" ; |
2393 | break; |
2394 | |
2395 | case T_SecLabelStmt: |
2396 | tag = "SECURITY LABEL" ; |
2397 | break; |
2398 | |
2399 | case T_CopyStmt: |
2400 | tag = "COPY" ; |
2401 | break; |
2402 | |
2403 | case T_RenameStmt: |
2404 | tag = AlterObjectTypeCommandTag(((RenameStmt *) parsetree)->renameType); |
2405 | break; |
2406 | |
2407 | case T_AlterObjectDependsStmt: |
2408 | tag = AlterObjectTypeCommandTag(((AlterObjectDependsStmt *) parsetree)->objectType); |
2409 | break; |
2410 | |
2411 | case T_AlterObjectSchemaStmt: |
2412 | tag = AlterObjectTypeCommandTag(((AlterObjectSchemaStmt *) parsetree)->objectType); |
2413 | break; |
2414 | |
2415 | case T_AlterOwnerStmt: |
2416 | tag = AlterObjectTypeCommandTag(((AlterOwnerStmt *) parsetree)->objectType); |
2417 | break; |
2418 | |
2419 | case T_AlterTableMoveAllStmt: |
2420 | tag = AlterObjectTypeCommandTag(((AlterTableMoveAllStmt *) parsetree)->objtype); |
2421 | break; |
2422 | |
2423 | case T_AlterTableStmt: |
2424 | tag = AlterObjectTypeCommandTag(((AlterTableStmt *) parsetree)->relkind); |
2425 | break; |
2426 | |
2427 | case T_AlterDomainStmt: |
2428 | tag = "ALTER DOMAIN" ; |
2429 | break; |
2430 | |
2431 | case T_AlterFunctionStmt: |
2432 | switch (((AlterFunctionStmt *) parsetree)->objtype) |
2433 | { |
2434 | case OBJECT_FUNCTION: |
2435 | tag = "ALTER FUNCTION" ; |
2436 | break; |
2437 | case OBJECT_PROCEDURE: |
2438 | tag = "ALTER PROCEDURE" ; |
2439 | break; |
2440 | case OBJECT_ROUTINE: |
2441 | tag = "ALTER ROUTINE" ; |
2442 | break; |
2443 | default: |
2444 | tag = "???" ; |
2445 | } |
2446 | break; |
2447 | |
2448 | case T_GrantStmt: |
2449 | { |
2450 | GrantStmt *stmt = (GrantStmt *) parsetree; |
2451 | |
2452 | tag = (stmt->is_grant) ? "GRANT" : "REVOKE" ; |
2453 | } |
2454 | break; |
2455 | |
2456 | case T_GrantRoleStmt: |
2457 | { |
2458 | GrantRoleStmt *stmt = (GrantRoleStmt *) parsetree; |
2459 | |
2460 | tag = (stmt->is_grant) ? "GRANT ROLE" : "REVOKE ROLE" ; |
2461 | } |
2462 | break; |
2463 | |
2464 | case T_AlterDefaultPrivilegesStmt: |
2465 | tag = "ALTER DEFAULT PRIVILEGES" ; |
2466 | break; |
2467 | |
2468 | case T_DefineStmt: |
2469 | switch (((DefineStmt *) parsetree)->kind) |
2470 | { |
2471 | case OBJECT_AGGREGATE: |
2472 | tag = "CREATE AGGREGATE" ; |
2473 | break; |
2474 | case OBJECT_OPERATOR: |
2475 | tag = "CREATE OPERATOR" ; |
2476 | break; |
2477 | case OBJECT_TYPE: |
2478 | tag = "CREATE TYPE" ; |
2479 | break; |
2480 | case OBJECT_TSPARSER: |
2481 | tag = "CREATE TEXT SEARCH PARSER" ; |
2482 | break; |
2483 | case OBJECT_TSDICTIONARY: |
2484 | tag = "CREATE TEXT SEARCH DICTIONARY" ; |
2485 | break; |
2486 | case OBJECT_TSTEMPLATE: |
2487 | tag = "CREATE TEXT SEARCH TEMPLATE" ; |
2488 | break; |
2489 | case OBJECT_TSCONFIGURATION: |
2490 | tag = "CREATE TEXT SEARCH CONFIGURATION" ; |
2491 | break; |
2492 | case OBJECT_COLLATION: |
2493 | tag = "CREATE COLLATION" ; |
2494 | break; |
2495 | case OBJECT_ACCESS_METHOD: |
2496 | tag = "CREATE ACCESS METHOD" ; |
2497 | break; |
2498 | default: |
2499 | tag = "???" ; |
2500 | } |
2501 | break; |
2502 | |
2503 | case T_CompositeTypeStmt: |
2504 | tag = "CREATE TYPE" ; |
2505 | break; |
2506 | |
2507 | case T_CreateEnumStmt: |
2508 | tag = "CREATE TYPE" ; |
2509 | break; |
2510 | |
2511 | case T_CreateRangeStmt: |
2512 | tag = "CREATE TYPE" ; |
2513 | break; |
2514 | |
2515 | case T_AlterEnumStmt: |
2516 | tag = "ALTER TYPE" ; |
2517 | break; |
2518 | |
2519 | case T_ViewStmt: |
2520 | tag = "CREATE VIEW" ; |
2521 | break; |
2522 | |
2523 | case T_CreateFunctionStmt: |
2524 | if (((CreateFunctionStmt *) parsetree)->is_procedure) |
2525 | tag = "CREATE PROCEDURE" ; |
2526 | else |
2527 | tag = "CREATE FUNCTION" ; |
2528 | break; |
2529 | |
2530 | case T_IndexStmt: |
2531 | tag = "CREATE INDEX" ; |
2532 | break; |
2533 | |
2534 | case T_RuleStmt: |
2535 | tag = "CREATE RULE" ; |
2536 | break; |
2537 | |
2538 | case T_CreateSeqStmt: |
2539 | tag = "CREATE SEQUENCE" ; |
2540 | break; |
2541 | |
2542 | case T_AlterSeqStmt: |
2543 | tag = "ALTER SEQUENCE" ; |
2544 | break; |
2545 | |
2546 | case T_DoStmt: |
2547 | tag = "DO" ; |
2548 | break; |
2549 | |
2550 | case T_CreatedbStmt: |
2551 | tag = "CREATE DATABASE" ; |
2552 | break; |
2553 | |
2554 | case T_AlterDatabaseStmt: |
2555 | tag = "ALTER DATABASE" ; |
2556 | break; |
2557 | |
2558 | case T_AlterDatabaseSetStmt: |
2559 | tag = "ALTER DATABASE" ; |
2560 | break; |
2561 | |
2562 | case T_DropdbStmt: |
2563 | tag = "DROP DATABASE" ; |
2564 | break; |
2565 | |
2566 | case T_NotifyStmt: |
2567 | tag = "NOTIFY" ; |
2568 | break; |
2569 | |
2570 | case T_ListenStmt: |
2571 | tag = "LISTEN" ; |
2572 | break; |
2573 | |
2574 | case T_UnlistenStmt: |
2575 | tag = "UNLISTEN" ; |
2576 | break; |
2577 | |
2578 | case T_LoadStmt: |
2579 | tag = "LOAD" ; |
2580 | break; |
2581 | |
2582 | case T_CallStmt: |
2583 | tag = "CALL" ; |
2584 | break; |
2585 | |
2586 | case T_ClusterStmt: |
2587 | tag = "CLUSTER" ; |
2588 | break; |
2589 | |
2590 | case T_VacuumStmt: |
2591 | if (((VacuumStmt *) parsetree)->is_vacuumcmd) |
2592 | tag = "VACUUM" ; |
2593 | else |
2594 | tag = "ANALYZE" ; |
2595 | break; |
2596 | |
2597 | case T_ExplainStmt: |
2598 | tag = "EXPLAIN" ; |
2599 | break; |
2600 | |
2601 | case T_CreateTableAsStmt: |
2602 | switch (((CreateTableAsStmt *) parsetree)->relkind) |
2603 | { |
2604 | case OBJECT_TABLE: |
2605 | if (((CreateTableAsStmt *) parsetree)->is_select_into) |
2606 | tag = "SELECT INTO" ; |
2607 | else |
2608 | tag = "CREATE TABLE AS" ; |
2609 | break; |
2610 | case OBJECT_MATVIEW: |
2611 | tag = "CREATE MATERIALIZED VIEW" ; |
2612 | break; |
2613 | default: |
2614 | tag = "???" ; |
2615 | } |
2616 | break; |
2617 | |
2618 | case T_RefreshMatViewStmt: |
2619 | tag = "REFRESH MATERIALIZED VIEW" ; |
2620 | break; |
2621 | |
2622 | case T_AlterSystemStmt: |
2623 | tag = "ALTER SYSTEM" ; |
2624 | break; |
2625 | |
2626 | case T_VariableSetStmt: |
2627 | switch (((VariableSetStmt *) parsetree)->kind) |
2628 | { |
2629 | case VAR_SET_VALUE: |
2630 | case VAR_SET_CURRENT: |
2631 | case VAR_SET_DEFAULT: |
2632 | case VAR_SET_MULTI: |
2633 | tag = "SET" ; |
2634 | break; |
2635 | case VAR_RESET: |
2636 | case VAR_RESET_ALL: |
2637 | tag = "RESET" ; |
2638 | break; |
2639 | default: |
2640 | tag = "???" ; |
2641 | } |
2642 | break; |
2643 | |
2644 | case T_VariableShowStmt: |
2645 | tag = "SHOW" ; |
2646 | break; |
2647 | |
2648 | case T_DiscardStmt: |
2649 | switch (((DiscardStmt *) parsetree)->target) |
2650 | { |
2651 | case DISCARD_ALL: |
2652 | tag = "DISCARD ALL" ; |
2653 | break; |
2654 | case DISCARD_PLANS: |
2655 | tag = "DISCARD PLANS" ; |
2656 | break; |
2657 | case DISCARD_TEMP: |
2658 | tag = "DISCARD TEMP" ; |
2659 | break; |
2660 | case DISCARD_SEQUENCES: |
2661 | tag = "DISCARD SEQUENCES" ; |
2662 | break; |
2663 | default: |
2664 | tag = "???" ; |
2665 | } |
2666 | break; |
2667 | |
2668 | case T_CreateTransformStmt: |
2669 | tag = "CREATE TRANSFORM" ; |
2670 | break; |
2671 | |
2672 | case T_CreateTrigStmt: |
2673 | tag = "CREATE TRIGGER" ; |
2674 | break; |
2675 | |
2676 | case T_CreateEventTrigStmt: |
2677 | tag = "CREATE EVENT TRIGGER" ; |
2678 | break; |
2679 | |
2680 | case T_AlterEventTrigStmt: |
2681 | tag = "ALTER EVENT TRIGGER" ; |
2682 | break; |
2683 | |
2684 | case T_CreatePLangStmt: |
2685 | tag = "CREATE LANGUAGE" ; |
2686 | break; |
2687 | |
2688 | case T_CreateRoleStmt: |
2689 | tag = "CREATE ROLE" ; |
2690 | break; |
2691 | |
2692 | case T_AlterRoleStmt: |
2693 | tag = "ALTER ROLE" ; |
2694 | break; |
2695 | |
2696 | case T_AlterRoleSetStmt: |
2697 | tag = "ALTER ROLE" ; |
2698 | break; |
2699 | |
2700 | case T_DropRoleStmt: |
2701 | tag = "DROP ROLE" ; |
2702 | break; |
2703 | |
2704 | case T_DropOwnedStmt: |
2705 | tag = "DROP OWNED" ; |
2706 | break; |
2707 | |
2708 | case T_ReassignOwnedStmt: |
2709 | tag = "REASSIGN OWNED" ; |
2710 | break; |
2711 | |
2712 | case T_LockStmt: |
2713 | tag = "LOCK TABLE" ; |
2714 | break; |
2715 | |
2716 | case T_ConstraintsSetStmt: |
2717 | tag = "SET CONSTRAINTS" ; |
2718 | break; |
2719 | |
2720 | case T_CheckPointStmt: |
2721 | tag = "CHECKPOINT" ; |
2722 | break; |
2723 | |
2724 | case T_ReindexStmt: |
2725 | tag = "REINDEX" ; |
2726 | break; |
2727 | |
2728 | case T_CreateConversionStmt: |
2729 | tag = "CREATE CONVERSION" ; |
2730 | break; |
2731 | |
2732 | case T_CreateCastStmt: |
2733 | tag = "CREATE CAST" ; |
2734 | break; |
2735 | |
2736 | case T_CreateOpClassStmt: |
2737 | tag = "CREATE OPERATOR CLASS" ; |
2738 | break; |
2739 | |
2740 | case T_CreateOpFamilyStmt: |
2741 | tag = "CREATE OPERATOR FAMILY" ; |
2742 | break; |
2743 | |
2744 | case T_AlterOpFamilyStmt: |
2745 | tag = "ALTER OPERATOR FAMILY" ; |
2746 | break; |
2747 | |
2748 | case T_AlterOperatorStmt: |
2749 | tag = "ALTER OPERATOR" ; |
2750 | break; |
2751 | |
2752 | case T_AlterTSDictionaryStmt: |
2753 | tag = "ALTER TEXT SEARCH DICTIONARY" ; |
2754 | break; |
2755 | |
2756 | case T_AlterTSConfigurationStmt: |
2757 | tag = "ALTER TEXT SEARCH CONFIGURATION" ; |
2758 | break; |
2759 | |
2760 | case T_CreatePolicyStmt: |
2761 | tag = "CREATE POLICY" ; |
2762 | break; |
2763 | |
2764 | case T_AlterPolicyStmt: |
2765 | tag = "ALTER POLICY" ; |
2766 | break; |
2767 | |
2768 | case T_CreateAmStmt: |
2769 | tag = "CREATE ACCESS METHOD" ; |
2770 | break; |
2771 | |
2772 | case T_CreatePublicationStmt: |
2773 | tag = "CREATE PUBLICATION" ; |
2774 | break; |
2775 | |
2776 | case T_AlterPublicationStmt: |
2777 | tag = "ALTER PUBLICATION" ; |
2778 | break; |
2779 | |
2780 | case T_CreateSubscriptionStmt: |
2781 | tag = "CREATE SUBSCRIPTION" ; |
2782 | break; |
2783 | |
2784 | case T_AlterSubscriptionStmt: |
2785 | tag = "ALTER SUBSCRIPTION" ; |
2786 | break; |
2787 | |
2788 | case T_DropSubscriptionStmt: |
2789 | tag = "DROP SUBSCRIPTION" ; |
2790 | break; |
2791 | |
2792 | case T_AlterCollationStmt: |
2793 | tag = "ALTER COLLATION" ; |
2794 | break; |
2795 | |
2796 | case T_PrepareStmt: |
2797 | tag = "PREPARE" ; |
2798 | break; |
2799 | |
2800 | case T_ExecuteStmt: |
2801 | tag = "EXECUTE" ; |
2802 | break; |
2803 | |
2804 | case T_CreateStatsStmt: |
2805 | tag = "CREATE STATISTICS" ; |
2806 | break; |
2807 | |
2808 | case T_DeallocateStmt: |
2809 | { |
2810 | DeallocateStmt *stmt = (DeallocateStmt *) parsetree; |
2811 | |
2812 | if (stmt->name == NULL) |
2813 | tag = "DEALLOCATE ALL" ; |
2814 | else |
2815 | tag = "DEALLOCATE" ; |
2816 | } |
2817 | break; |
2818 | |
2819 | /* already-planned queries */ |
2820 | case T_PlannedStmt: |
2821 | { |
2822 | PlannedStmt *stmt = (PlannedStmt *) parsetree; |
2823 | |
2824 | switch (stmt->commandType) |
2825 | { |
2826 | case CMD_SELECT: |
2827 | |
2828 | /* |
2829 | * We take a little extra care here so that the result |
2830 | * will be useful for complaints about read-only |
2831 | * statements |
2832 | */ |
2833 | if (stmt->rowMarks != NIL) |
2834 | { |
2835 | /* not 100% but probably close enough */ |
2836 | switch (((PlanRowMark *) linitial(stmt->rowMarks))->strength) |
2837 | { |
2838 | case LCS_FORKEYSHARE: |
2839 | tag = "SELECT FOR KEY SHARE" ; |
2840 | break; |
2841 | case LCS_FORSHARE: |
2842 | tag = "SELECT FOR SHARE" ; |
2843 | break; |
2844 | case LCS_FORNOKEYUPDATE: |
2845 | tag = "SELECT FOR NO KEY UPDATE" ; |
2846 | break; |
2847 | case LCS_FORUPDATE: |
2848 | tag = "SELECT FOR UPDATE" ; |
2849 | break; |
2850 | default: |
2851 | tag = "SELECT" ; |
2852 | break; |
2853 | } |
2854 | } |
2855 | else |
2856 | tag = "SELECT" ; |
2857 | break; |
2858 | case CMD_UPDATE: |
2859 | tag = "UPDATE" ; |
2860 | break; |
2861 | case CMD_INSERT: |
2862 | tag = "INSERT" ; |
2863 | break; |
2864 | case CMD_DELETE: |
2865 | tag = "DELETE" ; |
2866 | break; |
2867 | case CMD_UTILITY: |
2868 | tag = CreateCommandTag(stmt->utilityStmt); |
2869 | break; |
2870 | default: |
2871 | elog(WARNING, "unrecognized commandType: %d" , |
2872 | (int) stmt->commandType); |
2873 | tag = "???" ; |
2874 | break; |
2875 | } |
2876 | } |
2877 | break; |
2878 | |
2879 | /* parsed-and-rewritten-but-not-planned queries */ |
2880 | case T_Query: |
2881 | { |
2882 | Query *stmt = (Query *) parsetree; |
2883 | |
2884 | switch (stmt->commandType) |
2885 | { |
2886 | case CMD_SELECT: |
2887 | |
2888 | /* |
2889 | * We take a little extra care here so that the result |
2890 | * will be useful for complaints about read-only |
2891 | * statements |
2892 | */ |
2893 | if (stmt->rowMarks != NIL) |
2894 | { |
2895 | /* not 100% but probably close enough */ |
2896 | switch (((RowMarkClause *) linitial(stmt->rowMarks))->strength) |
2897 | { |
2898 | case LCS_FORKEYSHARE: |
2899 | tag = "SELECT FOR KEY SHARE" ; |
2900 | break; |
2901 | case LCS_FORSHARE: |
2902 | tag = "SELECT FOR SHARE" ; |
2903 | break; |
2904 | case LCS_FORNOKEYUPDATE: |
2905 | tag = "SELECT FOR NO KEY UPDATE" ; |
2906 | break; |
2907 | case LCS_FORUPDATE: |
2908 | tag = "SELECT FOR UPDATE" ; |
2909 | break; |
2910 | default: |
2911 | tag = "???" ; |
2912 | break; |
2913 | } |
2914 | } |
2915 | else |
2916 | tag = "SELECT" ; |
2917 | break; |
2918 | case CMD_UPDATE: |
2919 | tag = "UPDATE" ; |
2920 | break; |
2921 | case CMD_INSERT: |
2922 | tag = "INSERT" ; |
2923 | break; |
2924 | case CMD_DELETE: |
2925 | tag = "DELETE" ; |
2926 | break; |
2927 | case CMD_UTILITY: |
2928 | tag = CreateCommandTag(stmt->utilityStmt); |
2929 | break; |
2930 | default: |
2931 | elog(WARNING, "unrecognized commandType: %d" , |
2932 | (int) stmt->commandType); |
2933 | tag = "???" ; |
2934 | break; |
2935 | } |
2936 | } |
2937 | break; |
2938 | |
2939 | default: |
2940 | elog(WARNING, "unrecognized node type: %d" , |
2941 | (int) nodeTag(parsetree)); |
2942 | tag = "???" ; |
2943 | break; |
2944 | } |
2945 | |
2946 | return tag; |
2947 | } |
2948 | |
2949 | |
2950 | /* |
2951 | * GetCommandLogLevel |
2952 | * utility to get the minimum log_statement level for a command, |
2953 | * given either a raw (un-analyzed) parsetree, an analyzed Query, |
2954 | * or a PlannedStmt. |
2955 | * |
2956 | * This must handle all command types, but since the vast majority |
2957 | * of 'em are utility commands, it seems sensible to keep it here. |
2958 | */ |
2959 | LogStmtLevel |
2960 | GetCommandLogLevel(Node *parsetree) |
2961 | { |
2962 | LogStmtLevel lev; |
2963 | |
2964 | switch (nodeTag(parsetree)) |
2965 | { |
2966 | /* recurse if we're given a RawStmt */ |
2967 | case T_RawStmt: |
2968 | lev = GetCommandLogLevel(((RawStmt *) parsetree)->stmt); |
2969 | break; |
2970 | |
2971 | /* raw plannable queries */ |
2972 | case T_InsertStmt: |
2973 | case T_DeleteStmt: |
2974 | case T_UpdateStmt: |
2975 | lev = LOGSTMT_MOD; |
2976 | break; |
2977 | |
2978 | case T_SelectStmt: |
2979 | if (((SelectStmt *) parsetree)->intoClause) |
2980 | lev = LOGSTMT_DDL; /* SELECT INTO */ |
2981 | else |
2982 | lev = LOGSTMT_ALL; |
2983 | break; |
2984 | |
2985 | /* utility statements --- same whether raw or cooked */ |
2986 | case T_TransactionStmt: |
2987 | lev = LOGSTMT_ALL; |
2988 | break; |
2989 | |
2990 | case T_DeclareCursorStmt: |
2991 | lev = LOGSTMT_ALL; |
2992 | break; |
2993 | |
2994 | case T_ClosePortalStmt: |
2995 | lev = LOGSTMT_ALL; |
2996 | break; |
2997 | |
2998 | case T_FetchStmt: |
2999 | lev = LOGSTMT_ALL; |
3000 | break; |
3001 | |
3002 | case T_CreateSchemaStmt: |
3003 | lev = LOGSTMT_DDL; |
3004 | break; |
3005 | |
3006 | case T_CreateStmt: |
3007 | case T_CreateForeignTableStmt: |
3008 | lev = LOGSTMT_DDL; |
3009 | break; |
3010 | |
3011 | case T_CreateTableSpaceStmt: |
3012 | case T_DropTableSpaceStmt: |
3013 | case T_AlterTableSpaceOptionsStmt: |
3014 | lev = LOGSTMT_DDL; |
3015 | break; |
3016 | |
3017 | case T_CreateExtensionStmt: |
3018 | case T_AlterExtensionStmt: |
3019 | case T_AlterExtensionContentsStmt: |
3020 | lev = LOGSTMT_DDL; |
3021 | break; |
3022 | |
3023 | case T_CreateFdwStmt: |
3024 | case T_AlterFdwStmt: |
3025 | case T_CreateForeignServerStmt: |
3026 | case T_AlterForeignServerStmt: |
3027 | case T_CreateUserMappingStmt: |
3028 | case T_AlterUserMappingStmt: |
3029 | case T_DropUserMappingStmt: |
3030 | case T_ImportForeignSchemaStmt: |
3031 | lev = LOGSTMT_DDL; |
3032 | break; |
3033 | |
3034 | case T_DropStmt: |
3035 | lev = LOGSTMT_DDL; |
3036 | break; |
3037 | |
3038 | case T_TruncateStmt: |
3039 | lev = LOGSTMT_MOD; |
3040 | break; |
3041 | |
3042 | case T_CommentStmt: |
3043 | lev = LOGSTMT_DDL; |
3044 | break; |
3045 | |
3046 | case T_SecLabelStmt: |
3047 | lev = LOGSTMT_DDL; |
3048 | break; |
3049 | |
3050 | case T_CopyStmt: |
3051 | if (((CopyStmt *) parsetree)->is_from) |
3052 | lev = LOGSTMT_MOD; |
3053 | else |
3054 | lev = LOGSTMT_ALL; |
3055 | break; |
3056 | |
3057 | case T_PrepareStmt: |
3058 | { |
3059 | PrepareStmt *stmt = (PrepareStmt *) parsetree; |
3060 | |
3061 | /* Look through a PREPARE to the contained stmt */ |
3062 | lev = GetCommandLogLevel(stmt->query); |
3063 | } |
3064 | break; |
3065 | |
3066 | case T_ExecuteStmt: |
3067 | { |
3068 | ExecuteStmt *stmt = (ExecuteStmt *) parsetree; |
3069 | PreparedStatement *ps; |
3070 | |
3071 | /* Look through an EXECUTE to the referenced stmt */ |
3072 | ps = FetchPreparedStatement(stmt->name, false); |
3073 | if (ps && ps->plansource->raw_parse_tree) |
3074 | lev = GetCommandLogLevel(ps->plansource->raw_parse_tree->stmt); |
3075 | else |
3076 | lev = LOGSTMT_ALL; |
3077 | } |
3078 | break; |
3079 | |
3080 | case T_DeallocateStmt: |
3081 | lev = LOGSTMT_ALL; |
3082 | break; |
3083 | |
3084 | case T_RenameStmt: |
3085 | lev = LOGSTMT_DDL; |
3086 | break; |
3087 | |
3088 | case T_AlterObjectDependsStmt: |
3089 | lev = LOGSTMT_DDL; |
3090 | break; |
3091 | |
3092 | case T_AlterObjectSchemaStmt: |
3093 | lev = LOGSTMT_DDL; |
3094 | break; |
3095 | |
3096 | case T_AlterOwnerStmt: |
3097 | lev = LOGSTMT_DDL; |
3098 | break; |
3099 | |
3100 | case T_AlterOperatorStmt: |
3101 | lev = LOGSTMT_DDL; |
3102 | break; |
3103 | |
3104 | case T_AlterTableMoveAllStmt: |
3105 | case T_AlterTableStmt: |
3106 | lev = LOGSTMT_DDL; |
3107 | break; |
3108 | |
3109 | case T_AlterDomainStmt: |
3110 | lev = LOGSTMT_DDL; |
3111 | break; |
3112 | |
3113 | case T_GrantStmt: |
3114 | lev = LOGSTMT_DDL; |
3115 | break; |
3116 | |
3117 | case T_GrantRoleStmt: |
3118 | lev = LOGSTMT_DDL; |
3119 | break; |
3120 | |
3121 | case T_AlterDefaultPrivilegesStmt: |
3122 | lev = LOGSTMT_DDL; |
3123 | break; |
3124 | |
3125 | case T_DefineStmt: |
3126 | lev = LOGSTMT_DDL; |
3127 | break; |
3128 | |
3129 | case T_CompositeTypeStmt: |
3130 | lev = LOGSTMT_DDL; |
3131 | break; |
3132 | |
3133 | case T_CreateEnumStmt: |
3134 | lev = LOGSTMT_DDL; |
3135 | break; |
3136 | |
3137 | case T_CreateRangeStmt: |
3138 | lev = LOGSTMT_DDL; |
3139 | break; |
3140 | |
3141 | case T_AlterEnumStmt: |
3142 | lev = LOGSTMT_DDL; |
3143 | break; |
3144 | |
3145 | case T_ViewStmt: |
3146 | lev = LOGSTMT_DDL; |
3147 | break; |
3148 | |
3149 | case T_CreateFunctionStmt: |
3150 | lev = LOGSTMT_DDL; |
3151 | break; |
3152 | |
3153 | case T_AlterFunctionStmt: |
3154 | lev = LOGSTMT_DDL; |
3155 | break; |
3156 | |
3157 | case T_IndexStmt: |
3158 | lev = LOGSTMT_DDL; |
3159 | break; |
3160 | |
3161 | case T_RuleStmt: |
3162 | lev = LOGSTMT_DDL; |
3163 | break; |
3164 | |
3165 | case T_CreateSeqStmt: |
3166 | lev = LOGSTMT_DDL; |
3167 | break; |
3168 | |
3169 | case T_AlterSeqStmt: |
3170 | lev = LOGSTMT_DDL; |
3171 | break; |
3172 | |
3173 | case T_DoStmt: |
3174 | lev = LOGSTMT_ALL; |
3175 | break; |
3176 | |
3177 | case T_CreatedbStmt: |
3178 | lev = LOGSTMT_DDL; |
3179 | break; |
3180 | |
3181 | case T_AlterDatabaseStmt: |
3182 | lev = LOGSTMT_DDL; |
3183 | break; |
3184 | |
3185 | case T_AlterDatabaseSetStmt: |
3186 | lev = LOGSTMT_DDL; |
3187 | break; |
3188 | |
3189 | case T_DropdbStmt: |
3190 | lev = LOGSTMT_DDL; |
3191 | break; |
3192 | |
3193 | case T_NotifyStmt: |
3194 | lev = LOGSTMT_ALL; |
3195 | break; |
3196 | |
3197 | case T_ListenStmt: |
3198 | lev = LOGSTMT_ALL; |
3199 | break; |
3200 | |
3201 | case T_UnlistenStmt: |
3202 | lev = LOGSTMT_ALL; |
3203 | break; |
3204 | |
3205 | case T_LoadStmt: |
3206 | lev = LOGSTMT_ALL; |
3207 | break; |
3208 | |
3209 | case T_CallStmt: |
3210 | lev = LOGSTMT_ALL; |
3211 | break; |
3212 | |
3213 | case T_ClusterStmt: |
3214 | lev = LOGSTMT_DDL; |
3215 | break; |
3216 | |
3217 | case T_VacuumStmt: |
3218 | lev = LOGSTMT_ALL; |
3219 | break; |
3220 | |
3221 | case T_ExplainStmt: |
3222 | { |
3223 | ExplainStmt *stmt = (ExplainStmt *) parsetree; |
3224 | bool analyze = false; |
3225 | ListCell *lc; |
3226 | |
3227 | /* Look through an EXPLAIN ANALYZE to the contained stmt */ |
3228 | foreach(lc, stmt->options) |
3229 | { |
3230 | DefElem *opt = (DefElem *) lfirst(lc); |
3231 | |
3232 | if (strcmp(opt->defname, "analyze" ) == 0) |
3233 | analyze = defGetBoolean(opt); |
3234 | /* don't "break", as explain.c will use the last value */ |
3235 | } |
3236 | if (analyze) |
3237 | return GetCommandLogLevel(stmt->query); |
3238 | |
3239 | /* Plain EXPLAIN isn't so interesting */ |
3240 | lev = LOGSTMT_ALL; |
3241 | } |
3242 | break; |
3243 | |
3244 | case T_CreateTableAsStmt: |
3245 | lev = LOGSTMT_DDL; |
3246 | break; |
3247 | |
3248 | case T_RefreshMatViewStmt: |
3249 | lev = LOGSTMT_DDL; |
3250 | break; |
3251 | |
3252 | case T_AlterSystemStmt: |
3253 | lev = LOGSTMT_DDL; |
3254 | break; |
3255 | |
3256 | case T_VariableSetStmt: |
3257 | lev = LOGSTMT_ALL; |
3258 | break; |
3259 | |
3260 | case T_VariableShowStmt: |
3261 | lev = LOGSTMT_ALL; |
3262 | break; |
3263 | |
3264 | case T_DiscardStmt: |
3265 | lev = LOGSTMT_ALL; |
3266 | break; |
3267 | |
3268 | case T_CreateTrigStmt: |
3269 | lev = LOGSTMT_DDL; |
3270 | break; |
3271 | |
3272 | case T_CreateEventTrigStmt: |
3273 | lev = LOGSTMT_DDL; |
3274 | break; |
3275 | |
3276 | case T_AlterEventTrigStmt: |
3277 | lev = LOGSTMT_DDL; |
3278 | break; |
3279 | |
3280 | case T_CreatePLangStmt: |
3281 | lev = LOGSTMT_DDL; |
3282 | break; |
3283 | |
3284 | case T_CreateDomainStmt: |
3285 | lev = LOGSTMT_DDL; |
3286 | break; |
3287 | |
3288 | case T_CreateRoleStmt: |
3289 | lev = LOGSTMT_DDL; |
3290 | break; |
3291 | |
3292 | case T_AlterRoleStmt: |
3293 | lev = LOGSTMT_DDL; |
3294 | break; |
3295 | |
3296 | case T_AlterRoleSetStmt: |
3297 | lev = LOGSTMT_DDL; |
3298 | break; |
3299 | |
3300 | case T_DropRoleStmt: |
3301 | lev = LOGSTMT_DDL; |
3302 | break; |
3303 | |
3304 | case T_DropOwnedStmt: |
3305 | lev = LOGSTMT_DDL; |
3306 | break; |
3307 | |
3308 | case T_ReassignOwnedStmt: |
3309 | lev = LOGSTMT_DDL; |
3310 | break; |
3311 | |
3312 | case T_LockStmt: |
3313 | lev = LOGSTMT_ALL; |
3314 | break; |
3315 | |
3316 | case T_ConstraintsSetStmt: |
3317 | lev = LOGSTMT_ALL; |
3318 | break; |
3319 | |
3320 | case T_CheckPointStmt: |
3321 | lev = LOGSTMT_ALL; |
3322 | break; |
3323 | |
3324 | case T_ReindexStmt: |
3325 | lev = LOGSTMT_ALL; /* should this be DDL? */ |
3326 | break; |
3327 | |
3328 | case T_CreateConversionStmt: |
3329 | lev = LOGSTMT_DDL; |
3330 | break; |
3331 | |
3332 | case T_CreateCastStmt: |
3333 | lev = LOGSTMT_DDL; |
3334 | break; |
3335 | |
3336 | case T_CreateOpClassStmt: |
3337 | lev = LOGSTMT_DDL; |
3338 | break; |
3339 | |
3340 | case T_CreateOpFamilyStmt: |
3341 | lev = LOGSTMT_DDL; |
3342 | break; |
3343 | |
3344 | case T_CreateTransformStmt: |
3345 | lev = LOGSTMT_DDL; |
3346 | break; |
3347 | |
3348 | case T_AlterOpFamilyStmt: |
3349 | lev = LOGSTMT_DDL; |
3350 | break; |
3351 | |
3352 | case T_CreatePolicyStmt: |
3353 | lev = LOGSTMT_DDL; |
3354 | break; |
3355 | |
3356 | case T_AlterPolicyStmt: |
3357 | lev = LOGSTMT_DDL; |
3358 | break; |
3359 | |
3360 | case T_AlterTSDictionaryStmt: |
3361 | lev = LOGSTMT_DDL; |
3362 | break; |
3363 | |
3364 | case T_AlterTSConfigurationStmt: |
3365 | lev = LOGSTMT_DDL; |
3366 | break; |
3367 | |
3368 | case T_CreateAmStmt: |
3369 | lev = LOGSTMT_DDL; |
3370 | break; |
3371 | |
3372 | case T_CreatePublicationStmt: |
3373 | lev = LOGSTMT_DDL; |
3374 | break; |
3375 | |
3376 | case T_AlterPublicationStmt: |
3377 | lev = LOGSTMT_DDL; |
3378 | break; |
3379 | |
3380 | case T_CreateSubscriptionStmt: |
3381 | lev = LOGSTMT_DDL; |
3382 | break; |
3383 | |
3384 | case T_AlterSubscriptionStmt: |
3385 | lev = LOGSTMT_DDL; |
3386 | break; |
3387 | |
3388 | case T_DropSubscriptionStmt: |
3389 | lev = LOGSTMT_DDL; |
3390 | break; |
3391 | |
3392 | case T_CreateStatsStmt: |
3393 | lev = LOGSTMT_DDL; |
3394 | break; |
3395 | |
3396 | case T_AlterCollationStmt: |
3397 | lev = LOGSTMT_DDL; |
3398 | break; |
3399 | |
3400 | /* already-planned queries */ |
3401 | case T_PlannedStmt: |
3402 | { |
3403 | PlannedStmt *stmt = (PlannedStmt *) parsetree; |
3404 | |
3405 | switch (stmt->commandType) |
3406 | { |
3407 | case CMD_SELECT: |
3408 | lev = LOGSTMT_ALL; |
3409 | break; |
3410 | |
3411 | case CMD_UPDATE: |
3412 | case CMD_INSERT: |
3413 | case CMD_DELETE: |
3414 | lev = LOGSTMT_MOD; |
3415 | break; |
3416 | |
3417 | case CMD_UTILITY: |
3418 | lev = GetCommandLogLevel(stmt->utilityStmt); |
3419 | break; |
3420 | |
3421 | default: |
3422 | elog(WARNING, "unrecognized commandType: %d" , |
3423 | (int) stmt->commandType); |
3424 | lev = LOGSTMT_ALL; |
3425 | break; |
3426 | } |
3427 | } |
3428 | break; |
3429 | |
3430 | /* parsed-and-rewritten-but-not-planned queries */ |
3431 | case T_Query: |
3432 | { |
3433 | Query *stmt = (Query *) parsetree; |
3434 | |
3435 | switch (stmt->commandType) |
3436 | { |
3437 | case CMD_SELECT: |
3438 | lev = LOGSTMT_ALL; |
3439 | break; |
3440 | |
3441 | case CMD_UPDATE: |
3442 | case CMD_INSERT: |
3443 | case CMD_DELETE: |
3444 | lev = LOGSTMT_MOD; |
3445 | break; |
3446 | |
3447 | case CMD_UTILITY: |
3448 | lev = GetCommandLogLevel(stmt->utilityStmt); |
3449 | break; |
3450 | |
3451 | default: |
3452 | elog(WARNING, "unrecognized commandType: %d" , |
3453 | (int) stmt->commandType); |
3454 | lev = LOGSTMT_ALL; |
3455 | break; |
3456 | } |
3457 | |
3458 | } |
3459 | break; |
3460 | |
3461 | default: |
3462 | elog(WARNING, "unrecognized node type: %d" , |
3463 | (int) nodeTag(parsetree)); |
3464 | lev = LOGSTMT_ALL; |
3465 | break; |
3466 | } |
3467 | |
3468 | return lev; |
3469 | } |
3470 | |