-
Notifications
You must be signed in to change notification settings - Fork 15
/
check_attribute.c
124 lines (111 loc) · 3.34 KB
/
check_attribute.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
/*-------------------------------------------------------------------------
*
* check_attribute.c
*
* Custom checks for pg_attribute fields.
*
*-------------------------------------------------------------------------
*/
#include "postgres_fe.h"
#include "pg_catcheck.h"
/*
* pg_attribute_d.h was added by commit 372728b0d, which first appeared in
* v11. But including pg_attribute.h still worked fine until commit
* d939cb2fd appeared in v17. Realistically, pg_attribute_d.h should work
* for any version anyone still cares about, but for now, just enable it
* for new versions.
*/
#if PG_VERSION_NUM >= 170000
#include "catalog/pg_attribute_d.h"
#else
#include "catalog/pg_attribute.h"
#endif
typedef struct
{
pg_catalog_table *pg_class;
int attrelid_result_column;
int relnatts_result_column;
} attnum_cache;
/*
* Set up to check attnum.
*/
void
prepare_to_check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol)
{
add_table_dependency(tab, find_table_by_name("pg_class"));
}
/*
* Sanity-check the relnatts field.
*/
void
check_attnum(pg_catalog_table *tab, pg_catalog_column *tabcol, int rownum)
{
char *val = PQgetvalue(tab->data, rownum, tabcol->result_column);
char *attrelid_val;
char *relnatts_val;
char *endptr;
attnum_cache *cache;
int class_rownum;
long attnum;
long relnatts;
long min_attno;
/* Convert the value to a number. */
attnum = strtol(val, &endptr, 10);
if (*endptr != '\0')
{
pgcc_report(tab, tabcol, rownum, "must be an integer\n");
return;
}
/* Our attribute number should not be zero. */
if (attnum == 0)
{
pgcc_report(tab, tabcol, rownum, "must not be zero\n");
return;
}
/* And it should be at least -7 for PostgreSQL, -8 for EnterpriseDB. */
min_attno = remote_is_edb ? -8 : -7;
if (attnum < min_attno)
{
pgcc_report(tab, tabcol, rownum, "must be at least %ld\n",
min_attno);
return;
}
/* Find the pg_attribute table; cache result in check_private. */
if (tabcol->check_private == NULL)
{
cache = pg_malloc(sizeof(attnum_cache));
cache->pg_class = find_table_by_name("pg_class");
cache->attrelid_result_column = PQfnumber(tab->data, "attrelid");
cache->relnatts_result_column = PQfnumber(cache->pg_class->data,
"relnatts");
tabcol->check_private = cache;
}
else
cache = tabcol->check_private;
/*
* Skip max-bound checking if the pg_class data is not available, or if
* the pg_class.relnatts or pg_attribute.attrelid column is not available.
*/
if (cache->pg_class->ht == NULL || cache->relnatts_result_column == -1 ||
cache->attrelid_result_column == -1)
return;
/* Find row number of this table in pg_class. */
attrelid_val = PQgetvalue(tab->data, rownum,
cache->attrelid_result_column);
class_rownum = pgrhash_get(cache->pg_class->ht, &attrelid_val);
if (class_rownum == -1)
return; /* It's not our job to complain about
* attrelid. */
/* Get relnatts, as a number. */
relnatts_val = PQgetvalue(cache->pg_class->data, class_rownum,
cache->relnatts_result_column);
relnatts = strtol(relnatts_val, &endptr, 10);
if (*endptr != '\0' || relnatts < 0)
return; /* It's not our job to complain about
* relnatts. */
/* Our attribute number should be less than relnatts. */
if (attnum > relnatts)
pgcc_report(tab, tabcol, rownum,
"exceeds relnatts value of %ld\n",
relnatts);
}