1 /*-------------------------------------------------------------------------
2  *
3  * ginvalidate.c
4  *	  Opclass validator for GIN.
5  *
6  * Portions Copyright (c) 1996-2016, PostgreSQL Global Development Group
7  * Portions Copyright (c) 1994, Regents of the University of California
8  *
9  * IDENTIFICATION
10  *			src/backend/access/gin/ginvalidate.c
11  *
12  *-------------------------------------------------------------------------
13  */
14 #include "postgres.h"
15 
16 #include "access/amvalidate.h"
17 #include "access/gin_private.h"
18 #include "access/htup_details.h"
19 #include "catalog/pg_amop.h"
20 #include "catalog/pg_amproc.h"
21 #include "catalog/pg_opclass.h"
22 #include "catalog/pg_opfamily.h"
23 #include "catalog/pg_type.h"
24 #include "utils/builtins.h"
25 #include "utils/lsyscache.h"
26 #include "utils/syscache.h"
27 
28 
29 /*
30  * Validator for a GIN opclass.
31  */
32 bool
ginvalidate(Oid opclassoid)33 ginvalidate(Oid opclassoid)
34 {
35 	bool		result = true;
36 	HeapTuple	classtup;
37 	Form_pg_opclass classform;
38 	Oid			opfamilyoid;
39 	Oid			opcintype;
40 	Oid			opckeytype;
41 	char	   *opclassname;
42 	HeapTuple	familytup;
43 	Form_pg_opfamily familyform;
44 	char	   *opfamilyname;
45 	CatCList   *proclist,
46 			   *oprlist;
47 	List	   *grouplist;
48 	OpFamilyOpFuncGroup *opclassgroup;
49 	int			i;
50 	ListCell   *lc;
51 
52 	/* Fetch opclass information */
53 	classtup = SearchSysCache1(CLAOID, ObjectIdGetDatum(opclassoid));
54 	if (!HeapTupleIsValid(classtup))
55 		elog(ERROR, "cache lookup failed for operator class %u", opclassoid);
56 	classform = (Form_pg_opclass) GETSTRUCT(classtup);
57 
58 	opfamilyoid = classform->opcfamily;
59 	opcintype = classform->opcintype;
60 	opckeytype = classform->opckeytype;
61 	if (!OidIsValid(opckeytype))
62 		opckeytype = opcintype;
63 	opclassname = NameStr(classform->opcname);
64 
65 	/* Fetch opfamily information */
66 	familytup = SearchSysCache1(OPFAMILYOID, ObjectIdGetDatum(opfamilyoid));
67 	if (!HeapTupleIsValid(familytup))
68 		elog(ERROR, "cache lookup failed for operator family %u", opfamilyoid);
69 	familyform = (Form_pg_opfamily) GETSTRUCT(familytup);
70 
71 	opfamilyname = NameStr(familyform->opfname);
72 
73 	/* Fetch all operators and support functions of the opfamily */
74 	oprlist = SearchSysCacheList1(AMOPSTRATEGY, ObjectIdGetDatum(opfamilyoid));
75 	proclist = SearchSysCacheList1(AMPROCNUM, ObjectIdGetDatum(opfamilyoid));
76 
77 	/* Check individual support functions */
78 	for (i = 0; i < proclist->n_members; i++)
79 	{
80 		HeapTuple	proctup = &proclist->members[i]->tuple;
81 		Form_pg_amproc procform = (Form_pg_amproc) GETSTRUCT(proctup);
82 		bool		ok;
83 
84 		/*
85 		 * All GIN support functions should be registered with matching
86 		 * left/right types
87 		 */
88 		if (procform->amproclefttype != procform->amprocrighttype)
89 		{
90 			ereport(INFO,
91 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
92 					 errmsg("operator family \"%s\" of access method %s contains support procedure %s with different left and right input types",
93 							opfamilyname, "gin",
94 							format_procedure(procform->amproc))));
95 			result = false;
96 		}
97 
98 		/*
99 		 * We can't check signatures except within the specific opclass, since
100 		 * we need to know the associated opckeytype in many cases.
101 		 */
102 		if (procform->amproclefttype != opcintype)
103 			continue;
104 
105 		/* Check procedure numbers and function signatures */
106 		switch (procform->amprocnum)
107 		{
108 			case GIN_COMPARE_PROC:
109 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
110 											2, 2, opckeytype, opckeytype);
111 				break;
112 			case GIN_EXTRACTVALUE_PROC:
113 				/* Some opclasses omit nullFlags */
114 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
115 											2, 3, opcintype, INTERNALOID,
116 											INTERNALOID);
117 				break;
118 			case GIN_EXTRACTQUERY_PROC:
119 				/* Some opclasses omit nullFlags and searchMode */
120 				ok = check_amproc_signature(procform->amproc, INTERNALOID, false,
121 											5, 7, opcintype, INTERNALOID,
122 											INT2OID, INTERNALOID, INTERNALOID,
123 											INTERNALOID, INTERNALOID);
124 				break;
125 			case GIN_CONSISTENT_PROC:
126 				/* Some opclasses omit queryKeys and nullFlags */
127 				ok = check_amproc_signature(procform->amproc, BOOLOID, false,
128 											6, 8, INTERNALOID, INT2OID,
129 											opcintype, INT4OID,
130 											INTERNALOID, INTERNALOID,
131 											INTERNALOID, INTERNALOID);
132 				break;
133 			case GIN_COMPARE_PARTIAL_PROC:
134 				ok = check_amproc_signature(procform->amproc, INT4OID, false,
135 											4, 4, opckeytype, opckeytype,
136 											INT2OID, INTERNALOID);
137 				break;
138 			case GIN_TRICONSISTENT_PROC:
139 				ok = check_amproc_signature(procform->amproc, CHAROID, false,
140 											7, 7, INTERNALOID, INT2OID,
141 											opcintype, INT4OID,
142 											INTERNALOID, INTERNALOID,
143 											INTERNALOID);
144 				break;
145 			default:
146 				ereport(INFO,
147 						(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
148 						 errmsg("operator family \"%s\" of access method %s contains function %s with invalid support number %d",
149 								opfamilyname, "gin",
150 								format_procedure(procform->amproc),
151 								procform->amprocnum)));
152 				result = false;
153 				continue;		/* don't want additional message */
154 		}
155 
156 		if (!ok)
157 		{
158 			ereport(INFO,
159 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
160 					 errmsg("operator family \"%s\" of access method %s contains function %s with wrong signature for support number %d",
161 							opfamilyname, "gin",
162 							format_procedure(procform->amproc),
163 							procform->amprocnum)));
164 			result = false;
165 		}
166 	}
167 
168 	/* Check individual operators */
169 	for (i = 0; i < oprlist->n_members; i++)
170 	{
171 		HeapTuple	oprtup = &oprlist->members[i]->tuple;
172 		Form_pg_amop oprform = (Form_pg_amop) GETSTRUCT(oprtup);
173 
174 		/* TODO: Check that only allowed strategy numbers exist */
175 		if (oprform->amopstrategy < 1 || oprform->amopstrategy > 63)
176 		{
177 			ereport(INFO,
178 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
179 					 errmsg("operator family \"%s\" of access method %s contains operator %s with invalid strategy number %d",
180 							opfamilyname, "gin",
181 							format_operator(oprform->amopopr),
182 							oprform->amopstrategy)));
183 			result = false;
184 		}
185 
186 		/* gin doesn't support ORDER BY operators */
187 		if (oprform->amoppurpose != AMOP_SEARCH ||
188 			OidIsValid(oprform->amopsortfamily))
189 		{
190 			ereport(INFO,
191 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
192 					 errmsg("operator family \"%s\" of access method %s contains invalid ORDER BY specification for operator %s",
193 							opfamilyname, "gin",
194 							format_operator(oprform->amopopr))));
195 			result = false;
196 		}
197 
198 		/* Check operator signature --- same for all gin strategies */
199 		if (!check_amop_signature(oprform->amopopr, BOOLOID,
200 								  oprform->amoplefttype,
201 								  oprform->amoprighttype))
202 		{
203 			ereport(INFO,
204 					(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
205 					 errmsg("operator family \"%s\" of access method %s contains operator %s with wrong signature",
206 							opfamilyname, "gin",
207 							format_operator(oprform->amopopr))));
208 			result = false;
209 		}
210 	}
211 
212 	/* Now check for inconsistent groups of operators/functions */
213 	grouplist = identify_opfamily_groups(oprlist, proclist);
214 	opclassgroup = NULL;
215 	foreach(lc, grouplist)
216 	{
217 		OpFamilyOpFuncGroup *thisgroup = (OpFamilyOpFuncGroup *) lfirst(lc);
218 
219 		/* Remember the group exactly matching the test opclass */
220 		if (thisgroup->lefttype == opcintype &&
221 			thisgroup->righttype == opcintype)
222 			opclassgroup = thisgroup;
223 
224 		/*
225 		 * There is not a lot we can do to check the operator sets, since each
226 		 * GIN opclass is more or less a law unto itself, and some contain
227 		 * only operators that are binary-compatible with the opclass datatype
228 		 * (meaning that empty operator sets can be OK).  That case also means
229 		 * that we shouldn't insist on nonempty function sets except for the
230 		 * opclass's own group.
231 		 */
232 	}
233 
234 	/* Check that the originally-named opclass is complete */
235 	for (i = 1; i <= GINNProcs; i++)
236 	{
237 		if (opclassgroup &&
238 			(opclassgroup->functionset & (((uint64) 1) << i)) != 0)
239 			continue;			/* got it */
240 		if (i == GIN_COMPARE_PARTIAL_PROC)
241 			continue;			/* optional method */
242 		if (i == GIN_CONSISTENT_PROC || i == GIN_TRICONSISTENT_PROC)
243 			continue;			/* don't need both, see check below loop */
244 		ereport(INFO,
245 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
246 		   errmsg("operator class \"%s\" of access method %s is missing support function %d",
247 				  opclassname, "gin", i)));
248 		result = false;
249 	}
250 	if (!opclassgroup ||
251 		((opclassgroup->functionset & (1 << GIN_CONSISTENT_PROC)) == 0 &&
252 		 (opclassgroup->functionset & (1 << GIN_TRICONSISTENT_PROC)) == 0))
253 	{
254 		ereport(INFO,
255 				(errcode(ERRCODE_INVALID_OBJECT_DEFINITION),
256 				 errmsg("operator class \"%s\" of access method %s is missing support function %d or %d",
257 						opclassname, "gin",
258 						GIN_CONSISTENT_PROC, GIN_TRICONSISTENT_PROC)));
259 		result = false;
260 	}
261 
262 
263 	ReleaseCatCacheList(proclist);
264 	ReleaseCatCacheList(oprlist);
265 	ReleaseSysCache(familytup);
266 	ReleaseSysCache(classtup);
267 
268 	return result;
269 }
270