-
Notifications
You must be signed in to change notification settings - Fork 15
/
check_depend.c
833 lines (738 loc) · 24.3 KB
/
check_depend.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
/*-------------------------------------------------------------------------
*
* check_depend.c
*
* A number of PostgreSQL system catalogs store references to SQL objects
* of arbitrary type by recording a class ID (the OID of the system
* catalog that contains the referenced object) and an object ID (the OID
* of the referenced object within that catalog). In cases where the
* referenced object may be a table column, there is also a sub-ID;
* when the referenced object is a table column, (class ID, sub-ID) should
* match the pg_attribute row's (attrelid, attnum). In all other cases,
* the sub-ID should be zero.
*
* The code in this file aims to validate the class ID, object ID, and
* sub-ID. There is some duplication in the code structure, because to
* check the object ID, we must validate the class ID and look up the
* corresponding table. However, we try hard not to complain about what
* is in essence the same problem more than once, and to complain about
* it with respect to the correct column.
*
* The name of this file comes from the fact that the classic example of
* the class ID/object ID/sub-ID notation is in the pg_depend catalog,
* but we actually use this code to validate other tables that use a
* similar convention, such as pg_description.
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "pg_catcheck.h"
typedef enum
{
DEPEND_COLUMN_STYLE_OBJID, /* pg_(sh)depend, referring side */
DEPEND_COLUMN_STYLE_REFOBJID, /* pg_(sh)depend, referenced side */
DEPEND_COLUMN_STYLE_OBJOID, /* pg_(sh)description, pg_(sh)seclabel */
EDB_DDLTIME_COLUMN_STYLE_OBJID /* edb_last_ddl_time(_shared) */
} depend_column_style;
typedef struct check_depend_cache
{
depend_column_style style;
bool is_broken;
int database_result_column;
int class_result_column;
int object_result_column;
int deptype_result_column;
pgrhash *duplicate_owner_ht;
} check_depend_cache;
typedef struct class_id_mapping_type
{
char *oid;
pg_catalog_table *tab;
} class_id_mapping_type;
/*
* EnterpriseDB versions prior to 9.4 are expected to have a number of
* dangling dependency entries, unless initialized with --no-redwood-compat.
* We avoid complaining about these because (1) they're known and basically
* harmless and (2) we don't want to give the misimpression of real
* corruption.
*/
typedef struct exception_list
{
char *table_name;
char *class;
char *object;
} exception_list;
exception_list edb84_exception_list[] = {
{"pg_depend", "1255", "877"},
{"pg_depend", "1255", "883"},
{"pg_depend", "1255", "1777"},
{"pg_depend", "1255", "1780"},
{"pg_depend", "1255", "2049"},
{"pg_depend", "2617", "2779"},
{"pg_depend", "2617", "2780"},
{NULL}
};
exception_list edb90_exception_list[] = {
{"pg_depend", "1255", "877"},
{"pg_depend", "1255", "883"},
{"pg_depend", "1255", "1777"},
{"pg_depend", "1255", "1780"},
{"pg_depend", "1255", "2049"},
{"pg_depend", "2617", "2779"},
{"pg_depend", "2617", "2780"},
{NULL}
};
exception_list edb91_92_exception_list[] = {
{"pg_depend", "1255", "877"},
{"pg_depend", "1255", "883"},
{"pg_depend", "1255", "1777"},
{"pg_depend", "1255", "1780"},
{"pg_depend", "1255", "2049"},
{"pg_depend", "2617", "2779"},
{"pg_depend", "2617", "2780"},
{"pg_description", "2617", "2779"},
{"pg_description", "2617", "2780"},
{NULL}
};
exception_list edb93_exception_list[] = {
{"pg_depend", "1255", "877"},
{"pg_depend", "1255", "883"},
{"pg_depend", "1255", "1777"},
{"pg_depend", "1255", "1780"},
{"pg_depend", "1255", "2049"},
{NULL}
};
static bool class_id_mappings_attempted;
static int num_class_id_mapping;
static class_id_mapping_type *class_id_mapping;
static char *pg_class_oid;
static pg_catalog_table *pg_attribute_table;
static pg_catalog_table *pg_type_table;
static pg_catalog_table *lookup_class_id(char *oid);
static void build_class_id_mappings(void);
static bool table_key_is_oid(pg_catalog_table *tab);
static check_depend_cache *build_depend_cache(pg_catalog_table *tab,
pg_catalog_column *tabcol);
static bool not_for_this_database(check_depend_cache *cache,
pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum);
static depend_column_style get_style(char *table_name, char *column_name);
static bool check_for_exception(char *table_name, char *classval,
char *objval);
/*
* Set up to check a class ID.
*/
void
prepare_to_check_dependency_class_id(pg_catalog_table *tab,
pg_catalog_column *tabcol)
{
pg_catalog_table *pg_class = find_table_by_name("pg_class");
pg_catalog_column *pg_class_relname;
pg_catalog_column *pg_class_relnamespace;
/*
* We need pg_class to figure out system catalog table OIDs.
*/
add_table_dependency(tab, pg_class);
pg_class_relname = find_column_by_name(pg_class, "relname");
pg_class_relname->needed = true;
pg_class_relnamespace = find_column_by_name(pg_class, "relnamespace");
pg_class_relnamespace->needed = true;
/* We need this to determine whether the class ID can legally zero. */
if (get_style(tab->table_name, tabcol->name) == DEPEND_COLUMN_STYLE_OBJID)
{
pg_catalog_column *deptype = find_column_by_name(tab, "deptype");
deptype->needed = true;
}
}
/*
* Set up to check an object ID.
*/
void
prepare_to_check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol)
{
pg_catalog_table *cattab;
pg_catalog_column *classid;
/*
* Just as when checking a class ID, we need pg_class to map class IDs to
* catalog tables.
*/
prepare_to_check_dependency_class_id(tab, tabcol);
/*
* All catalog tables that have an OID column must be loaded before we can
* check dependency IDs.
*/
for (cattab = pg_catalog_tables; cattab->table_name != NULL; ++cattab)
if (table_key_is_oid(cattab))
add_table_dependency(tab, cattab);
/* Force the necessary classid column to be selected. */
switch (get_style(tab->table_name, tabcol->name))
{
case DEPEND_COLUMN_STYLE_OBJID:
case EDB_DDLTIME_COLUMN_STYLE_OBJID:
classid = find_column_by_name(tab, "classid");
break;
case DEPEND_COLUMN_STYLE_REFOBJID:
classid = find_column_by_name(tab, "refclassid");
break;
case DEPEND_COLUMN_STYLE_OBJOID:
classid = find_column_by_name(tab, "classoid");
break;
default:
pgcc_log(PGCC_FATAL, "unexpected depend column style");
return; /* placate compiler */
}
classid->needed = true;
}
/*
* Set up to check a sub-ID.
*/
void
prepare_to_check_dependency_subid(pg_catalog_table *tab,
pg_catalog_column *tabcol)
{
pg_catalog_column *classid;
pg_catalog_column *objectid;
/*
* Just as when checking a class ID, we need pg_class to map class IDs to
* catalog tables. Specifically, we've got to be able to identify the OID
* of pg_class itself, so that we know whether a non-zero sub-ID is legal.
* Currently, that OID is the same in all server versions we support, so
* we could hard-code it, but there's little harm in doing it this way: if
* pg_class is too botched to interpret, the chances of anything else
* making much sense are slim to none.
*/
prepare_to_check_dependency_class_id(tab, tabcol);
/* We need the pg_attribute table to check sub-IDs. */
add_table_dependency(tab, find_table_by_name("pg_attribute"));
/* Make sure we have the class and object IDs. */
switch (get_style(tab->table_name, tabcol->name))
{
case DEPEND_COLUMN_STYLE_OBJID:
case EDB_DDLTIME_COLUMN_STYLE_OBJID:
classid = find_column_by_name(tab, "classid");
objectid = find_column_by_name(tab, "objid");
break;
case DEPEND_COLUMN_STYLE_REFOBJID:
classid = find_column_by_name(tab, "refclassid");
objectid = find_column_by_name(tab, "refobjid");
break;
case DEPEND_COLUMN_STYLE_OBJOID:
classid = find_column_by_name(tab, "classoid");
objectid = find_column_by_name(tab, "objoid");
break;
default:
pgcc_log(PGCC_FATAL, "unexpected depend column style");
return; /* placate compiler */
}
classid->needed = true;
objectid->needed = true;
}
/*
* Check a class ID.
*
* This is basically just testing that the class ID is a system catalog
* table that we know about and that's supposed to exists in this server
* version, or else 0 if that's a legal value in this context.
*/
void
check_dependency_class_id(pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum)
{
char *val;
check_depend_cache *cache;
cache = build_depend_cache(tab, tabcol);
if (cache->is_broken)
return;
if (not_for_this_database(cache, tab, tabcol, rownum))
return;
val = PQgetvalue(tab->data, rownum, tabcol->result_column);
/*
* We normally expect that the class ID is non-zero, but "pin" depedencies
* are an exception.
*/
if (strcmp(val, "0") == 0)
{
bool complain = true;
if (cache->style == DEPEND_COLUMN_STYLE_OBJID)
{
char *deptype;
deptype = PQgetvalue(tab->data, rownum,
cache->deptype_result_column);
if (strcmp(deptype, "p") == 0)
complain = false;
}
if (complain)
pgcc_report(tab, tabcol, rownum, "unexpected zero value\n");
return;
}
if (lookup_class_id(val) == NULL)
{
/*
* Workaround for an old EnterpriseDB bug: 8.4 installed a bogus
* dependency with reclassid 16722.
*/
if (remote_is_edb && remote_version <= 90000 &&
strcmp(val, "16722") == 0)
{
pgcc_log(PGCC_DEBUG, "ignoring reference to class ID 16722\n");
return;
}
pgcc_report(tab, tabcol, rownum, "not a system catalog OID\n");
}
}
/*
* Check a dependency ID.
*
* We have to examine the class ID to figure out which table ought to
* contain the indicated object. We then look up that table and check
* whether the value appears in its OID column.
*/
void
check_dependency_id(pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum)
{
char *classval;
char *val;
pg_catalog_table *object_tab;
check_depend_cache *cache;
cache = build_depend_cache(tab, tabcol);
if (cache->is_broken)
return;
if (not_for_this_database(cache, tab, tabcol, rownum))
return;
/*
* Check for multiple owner dependencies for the same object.
*
* FIXME: Current pg_catcheck design don't support table-level checks,
* all checks are column-level. We might want to re-architect this at
* some point in the future. For now, we check this here.
*/
if (cache->duplicate_owner_ht != NULL &&
strcmp(PQgetvalue(tab->data, rownum,
PQfnumber(tab->data, "deptype")), "o") == 0 &&
pgrhash_insert(cache->duplicate_owner_ht, rownum) != -1)
pgcc_report(tab, NULL, rownum, "duplicate owner dependency\n");
/* Fetch the class ID and object ID. */
classval = PQgetvalue(tab->data, rownum, cache->class_result_column);
val = PQgetvalue(tab->data, rownum, tabcol->result_column);
/* If the class ID is zero, the object ID should be zero as well. */
if (strcmp(classval, "0") == 0)
{
if (strcmp(val, "0") != 0)
pgcc_report(tab, tabcol, rownum,
"class ID is zero, but object ID is non-zero\n");
return;
}
/* Find the correct table. */
object_tab = lookup_class_id(classval);
if (object_tab == NULL || object_tab->ht == NULL)
return;
/*
* Workaround for EnterpriseDB bug: EnterpriseDB versions prior to 9.4
* would sometimes create bogus dependencies on type ID 0. Since we'll
* never create a real type with that OID, this was (as far as we know)
* harmless, so just ignore them.
*/
if (pg_type_table == NULL)
pg_type_table = find_table_by_name("pg_type");
if (remote_version < 90400 && remote_is_edb && object_tab == pg_type_table
&& strcmp(val, "0") == 0)
{
pgcc_log(PGCC_DEBUG,
"ignoring reference to pg_type OID 0\n");
return;
}
/*
* lookup_class_id() will only return tables where the only key column is
* the OID column. So this is safe.
*/
if (pgrhash_get(object_tab->ht, &val) == -1 &&
!check_for_exception(tab->table_name, classval, val))
pgcc_report(tab, tabcol, rownum, "no matching entry in %s\n",
object_tab->table_name);
}
/*
* Check a dependency sub-ID.
*
* This should always be zero except in the where the class ID points to
* pg_class. In that case, should be able to find <object ID, sub-ID> in
* pg_attribute. The object ID will appear in attrelid and the sub-ID in attnum.
*/
void
check_dependency_subid(pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum)
{
char *classval;
char *vals[2];
check_depend_cache *cache;
cache = build_depend_cache(tab, tabcol);
if (cache->is_broken)
return;
if (not_for_this_database(cache, tab, tabcol, rownum))
return;
/*
* We find pg_attribute on our first trip through this function and avoid
* repeating the lookup thereafter using a global variable.
*/
if (pg_attribute_table == NULL)
pg_attribute_table = find_table_by_name("pg_attribute");
/* Fetch the class ID, object ID, and sub-ID. */
classval = PQgetvalue(tab->data, rownum, cache->class_result_column);
vals[0] = PQgetvalue(tab->data, rownum, cache->object_result_column);
vals[1] = PQgetvalue(tab->data, rownum, tabcol->result_column);
/* Sub-ID is always permitted to be zero. */
if (strcmp(vals[1], "0") == 0)
return;
/*
* If we get here, the sub-ID is non-zero. Therefore, the class ID should
* definitely point to pg_class; if it does not, that's an inconsistency.
* If it does point to pg_class, then a matching pg_attribute row should
* exist.
*/
if (strcmp(classval, pg_class_oid) != 0)
pgcc_report(tab, tabcol, rownum,
"class ID %s is not pg_class, but sub-ID is non-zero\n",
classval);
else if (pg_attribute_table->ht) /* We might have failed to read it. */
{
if (pgrhash_get(pg_attribute_table->ht, vals) == -1)
pgcc_report(tab, tabcol, rownum, "no matching entry in %s\n",
pg_attribute_table->table_name);
}
}
/*
* Given a text-form OID found in an objid or refobjid table, search for a
* corresponding catalog table.
*
* NB: We could make this more efficient by teaching build_class_id_mappings()
* to sort the array, and then using binary search.
*/
static pg_catalog_table *
lookup_class_id(char *oid)
{
int i;
Assert(class_id_mappings_attempted && class_id_mapping != NULL);
/* For LargeObjectRelationId, substitute LargeObjectMetadataOidIndexId. */
if (strcmp(oid, "2613") == 0)
oid = "2995";
for (i = 0; i < num_class_id_mapping; ++i)
if (strcmp(class_id_mapping[i].oid, oid) == 0)
return class_id_mapping[i].tab;
return NULL;
}
/*
* Build a set of mappings from text-form OIDs which PQgetvalue might hand
* back for an objid or refobjid column to pg_catalog_table objects.
*/
static void
build_class_id_mappings(void)
{
int map_used = 0;
int map_size = 10;
class_id_mapping_type *map;
pg_catalog_table *pg_class_tab;
int oid_column;
int relnamespace_column;
int relname_column;
int ntups = 0;
int i;
/*
* If we fall out of this function due to some kind of unexpected error,
* we do not want to retry, as we'll just hit the same problem the second
* time. Set a flag so that callers can detect this case.
*/
class_id_mappings_attempted = true;
/* Find the pg_class table. */
pg_class_tab = find_table_by_name("pg_class");
if (pg_class_tab->data != NULL)
ntups = PQntuples(pg_class_tab->data);
if (ntups == 0)
{
pgcc_log(PGCC_WARNING,
"can't identify class IDs: no pg_class data\n");
return;
}
/* Find the pg_class columns we need. */
oid_column = PQfnumber(pg_class_tab->data, "oid");
relnamespace_column = PQfnumber(pg_class_tab->data, "relnamespace");
relname_column = PQfnumber(pg_class_tab->data, "relname");
if (oid_column == -1 || relname_column == -1 || relnamespace_column == -1)
{
pgcc_log(PGCC_WARNING,
"can't identify class IDs: missing pg_class columns\n");
return;
}
/* Initialize map data structure. */
map = pg_malloc(sizeof(class_id_mapping_type) * map_size);
/* Scan pg_class rows to construct mapping table. */
for (i = 0; i < ntups; ++i)
{
pg_catalog_table *tab;
char *relnamespace;
char *relname;
/* Skip tables that are not part of the pg_catalog namespace. */
relnamespace = PQgetvalue(pg_class_tab->data, i, relnamespace_column);
if (strcmp(relnamespace, "11") != 0)
continue;
/* See if it's a catalog table we know about. */
relname = PQgetvalue(pg_class_tab->data, i, relname_column);
for (tab = pg_catalog_tables; tab->table_name != NULL; ++tab)
{
/* Skip table if name does not match. */
if (strcmp(tab->table_name, relname) != 0)
continue;
/* Ignore matching table if it's not available. */
if (!tab->available)
break;
/* Ignore matching table if not keyed by OID. */
if (!table_key_is_oid(tab))
break;
/* Increase map space if required. */
if (map_used >= map_size)
{
map_size *= 2;
map = pg_realloc(map,
sizeof(class_id_mapping_type) * map_size);
}
/* Create map entry. */
map[map_used].oid = PQgetvalue(pg_class_tab->data, i, oid_column);
map[map_used].tab = tab;
/*
* Special bookkeeping for pg_class itself, due to its role in
* checking sub-IDs.
*/
if (pg_class_tab == tab)
pg_class_oid = map[map_used].oid;
++map_used;
break;
}
}
/* Avoid installing a bogus empty mapping. */
if (map_used == 0)
{
pgcc_log(PGCC_WARNING,
"can't identify class IDs: no catalog tables found in pg_class\n");
return;
}
/*
* Avoid installing mapping that doesn't include pg_class itself.
*
* This is probably a good sanity check on general principal, but what
* truly makes it necessary is that sub-ID verification needs
* pg_class_oid.
*/
if (pg_class_oid == NULL)
{
pgcc_log(PGCC_WARNING,
"can't identify class IDs: pg_class not found in pg_class\n");
return;
}
/* Install new mapping table. */
class_id_mapping = map;
num_class_id_mapping = map_used;
}
/*
* Is oid the only key column for this table?
*/
static bool
table_key_is_oid(pg_catalog_table *tab)
{
bool has_oid_key = false;
bool has_other_key = false;
pg_catalog_column *tabcol;
for (tabcol = tab->cols; tabcol->name != NULL; ++tabcol)
{
if (!tabcol->is_key_column)
continue;
if (strcmp(tabcol->name, "oid") == 0)
has_oid_key = true;
else
has_other_key = true;
}
return has_oid_key && !has_other_key;
}
/*
* Cache per-column dependency checking information, basically column indexes
* into the PGresult structure so that we can quickly find the class ID for
* an object ID and the class and object ID for a sub-ID.
*/
static check_depend_cache *
build_depend_cache(pg_catalog_table *tab, pg_catalog_column *tabcol)
{
check_depend_cache *cache;
bool columns_missing = false;
/* If we've already built the cache, just return the existing data. */
if (tabcol->check_private != NULL)
return tabcol->check_private;
/* Create and initialize the cache object. */
cache = pg_malloc0(sizeof(check_depend_cache));
cache->style = get_style(tab->table_name, tabcol->name);
cache->is_broken = false;
switch (cache->style)
{
case DEPEND_COLUMN_STYLE_OBJID:
/* special case for pg_shdepend */
cache->database_result_column = PQfnumber(tab->data, "dbid");
cache->class_result_column = PQfnumber(tab->data, "classid");
cache->object_result_column = PQfnumber(tab->data, "objid");
break;
case EDB_DDLTIME_COLUMN_STYLE_OBJID:
cache->database_result_column = -1;
cache->class_result_column = PQfnumber(tab->data, "classid");
cache->object_result_column = PQfnumber(tab->data, "objid");
break;
case DEPEND_COLUMN_STYLE_REFOBJID:
cache->database_result_column = -1;
cache->class_result_column = PQfnumber(tab->data, "refclassid");
cache->object_result_column = PQfnumber(tab->data, "refobjid");
break;
case DEPEND_COLUMN_STYLE_OBJOID:
cache->database_result_column = -1;
cache->class_result_column = PQfnumber(tab->data, "classoid");
cache->object_result_column = PQfnumber(tab->data, "objoid");
break;
default:
pgcc_log(PGCC_FATAL, "unexpected depend column style");
break;
}
/* Verify that PQfnumber() worked as expected. */
if (cache->class_result_column == -1)
columns_missing = true;
if (cache->object_result_column == -1)
columns_missing = true;
/* Look up the column number for the deptype column, if expected. */
if (cache->style == DEPEND_COLUMN_STYLE_OBJID)
{
cache->deptype_result_column = PQfnumber(tab->data, "deptype");
if (cache->deptype_result_column == -1)
columns_missing = true;
}
/*
* If we failed to find the relevant columns, then mark the cache as
* broken, which will cause the individual rows not to be checked.
* Otherwise, we'd end up having to emit a message for every row, which
* would be too much chatter.
*/
if (columns_missing)
{
pgcc_log(PGCC_WARNING,
"can't identify class IDs: columns missing from %s\n",
tab->table_name);
cache->is_broken = true;
}
/*
* For the convenience of our callers, we also try to build the global
* cache data here, namely the mappings from class IDs to pg_catalog_table
* objects. Unlike the data stored in the cache object, these mappings
* can be reused across all columns where we check dependencies.
*/
if (!class_id_mappings_attempted)
build_class_id_mappings();
/*
* If we failed to build the mappings, either now or previously, mark the
* cache object as broken, to avoid log chatter for each separate row.
*/
if (class_id_mapping == NULL)
cache->is_broken = true;
/*
* If needed, create hash table for duplicate-owner-dependency cheecking.
*/
if (!cache->is_broken && cache->database_result_column != -1 &&
cache->deptype_result_column != -1)
{
int keycols[3];
keycols[0] = cache->database_result_column;
keycols[1] = cache->class_result_column;
keycols[2] = cache->object_result_column;
cache->duplicate_owner_ht = pgrhash_create(tab->data, 3, keycols);
}
/* We're done. */
tabcol->check_private = cache;
return cache;
}
/*
* Determine whether this dependency should be ignored because it's not
* part of this database. This will only ever return true when we're checking
* a table that has a dbid column, which currently means just pg_shdepend.
*/
static bool
not_for_this_database(check_depend_cache *cache, pg_catalog_table *tab,
pg_catalog_column *tabcol, int rownum)
{
char *dbval;
/* If there's no dbid column, then it's part of this database. */
if (cache->database_result_column == -1)
return false;
/* Look up the value in that column. */
dbval = PQgetvalue(tab->data, rownum, cache->database_result_column);
/* 0 means it's a global object, so it's fine to check it here. */
if (strcmp(dbval, "0") == 0)
return false;
/*
* If we don't know the database OID, skip the check, to avoid bogus
* complaints.
*/
if (database_oid == NULL)
return true;
/* Straightforward comparison. */
return strcmp(database_oid, dbval) != 0;
}
/*
* Determine which naming style applies to this table and column.
*
* There are three naming conventions that are used for references to objects
* in arbitrary catalogs. pg_depend and pg_shdepend use classid/objid/objsubid
* for one side of the dependency and refclassid/refobjid/refobjsubid for the
* other. Other tables that contain similar information, such as
* pg_description, pg_shdescription, pg_seclabel, and pg_shseclabel, use
* objoid/classoid/objsubid.
*
* edb_last_ddl_time and edb_last_ddl_time_shared use objid/classid/objsubid
* like pg_depend but need a separate enum value because they do not have a
* deptype column, which prepare_to_check_dependency_class_id() would expect
* if we used DEPEND_COLUMN_STYLE_OBJID.
*/
static depend_column_style
get_style(char *table_name, char *column_name)
{
if (strncmp(column_name, "ref", 3) == 0)
return DEPEND_COLUMN_STYLE_REFOBJID;
else if (strstr(table_name, "depend"))
return DEPEND_COLUMN_STYLE_OBJID;
else if (strstr(table_name, "edb_last_ddl_time"))
return EDB_DDLTIME_COLUMN_STYLE_OBJID;
else
return DEPEND_COLUMN_STYLE_OBJOID;
}
/*
* Check whether a detected inconsistency is one that we were expecting.
*/
static bool
check_for_exception(char *table_name, char *classval, char *objval)
{
exception_list *exc;
if (!remote_is_edb || remote_version >= 90400)
return false;
if (remote_version >= 90300)
exc = edb93_exception_list;
else if (remote_version >= 90100)
exc = edb91_92_exception_list;
else if (remote_version >= 90000)
exc = edb90_exception_list;
else
exc = edb84_exception_list;
while (exc->table_name != NULL)
{
if (strcmp(exc->table_name, table_name) == 0 &&
strcmp(exc->class, classval) == 0 &&
strcmp(exc->object, objval) == 0)
{
pgcc_log(PGCC_DEBUG,
"ignoring reference to class ID %s object ID %s in %s\n",
exc->class, exc->object, exc->table_name);
return true;
}
++exc;
}
return false;
}