File: check_attribute.c

package info (click to toggle)
pg-catcheck 1.6.0-1
  • links: PTS, VCS
  • area: main
  • in suites: trixie
  • size: 280 kB
  • sloc: ansic: 2,984; makefile: 40; sh: 4
file content (125 lines) | stat: -rw-r--r-- 3,438 bytes parent folder | download | duplicates (2)
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
/*-------------------------------------------------------------------------
 *
 * check_attribute.c
 *
 * Custom checks for pg_attribute fields.
 *
 *-------------------------------------------------------------------------
 */

#include "postgres.h"
#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);
}