mirror of https://github.com/postgres/postgres
The amvalidate functions added in commit 65c5fcd353
were on the
crude side. Improve them in a few ways:
* Perform signature checking for operators and support functions.
* Apply more thorough checks for missing operators and functions,
where possible.
* Instead of reporting problems as ERRORs, report most problems as INFO
messages and make the amvalidate function return FALSE. This allows
more than one problem to be discovered per run.
* Report object names rather than OIDs, and work a bit harder on making
the messages understandable.
Also, remove a few more opr_sanity regression test queries that are
now superseded by the amvalidate checks.
pull/10/head
parent
b99551832e
commit
be44ed27b8
@ -0,0 +1,246 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* amvalidate.c |
||||
* Support routines for index access methods' amvalidate functions. |
||||
* |
||||
* Copyright (c) 2016, PostgreSQL Global Development Group |
||||
* |
||||
* |
||||
* IDENTIFICATION |
||||
* src/backend/access/index/amvalidate.c |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#include "postgres.h" |
||||
|
||||
#include "access/amvalidate.h" |
||||
#include "access/htup_details.h" |
||||
#include "catalog/pg_am.h" |
||||
#include "catalog/pg_amop.h" |
||||
#include "catalog/pg_amproc.h" |
||||
#include "catalog/pg_opclass.h" |
||||
#include "catalog/pg_operator.h" |
||||
#include "catalog/pg_proc.h" |
||||
#include "parser/parse_coerce.h" |
||||
#include "utils/syscache.h" |
||||
|
||||
|
||||
/*
|
||||
* identify_opfamily_groups() returns a List of OpFamilyOpFuncGroup structs, |
||||
* one for each combination of lefttype/righttype present in the family's |
||||
* operator and support function lists. If amopstrategy K is present for |
||||
* this datatype combination, we set bit 1 << K in operatorset, and similarly |
||||
* for the support functions. With uint64 fields we can handle operator and |
||||
* function numbers up to 63, which is plenty for the foreseeable future. |
||||
* |
||||
* The given CatCLists are expected to represent a single opfamily fetched |
||||
* from the AMOPSTRATEGY and AMPROCNUM caches, so that they will be in |
||||
* order by those caches' second and third cache keys, namely the datatypes. |
||||
*/ |
||||
List * |
||||
identify_opfamily_groups(CatCList *oprlist, CatCList *proclist) |
||||
{ |
||||
List *result = NIL; |
||||
OpFamilyOpFuncGroup *thisgroup; |
||||
Form_pg_amop oprform; |
||||
Form_pg_amproc procform; |
||||
int io, |
||||
ip; |
||||
|
||||
/* We need the lists to be ordered; should be true in normal operation */ |
||||
if (!oprlist->ordered || !proclist->ordered) |
||||
elog(ERROR, "cannot validate operator family without ordered data"); |
||||
|
||||
/*
|
||||
* Advance through the lists concurrently. Thanks to the ordering, we |
||||
* should see all operators and functions of a given datatype pair |
||||
* consecutively. |
||||
*/ |
||||
thisgroup = NULL; |
||||
io = ip = 0; |
||||
if (io < oprlist->n_members) |
||||
{ |
||||
oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple); |
||||
io++; |
||||
} |
||||
else |
||||
oprform = NULL; |
||||
if (ip < proclist->n_members) |
||||
{ |
||||
procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple); |
||||
ip++; |
||||
} |
||||
else |
||||
procform = NULL; |
||||
|
||||
while (oprform || procform) |
||||
{ |
||||
if (oprform && thisgroup && |
||||
oprform->amoplefttype == thisgroup->lefttype && |
||||
oprform->amoprighttype == thisgroup->righttype) |
||||
{ |
||||
/* Operator belongs to current group; include it and advance */ |
||||
|
||||
/* Ignore strategy numbers outside supported range */ |
||||
if (oprform->amopstrategy > 0 && oprform->amopstrategy < 64) |
||||
thisgroup->operatorset |= ((uint64) 1) << oprform->amopstrategy; |
||||
|
||||
if (io < oprlist->n_members) |
||||
{ |
||||
oprform = (Form_pg_amop) GETSTRUCT(&oprlist->members[io]->tuple); |
||||
io++; |
||||
} |
||||
else |
||||
oprform = NULL; |
||||
continue; |
||||
} |
||||
|
||||
if (procform && thisgroup && |
||||
procform->amproclefttype == thisgroup->lefttype && |
||||
procform->amprocrighttype == thisgroup->righttype) |
||||
{ |
||||
/* Procedure belongs to current group; include it and advance */ |
||||
|
||||
/* Ignore function numbers outside supported range */ |
||||
if (procform->amprocnum > 0 && procform->amprocnum < 64) |
||||
thisgroup->functionset |= ((uint64) 1) << procform->amprocnum; |
||||
|
||||
if (ip < proclist->n_members) |
||||
{ |
||||
procform = (Form_pg_amproc) GETSTRUCT(&proclist->members[ip]->tuple); |
||||
ip++; |
||||
} |
||||
else |
||||
procform = NULL; |
||||
continue; |
||||
} |
||||
|
||||
/* Time for a new group */ |
||||
thisgroup = (OpFamilyOpFuncGroup *) palloc(sizeof(OpFamilyOpFuncGroup)); |
||||
if (oprform && |
||||
(!procform || |
||||
(oprform->amoplefttype < procform->amproclefttype || |
||||
(oprform->amoplefttype == procform->amproclefttype && |
||||
oprform->amoprighttype < procform->amprocrighttype)))) |
||||
{ |
||||
thisgroup->lefttype = oprform->amoplefttype; |
||||
thisgroup->righttype = oprform->amoprighttype; |
||||
} |
||||
else |
||||
{ |
||||
thisgroup->lefttype = procform->amproclefttype; |
||||
thisgroup->righttype = procform->amprocrighttype; |
||||
} |
||||
thisgroup->operatorset = thisgroup->functionset = 0; |
||||
result = lappend(result, thisgroup); |
||||
} |
||||
|
||||
return result; |
||||
} |
||||
|
||||
/*
|
||||
* Validate the signature (argument and result types) of an opclass support |
||||
* function. Return TRUE if OK, FALSE if not. |
||||
* |
||||
* The "..." represents maxargs argument-type OIDs. If "exact" is TRUE, they |
||||
* must match the function arg types exactly, else only binary-coercibly. |
||||
* In any case the function result type must match restype exactly. |
||||
*/ |
||||
bool |
||||
check_amproc_signature(Oid funcid, Oid restype, bool exact, |
||||
int minargs, int maxargs,...) |
||||
{ |
||||
bool result = true; |
||||
HeapTuple tp; |
||||
Form_pg_proc procform; |
||||
va_list ap; |
||||
int i; |
||||
|
||||
tp = SearchSysCache1(PROCOID, ObjectIdGetDatum(funcid)); |
||||
if (!HeapTupleIsValid(tp)) |
||||
elog(ERROR, "cache lookup failed for function %u", funcid); |
||||
procform = (Form_pg_proc) GETSTRUCT(tp); |
||||
|
||||
if (procform->prorettype != restype || procform->proretset || |
||||
procform->pronargs < minargs || procform->pronargs > maxargs) |
||||
result = false; |
||||
|
||||
va_start(ap, maxargs); |
||||
for (i = 0; i < maxargs; i++) |
||||
{ |
||||
Oid argtype = va_arg(ap, Oid); |
||||
|
||||
if (i >= procform->pronargs) |
||||
continue; |
||||
if (exact ? (argtype != procform->proargtypes.values[i]) : |
||||
!IsBinaryCoercible(argtype, procform->proargtypes.values[i])) |
||||
result = false; |
||||
} |
||||
va_end(ap); |
||||
|
||||
ReleaseSysCache(tp); |
||||
return result; |
||||
} |
||||
|
||||
/*
|
||||
* Validate the signature (argument and result types) of an opclass operator. |
||||
* Return TRUE if OK, FALSE if not. |
||||
* |
||||
* Currently, we can hard-wire this as accepting only binary operators. Also, |
||||
* we can insist on exact type matches, since the given lefttype/righttype |
||||
* come from pg_amop and should always match the operator exactly. |
||||
*/ |
||||
bool |
||||
check_amop_signature(Oid opno, Oid restype, Oid lefttype, Oid righttype) |
||||
{ |
||||
bool result = true; |
||||
HeapTuple tp; |
||||
Form_pg_operator opform; |
||||
|
||||
tp = SearchSysCache1(OPEROID, ObjectIdGetDatum(opno)); |
||||
if (!HeapTupleIsValid(tp)) /* shouldn't happen */ |
||||
elog(ERROR, "cache lookup failed for operator %u", opno); |
||||
opform = (Form_pg_operator) GETSTRUCT(tp); |
||||
|
||||
if (opform->oprresult != restype || opform->oprkind != 'b' || |
||||
opform->oprleft != lefttype || opform->oprright != righttype) |
||||
result = false; |
||||
|
||||
ReleaseSysCache(tp); |
||||
return result; |
||||
} |
||||
|
||||
/*
|
||||
* Is the datatype a legitimate input type for the btree opfamily? |
||||
*/ |
||||
bool |
||||
opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid) |
||||
{ |
||||
bool result = false; |
||||
CatCList *opclist; |
||||
int i; |
||||
|
||||
/*
|
||||
* We search through all btree opclasses to see if one matches. This is a |
||||
* bit inefficient but there is no better index available. It also saves |
||||
* making an explicit check that the opfamily belongs to btree. |
||||
*/ |
||||
opclist = SearchSysCacheList1(CLAAMNAMENSP, ObjectIdGetDatum(BTREE_AM_OID)); |
||||
|
||||
for (i = 0; i < opclist->n_members; i++) |
||||
{ |
||||
HeapTuple classtup = &opclist->members[i]->tuple; |
||||
Form_pg_opclass classform = (Form_pg_opclass) GETSTRUCT(classtup); |
||||
|
||||
if (classform->opcfamily == opfamilyoid && |
||||
classform->opcintype == datatypeoid) |
||||
{ |
||||
result = true; |
||||
break; |
||||
} |
||||
} |
||||
|
||||
ReleaseCatCacheList(opclist); |
||||
|
||||
return result; |
||||
} |
@ -0,0 +1,36 @@ |
||||
/*-------------------------------------------------------------------------
|
||||
* |
||||
* amvalidate.h |
||||
* Support routines for index access methods' amvalidate functions. |
||||
* |
||||
* Copyright (c) 2016, PostgreSQL Global Development Group |
||||
* |
||||
* src/include/access/amvalidate.h |
||||
* |
||||
*------------------------------------------------------------------------- |
||||
*/ |
||||
#ifndef AMVALIDATE_H |
||||
#define AMVALIDATE_H |
||||
|
||||
#include "utils/catcache.h" |
||||
|
||||
|
||||
/* Struct returned (in a list) by identify_opfamily_groups() */ |
||||
typedef struct OpFamilyOpFuncGroup |
||||
{ |
||||
Oid lefttype; /* amoplefttype/amproclefttype */ |
||||
Oid righttype; /* amoprighttype/amprocrighttype */ |
||||
uint64 operatorset; /* bitmask of operators with these types */ |
||||
uint64 functionset; /* bitmask of support funcs with these types */ |
||||
} OpFamilyOpFuncGroup; |
||||
|
||||
|
||||
/* Functions in access/index/amvalidate.c */ |
||||
extern List *identify_opfamily_groups(CatCList *oprlist, CatCList *proclist); |
||||
extern bool check_amproc_signature(Oid funcid, Oid restype, bool exact, |
||||
int minargs, int maxargs,...); |
||||
extern bool check_amop_signature(Oid opno, Oid restype, |
||||
Oid lefttype, Oid righttype); |
||||
extern bool opfamily_can_sort_type(Oid opfamilyoid, Oid datatypeoid); |
||||
|
||||
#endif /* AMVALIDATE_H */ |
Loading…
Reference in new issue