forked from 2ndQuadrant/pglogical
-
Notifications
You must be signed in to change notification settings - Fork 0
/
pglogical_dependency.c
2149 lines (1931 loc) · 65.2 KB
/
pglogical_dependency.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/*-------------------------------------------------------------------------
*
* pglogical_dependency.c
* pglogical dependenct handling
*
* Most of the code here is taken from dependency.c as the dependency
* handling in postgres is sadly not extensible.
*
* See
*
* git diff REL_10_STABLE..REL_11_STABLE -- src/backend/catalog/dependency.c
*
* for version comparisons, but you can't apply directly as you must
* keep old-version compatibility.
*
* IDENTIFICATION
* pglogical_functions.c
*
*-------------------------------------------------------------------------
*/
#include "postgres.h"
#if PG_VERSION_NUM >= 120000
#include "access/heapam.h"
#endif
#include "access/htup_details.h"
#include "access/xact.h"
#include "catalog/dependency.h"
#include "catalog/heap.h"
#include "catalog/index.h"
#include "catalog/objectaccess.h"
#include "catalog/pg_am.h"
#include "catalog/pg_amop.h"
#include "catalog/pg_amproc.h"
#include "catalog/pg_attrdef.h"
#include "catalog/pg_authid.h"
#include "catalog/pg_cast.h"
#include "catalog/pg_collation.h"
#if PG_VERSION_NUM < 110000
#include "catalog/pg_collation_fn.h"
#endif
#include "catalog/pg_constraint.h"
#include "catalog/pg_conversion.h"
#if PG_VERSION_NUM < 110000
#include "catalog/pg_conversion_fn.h"
#endif
#include "catalog/pg_database.h"
#include "catalog/pg_default_acl.h"
#include "catalog/pg_event_trigger.h"
#include "catalog/pg_extension.h"
#include "catalog/pg_foreign_data_wrapper.h"
#include "catalog/pg_foreign_server.h"
#include "catalog/pg_language.h"
#include "catalog/pg_largeobject.h"
#include "catalog/pg_namespace.h"
#include "catalog/pg_opclass.h"
#include "catalog/pg_operator.h"
#include "catalog/pg_opfamily.h"
#include "catalog/pg_proc.h"
#include "catalog/pg_rewrite.h"
#include "catalog/pg_tablespace.h"
#include "catalog/pg_trigger.h"
#include "catalog/pg_ts_config.h"
#include "catalog/pg_ts_dict.h"
#include "catalog/pg_ts_parser.h"
#include "catalog/pg_ts_template.h"
#include "catalog/pg_type.h"
#include "catalog/pg_user_mapping.h"
#include "commands/comment.h"
#include "commands/defrem.h"
#include "commands/event_trigger.h"
#include "commands/extension.h"
#include "commands/proclang.h"
#include "commands/schemacmds.h"
#include "commands/seclabel.h"
#include "commands/trigger.h"
#include "commands/typecmds.h"
#include "nodes/nodeFuncs.h"
#include "parser/parsetree.h"
#include "rewrite/rewriteRemove.h"
#include "storage/lmgr.h"
#include "utils/fmgroids.h"
#include "utils/guc.h"
#include "utils/lsyscache.h"
#include "utils/memutils.h"
#include "utils/syscache.h"
#if PG_VERSION_NUM < 120000
#include "utils/tqual.h"
#endif
#include "pglogical_dependency.h"
#include "pglogical_sync.h"
#include "pglogical_repset.h"
#include "pglogical.h"
#define CATALOG_REPSET_RELATION "depend"
typedef struct FormData_pglogical_depend
{
/*
* Identification of the dependent (referencing) object.
*
* These fields are all zeroes for a DEPENDENCY_PIN entry.
*/
Oid classid; /* OID of table containing object */
Oid objid; /* OID of object itself */
int32 objsubid; /* column number, or 0 if not used */
/*
* Identification of the independent (referenced) object.
*/
Oid refclassid; /* OID of table containing object */
Oid refobjid; /* OID of object itself */
int32 refobjsubid; /* column number, or 0 if not used */
/*
* Precise semantics of the relationship are specified by the deptype
* field. See DependencyType in catalog/dependency.h.
*/
char deptype; /* see codes in dependency.h */
} FormData_pglogical_depend;
typedef FormData_pglogical_depend *Form_pglogical_depend;
#define Natts_pglogical_depend 7
#define Anum_pglogical_depend_classid 1
#define Anum_pglogical_depend_objid 2
#define Anum_pglogical_depend_objsubid 3
#define Anum_pglogical_depend_refclassid 4
#define Anum_pglogical_depend_refobjid 5
#define Anum_pglogical_depend_refobjsubid 6
#define Anum_pglogical_depend_deptype 7
static Oid
get_pglogical_depend_rel_oid(void);
/*
* Deletion processing requires additional state for each ObjectAddress that
* it's planning to delete. For simplicity and code-sharing we make the
* ObjectAddresses code support arrays with or without this extra state.
*/
typedef struct
{
int flags; /* bitmask, see bit definitions below */
ObjectAddress dependee; /* object whose deletion forced this one */
} ObjectAddressExtra;
/* ObjectAddressExtra flag bits */
#define DEPFLAG_ORIGINAL 0x0001 /* an original deletion target */
#define DEPFLAG_NORMAL 0x0002 /* reached via normal dependency */
#define DEPFLAG_AUTO 0x0004 /* reached via auto dependency */
#define DEPFLAG_INTERNAL 0x0008 /* reached via internal dependency */
#define DEPFLAG_EXTENSION 0x0010 /* reached via extension dependency */
#define DEPFLAG_REVERSE 0x0020 /* reverse internal/extension link */
/* expansible list of ObjectAddresses */
struct ObjectAddresses
{
ObjectAddress *refs; /* => palloc'd array */
ObjectAddressExtra *extras; /* => palloc'd array, or NULL if not used */
int numrefs; /* current number of references */
int maxrefs; /* current size of palloc'd array(s) */
};
/* typedef ObjectAddresses appears in dependency.h */
/* threaded list of ObjectAddresses, for recursion detection */
typedef struct ObjectAddressStack
{
const ObjectAddress *object; /* object being visited */
int flags; /* its current flag bits */
struct ObjectAddressStack *next; /* next outer stack level */
} ObjectAddressStack;
/* for find_expr_references_walker */
typedef struct
{
ObjectAddresses *addrs; /* addresses being accumulated */
List *rtables; /* list of rangetables to resolve Vars */
} find_expr_references_context;
/*
* This constant table maps ObjectClasses to the corresponding catalog OIDs.
* See also getObjectClass().
*/
static const Oid object_classes[] = {
RelationRelationId, /* OCLASS_CLASS */
ProcedureRelationId, /* OCLASS_PROC */
TypeRelationId, /* OCLASS_TYPE */
CastRelationId, /* OCLASS_CAST */
CollationRelationId, /* OCLASS_COLLATION */
ConstraintRelationId, /* OCLASS_CONSTRAINT */
ConversionRelationId, /* OCLASS_CONVERSION */
AttrDefaultRelationId, /* OCLASS_DEFAULT */
LanguageRelationId, /* OCLASS_LANGUAGE */
LargeObjectRelationId, /* OCLASS_LARGEOBJECT */
OperatorRelationId, /* OCLASS_OPERATOR */
OperatorClassRelationId, /* OCLASS_OPCLASS */
OperatorFamilyRelationId, /* OCLASS_OPFAMILY */
AccessMethodRelationId, /* OCLASS_AM */
AccessMethodOperatorRelationId, /* OCLASS_AMOP */
AccessMethodProcedureRelationId, /* OCLASS_AMPROC */
RewriteRelationId, /* OCLASS_REWRITE */
TriggerRelationId, /* OCLASS_TRIGGER */
NamespaceRelationId, /* OCLASS_SCHEMA */
TSParserRelationId, /* OCLASS_TSPARSER */
TSDictionaryRelationId, /* OCLASS_TSDICT */
TSTemplateRelationId, /* OCLASS_TSTEMPLATE */
TSConfigRelationId, /* OCLASS_TSCONFIG */
AuthIdRelationId, /* OCLASS_ROLE */
DatabaseRelationId, /* OCLASS_DATABASE */
TableSpaceRelationId, /* OCLASS_TBLSPACE */
ForeignDataWrapperRelationId, /* OCLASS_FDW */
ForeignServerRelationId, /* OCLASS_FOREIGN_SERVER */
UserMappingRelationId, /* OCLASS_USER_MAPPING */
DefaultAclRelationId, /* OCLASS_DEFACL */
ExtensionRelationId, /* OCLASS_EXTENSION */
EventTriggerRelationId, /* OCLASS_EVENT_TRIGGER */
// PolicyRelationId, /* OCLASS_POLICY */
// TransformRelationId /* OCLASS_TRANSFORM */
};
static void findDependentObjects(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack,
ObjectAddresses *targetObjects,
const ObjectAddresses *pendingObjects,
Relation *depRel);
static void reportDependentObjects(const ObjectAddresses *targetObjects,
DropBehavior behavior,
int msglevel,
const ObjectAddress *origObject);
static void PGLAcquireDeletionLock(const ObjectAddress *object, int flags);
static void PGLReleaseDeletionLock(const ObjectAddress *object);
static bool find_expr_references_walker(Node *node,
find_expr_references_context *context);
static void eliminate_duplicate_dependencies(ObjectAddresses *addrs);
static int object_address_comparator(const void *a, const void *b);
static void add_object_address(ObjectClass oclass, Oid objectId, int32 subId,
ObjectAddresses *addrs);
static void add_exact_object_address_extra(const ObjectAddress *object,
const ObjectAddressExtra *extra,
ObjectAddresses *addrs);
static bool object_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddresses *addrs);
static bool stack_address_present_add_flags(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack);
static void deleteOneObjectDepencencyRecord(const ObjectAddress *object, Relation *depRel);
static void deleteOneObject(const ObjectAddress *object, Relation *depRel);
static void doDeletion(const ObjectAddress *object);
static char *pglogical_getObjectDescription(const ObjectAddress *object);
/*
* Go through the objects given running the final actions on them, and execute
* the actual deletion.
*/
static void
deleteObjectsInList(ObjectAddresses *targetObjects, Relation *depRel)
{
int i;
/*
* Delete all the objects in the proper order.
*/
for (i = 0; i < targetObjects->numrefs; i++)
{
ObjectAddress *thisobj = targetObjects->refs + i;
deleteOneObject(thisobj, depRel);
}
}
/*
* Record a dependency between 2 objects via their respective objectAddress.
* The first argument is the dependent object, the second the one it
* references.
*
* This simply creates an entry in pglogical_depend, without any other processing.
*/
void
pglogical_recordDependencyOn(const ObjectAddress *depender,
const ObjectAddress *referenced,
DependencyType behavior)
{
pglogical_recordMultipleDependencies(depender, referenced, 1, behavior);
}
/*
* Record multiple dependencies (of the same kind) for a single dependent
* object. This has a little less overhead than recording each separately.
*/
void
pglogical_recordMultipleDependencies(const ObjectAddress *depender,
const ObjectAddress *referenced,
int nreferenced,
DependencyType behavior)
{
Relation dependDesc;
HeapTuple tup;
int i;
bool nulls[Natts_pglogical_depend];
Datum values[Natts_pglogical_depend];
if (nreferenced <= 0)
return; /* nothing to do */
dependDesc = table_open(get_pglogical_depend_rel_oid(),
RowExclusiveLock);
memset(nulls, false, sizeof(nulls));
for (i = 0; i < nreferenced; i++, referenced++)
{
/*
* Record the Dependency. Note we don't bother to check for
* duplicate dependencies; there's no harm in them.
*/
values[Anum_pglogical_depend_classid - 1] = ObjectIdGetDatum(depender->classId);
values[Anum_pglogical_depend_objid - 1] = ObjectIdGetDatum(depender->objectId);
values[Anum_pglogical_depend_objsubid - 1] = Int32GetDatum(depender->objectSubId);
values[Anum_pglogical_depend_refclassid - 1] = ObjectIdGetDatum(referenced->classId);
values[Anum_pglogical_depend_refobjid - 1] = ObjectIdGetDatum(referenced->objectId);
values[Anum_pglogical_depend_refobjsubid - 1] = Int32GetDatum(referenced->objectSubId);
values[Anum_pglogical_depend_deptype - 1] = CharGetDatum((char) behavior);
tup = heap_form_tuple(dependDesc->rd_att, values, nulls);
CatalogTupleInsert(dependDesc, tup);
heap_freetuple(tup);
}
table_close(dependDesc, RowExclusiveLock);
}
/*
* findDependentObjects - find all objects that depend on 'object'
*
* For every object that depends on the starting object, acquire a deletion
* lock on the object, add it to targetObjects (if not already there),
* and recursively find objects that depend on it. An object's dependencies
* will be placed into targetObjects before the object itself; this means
* that the finished list's order represents a safe deletion order.
*
* The caller must already have a deletion lock on 'object' itself,
* but must not have added it to targetObjects. (Note: there are corner
* cases where we won't add the object either, and will also release the
* caller-taken lock. This is a bit ugly, but the API is set up this way
* to allow easy rechecking of an object's liveness after we lock it. See
* notes within the function.)
*
* When dropping a whole object (subId = 0), we find dependencies for
* its sub-objects too.
*
* object: the object to add to targetObjects and find dependencies on
* flags: flags to be ORed into the object's targetObjects entry
* stack: list of objects being visited in current recursion; topmost item
* is the object that we recursed from (NULL for external callers)
* targetObjects: list of objects that are scheduled to be deleted
* pendingObjects: list of other objects slated for destruction, but
* not necessarily in targetObjects yet (can be NULL if none)
* *depRel: already opened pglogical_depend relation
*/
static void
findDependentObjects(const ObjectAddress *object,
int flags,
ObjectAddressStack *stack,
ObjectAddresses *targetObjects,
const ObjectAddresses *pendingObjects,
Relation *depRel)
{
ScanKeyData key[3];
int nkeys;
SysScanDesc scan;
HeapTuple tup;
ObjectAddress otherObject;
ObjectAddressStack mystack;
ObjectAddressExtra extra;
/*
* If the target object is already being visited in an outer recursion
* level, just report the current flags back to that level and exit. This
* is needed to avoid infinite recursion in the face of circular
* dependencies.
*
* The stack check alone would result in dependency loops being broken at
* an arbitrary point, ie, the first member object of the loop to be
* visited is the last one to be deleted. This is obviously unworkable.
* However, the check for internal dependency below guarantees that we
* will not break a loop at an internal dependency: if we enter the loop
* at an "owned" object we will switch and start at the "owning" object
* instead. We could probably hack something up to avoid breaking at an
* auto dependency, too, if we had to. However there are no known cases
* where that would be necessary.
*/
if (stack_address_present_add_flags(object, flags, stack))
return;
/*
* It's also possible that the target object has already been completely
* processed and put into targetObjects. If so, again we just add the
* specified flags to its entry and return.
*
* (Note: in these early-exit cases we could release the caller-taken
* lock, since the object is presumably now locked multiple times; but it
* seems not worth the cycles.)
*/
if (object_address_present_add_flags(object, flags, targetObjects))
return;
/*
* The target object might be internally dependent on some other object
* (its "owner"), and/or be a member of an extension (also considered its
* owner). If so, and if we aren't recursing from the owning object, we
* have to transform this deletion request into a deletion request of the
* owning object. (We'll eventually recurse back to this object, but the
* owning object has to be visited first so it will be deleted after.) The
* way to find out about this is to scan the pglogical_depend entries that show
* what this object depends on.
*/
ScanKeyInit(&key[0],
Anum_pglogical_depend_classid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyInit(&key[1],
Anum_pglogical_depend_objid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyInit(&key[2],
Anum_pglogical_depend_objsubid,
BTEqualStrategyNumber, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(*depRel, InvalidOid, false,
NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pglogical_depend foundDep = (Form_pglogical_depend) GETSTRUCT(tup);
otherObject.classId = foundDep->refclassid;
otherObject.objectId = foundDep->refobjid;
otherObject.objectSubId = foundDep->refobjsubid;
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
case DEPENDENCY_AUTO:
#if PG_VERSION_NUM >= 90600
case DEPENDENCY_AUTO_EXTENSION:
#endif
/* no problem */
break;
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
case DEPENDENCY_INTERNAL_AUTO:
#endif
case DEPENDENCY_INTERNAL:
case DEPENDENCY_EXTENSION:
/*
* This object is part of the internal implementation of
* another object, or is part of the extension that is the
* other object. We have three cases:
*
* 1. At the outermost recursion level, we normally disallow
* the DROP. (We just ereport here, rather than proceeding,
* since no other dependencies are likely to be interesting.)
* However, there are exceptions.
*/
if (stack == NULL)
{
char *otherObjDesc;
/*
* Exception 1a: if the owning object is listed in
* pendingObjects, just release the caller's lock and
* return. We'll eventually complete the DROP when we
* reach that entry in the pending list.
*/
if (pendingObjects &&
object_address_present(&otherObject, pendingObjects))
{
systable_endscan(scan);
/* need to release caller's lock; see notes below */
PGLReleaseDeletionLock(object);
return;
}
/*
* Exception 1b: if the owning object is the extension
* currently being created/altered, it's okay to continue
* with the deletion. This allows dropping of an
* extension's objects within the extension's scripts, as
* well as corner cases such as dropping a transient
* object created within such a script.
*
* Note that pglogical currently does not care about
* extension dependencies and CurrentExtensionObject is
* not PGDLLIMPORTed so we relax this and just skip any
* extension dependencies.
*/
if (creating_extension &&
otherObject.classId == ExtensionRelationId /*&&
otherObject.objectId == CurrentExtensionObject*/)
break;
/* No exception applies, so throw the error */
otherObjDesc = pglogical_getObjectDescription(&otherObject);
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because %s requires it",
pglogical_getObjectDescription(object),
otherObjDesc),
errhint("You can drop %s instead.",
otherObjDesc)));
}
/*
* 2. When recursing from the other end of this dependency,
* it's okay to continue with the deletion. This holds when
* recursing from a whole object that includes the nominal
* other end as a component, too. Since there can be more
* than one "owning" object, we have to allow matches that are
* more than one level down in the stack.
*/
if (stack_address_present_add_flags(&otherObject, 0, stack))
break;
/*
* 3. Not all the owning objects have been visited, so
* transform this deletion request into a delete of this
* owning object.
*
* For INTERNAL_AUTO dependencies, we don't enforce this; in
* other words, we don't follow the links back to the owning
* object.
*/
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
if (foundDep->deptype == DEPENDENCY_INTERNAL_AUTO)
break;
#endif
/*
* First, release caller's lock on this object and get
* deletion lock on the owning object. (We must release
* caller's lock to avoid deadlock against a concurrent
* deletion of the owning object.)
*/
PGLReleaseDeletionLock(object);
PGLAcquireDeletionLock(&otherObject, 0);
/*
* The owning object might have been deleted while we waited
* to lock it; if so, neither it nor the current object are
* interesting anymore. We test this by checking the
* pglogical_depend entry (see notes below).
*/
if (!systable_recheck_tuple(scan, tup))
{
systable_endscan(scan);
PGLReleaseDeletionLock(&otherObject);
return;
}
/*
* Okay, recurse to the owning object instead of proceeding.
*
* We do not need to stack the current object; we want the
* traversal order to be as if the original reference had
* linked to the owning object instead of this one.
*
* The dependency type is a "reverse" dependency: we need to
* delete the owning object if this one is to be deleted, but
* this linkage is never a reason for an automatic deletion.
*/
findDependentObjects(&otherObject,
DEPFLAG_REVERSE,
stack,
targetObjects,
pendingObjects,
depRel);
/* And we're done here. */
systable_endscan(scan);
return;
case DEPENDENCY_PIN:
/*
* Should not happen; PIN dependencies should have zeroes in
* the depender fields...
*/
elog(ERROR, "incorrect use of PIN dependency with %s",
pglogical_getObjectDescription(object));
break;
default:
elog(ERROR, "unrecognized dependency type '%c' for %s",
foundDep->deptype, pglogical_getObjectDescription(object));
break;
}
}
systable_endscan(scan);
/*
* Now recurse to any dependent objects. We must visit them first since
* they have to be deleted before the current object.
*/
mystack.object = object; /* set up a new stack level */
mystack.flags = flags;
mystack.next = stack;
ScanKeyInit(&key[0],
Anum_pglogical_depend_refclassid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->classId));
ScanKeyInit(&key[1],
Anum_pglogical_depend_refobjid,
BTEqualStrategyNumber, F_OIDEQ,
ObjectIdGetDatum(object->objectId));
if (object->objectSubId != 0)
{
ScanKeyInit(&key[2],
Anum_pglogical_depend_refobjsubid,
BTEqualStrategyNumber, F_INT4EQ,
Int32GetDatum(object->objectSubId));
nkeys = 3;
}
else
nkeys = 2;
scan = systable_beginscan(*depRel, InvalidOid, false,
NULL, nkeys, key);
while (HeapTupleIsValid(tup = systable_getnext(scan)))
{
Form_pglogical_depend foundDep = (Form_pglogical_depend) GETSTRUCT(tup);
int subflags;
otherObject.classId = foundDep->classid;
otherObject.objectId = foundDep->objid;
otherObject.objectSubId = foundDep->objsubid;
/*
* Must lock the dependent object before recursing to it.
*/
PGLAcquireDeletionLock(&otherObject, 0);
/*
* The dependent object might have been deleted while we waited to
* lock it; if so, we don't need to do anything more with it. We can
* test this cheaply and independently of the object's type by seeing
* if the pglogical_depend tuple we are looking at is still live. (If the
* object got deleted, the tuple would have been deleted too.)
*/
if (!systable_recheck_tuple(scan, tup))
{
/* release the now-useless lock */
PGLReleaseDeletionLock(&otherObject);
/* and continue scanning for dependencies */
continue;
}
/* Recurse, passing flags indicating the dependency type */
switch (foundDep->deptype)
{
case DEPENDENCY_NORMAL:
subflags = DEPFLAG_NORMAL;
break;
case DEPENDENCY_AUTO:
#if PG_VERSION_NUM >= 90600
case DEPENDENCY_AUTO_EXTENSION:
subflags = DEPFLAG_AUTO;
break;
#endif
case DEPENDENCY_INTERNAL:
#if PG_VERSION_NUM >= 110000 && PG_VERSION_NUM < 120000
case DEPENDENCY_INTERNAL_AUTO:
#endif
subflags = DEPFLAG_INTERNAL;
break;
case DEPENDENCY_EXTENSION:
subflags = DEPFLAG_EXTENSION;
break;
case DEPENDENCY_PIN:
/*
* For a PIN dependency we just ereport immediately; there
* won't be any others to report.
*/
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because it is required by the database system",
pglogical_getObjectDescription(object))));
subflags = 0; /* keep compiler quiet */
break;
default:
elog(ERROR, "unrecognized dependency type '%c' for %s",
foundDep->deptype, pglogical_getObjectDescription(object));
subflags = 0; /* keep compiler quiet */
break;
}
findDependentObjects(&otherObject,
subflags,
&mystack,
targetObjects,
pendingObjects,
depRel);
}
systable_endscan(scan);
/*
* Finally, we can add the target object to targetObjects. Be careful to
* include any flags that were passed back down to us from inner recursion
* levels.
*/
extra.flags = mystack.flags;
if (stack)
extra.dependee = *stack->object;
else
memset(&extra.dependee, 0, sizeof(extra.dependee));
add_exact_object_address_extra(object, &extra, targetObjects);
}
/*
* reportDependentObjects - report about dependencies, and fail if RESTRICT
*
* Tell the user about dependent objects that we are going to delete
* (or would need to delete, but are prevented by RESTRICT mode);
* then error out if there are any and it's not CASCADE mode.
*
* targetObjects: list of objects that are scheduled to be deleted
* behavior: RESTRICT or CASCADE
* msglevel: elog level for non-error report messages
* origObject: base object of deletion, or NULL if not available
* (the latter case occurs in DROP OWNED)
*/
static void
reportDependentObjects(const ObjectAddresses *targetObjects,
DropBehavior behavior,
int msglevel,
const ObjectAddress *origObject)
{
bool ok = true;
StringInfoData clientdetail;
StringInfoData logdetail;
int numReportedClient = 0;
int numNotReportedClient = 0;
int i;
int my_client_min_messages;
int my_log_min_messages;
/*
* This is cludge for Windows (Postgres des not define the GUC variables
* as PGDDLIMPORT)
*/
my_client_min_messages = atoi(GetConfigOptionByName("client_min_messages",
NULL, false));
my_log_min_messages = atoi(GetConfigOptionByName("log_min_messages",
NULL, false));
/*
* If no error is to be thrown, and the msglevel is too low to be shown to
* either client or server log, there's no need to do any of the work.
*
* Note: this code doesn't know all there is to be known about elog
* levels, but it works for NOTICE and DEBUG2, which are the only values
* msglevel can currently have. We also assume we are running in a normal
* operating environment.
*/
if (behavior == DROP_CASCADE &&
msglevel < my_client_min_messages &&
(msglevel < my_log_min_messages || my_log_min_messages == LOG))
return;
/*
* We limit the number of dependencies reported to the client to
* MAX_REPORTED_DEPS, since client software may not deal well with
* enormous error strings. The server log always gets a full report.
*/
#define MAX_REPORTED_DEPS 100
initStringInfo(&clientdetail);
initStringInfo(&logdetail);
/*
* We process the list back to front (ie, in dependency order not deletion
* order), since this makes for a more understandable display.
*/
for (i = targetObjects->numrefs - 1; i >= 0; i--)
{
const ObjectAddress *obj = &targetObjects->refs[i];
const ObjectAddressExtra *extra = &targetObjects->extras[i];
char *objDesc;
/* Ignore the original deletion target(s) */
if (extra->flags & DEPFLAG_ORIGINAL)
continue;
objDesc = pglogical_getObjectDescription(obj);
/*
* If, at any stage of the recursive search, we reached the object via
* an AUTO, INTERNAL, or EXTENSION dependency, then it's okay to
* delete it even in RESTRICT mode.
*/
if (extra->flags & (DEPFLAG_AUTO |
DEPFLAG_INTERNAL |
DEPFLAG_EXTENSION))
{
/*
* auto-cascades are reported at DEBUG2, not msglevel. We don't
* try to combine them with the regular message because the
* results are too confusing when client_min_messages and
* log_min_messages are different.
*/
ereport(DEBUG2,
(errmsg("drop auto-cascades to %s",
objDesc)));
}
else if (behavior == DROP_RESTRICT)
{
char *otherDesc = pglogical_getObjectDescription(&extra->dependee);
if (numReportedClient < MAX_REPORTED_DEPS)
{
/* separate entries with a newline */
if (clientdetail.len != 0)
appendStringInfoChar(&clientdetail, '\n');
appendStringInfo(&clientdetail, _("%s depends on %s"),
objDesc, otherDesc);
numReportedClient++;
}
else
numNotReportedClient++;
/* separate entries with a newline */
if (logdetail.len != 0)
appendStringInfoChar(&logdetail, '\n');
appendStringInfo(&logdetail, _("%s depends on %s"),
objDesc, otherDesc);
pfree(otherDesc);
ok = false;
}
else
{
if (numReportedClient < MAX_REPORTED_DEPS)
{
/* separate entries with a newline */
if (clientdetail.len != 0)
appendStringInfoChar(&clientdetail, '\n');
appendStringInfo(&clientdetail, _("drop cascades to %s"),
objDesc);
numReportedClient++;
}
else
numNotReportedClient++;
/* separate entries with a newline */
if (logdetail.len != 0)
appendStringInfoChar(&logdetail, '\n');
appendStringInfo(&logdetail, _("drop cascades to %s"),
objDesc);
}
pfree(objDesc);
}
if (numNotReportedClient > 0)
appendStringInfo(&clientdetail, ngettext("\nand %d other object "
"(see server log for list)",
"\nand %d other objects "
"(see server log for list)",
numNotReportedClient),
numNotReportedClient);
if (!ok)
{
if (origObject)
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop %s because other objects depend on it",
pglogical_getObjectDescription(origObject)),
errdetail("%s", clientdetail.data),
errdetail_log("%s", logdetail.data),
errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
else
ereport(ERROR,
(errcode(ERRCODE_DEPENDENT_OBJECTS_STILL_EXIST),
errmsg("cannot drop desired object(s) because other objects depend on them"),
errdetail("%s", clientdetail.data),
errdetail_log("%s", logdetail.data),
errhint("Use DROP ... CASCADE to drop the dependent objects too.")));
}
else if (numReportedClient > 1)
{
ereport(msglevel,
/* translator: %d always has a value larger than 1 */
(errmsg_plural("drop cascades to %d other object",
"drop cascades to %d other objects",
numReportedClient + numNotReportedClient,
numReportedClient + numNotReportedClient),
errdetail("%s", clientdetail.data),
errdetail_log("%s", logdetail.data)));
}
else if (numReportedClient == 1)
{
/* we just use the single item as-is */
ereport(msglevel,
(errmsg_internal("%s", clientdetail.data)));
}
pfree(clientdetail.data);
pfree(logdetail.data);
}
/*
* PGLAcquireDeletionLock - acquire a suitable lock for deleting an object
*
* We use LockRelation for relations, LockDatabaseObject for everything
* else. Note that dependency.c is not concerned with deleting any kind of
* shared-across-databases object, so we have no need for LockSharedObject.
*/
static void
PGLAcquireDeletionLock(const ObjectAddress *object, int flags)
{
if (object->classId == RelationRelationId)
{
/*
* In DROP INDEX CONCURRENTLY, take only ShareUpdateExclusiveLock on
* the index for the moment. index_drop() will promote the lock once
* it's safe to do so. In all other cases we need full exclusive
* lock.
*/
if (flags & PERFORM_DELETION_CONCURRENTLY)
LockRelationOid(object->objectId, ShareUpdateExclusiveLock);
else
LockRelationOid(object->objectId, AccessExclusiveLock);
}
else
{
/* assume we should lock the whole object not a sub-object */
LockDatabaseObject(object->classId, object->objectId, 0,
AccessExclusiveLock);
}
}
/*
* PGLReleaseDeletionLock - release an object deletion lock
*/
static void
PGLReleaseDeletionLock(const ObjectAddress *object)
{
if (object->classId == RelationRelationId)
UnlockRelationOid(object->objectId, AccessExclusiveLock);
else
/* assume we should lock the whole object not a sub-object */
UnlockDatabaseObject(object->classId, object->objectId, 0,
AccessExclusiveLock);
}
/*
* recordDependencyOnSingleRelExpr - find expression dependencies
*
* As above, but only one relation is expected to be referenced (with
* varno = 1 and varlevelsup = 0). Pass the relation OID instead of a
* range table. An additional frammish is that dependencies on that
* relation (or its component columns) will be marked with 'self_behavior',
* whereas 'behavior' is used for everything else.
*
* NOTE: the caller should ensure that a whole-table dependency on the
* specified relation is created separately, if one is needed. In particular,
* a whole-row Var "relation.*" will not cause this routine to emit any
* dependency item. This is appropriate behavior for subexpressions of an
* ordinary query, so other cases need to cope as necessary.
*/
void
pglogical_recordDependencyOnSingleRelExpr(const ObjectAddress *depender,
Node *expr, Oid relId,
DependencyType behavior,
DependencyType self_behavior)
{
find_expr_references_context context;
RangeTblEntry rte;
context.addrs = new_object_addresses();