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
|
/*-------------------------------------------------------------------------
*
* check_oids.c
*
* Many PostgreSQL system catalogs have OID, OID array, or OID vector
* columns where each OID identifies a row in some other catalog table.
* Although not marked as such, these are essentially foreign key
* relationships. The code in this file aims to validate that every
* object referenced in such a column actually exists.
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "pg_catcheck.h"
static void do_oid_check(pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum, pg_catalog_check_oid * check_oid,
pg_catalog_table *reftab, char *value);
/*
* Set up for an OID referential integrity check.
*
* We simply need to make sure that the referenced table will be available.
*/
void
prepare_to_check_oid_reference(pg_catalog_table *tab,
pg_catalog_column *tabcol)
{
pg_catalog_check_oid *check_oid = tabcol->check;
pg_catalog_table *reftab;
reftab = find_table_by_name(check_oid->oid_references_table);
add_table_dependency(tab, reftab);
}
/*
* Perform an OID referential integrity check.
*/
void
check_oid_reference(pg_catalog_table *tab, pg_catalog_column *tabcol,
int rownum)
{
pg_catalog_check_oid *check_oid = tabcol->check;
char *val = PQgetvalue(tab->data, rownum, tabcol->result_column);
pg_catalog_table *reftab;
/*
* Find the hash table we need in order to perform the check.
*
* Since find_table_by_name is O(n) in the number of catalog tables being
* checked, we cache the result, so that we only need to do that work
* once.
*/
if (tabcol->check_private == NULL)
{
reftab = find_table_by_name(check_oid->oid_references_table);
tabcol->check_private = reftab;
}
else
reftab = tabcol->check_private;
/*
* The table might not be available in this server version, or we might
* have failed to read it. There's actually one real case where the
* referenced table was adding later than referring table: pg_largeobject
* has existed for a long time, but pg_largeobject_metadata is newer.
*/
if (!reftab->ht)
return;
switch (check_oid->type)
{
case CHECK_OID_REFERENCE:
/*
* Simple OID reference. Easy!
*
* We don't use do do_oid_check() here because the error message
* is a little different in this case.
*/
{
if (check_oid->zero_oid_ok && strcmp(val, "0") == 0)
return;
if (pgrhash_get(reftab->ht, &val) == -1)
pgcc_report(tab, tabcol, rownum,
"no matching entry in %s\n", reftab->table_name);
}
break;
case CHECK_OID_VECTOR_REFERENCE:
/* Space-separated list of values. */
{
char *s = val;
char buf[32];
for (;;)
{
/* Find next word boundary. */
while (*s != '\0' && *s != ' ')
++s;
/* If it's the last word, we're done here! */
if (*s == '\0')
{
if (s > val)
{
/* If last entry is non-empty, check it. */
do_oid_check(tab, tabcol, rownum, check_oid,
reftab, val);
}
break;
}
/* Make a copy of the data we need to check. */
if (s - val >= sizeof buf)
{
/* OIDs can't be this long. */
pgcc_report(tab, tabcol, rownum,
"contains a token of %ld characters\n", s - val);
return;
}
memcpy(buf, val, s - val);
buf[s - val] = '\0';
/* Check it and move ahead one character. */
do_oid_check(tab, tabcol, rownum, check_oid, reftab, buf);
val = ++s;
}
}
break;
case CHECK_OID_ARRAY_REFERENCE:
/* Opening curly brace, comma-separated values, closing brace. */
{
char *s = val;
char buf[32];
bool bad = false;
/* Allow a completely empty field. */
if (*s == '\0')
break;
/* Otherwise, expect the opening delimeter. */
if (*s == '{')
val = ++s;
else
bad = true;
while (!bad)
{
/* Find next delimeter. */
while (*s != '\0' && *s != ',' && *s != '}')
++s;
/*
* If we hit '\0' before '}', that's bad; and if we hit
* two consecutive delimeters, that's also bad.
*/
if (val == s || *s == '\0')
{
bad = true;
break;
}
/* Make a copy of the data we need to check. */
if (s - val >= sizeof buf)
{
/* OIDs can't be this long. */
pgcc_report(tab, tabcol, rownum,
"contains a token of %ld characters\n", s - val);
return;
}
memcpy(buf, val, s - val);
buf[s - val] = '\0';
/* Check it. */
if (!check_oid->zero_oid_ok || strcmp(buf, "0") != 0)
do_oid_check(tab, tabcol, rownum, check_oid, reftab,
buf);
/* Expect end of string if at '}'. */
if (*s == '}')
{
val = ++s;
if (*s != '\0')
bad = true;
break;
}
/* Skip comma and continue. */
val = ++s;
}
if (bad)
pgcc_report(tab, tabcol, rownum, "not a valid 1-D array");
}
break;
default:
Assert(false);
break;
}
}
/*
* Check one of possibly several OIDs found in a single column.
*/
static void
do_oid_check(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum,
pg_catalog_check_oid * check_oid,
pg_catalog_table *reftab, char *value)
{
if (check_oid->zero_oid_ok && strcmp(value, "0") == 0)
return;
if (pgrhash_get(reftab->ht, &value) == -1)
pgcc_report(tab, tabcol, rownum,
"\"%s\" not found in %s\n", value, reftab->table_name);
}
|