← Back to Overview

src/backend/commands/trigger.c

Coverage: 91/94 lines (96.8%)
Total Lines
94
modified
Covered
91
96.8%
Uncovered
3
3.2%
Keyboard navigation
CreateTrigger() lines 163-173
Modified Lines Coverage: 1/1 lines (100.0%)
LineHitsSourceCommit
163 10601 CreateTrigger(CreateTrigStmt *stmt, const char *queryString, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
164 - Oid relOid, Oid refRelOid, Oid constraintOid, Oid indexOid, -
165 - Oid funcoid, Oid parentTriggerOid, Node *whenClause, -
166 - bool isInternal, bool in_partition) -
167 - { -
168 - return -
169 - CreateTriggerFiringOn(stmt, queryString, relOid, refRelOid, -
170 - constraintOid, indexOid, funcoid, -
171 - parentTriggerOid, whenClause, isInternal, -
172 - in_partition, TRIGGER_FIRES_ON_ORIGIN); -
173 - } -
CreateTriggerFiringOn() lines 180-1224
Modified Lines Coverage: 10/10 lines (100.0%)
LineHitsSourceCommit
180 14000 CreateTriggerFiringOn(CreateTrigStmt *stmt, const char *queryString, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
181 - Oid relOid, Oid refRelOid, Oid constraintOid, -
182 - Oid indexOid, Oid funcoid, Oid parentTriggerOid, -
183 - Node *whenClause, bool isInternal, bool in_partition, -
184 - char trigger_fires_when) -
185 - { -
186 - int16 tgtype; -
187 - int ncolumns; -
188 - int16 *columns; -
189 - int2vector *tgattr; -
190 - List *whenRtable; -
191 - char *qual; -
192 - Datum values[Natts_pg_trigger]; -
193 - bool nulls[Natts_pg_trigger]; -
194 - Relation rel; -
195 - AclResult aclresult; -
196 - Relation tgrel; -
197 - Relation pgrel; -
198 - HeapTuple tuple = NULL; -
199 - Oid funcrettype; -
200 - Oid trigoid = InvalidOid; -
201 - char internaltrigname[NAMEDATALEN]; -
202 - char *trigname; -
203 - Oid constrrelid = InvalidOid; -
204 - ObjectAddress myself, -
205 - referenced; -
206 - char *oldtablename = NULL; -
207 - char *newtablename = NULL; -
208 - bool partition_recurse; -
209 - bool trigger_exists = false; -
210 - Oid existing_constraint_oid = InvalidOid; -
211 - bool existing_isInternal = false; -
212 - bool existing_isClone = false; -
213 - -
214 - if (OidIsValid(relOid)) -
215 - rel = table_open(relOid, ShareRowExclusiveLock); -
216 - else -
217 - rel = table_openrv(stmt->relation, ShareRowExclusiveLock); -
218 - -
219 - /* -
220 - * Triggers must be on tables or views, and there are additional -
221 - * relation-type-specific restrictions. -
222 - */ -
223 - if (rel->rd_rel->relkind == RELKIND_RELATION) -
224 - { -
225 - /* Tables can't have INSTEAD OF triggers */ -
226 - if (stmt->timing != TRIGGER_TYPE_BEFORE && -
227 - stmt->timing != TRIGGER_TYPE_AFTER) -
228 - ereport(ERROR, -
229 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
230 - errmsg("\"%s\" is a table", -
231 - RelationGetRelationName(rel)), -
232 - errdetail("Tables cannot have INSTEAD OF triggers."))); -
233 - } -
234 - else if (rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE) -
235 - { -
236 - /* Partitioned tables can't have INSTEAD OF triggers */ -
237 - if (stmt->timing != TRIGGER_TYPE_BEFORE && -
238 - stmt->timing != TRIGGER_TYPE_AFTER) -
239 - ereport(ERROR, -
240 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
241 - errmsg("\"%s\" is a table", -
242 - RelationGetRelationName(rel)), -
243 - errdetail("Tables cannot have INSTEAD OF triggers."))); -
244 - -
245 - /* -
246 - * FOR EACH ROW triggers have further restrictions -
247 - */ -
248 - if (stmt->row) -
249 - { -
250 - /* -
251 - * Disallow use of transition tables. -
252 - * -
253 - * Note that we have another restriction about transition tables -
254 - * in partitions; search for 'has_superclass' below for an -
255 - * explanation. The check here is just to protect from the fact -
256 - * that if we allowed it here, the creation would succeed for a -
257 - * partitioned table with no partitions, but would be blocked by -
258 - * the other restriction when the first partition was created, -
259 - * which is very unfriendly behavior. -
260 - */ -
261 - if (stmt->transitionRels != NIL) -
262 - ereport(ERROR, -
263 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
264 - errmsg("\"%s\" is a partitioned table", -
265 - RelationGetRelationName(rel)), -
266 - errdetail("ROW triggers with transition tables are not supported on partitioned tables."))); -
267 - } -
268 - } -
269 - else if (rel->rd_rel->relkind == RELKIND_VIEW) -
270 - { -
271 - /* -
272 - * Views can have INSTEAD OF triggers (which we check below are -
273 - * row-level), or statement-level BEFORE/AFTER triggers. -
274 - */ -
275 - if (stmt->timing != TRIGGER_TYPE_INSTEAD && stmt->row) -
276 - ereport(ERROR, -
277 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
278 - errmsg("\"%s\" is a view", -
279 - RelationGetRelationName(rel)), -
280 - errdetail("Views cannot have row-level BEFORE or AFTER triggers."))); -
281 - /* Disallow TRUNCATE triggers on VIEWs */ -
282 - if (TRIGGER_FOR_TRUNCATE(stmt->events)) -
283 - ereport(ERROR, -
284 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
285 - errmsg("\"%s\" is a view", -
286 - RelationGetRelationName(rel)), -
287 - errdetail("Views cannot have TRUNCATE triggers."))); -
288 - } -
289 - else if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) -
290 - { -
291 - if (stmt->timing != TRIGGER_TYPE_BEFORE && -
292 - stmt->timing != TRIGGER_TYPE_AFTER) -
293 - ereport(ERROR, -
294 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
295 - errmsg("\"%s\" is a foreign table", -
296 - RelationGetRelationName(rel)), -
297 - errdetail("Foreign tables cannot have INSTEAD OF triggers."))); -
298 - -
299 - /* -
300 - * We disallow constraint triggers to protect the assumption that -
301 - * triggers on FKs can't be deferred. See notes with AfterTriggers -
302 - * data structures, below. -
303 - */ -
304 - if (stmt->isconstraint) -
305 - ereport(ERROR, -
306 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
307 - errmsg("\"%s\" is a foreign table", -
308 - RelationGetRelationName(rel)), -
309 - errdetail("Foreign tables cannot have constraint triggers."))); -
310 - } -
311 - else -
312 - ereport(ERROR, -
313 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
314 - errmsg("relation \"%s\" cannot have triggers", -
315 - RelationGetRelationName(rel)), -
316 - errdetail_relkind_not_supported(rel->rd_rel->relkind))); -
317 - -
318 - if (!allowSystemTableMods && IsSystemRelation(rel)) -
319 - ereport(ERROR, -
320 - (errcode(ERRCODE_INSUFFICIENT_PRIVILEGE), -
321 - errmsg("permission denied: \"%s\" is a system catalog", -
322 - RelationGetRelationName(rel)))); -
323 - -
324 - if (stmt->isconstraint) -
325 - { -
326 - /* -
327 - * We must take a lock on the target relation to protect against -
328 - * concurrent drop. It's not clear that AccessShareLock is strong -
329 - * enough, but we certainly need at least that much... otherwise, we -
330 - * might end up creating a pg_constraint entry referencing a -
331 - * nonexistent table. -
332 - */ -
333 - if (OidIsValid(refRelOid)) -
334 - { -
335 - LockRelationOid(refRelOid, AccessShareLock); -
336 - constrrelid = refRelOid; -
337 - } -
338 - else if (stmt->constrrel != NULL) -
339 - constrrelid = RangeVarGetRelid(stmt->constrrel, AccessShareLock, -
340 - false); -
341 - } -
342 - -
343 - /* permission checks */ -
344 - if (!isInternal) -
345 - { -
346 - aclresult = pg_class_aclcheck(RelationGetRelid(rel), GetUserId(), -
347 - ACL_TRIGGER); -
348 - if (aclresult != ACLCHECK_OK) -
349 - aclcheck_error(aclresult, get_relkind_objtype(rel->rd_rel->relkind), -
350 - RelationGetRelationName(rel)); -
351 - -
352 - if (OidIsValid(constrrelid)) -
353 - { -
354 - aclresult = pg_class_aclcheck(constrrelid, GetUserId(), -
355 - ACL_TRIGGER); -
356 - if (aclresult != ACLCHECK_OK) -
357 - aclcheck_error(aclresult, get_relkind_objtype(get_rel_relkind(constrrelid)), -
358 - get_rel_name(constrrelid)); -
359 - } -
360 - } -
361 - -
362 - /* -
363 - * When called on a partitioned table to create a FOR EACH ROW trigger -
364 - * that's not internal, we create one trigger for each partition, too. -
365 - * -
366 - * For that, we'd better hold lock on all of them ahead of time. -
367 - */ -
368 - partition_recurse = !isInternal && stmt->row && -
369 - rel->rd_rel->relkind == RELKIND_PARTITIONED_TABLE; -
370 - if (partition_recurse) -
371 - list_free(find_all_inheritors(RelationGetRelid(rel), -
372 - ShareRowExclusiveLock, NULL)); -
373 - -
374 - /* Compute tgtype */ -
375 - TRIGGER_CLEAR_TYPE(tgtype); -
376 - if (stmt->row) -
377 - TRIGGER_SETT_ROW(tgtype); -
378 - tgtype |= stmt->timing; -
379 - tgtype |= stmt->events; -
380 - -
381 - /* Disallow ROW-level TRUNCATE triggers */ -
382 - if (TRIGGER_FOR_ROW(tgtype) && TRIGGER_FOR_TRUNCATE(tgtype)) -
383 - ereport(ERROR, -
384 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
385 - errmsg("TRUNCATE FOR EACH ROW triggers are not supported"))); -
386 - -
387 - /* INSTEAD triggers must be row-level, and can't have WHEN or columns */ -
388 - if (TRIGGER_FOR_INSTEAD(tgtype)) -
389 - { -
390 - if (!TRIGGER_FOR_ROW(tgtype)) -
391 - ereport(ERROR, -
392 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
393 - errmsg("INSTEAD OF triggers must be FOR EACH ROW"))); -
394 - if (stmt->whenClause) -
395 - ereport(ERROR, -
396 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
397 - errmsg("INSTEAD OF triggers cannot have WHEN conditions"))); -
398 - if (stmt->columns != NIL) -
399 - ereport(ERROR, -
400 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
401 - errmsg("INSTEAD OF triggers cannot have column lists"))); -
402 - } -
403 - -
404 - /* -
405 - * We don't yet support naming ROW transition variables, but the parser -
406 - * recognizes the syntax so we can give a nicer message here. -
407 - * -
408 - * Per standard, REFERENCING TABLE names are only allowed on AFTER -
409 - * triggers. Per standard, REFERENCING ROW names are not allowed with FOR -
410 - * EACH STATEMENT. Per standard, each OLD/NEW, ROW/TABLE permutation is -
411 - * only allowed once. Per standard, OLD may not be specified when -
412 - * creating a trigger only for INSERT, and NEW may not be specified when -
413 - * creating a trigger only for DELETE. -
414 - * -
415 - * Notice that the standard allows an AFTER ... FOR EACH ROW trigger to -
416 - * reference both ROW and TABLE transition data. -
417 - */ -
418 - if (stmt->transitionRels != NIL) -
419 - { -
420 - List *varList = stmt->transitionRels; -
421 - ListCell *lc; -
422 - -
423 - foreach(lc, varList) -
424 - { -
425 - TriggerTransition *tt = lfirst_node(TriggerTransition, lc); -
426 - -
427 - if (!(tt->isTable)) -
428 - ereport(ERROR, -
429 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
430 - errmsg("ROW variable naming in the REFERENCING clause is not supported"), -
431 - errhint("Use OLD TABLE or NEW TABLE for naming transition tables."))); -
432 - -
433 - /* -
434 - * Because of the above test, we omit further ROW-related testing -
435 - * below. If we later allow naming OLD and NEW ROW variables, -
436 - * adjustments will be needed below. -
437 - */ -
438 - -
439 - if (rel->rd_rel->relkind == RELKIND_FOREIGN_TABLE) -
440 - ereport(ERROR, -
441 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
442 - errmsg("\"%s\" is a foreign table", -
443 - RelationGetRelationName(rel)), -
444 - errdetail("Triggers on foreign tables cannot have transition tables."))); -
445 - -
446 - if (rel->rd_rel->relkind == RELKIND_VIEW) -
447 - ereport(ERROR, -
448 - (errcode(ERRCODE_WRONG_OBJECT_TYPE), -
449 - errmsg("\"%s\" is a view", -
450 - RelationGetRelationName(rel)), -
451 - errdetail("Triggers on views cannot have transition tables."))); -
452 - -
453 - /* -
454 - * We currently don't allow row-level triggers with transition -
455 - * tables on partition or inheritance children. Such triggers -
456 - * would somehow need to see tuples converted to the format of the -
457 - * table they're attached to, and it's not clear which subset of -
458 - * tuples each child should see. See also the prohibitions in -
459 - * ATExecAttachPartition() and ATExecAddInherit(). -
460 - */ -
461 - if (TRIGGER_FOR_ROW(tgtype) && has_superclass(rel->rd_id)) -
462 - { -
463 - /* Use appropriate error message. */ -
464 - if (rel->rd_rel->relispartition) -
465 - ereport(ERROR, -
466 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
467 - errmsg("ROW triggers with transition tables are not supported on partitions"))); -
468 - else -
469 - ereport(ERROR, -
470 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
471 - errmsg("ROW triggers with transition tables are not supported on inheritance children"))); -
472 - } -
473 - -
474 - if (stmt->timing != TRIGGER_TYPE_AFTER) -
475 - ereport(ERROR, -
476 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
477 - errmsg("transition table name can only be specified for an AFTER trigger"))); -
478 - -
479 - if (TRIGGER_FOR_TRUNCATE(tgtype)) -
480 - ereport(ERROR, -
481 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
482 - errmsg("TRUNCATE triggers with transition tables are not supported"))); -
483 - -
484 - /* -
485 - * We currently don't allow multi-event triggers ("INSERT OR -
486 - * UPDATE") with transition tables, because it's not clear how to -
487 - * handle INSERT ... ON CONFLICT statements which can fire both -
488 - * INSERT and UPDATE triggers. We show the inserted tuples to -
489 - * INSERT triggers and the updated tuples to UPDATE triggers, but -
490 - * it's not yet clear what INSERT OR UPDATE trigger should see. -
491 - * This restriction could be lifted if we can decide on the right -
492 - * semantics in a later release. -
493 - */ -
494 - if (((TRIGGER_FOR_INSERT(tgtype) ? 1 : 0) + -
495 - (TRIGGER_FOR_UPDATE(tgtype) ? 1 : 0) + -
496 - (TRIGGER_FOR_DELETE(tgtype) ? 1 : 0)) != 1) -
497 - ereport(ERROR, -
498 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
499 - errmsg("transition tables cannot be specified for triggers with more than one event"))); -
500 - -
501 - /* -
502 - * We currently don't allow column-specific triggers with -
503 - * transition tables. Per spec, that seems to require -
504 - * accumulating separate transition tables for each combination of -
505 - * columns, which is a lot of work for a rather marginal feature. -
506 - */ -
507 - if (stmt->columns != NIL) -
508 - ereport(ERROR, -
509 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
510 - errmsg("transition tables cannot be specified for triggers with column lists"))); -
511 - -
512 - /* -
513 - * We disallow constraint triggers with transition tables, to -
514 - * protect the assumption that such triggers can't be deferred. -
515 - * See notes with AfterTriggers data structures, below. -
516 - * -
517 - * Currently this is enforced by the grammar, so just Assert here. -
518 - */ -
519 - Assert(!stmt->isconstraint); -
520 - -
521 - if (tt->isNew) -
522 - { -
523 - if (!(TRIGGER_FOR_INSERT(tgtype) || -
524 - TRIGGER_FOR_UPDATE(tgtype))) -
525 - ereport(ERROR, -
526 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
527 - errmsg("NEW TABLE can only be specified for an INSERT or UPDATE trigger"))); -
528 - -
529 - if (newtablename != NULL) -
530 - ereport(ERROR, -
531 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
532 - errmsg("NEW TABLE cannot be specified multiple times"))); -
533 - -
534 - newtablename = tt->name; -
535 - } -
536 - else -
537 - { -
538 - if (!(TRIGGER_FOR_DELETE(tgtype) || -
539 - TRIGGER_FOR_UPDATE(tgtype))) -
540 - ereport(ERROR, -
541 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
542 - errmsg("OLD TABLE can only be specified for a DELETE or UPDATE trigger"))); -
543 - -
544 - if (oldtablename != NULL) -
545 - ereport(ERROR, -
546 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
547 - errmsg("OLD TABLE cannot be specified multiple times"))); -
548 - -
549 - oldtablename = tt->name; -
550 - } -
551 - } -
552 - -
553 - if (newtablename != NULL && oldtablename != NULL && -
554 - strcmp(newtablename, oldtablename) == 0) -
555 - ereport(ERROR, -
556 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
557 - errmsg("OLD TABLE name and NEW TABLE name cannot be the same"))); -
558 - } -
559 - -
560 - /* -
561 - * Parse the WHEN clause, if any and we weren't passed an already -
562 - * transformed one. -
563 - * -
564 - * Note that as a side effect, we fill whenRtable when parsing. If we got -
565 - * an already parsed clause, this does not occur, which is what we want -- -
566 - * no point in adding redundant dependencies below. -
567 - */ -
568 - if (!whenClause && stmt->whenClause) -
569 - { -
570 - ParseState *pstate; -
571 - ParseNamespaceItem *nsitem; -
572 - List *varList; -
573 - ListCell *lc; -
574 - -
575 - /* Set up a pstate to parse with */ -
576 - pstate = make_parsestate(NULL); -
577 - pstate->p_sourcetext = queryString; -
578 - -
579 - /* -
580 - * Set up nsitems for OLD and NEW references. -
581 - * -
582 - * 'OLD' must always have varno equal to 1 and 'NEW' equal to 2. -
583 - */ -
584 - nsitem = addRangeTableEntryForRelation(pstate, rel, -
585 - AccessShareLock, -
586 - makeAlias("old", NIL), -
587 - false, false); -
588 - addNSItemToQuery(pstate, nsitem, false, true, true); -
589 - nsitem = addRangeTableEntryForRelation(pstate, rel, -
590 - AccessShareLock, -
591 - makeAlias("new", NIL), -
592 - false, false); -
593 - addNSItemToQuery(pstate, nsitem, false, true, true); -
594 - -
595 183 if (stmt->transformed) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
596 20 whenClause = stmt->whenClause; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
597 - else e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
598 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
599 - /* Transform expression. Copy to be sure we don't modify original */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
600 163 whenClause = transformWhereClause(pstate, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
601 163 copyObject(stmt->whenClause), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
602 - EXPR_KIND_TRIGGER_WHEN, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
603 - "WHEN"); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
604 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
605 - /* we have to fix its collations too */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
606 163 assign_expr_collations(pstate, whenClause); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
607 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
608 163 stmt->transformed = true; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
609 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
610 - -
611 - /* -
612 - * Check for disallowed references to OLD/NEW. -
613 - * -
614 - * NB: pull_var_clause is okay here only because we don't allow -
615 - * subselects in WHEN clauses; it would fail to examine the contents -
616 - * of subselects. -
617 - */ -
618 - varList = pull_var_clause(whenClause, 0); -
619 - foreach(lc, varList) -
620 - { -
621 - Var *var = (Var *) lfirst(lc); -
622 - -
623 - switch (var->varno) -
624 - { -
625 - case PRS2_OLD_VARNO: -
626 - if (!TRIGGER_FOR_ROW(tgtype)) -
627 - ereport(ERROR, -
628 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
629 - errmsg("statement trigger's WHEN condition cannot reference column values"), -
630 - parser_errposition(pstate, var->location))); -
631 - if (TRIGGER_FOR_INSERT(tgtype)) -
632 - ereport(ERROR, -
633 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
634 - errmsg("INSERT trigger's WHEN condition cannot reference OLD values"), -
635 - parser_errposition(pstate, var->location))); -
636 - /* system columns are okay here */ -
637 - break; -
638 - case PRS2_NEW_VARNO: -
639 - if (!TRIGGER_FOR_ROW(tgtype)) -
640 - ereport(ERROR, -
641 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
642 - errmsg("statement trigger's WHEN condition cannot reference column values"), -
643 - parser_errposition(pstate, var->location))); -
644 - if (TRIGGER_FOR_DELETE(tgtype)) -
645 - ereport(ERROR, -
646 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
647 - errmsg("DELETE trigger's WHEN condition cannot reference NEW values"), -
648 - parser_errposition(pstate, var->location))); -
649 - if (var->varattno < 0 && TRIGGER_FOR_BEFORE(tgtype)) -
650 - ereport(ERROR, -
651 - (errcode(ERRCODE_FEATURE_NOT_SUPPORTED), -
652 - errmsg("BEFORE trigger's WHEN condition cannot reference NEW system columns"), -
653 - parser_errposition(pstate, var->location))); -
654 - if (TRIGGER_FOR_BEFORE(tgtype) && -
655 - var->varattno == 0 && -
656 - RelationGetDescr(rel)->constr && -
657 - (RelationGetDescr(rel)->constr->has_generated_stored || -
658 - RelationGetDescr(rel)->constr->has_generated_virtual)) -
659 - ereport(ERROR, -
660 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
661 - errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"), -
662 - errdetail("A whole-row reference is used and the table contains generated columns."), -
663 - parser_errposition(pstate, var->location))); -
664 - if (TRIGGER_FOR_BEFORE(tgtype) && -
665 - var->varattno > 0 && -
666 - TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attgenerated) -
667 - ereport(ERROR, -
668 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
669 - errmsg("BEFORE trigger's WHEN condition cannot reference NEW generated columns"), -
670 - errdetail("Column \"%s\" is a generated column.", -
671 - NameStr(TupleDescAttr(RelationGetDescr(rel), var->varattno - 1)->attname)), -
672 - parser_errposition(pstate, var->location))); -
673 - break; -
674 - default: -
675 - /* can't happen without add_missing_from, so just elog */ -
676 - elog(ERROR, "trigger WHEN condition cannot contain references to other relations"); -
677 - break; -
678 - } -
679 - } -
680 - -
681 - /* we'll need the rtable for recordDependencyOnExpr */ -
682 - whenRtable = pstate->p_rtable; -
683 - -
684 - qual = nodeToString(whenClause); -
685 - -
686 - free_parsestate(pstate); -
687 - } -
688 - else if (!whenClause) -
689 - { -
690 - whenClause = NULL; -
691 - whenRtable = NIL; -
692 - qual = NULL; -
693 - } -
694 - else -
695 - { -
696 - qual = nodeToString(whenClause); -
697 - whenRtable = NIL; -
698 - } -
699 - -
700 - /* -
701 - * Find and validate the trigger function. -
702 - */ -
703 - if (!OidIsValid(funcoid)) -
704 - funcoid = LookupFuncName(stmt->funcname, 0, NULL, false); -
705 - if (!isInternal) -
706 - { -
707 - aclresult = object_aclcheck(ProcedureRelationId, funcoid, GetUserId(), ACL_EXECUTE); -
708 - if (aclresult != ACLCHECK_OK) -
709 - aclcheck_error(aclresult, OBJECT_FUNCTION, -
710 - NameListToString(stmt->funcname)); -
711 - } -
712 - funcrettype = get_func_rettype(funcoid); -
713 - if (funcrettype != TRIGGEROID) -
714 - ereport(ERROR, -
715 - (errcode(ERRCODE_INVALID_OBJECT_DEFINITION), -
716 - errmsg("function %s must return type %s", -
717 - NameListToString(stmt->funcname), "trigger"))); -
718 - -
719 - /* -
720 - * Scan pg_trigger to see if there is already a trigger of the same name. -
721 - * Skip this for internally generated triggers, since we'll modify the -
722 - * name to be unique below. -
723 - * -
724 - * NOTE that this is cool only because we have ShareRowExclusiveLock on -
725 - * the relation, so the trigger set won't be changing underneath us. -
726 - */ -
727 - tgrel = table_open(TriggerRelationId, RowExclusiveLock); -
728 - if (!isInternal) -
729 - { -
730 - ScanKeyData skeys[2]; -
731 - SysScanDesc tgscan; -
732 - -
733 - ScanKeyInit(&skeys[0], -
734 - Anum_pg_trigger_tgrelid, -
735 - BTEqualStrategyNumber, F_OIDEQ, -
736 - ObjectIdGetDatum(RelationGetRelid(rel))); -
737 - -
738 - ScanKeyInit(&skeys[1], -
739 - Anum_pg_trigger_tgname, -
740 - BTEqualStrategyNumber, F_NAMEEQ, -
741 - CStringGetDatum(stmt->trigname)); -
742 - -
743 - tgscan = systable_beginscan(tgrel, TriggerRelidNameIndexId, true, -
744 - NULL, 2, skeys); -
745 - -
746 - /* There should be at most one matching tuple */ -
747 - if (HeapTupleIsValid(tuple = systable_getnext(tgscan))) -
748 - { -
749 - Form_pg_trigger oldtrigger = (Form_pg_trigger) GETSTRUCT(tuple); -
750 - -
751 - trigoid = oldtrigger->oid; -
752 - existing_constraint_oid = oldtrigger->tgconstraint; -
753 - existing_isInternal = oldtrigger->tgisinternal; -
754 - existing_isClone = OidIsValid(oldtrigger->tgparentid); -
755 - trigger_exists = true; -
756 - /* copy the tuple to use in CatalogTupleUpdate() */ -
757 - tuple = heap_copytuple(tuple); -
758 - } -
759 - systable_endscan(tgscan); -
760 - } -
761 - -
762 - if (!trigger_exists) -
763 - { -
764 - /* Generate the OID for the new trigger. */ -
765 - trigoid = GetNewOidWithIndex(tgrel, TriggerOidIndexId, -
766 - Anum_pg_trigger_oid); -
767 - } -
768 - else -
769 - { -
770 - /* -
771 - * If OR REPLACE was specified, we'll replace the old trigger; -
772 - * otherwise complain about the duplicate name. -
773 - */ -
774 - if (!stmt->replace) -
775 - ereport(ERROR, -
776 - (errcode(ERRCODE_DUPLICATE_OBJECT), -
777 - errmsg("trigger \"%s\" for relation \"%s\" already exists", -
778 - stmt->trigname, RelationGetRelationName(rel)))); -
779 - -
780 - /* -
781 - * An internal trigger or a child trigger (isClone) cannot be replaced -
782 - * by a user-defined trigger. However, skip this test when -
783 - * in_partition, because then we're recursing from a partitioned table -
784 - * and the check was made at the parent level. -
785 - */ -
786 - if ((existing_isInternal || existing_isClone) && -
787 - !isInternal && !in_partition) -
788 - ereport(ERROR, -
789 - (errcode(ERRCODE_DUPLICATE_OBJECT), -
790 - errmsg("trigger \"%s\" for relation \"%s\" is an internal or a child trigger", -
791 - stmt->trigname, RelationGetRelationName(rel)))); -
792 - -
793 - /* -
794 - * It is not allowed to replace with a constraint trigger; gram.y -
795 - * should have enforced this already. -
796 - */ -
797 - Assert(!stmt->isconstraint); -
798 - -
799 - /* -
800 - * It is not allowed to replace an existing constraint trigger, -
801 - * either. (The reason for these restrictions is partly that it seems -
802 - * difficult to deal with pending trigger events in such cases, and -
803 - * partly that the command might imply changing the constraint's -
804 - * properties as well, which doesn't seem nice.) -
805 - */ -
806 - if (OidIsValid(existing_constraint_oid)) -
807 - ereport(ERROR, -
808 - (errcode(ERRCODE_DUPLICATE_OBJECT), -
809 - errmsg("trigger \"%s\" for relation \"%s\" is a constraint trigger", -
810 - stmt->trigname, RelationGetRelationName(rel)))); -
811 - } -
812 - -
813 - /* -
814 - * If it's a user-entered CREATE CONSTRAINT TRIGGER command, make a -
815 - * corresponding pg_constraint entry. -
816 - */ -
817 - if (stmt->isconstraint && !OidIsValid(constraintOid)) -
818 - { -
819 - /* Internal callers should have made their own constraints */ -
820 - Assert(!isInternal); -
821 - constraintOid = CreateConstraintEntry(stmt->trigname, -
822 - RelationGetNamespace(rel), -
823 - CONSTRAINT_TRIGGER, -
824 - stmt->deferrable, -
825 - stmt->initdeferred, -
826 - true, /* Is Enforced */ -
827 - true, -
828 - InvalidOid, /* no parent */ -
829 - RelationGetRelid(rel), -
830 - NULL, /* no conkey */ -
831 - 0, -
832 - 0, -
833 - InvalidOid, /* no domain */ -
834 - InvalidOid, /* no index */ -
835 - InvalidOid, /* no foreign key */ -
836 - NULL, -
837 - NULL, -
838 - NULL, -
839 - NULL, -
840 - 0, -
841 - ' ', -
842 - ' ', -
843 - NULL, -
844 - 0, -
845 - ' ', -
846 - NULL, /* no exclusion */ -
847 - NULL, /* no check constraint */ -
848 - NULL, -
849 - true, /* islocal */ -
850 - 0, /* inhcount */ -
851 - true, /* noinherit */ -
852 - false, /* conperiod */ -
853 - isInternal); /* is_internal */ -
854 - } -
855 - -
856 - /* -
857 - * If trigger is internally generated, modify the provided trigger name to -
858 - * ensure uniqueness by appending the trigger OID. (Callers will usually -
859 - * supply a simple constant trigger name in these cases.) -
860 - */ -
861 - if (isInternal) -
862 - { -
863 - snprintf(internaltrigname, sizeof(internaltrigname), -
864 - "%s_%u", stmt->trigname, trigoid); -
865 - trigname = internaltrigname; -
866 - } -
867 - else -
868 - { -
869 - /* user-defined trigger; use the specified trigger name as-is */ -
870 - trigname = stmt->trigname; -
871 - } -
872 - -
873 - /* -
874 - * Build the new pg_trigger tuple. -
875 - */ -
876 - memset(nulls, false, sizeof(nulls)); -
877 - -
878 - values[Anum_pg_trigger_oid - 1] = ObjectIdGetDatum(trigoid); -
879 - values[Anum_pg_trigger_tgrelid - 1] = ObjectIdGetDatum(RelationGetRelid(rel)); -
880 - values[Anum_pg_trigger_tgparentid - 1] = ObjectIdGetDatum(parentTriggerOid); -
881 - values[Anum_pg_trigger_tgname - 1] = DirectFunctionCall1(namein, -
882 - CStringGetDatum(trigname)); -
883 - values[Anum_pg_trigger_tgfoid - 1] = ObjectIdGetDatum(funcoid); -
884 - values[Anum_pg_trigger_tgtype - 1] = Int16GetDatum(tgtype); -
885 - values[Anum_pg_trigger_tgenabled - 1] = CharGetDatum(trigger_fires_when); -
886 - values[Anum_pg_trigger_tgisinternal - 1] = BoolGetDatum(isInternal); -
887 - values[Anum_pg_trigger_tgconstrrelid - 1] = ObjectIdGetDatum(constrrelid); -
888 - values[Anum_pg_trigger_tgconstrindid - 1] = ObjectIdGetDatum(indexOid); -
889 - values[Anum_pg_trigger_tgconstraint - 1] = ObjectIdGetDatum(constraintOid); -
890 - values[Anum_pg_trigger_tgdeferrable - 1] = BoolGetDatum(stmt->deferrable); -
891 - values[Anum_pg_trigger_tginitdeferred - 1] = BoolGetDatum(stmt->initdeferred); -
892 - -
893 - if (stmt->args) -
894 - { -
895 - ListCell *le; -
896 - char *args; -
897 - int16 nargs = list_length(stmt->args); -
898 - int len = 0; -
899 - -
900 - foreach(le, stmt->args) -
901 - { -
902 - char *ar = strVal(lfirst(le)); -
903 - -
904 - len += strlen(ar) + 4; -
905 - for (; *ar; ar++) -
906 - { -
907 - if (*ar == '\\') -
908 - len++; -
909 - } -
910 - } -
911 - args = (char *) palloc(len + 1); -
912 - args[0] = '\0'; -
913 - foreach(le, stmt->args) -
914 - { -
915 - char *s = strVal(lfirst(le)); -
916 - char *d = args + strlen(args); -
917 - -
918 - while (*s) -
919 - { -
920 - if (*s == '\\') -
921 - *d++ = '\\'; -
922 - *d++ = *s++; -
923 - } -
924 - strcpy(d, "\\000"); -
925 - } -
926 - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(nargs); -
927 - values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain, -
928 - CStringGetDatum(args)); -
929 - } -
930 - else -
931 - { -
932 - values[Anum_pg_trigger_tgnargs - 1] = Int16GetDatum(0); -
933 - values[Anum_pg_trigger_tgargs - 1] = DirectFunctionCall1(byteain, -
934 - CStringGetDatum("")); -
935 - } -
936 - -
937 - /* build column number array if it's a column-specific trigger */ -
938 - ncolumns = list_length(stmt->columns); -
939 - if (ncolumns == 0) -
940 - columns = NULL; -
941 - else -
942 - { -
943 - ListCell *cell; -
944 - int i = 0; -
945 - -
946 - columns = (int16 *) palloc(ncolumns * sizeof(int16)); -
947 - foreach(cell, stmt->columns) -
948 - { -
949 - char *name = strVal(lfirst(cell)); -
950 - int16 attnum; -
951 - int j; -
952 - -
953 - /* Lookup column name. System columns are not allowed */ -
954 - attnum = attnameAttNum(rel, name, false); -
955 - if (attnum == InvalidAttrNumber) -
956 - ereport(ERROR, -
957 - (errcode(ERRCODE_UNDEFINED_COLUMN), -
958 - errmsg("column \"%s\" of relation \"%s\" does not exist", -
959 - name, RelationGetRelationName(rel)))); -
960 - -
961 - /* Check for duplicates */ -
962 - for (j = i - 1; j >= 0; j--) -
963 - { -
964 - if (columns[j] == attnum) -
965 - ereport(ERROR, -
966 - (errcode(ERRCODE_DUPLICATE_COLUMN), -
967 - errmsg("column \"%s\" specified more than once", -
968 - name))); -
969 - } -
970 - -
971 - columns[i++] = attnum; -
972 - } -
973 - } -
974 - tgattr = buildint2vector(columns, ncolumns); -
975 - values[Anum_pg_trigger_tgattr - 1] = PointerGetDatum(tgattr); -
976 - -
977 - /* set tgqual if trigger has WHEN clause */ -
978 - if (qual) -
979 - values[Anum_pg_trigger_tgqual - 1] = CStringGetTextDatum(qual); -
980 - else -
981 - nulls[Anum_pg_trigger_tgqual - 1] = true; -
982 - -
983 - if (oldtablename) -
984 - values[Anum_pg_trigger_tgoldtable - 1] = DirectFunctionCall1(namein, -
985 - CStringGetDatum(oldtablename)); -
986 - else -
987 - nulls[Anum_pg_trigger_tgoldtable - 1] = true; -
988 - if (newtablename) -
989 - values[Anum_pg_trigger_tgnewtable - 1] = DirectFunctionCall1(namein, -
990 - CStringGetDatum(newtablename)); -
991 - else -
992 - nulls[Anum_pg_trigger_tgnewtable - 1] = true; -
993 - -
994 - /* -
995 - * Insert or replace tuple in pg_trigger. -
996 - */ -
997 - if (!trigger_exists) -
998 - { -
999 - tuple = heap_form_tuple(tgrel->rd_att, values, nulls); -
1000 - CatalogTupleInsert(tgrel, tuple); -
1001 - } -
1002 - else -
1003 - { -
1004 - HeapTuple newtup; -
1005 - -
1006 - newtup = heap_form_tuple(tgrel->rd_att, values, nulls); -
1007 - CatalogTupleUpdate(tgrel, &tuple->t_self, newtup); -
1008 - heap_freetuple(newtup); -
1009 - } -
1010 - -
1011 - heap_freetuple(tuple); /* free either original or new tuple */ -
1012 - table_close(tgrel, RowExclusiveLock); -
1013 - -
1014 - pfree(DatumGetPointer(values[Anum_pg_trigger_tgname - 1])); -
1015 - pfree(DatumGetPointer(values[Anum_pg_trigger_tgargs - 1])); -
1016 - pfree(DatumGetPointer(values[Anum_pg_trigger_tgattr - 1])); -
1017 - if (oldtablename) -
1018 - pfree(DatumGetPointer(values[Anum_pg_trigger_tgoldtable - 1])); -
1019 - if (newtablename) -
1020 - pfree(DatumGetPointer(values[Anum_pg_trigger_tgnewtable - 1])); -
1021 - -
1022 - /* -
1023 - * Update relation's pg_class entry; if necessary; and if not, send an SI -
1024 - * message to make other backends (and this one) rebuild relcache entries. -
1025 - */ -
1026 - pgrel = table_open(RelationRelationId, RowExclusiveLock); -
1027 - tuple = SearchSysCacheCopy1(RELOID, -
1028 - ObjectIdGetDatum(RelationGetRelid(rel))); -
1029 - if (!HeapTupleIsValid(tuple)) -
1030 - elog(ERROR, "cache lookup failed for relation %u", -
1031 - RelationGetRelid(rel)); -
1032 - if (!((Form_pg_class) GETSTRUCT(tuple))->relhastriggers) -
1033 - { -
1034 - ((Form_pg_class) GETSTRUCT(tuple))->relhastriggers = true; -
1035 - -
1036 - CatalogTupleUpdate(pgrel, &tuple->t_self, tuple); -
1037 - -
1038 - CommandCounterIncrement(); -
1039 - } -
1040 - else -
1041 - CacheInvalidateRelcacheByTuple(tuple); -
1042 - -
1043 - heap_freetuple(tuple); -
1044 - table_close(pgrel, RowExclusiveLock); -
1045 - -
1046 - /* -
1047 - * If we're replacing a trigger, flush all the old dependencies before -
1048 - * recording new ones. -
1049 - */ -
1050 - if (trigger_exists) -
1051 - deleteDependencyRecordsFor(TriggerRelationId, trigoid, true); -
1052 - -
1053 - /* -
1054 - * Record dependencies for trigger. Always place a normal dependency on -
1055 - * the function. -
1056 - */ -
1057 - myself.classId = TriggerRelationId; -
1058 - myself.objectId = trigoid; -
1059 - myself.objectSubId = 0; -
1060 - -
1061 - referenced.classId = ProcedureRelationId; -
1062 - referenced.objectId = funcoid; -
1063 - referenced.objectSubId = 0; -
1064 - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); -
1065 - -
1066 - if (isInternal && OidIsValid(constraintOid)) -
1067 - { -
1068 - /* -
1069 - * Internally-generated trigger for a constraint, so make it an -
1070 - * internal dependency of the constraint. We can skip depending on -
1071 - * the relation(s), as there'll be an indirect dependency via the -
1072 - * constraint. -
1073 - */ -
1074 - referenced.classId = ConstraintRelationId; -
1075 - referenced.objectId = constraintOid; -
1076 - referenced.objectSubId = 0; -
1077 - recordDependencyOn(&myself, &referenced, DEPENDENCY_INTERNAL); -
1078 - } -
1079 - else -
1080 - { -
1081 - /* -
1082 - * User CREATE TRIGGER, so place dependencies. We make trigger be -
1083 - * auto-dropped if its relation is dropped or if the FK relation is -
1084 - * dropped. (Auto drop is compatible with our pre-7.3 behavior.) -
1085 - */ -
1086 - referenced.classId = RelationRelationId; -
1087 - referenced.objectId = RelationGetRelid(rel); -
1088 - referenced.objectSubId = 0; -
1089 - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); -
1090 - -
1091 - if (OidIsValid(constrrelid)) -
1092 - { -
1093 - referenced.classId = RelationRelationId; -
1094 - referenced.objectId = constrrelid; -
1095 - referenced.objectSubId = 0; -
1096 - recordDependencyOn(&myself, &referenced, DEPENDENCY_AUTO); -
1097 - } -
1098 - /* Not possible to have an index dependency in this case */ -
1099 - Assert(!OidIsValid(indexOid)); -
1100 - -
1101 - /* -
1102 - * If it's a user-specified constraint trigger, make the constraint -
1103 - * internally dependent on the trigger instead of vice versa. -
1104 - */ -
1105 - if (OidIsValid(constraintOid)) -
1106 - { -
1107 - referenced.classId = ConstraintRelationId; -
1108 - referenced.objectId = constraintOid; -
1109 - referenced.objectSubId = 0; -
1110 - recordDependencyOn(&referenced, &myself, DEPENDENCY_INTERNAL); -
1111 - } -
1112 - -
1113 - /* -
1114 - * If it's a partition trigger, create the partition dependencies. -
1115 - */ -
1116 - if (OidIsValid(parentTriggerOid)) -
1117 - { -
1118 - ObjectAddressSet(referenced, TriggerRelationId, parentTriggerOid); -
1119 - recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_PRI); -
1120 - ObjectAddressSet(referenced, RelationRelationId, RelationGetRelid(rel)); -
1121 - recordDependencyOn(&myself, &referenced, DEPENDENCY_PARTITION_SEC); -
1122 - } -
1123 - } -
1124 - -
1125 - /* If column-specific trigger, add normal dependencies on columns */ -
1126 - if (columns != NULL) -
1127 - { -
1128 - int i; -
1129 - -
1130 - referenced.classId = RelationRelationId; -
1131 - referenced.objectId = RelationGetRelid(rel); -
1132 - for (i = 0; i < ncolumns; i++) -
1133 - { -
1134 - referenced.objectSubId = columns[i]; -
1135 - recordDependencyOn(&myself, &referenced, DEPENDENCY_NORMAL); -
1136 - } -
1137 - } -
1138 - -
1139 - /* -
1140 - * If it has a WHEN clause, add dependencies on objects mentioned in the -
1141 - * expression (eg, functions, as well as any columns used). -
1142 - */ -
1143 - if (whenRtable != NIL) -
1144 - recordDependencyOnExpr(&myself, whenClause, whenRtable, -
1145 - DEPENDENCY_NORMAL); -
1146 - -
1147 - /* Post creation hook for new trigger */ -
1148 - InvokeObjectPostCreateHookArg(TriggerRelationId, trigoid, 0, -
1149 - isInternal); -
1150 - -
1151 - /* -
1152 - * Lastly, create the trigger on child relations, if needed. -
1153 - */ -
1154 - if (partition_recurse) -
1155 - { -
1156 - PartitionDesc partdesc = RelationGetPartitionDesc(rel, true); -
1157 - int i; -
1158 - MemoryContext oldcxt, -
1159 - perChildCxt; -
1160 - -
1161 - perChildCxt = AllocSetContextCreate(CurrentMemoryContext, -
1162 - "part trig clone", -
1163 - ALLOCSET_SMALL_SIZES); -
1164 - -
1165 - /* -
1166 - * We don't currently expect to be called with a valid indexOid. If -
1167 - * that ever changes then we'll need to write code here to find the -
1168 - * corresponding child index. -
1169 - */ -
1170 - Assert(!OidIsValid(indexOid)); -
1171 - -
1172 - oldcxt = MemoryContextSwitchTo(perChildCxt); -
1173 - -
1174 - /* Iterate to create the trigger on each existing partition */ -
1175 - for (i = 0; i < partdesc->nparts; i++) -
1176 - { -
1177 - CreateTrigStmt *childStmt; -
1178 - Relation childTbl; -
1179 - Node *qual; -
1180 - -
1181 - childTbl = table_open(partdesc->oids[i], ShareRowExclusiveLock); -
1182 - -
1183 - /* -
1184 - * Initialize our fabricated parse node by copying the original -
1185 - * one, then resetting fields that we pass separately. -
1186 - */ -
1187 - childStmt = copyObject(stmt); -
1188 - childStmt->funcname = NIL; -
1189 - childStmt->whenClause = NULL; -
1190 - -
1191 - /* If there is a WHEN clause, create a modified copy of it */ -
1192 - qual = copyObject(whenClause); -
1193 - qual = (Node *) -
1194 - map_partition_varattnos((List *) qual, PRS2_OLD_VARNO, -
1195 - childTbl, rel); -
1196 - qual = (Node *) -
1197 - map_partition_varattnos((List *) qual, PRS2_NEW_VARNO, -
1198 - childTbl, rel); -
1199 - -
1200 - CreateTriggerFiringOn(childStmt, queryString, -
1201 - partdesc->oids[i], refRelOid, -
1202 - InvalidOid, InvalidOid, -
1203 - funcoid, trigoid, qual, -
1204 - isInternal, true, trigger_fires_when); -
1205 - -
1206 - table_close(childTbl, NoLock); -
1207 - -
1208 - MemoryContextReset(perChildCxt); -
1209 - } -
1210 - -
1211 - MemoryContextSwitchTo(oldcxt); -
1212 - MemoryContextDelete(perChildCxt); -
1213 - } -
1214 - -
1215 - /* Keep lock on target rel until end of xact */ -
1216 - table_close(rel, NoLock); -
1217 - -
1218 - /* Add any requested comment */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
1219 13799 if (stmt->trigcomment != NULL) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
1220 5 CreateComments(trigoid, TriggerRelationId, 0, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
1221 5 stmt->trigcomment); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
1222 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
1223 - return myself; -
1224 - } -
generateClonedTriggerStmt() lines 6925-7118
Modified Lines Coverage: 80/83 lines (96.4%)
LineHitsSourceCommit
6925 140 generateClonedTriggerStmt(RangeVar *heapRel, Oid source_trigid, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6926 - Relation source_rel, const AttrMap *attmap) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6927 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6928 - HeapTuple triggerTuple; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6929 - HeapTuple proctup; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6930 - Form_pg_trigger trigForm; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6931 - Form_pg_proc procform; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6932 - Relation pg_trigger; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6933 140 RangeVar *constrrel = NULL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6934 - SysScanDesc tgscan; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6935 - ScanKeyData skey[1]; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6936 - Datum value; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6937 - bool isnull; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6938 140 Node *qual = NULL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6939 140 List *trigargs = NIL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6940 140 List *cols = NIL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6941 140 List *funcname = NIL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6942 140 List *transitionRels = NIL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6943 - char *funcschema; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6944 - CreateTrigStmt *trigStmt; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6945 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6946 140 pg_trigger = table_open(TriggerRelationId, AccessShareLock); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6947 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6948 - /* Find the trigger to copy */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6949 140 ScanKeyInit(&skey[0], e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6950 - Anum_pg_trigger_oid, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6951 - BTEqualStrategyNumber, F_OIDEQ, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6952 - ObjectIdGetDatum(source_trigid)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6953 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6954 140 tgscan = systable_beginscan(pg_trigger, TriggerOidIndexId, true, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6955 - NULL, 1, skey); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6956 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6957 140 triggerTuple = systable_getnext(tgscan); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6958 140 if (!HeapTupleIsValid(triggerTuple)) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6959 0 elog(ERROR, "could not find tuple for trigger %u", source_trigid); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6960 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6961 140 trigForm = (Form_pg_trigger) GETSTRUCT(triggerTuple); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6962 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6963 - /* Reconstruct trigger function String list */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6964 140 proctup = SearchSysCache1(PROCOID, ObjectIdGetDatum(trigForm->tgfoid)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6965 140 if (!HeapTupleIsValid(proctup)) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6966 0 elog(ERROR, "cache lookup failed for function %u", trigForm->tgfoid); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6967 140 procform = (Form_pg_proc) GETSTRUCT(proctup); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6968 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6969 140 funcschema = get_namespace_name(procform->pronamespace); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6970 140 funcname = list_make2(makeString(funcschema), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6971 - makeString(pstrdup(NameStr(procform->proname)))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6972 140 ReleaseSysCache(proctup); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6973 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6974 - /* e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6975 - * If there is a column list, transform it to a list of column names. Note e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6976 - * we don't need to map this list in any way ... e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6977 - */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6978 140 if (trigForm->tgattr.dim1 > 0) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6979 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6980 80 for (int i = 0; i < trigForm->tgattr.dim1; i++) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6981 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6982 - Form_pg_attribute col; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6983 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6984 45 col = TupleDescAttr(RelationGetDescr(source_rel), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6985 45 trigForm->tgattr.values[i] - 1); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6986 45 cols = lappend(cols, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6987 45 makeString(pstrdup(NameStr(col->attname)))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6988 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6989 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6990 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6991 - /* Reconstruct trigger arguments list */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6992 140 if (trigForm->tgnargs > 0) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6993 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6994 - bytea *val; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6995 - char *p; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6996 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6997 110 val = DatumGetByteaPP(fastgetattr(triggerTuple, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6998 - Anum_pg_trigger_tgargs, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
6999 - RelationGetDescr(pg_trigger), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7000 - &isnull)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7001 110 if (isnull) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7002 0 elog(ERROR, "tgargs is null in trigger \"%s\" for relation \"%s\"", e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7003 - NameStr(trigForm->tgname), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7004 - RelationGetRelationName(source_rel)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7005 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7006 110 p = (char *) VARDATA_ANY(val); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7007 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7008 220 for (int i = 0; i < trigForm->tgnargs; i++) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7009 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7010 110 trigargs = lappend(trigargs, makeString(pstrdup(p))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7011 110 p += strlen(p) + 1; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7012 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7013 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7014 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7015 - /* If the trigger has a WHEN qualification, add that */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7016 140 value = fastgetattr(triggerTuple, Anum_pg_trigger_tgqual, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7017 - RelationGetDescr(pg_trigger), &isnull); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7018 140 if (!isnull) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7019 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7020 - bool found_whole_row; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7021 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7022 30 qual = stringToNode(TextDatumGetCString(value)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7023 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7024 - /* Adjust Vars to match new table's column numbering */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7025 30 qual = map_variable_attnos(qual, PRS2_NEW_VARNO, 0, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7026 - attmap, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7027 - InvalidOid, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7028 - &found_whole_row); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7029 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7030 - /* As in expandTableLikeClause, reject whole-row variables */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7031 30 if (found_whole_row) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7032 5 ereport(ERROR, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7033 - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7034 - errmsg("cannot convert whole-row table reference"), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7035 - errdetail("Trigger \"%s\" contains a whole-row table reference.", e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7036 - NameStr(trigForm->tgname))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7037 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7038 25 qual = map_variable_attnos(qual, PRS2_OLD_VARNO, 0, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7039 - attmap, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7040 - InvalidOid, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7041 - &found_whole_row); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7042 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7043 - /* As in expandTableLikeClause, reject whole-row variables */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7044 25 if (found_whole_row) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7045 5 ereport(ERROR, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7046 - errcode(ERRCODE_FEATURE_NOT_SUPPORTED), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7047 - errmsg("cannot convert whole-row table reference"), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7048 - errdetail("Trigger \"%s\" contains a whole-row table reference.", e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7049 - NameStr(trigForm->tgname))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7050 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7051 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7052 - /* Reconstruct trigger old transition table */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7053 130 value = fastgetattr(triggerTuple, Anum_pg_trigger_tgoldtable, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7054 - RelationGetDescr(pg_trigger), &isnull); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7055 130 if (!isnull) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7056 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7057 5 TriggerTransition *old = makeNode(TriggerTransition); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7058 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7059 5 old->isNew = false; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7060 5 old->name = pstrdup(NameStr(*DatumGetName(value))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7061 5 old->isTable = true; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7062 5 transitionRels = lappend(transitionRels, old); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7063 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7064 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7065 - /* Reconstruct trigger new transition table */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7066 130 value = fastgetattr(triggerTuple, Anum_pg_trigger_tgnewtable, e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7067 - RelationGetDescr(pg_trigger), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7068 - &isnull); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7069 130 if (!isnull) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7070 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7071 10 TriggerTransition *new = makeNode(TriggerTransition); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7072 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7073 10 new->isNew = true; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7074 10 new->name = pstrdup(NameStr(*DatumGetName(value))); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7075 10 new->isTable = true; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7076 10 transitionRels = lappend(transitionRels, new); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7077 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7078 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7079 - /* Reconstruct trigger constraint's FROM table */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7080 130 if (OidIsValid(trigForm->tgconstrrelid)) e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7081 - { e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7082 - /* e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7083 - * Acquire the AccessShareLock lock on tgconstrrelid now, as it will e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7084 - * be required later in CreateTriggerFiringOn. e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7085 - */ e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7086 5 LockRelationOid(trigForm->tgconstrrelid, AccessShareLock); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7087 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7088 - constrrel = e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7089 5 makeRangeVar(get_namespace_name(get_rel_namespace(trigForm->tgconstrrelid)), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7090 - get_rel_name(trigForm->tgconstrrelid), e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7091 - -1); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7092 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7093 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7094 130 trigStmt = makeNode(CreateTrigStmt); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7095 130 trigStmt->replace = false; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7096 130 trigStmt->tgenabled = trigForm->tgenabled; 39ae762CREATE TABLE LIKE INCLUDING TRIGGERS copies tgenabled
7097 130 trigStmt->isconstraint = OidIsValid(trigForm->tgconstraint); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7098 130 trigStmt->trigname = pstrdup(NameStr(trigForm->tgname)); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7099 130 trigStmt->relation = heapRel; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7100 130 trigStmt->funcname = funcname; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7101 130 trigStmt->args = trigargs; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7102 130 trigStmt->row = TRIGGER_FOR_ROW(trigForm->tgtype); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7103 130 trigStmt->timing = trigForm->tgtype & TRIGGER_TYPE_TIMING_MASK; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7104 130 trigStmt->events = trigForm->tgtype & TRIGGER_TYPE_EVENT_MASK; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7105 130 trigStmt->columns = cols; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7106 130 trigStmt->whenClause = qual; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7107 130 trigStmt->transitionRels = transitionRels; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7108 130 trigStmt->deferrable = trigForm->tgdeferrable; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7109 130 trigStmt->initdeferred = trigForm->tginitdeferred; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7110 130 trigStmt->constrrel = constrrel; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7111 130 trigStmt->trigcomment = NULL; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7112 130 trigStmt->transformed = true; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7113 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7114 130 systable_endscan(tgscan); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7115 130 table_close(pg_trigger, AccessShareLock); e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7116 - e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7117 130 return trigStmt; e73f551CREATE TABLE LIKE INCLUDING TRIGGERS
7118 - } e73f551CREATE TABLE LIKE INCLUDING TRIGGERS