2016-01-22 01:47:15 +01:00
|
|
|
/*-------------------------------------------------------------------------
|
|
|
|
*
|
|
|
|
* amvalidate.c
|
|
|
|
* Support routines for index access methods' amvalidate functions.
|
|
|
|
*
|
2018-01-03 05:30:12 +01:00
|
|
|
* Copyright (c) 2016-2018, PostgreSQL Global Development Group
|
2016-01-22 01:47:15 +01:00
|
|
|
*
|
|
|
|
*
|
|
|
|
* 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
|
2017-08-16 06:22:32 +02:00
|
|
|
* function. Return true if OK, false if not.
|
2016-01-22 01:47:15 +01:00
|
|
|
*
|
2017-08-16 06:22:32 +02:00
|
|
|
* The "..." represents maxargs argument-type OIDs. If "exact" is true, they
|
2016-01-22 01:47:15 +01:00
|
|
|
* 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.
|
2017-08-16 06:22:32 +02:00
|
|
|
* Return true if OK, false if not.
|
2016-01-22 01:47:15 +01:00
|
|
|
*
|
|
|
|
* 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;
|
|
|
|
}
|