
This requires adjusting the API for syscache callback functions: they now get a hash value, not a TID, to identify the target tuple. Most of them weren't paying any attention to that argument anyway, but plancache did require a small amount of fixing. Also, improve performance a trifle by avoiding sending duplicate inval messages when a heap_update isn't changing the catcache lookup columns.
1125 lines
31 KiB
C
1125 lines
31 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* parse_oper.c
|
|
* handle operator things for parser
|
|
*
|
|
* Portions Copyright (c) 1996-2011, PostgreSQL Global Development Group
|
|
* Portions Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* src/backend/parser/parse_oper.c
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
|
|
#include "postgres.h"
|
|
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_type.h"
|
|
#include "lib/stringinfo.h"
|
|
#include "nodes/nodeFuncs.h"
|
|
#include "parser/parse_coerce.h"
|
|
#include "parser/parse_func.h"
|
|
#include "parser/parse_oper.h"
|
|
#include "parser/parse_type.h"
|
|
#include "utils/builtins.h"
|
|
#include "utils/hsearch.h"
|
|
#include "utils/inval.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "utils/syscache.h"
|
|
#include "utils/typcache.h"
|
|
|
|
|
|
/*
|
|
* The lookup key for the operator lookaside hash table. Unused bits must be
|
|
* zeroes to ensure hashing works consistently --- in particular, oprname
|
|
* must be zero-padded and any unused entries in search_path must be zero.
|
|
*
|
|
* search_path contains the actual search_path with which the entry was
|
|
* derived (minus temp namespace if any), or else the single specified
|
|
* schema OID if we are looking up an explicitly-qualified operator name.
|
|
*
|
|
* search_path has to be fixed-length since the hashtable code insists on
|
|
* fixed-size keys. If your search path is longer than that, we just punt
|
|
* and don't cache anything.
|
|
*/
|
|
|
|
/* If your search_path is longer than this, sucks to be you ... */
|
|
#define MAX_CACHED_PATH_LEN 16
|
|
|
|
typedef struct OprCacheKey
|
|
{
|
|
char oprname[NAMEDATALEN];
|
|
Oid left_arg; /* Left input OID, or 0 if prefix op */
|
|
Oid right_arg; /* Right input OID, or 0 if postfix op */
|
|
Oid search_path[MAX_CACHED_PATH_LEN];
|
|
} OprCacheKey;
|
|
|
|
typedef struct OprCacheEntry
|
|
{
|
|
/* the hash lookup key MUST BE FIRST */
|
|
OprCacheKey key;
|
|
|
|
Oid opr_oid; /* OID of the resolved operator */
|
|
} OprCacheEntry;
|
|
|
|
|
|
static Oid binary_oper_exact(List *opname, Oid arg1, Oid arg2);
|
|
static FuncDetailCode oper_select_candidate(int nargs,
|
|
Oid *input_typeids,
|
|
FuncCandidateList candidates,
|
|
Oid *operOid);
|
|
static const char *op_signature_string(List *op, char oprkind,
|
|
Oid arg1, Oid arg2);
|
|
static void op_error(ParseState *pstate, List *op, char oprkind,
|
|
Oid arg1, Oid arg2,
|
|
FuncDetailCode fdresult, int location);
|
|
static bool make_oper_cache_key(OprCacheKey *key, List *opname,
|
|
Oid ltypeId, Oid rtypeId);
|
|
static Oid find_oper_cache_entry(OprCacheKey *key);
|
|
static void make_oper_cache_entry(OprCacheKey *key, Oid opr_oid);
|
|
static void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue);
|
|
|
|
|
|
/*
|
|
* LookupOperName
|
|
* Given a possibly-qualified operator name and exact input datatypes,
|
|
* look up the operator.
|
|
*
|
|
* Pass oprleft = InvalidOid for a prefix op, oprright = InvalidOid for
|
|
* a postfix op.
|
|
*
|
|
* If the operator name is not schema-qualified, it is sought in the current
|
|
* namespace search path.
|
|
*
|
|
* If the operator is not found, we return InvalidOid if noError is true,
|
|
* else raise an error. pstate and location are used only to report the
|
|
* error position; pass NULL/-1 if not available.
|
|
*/
|
|
Oid
|
|
LookupOperName(ParseState *pstate, List *opername, Oid oprleft, Oid oprright,
|
|
bool noError, int location)
|
|
{
|
|
Oid result;
|
|
|
|
result = OpernameGetOprid(opername, oprleft, oprright);
|
|
if (OidIsValid(result))
|
|
return result;
|
|
|
|
/* we don't use op_error here because only an exact match is wanted */
|
|
if (!noError)
|
|
{
|
|
char oprkind;
|
|
|
|
if (!OidIsValid(oprleft))
|
|
oprkind = 'l';
|
|
else if (!OidIsValid(oprright))
|
|
oprkind = 'r';
|
|
else
|
|
oprkind = 'b';
|
|
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operator does not exist: %s",
|
|
op_signature_string(opername, oprkind,
|
|
oprleft, oprright)),
|
|
parser_errposition(pstate, location)));
|
|
}
|
|
|
|
return InvalidOid;
|
|
}
|
|
|
|
/*
|
|
* LookupOperNameTypeNames
|
|
* Like LookupOperName, but the argument types are specified by
|
|
* TypeName nodes.
|
|
*
|
|
* Pass oprleft = NULL for a prefix op, oprright = NULL for a postfix op.
|
|
*/
|
|
Oid
|
|
LookupOperNameTypeNames(ParseState *pstate, List *opername,
|
|
TypeName *oprleft, TypeName *oprright,
|
|
bool noError, int location)
|
|
{
|
|
Oid leftoid,
|
|
rightoid;
|
|
|
|
if (oprleft == NULL)
|
|
leftoid = InvalidOid;
|
|
else
|
|
leftoid = typenameTypeId(pstate, oprleft);
|
|
|
|
if (oprright == NULL)
|
|
rightoid = InvalidOid;
|
|
else
|
|
rightoid = typenameTypeId(pstate, oprright);
|
|
|
|
return LookupOperName(pstate, opername, leftoid, rightoid,
|
|
noError, location);
|
|
}
|
|
|
|
/*
|
|
* get_sort_group_operators - get default sorting/grouping operators for type
|
|
*
|
|
* We fetch the "<", "=", and ">" operators all at once to reduce lookup
|
|
* overhead (knowing that most callers will be interested in at least two).
|
|
* However, a given datatype might have only an "=" operator, if it is
|
|
* hashable but not sortable. (Other combinations of present and missing
|
|
* operators shouldn't happen, unless the system catalogs are messed up.)
|
|
*
|
|
* If an operator is missing and the corresponding needXX flag is true,
|
|
* throw a standard error message, else return InvalidOid.
|
|
*
|
|
* In addition to the operator OIDs themselves, this function can identify
|
|
* whether the "=" operator is hashable.
|
|
*
|
|
* Callers can pass NULL pointers for any results they don't care to get.
|
|
*
|
|
* Note: the results are guaranteed to be exact or binary-compatible matches,
|
|
* since most callers are not prepared to cope with adding any run-time type
|
|
* coercion steps.
|
|
*/
|
|
void
|
|
get_sort_group_operators(Oid argtype,
|
|
bool needLT, bool needEQ, bool needGT,
|
|
Oid *ltOpr, Oid *eqOpr, Oid *gtOpr,
|
|
bool *isHashable)
|
|
{
|
|
TypeCacheEntry *typentry;
|
|
int cache_flags;
|
|
Oid lt_opr;
|
|
Oid eq_opr;
|
|
Oid gt_opr;
|
|
bool hashable;
|
|
|
|
/*
|
|
* Look up the operators using the type cache.
|
|
*
|
|
* Note: the search algorithm used by typcache.c ensures that the results
|
|
* are consistent, ie all from matching opclasses.
|
|
*/
|
|
if (isHashable != NULL)
|
|
cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR |
|
|
TYPECACHE_HASH_PROC;
|
|
else
|
|
cache_flags = TYPECACHE_LT_OPR | TYPECACHE_EQ_OPR | TYPECACHE_GT_OPR;
|
|
|
|
typentry = lookup_type_cache(argtype, cache_flags);
|
|
lt_opr = typentry->lt_opr;
|
|
eq_opr = typentry->eq_opr;
|
|
gt_opr = typentry->gt_opr;
|
|
hashable = OidIsValid(typentry->hash_proc);
|
|
|
|
/* Report errors if needed */
|
|
if ((needLT && !OidIsValid(lt_opr)) ||
|
|
(needGT && !OidIsValid(gt_opr)))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("could not identify an ordering operator for type %s",
|
|
format_type_be(argtype)),
|
|
errhint("Use an explicit ordering operator or modify the query.")));
|
|
if (needEQ && !OidIsValid(eq_opr))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("could not identify an equality operator for type %s",
|
|
format_type_be(argtype))));
|
|
|
|
/* Return results as needed */
|
|
if (ltOpr)
|
|
*ltOpr = lt_opr;
|
|
if (eqOpr)
|
|
*eqOpr = eq_opr;
|
|
if (gtOpr)
|
|
*gtOpr = gt_opr;
|
|
if (isHashable)
|
|
*isHashable = hashable;
|
|
}
|
|
|
|
|
|
/* given operator tuple, return the operator OID */
|
|
Oid
|
|
oprid(Operator op)
|
|
{
|
|
return HeapTupleGetOid(op);
|
|
}
|
|
|
|
/* given operator tuple, return the underlying function's OID */
|
|
Oid
|
|
oprfuncid(Operator op)
|
|
{
|
|
Form_pg_operator pgopform = (Form_pg_operator) GETSTRUCT(op);
|
|
|
|
return pgopform->oprcode;
|
|
}
|
|
|
|
|
|
/* binary_oper_exact()
|
|
* Check for an "exact" match to the specified operand types.
|
|
*
|
|
* If one operand is an unknown literal, assume it should be taken to be
|
|
* the same type as the other operand for this purpose. Also, consider
|
|
* the possibility that the other operand is a domain type that needs to
|
|
* be reduced to its base type to find an "exact" match.
|
|
*/
|
|
static Oid
|
|
binary_oper_exact(List *opname, Oid arg1, Oid arg2)
|
|
{
|
|
Oid result;
|
|
bool was_unknown = false;
|
|
|
|
/* Unspecified type for one of the arguments? then use the other */
|
|
if ((arg1 == UNKNOWNOID) && (arg2 != InvalidOid))
|
|
{
|
|
arg1 = arg2;
|
|
was_unknown = true;
|
|
}
|
|
else if ((arg2 == UNKNOWNOID) && (arg1 != InvalidOid))
|
|
{
|
|
arg2 = arg1;
|
|
was_unknown = true;
|
|
}
|
|
|
|
result = OpernameGetOprid(opname, arg1, arg2);
|
|
if (OidIsValid(result))
|
|
return result;
|
|
|
|
if (was_unknown)
|
|
{
|
|
/* arg1 and arg2 are the same here, need only look at arg1 */
|
|
Oid basetype = getBaseType(arg1);
|
|
|
|
if (basetype != arg1)
|
|
{
|
|
result = OpernameGetOprid(opname, basetype, basetype);
|
|
if (OidIsValid(result))
|
|
return result;
|
|
}
|
|
}
|
|
|
|
return InvalidOid;
|
|
}
|
|
|
|
|
|
/* oper_select_candidate()
|
|
* Given the input argtype array and one or more candidates
|
|
* for the operator, attempt to resolve the conflict.
|
|
*
|
|
* Returns FUNCDETAIL_NOTFOUND, FUNCDETAIL_MULTIPLE, or FUNCDETAIL_NORMAL.
|
|
* In the success case the Oid of the best candidate is stored in *operOid.
|
|
*
|
|
* Note that the caller has already determined that there is no candidate
|
|
* exactly matching the input argtype(s). Incompatible candidates are not yet
|
|
* pruned away, however.
|
|
*/
|
|
static FuncDetailCode
|
|
oper_select_candidate(int nargs,
|
|
Oid *input_typeids,
|
|
FuncCandidateList candidates,
|
|
Oid *operOid) /* output argument */
|
|
{
|
|
int ncandidates;
|
|
|
|
/*
|
|
* Delete any candidates that cannot actually accept the given input
|
|
* types, whether directly or by coercion.
|
|
*/
|
|
ncandidates = func_match_argtypes(nargs, input_typeids,
|
|
candidates, &candidates);
|
|
|
|
/* Done if no candidate or only one candidate survives */
|
|
if (ncandidates == 0)
|
|
{
|
|
*operOid = InvalidOid;
|
|
return FUNCDETAIL_NOTFOUND;
|
|
}
|
|
if (ncandidates == 1)
|
|
{
|
|
*operOid = candidates->oid;
|
|
return FUNCDETAIL_NORMAL;
|
|
}
|
|
|
|
/*
|
|
* Use the same heuristics as for ambiguous functions to resolve the
|
|
* conflict.
|
|
*/
|
|
candidates = func_select_candidate(nargs, input_typeids, candidates);
|
|
|
|
if (candidates)
|
|
{
|
|
*operOid = candidates->oid;
|
|
return FUNCDETAIL_NORMAL;
|
|
}
|
|
|
|
*operOid = InvalidOid;
|
|
return FUNCDETAIL_MULTIPLE; /* failed to select a best candidate */
|
|
}
|
|
|
|
|
|
/* oper() -- search for a binary operator
|
|
* Given operator name, types of arg1 and arg2, return oper struct.
|
|
*
|
|
* IMPORTANT: the returned operator (if any) is only promised to be
|
|
* coercion-compatible with the input datatypes. Do not use this if
|
|
* you need an exact- or binary-compatible match; see compatible_oper.
|
|
*
|
|
* If no matching operator found, return NULL if noError is true,
|
|
* raise an error if it is false. pstate and location are used only to report
|
|
* the error position; pass NULL/-1 if not available.
|
|
*
|
|
* NOTE: on success, the returned object is a syscache entry. The caller
|
|
* must ReleaseSysCache() the entry when done with it.
|
|
*/
|
|
Operator
|
|
oper(ParseState *pstate, List *opname, Oid ltypeId, Oid rtypeId,
|
|
bool noError, int location)
|
|
{
|
|
Oid operOid;
|
|
OprCacheKey key;
|
|
bool key_ok;
|
|
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
|
|
HeapTuple tup = NULL;
|
|
|
|
/*
|
|
* Try to find the mapping in the lookaside cache.
|
|
*/
|
|
key_ok = make_oper_cache_key(&key, opname, ltypeId, rtypeId);
|
|
if (key_ok)
|
|
{
|
|
operOid = find_oper_cache_entry(&key);
|
|
if (OidIsValid(operOid))
|
|
{
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
if (HeapTupleIsValid(tup))
|
|
return (Operator) tup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First try for an "exact" match.
|
|
*/
|
|
operOid = binary_oper_exact(opname, ltypeId, rtypeId);
|
|
if (!OidIsValid(operOid))
|
|
{
|
|
/*
|
|
* Otherwise, search for the most suitable candidate.
|
|
*/
|
|
FuncCandidateList clist;
|
|
|
|
/* Get binary operators of given name */
|
|
clist = OpernameGetCandidates(opname, 'b');
|
|
|
|
/* No operators found? Then fail... */
|
|
if (clist != NULL)
|
|
{
|
|
/*
|
|
* Unspecified type for one of the arguments? then use the other
|
|
* (XXX this is probably dead code?)
|
|
*/
|
|
Oid inputOids[2];
|
|
|
|
if (rtypeId == InvalidOid)
|
|
rtypeId = ltypeId;
|
|
else if (ltypeId == InvalidOid)
|
|
ltypeId = rtypeId;
|
|
inputOids[0] = ltypeId;
|
|
inputOids[1] = rtypeId;
|
|
fdresult = oper_select_candidate(2, inputOids, clist, &operOid);
|
|
}
|
|
}
|
|
|
|
if (OidIsValid(operOid))
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
{
|
|
if (key_ok)
|
|
make_oper_cache_entry(&key, operOid);
|
|
}
|
|
else if (!noError)
|
|
op_error(pstate, opname, 'b', ltypeId, rtypeId, fdresult, location);
|
|
|
|
return (Operator) tup;
|
|
}
|
|
|
|
/* compatible_oper()
|
|
* given an opname and input datatypes, find a compatible binary operator
|
|
*
|
|
* This is tighter than oper() because it will not return an operator that
|
|
* requires coercion of the input datatypes (but binary-compatible operators
|
|
* are accepted). Otherwise, the semantics are the same.
|
|
*/
|
|
Operator
|
|
compatible_oper(ParseState *pstate, List *op, Oid arg1, Oid arg2,
|
|
bool noError, int location)
|
|
{
|
|
Operator optup;
|
|
Form_pg_operator opform;
|
|
|
|
/* oper() will find the best available match */
|
|
optup = oper(pstate, op, arg1, arg2, noError, location);
|
|
if (optup == (Operator) NULL)
|
|
return (Operator) NULL; /* must be noError case */
|
|
|
|
/* but is it good enough? */
|
|
opform = (Form_pg_operator) GETSTRUCT(optup);
|
|
if (IsBinaryCoercible(arg1, opform->oprleft) &&
|
|
IsBinaryCoercible(arg2, opform->oprright))
|
|
return optup;
|
|
|
|
/* nope... */
|
|
ReleaseSysCache(optup);
|
|
|
|
if (!noError)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operator requires run-time type coercion: %s",
|
|
op_signature_string(op, 'b', arg1, arg2)),
|
|
parser_errposition(pstate, location)));
|
|
|
|
return (Operator) NULL;
|
|
}
|
|
|
|
/* compatible_oper_opid() -- get OID of a binary operator
|
|
*
|
|
* This is a convenience routine that extracts only the operator OID
|
|
* from the result of compatible_oper(). InvalidOid is returned if the
|
|
* lookup fails and noError is true.
|
|
*/
|
|
Oid
|
|
compatible_oper_opid(List *op, Oid arg1, Oid arg2, bool noError)
|
|
{
|
|
Operator optup;
|
|
Oid result;
|
|
|
|
optup = compatible_oper(NULL, op, arg1, arg2, noError, -1);
|
|
if (optup != NULL)
|
|
{
|
|
result = oprid(optup);
|
|
ReleaseSysCache(optup);
|
|
return result;
|
|
}
|
|
return InvalidOid;
|
|
}
|
|
|
|
|
|
/* right_oper() -- search for a unary right operator (postfix operator)
|
|
* Given operator name and type of arg, return oper struct.
|
|
*
|
|
* IMPORTANT: the returned operator (if any) is only promised to be
|
|
* coercion-compatible with the input datatype. Do not use this if
|
|
* you need an exact- or binary-compatible match.
|
|
*
|
|
* If no matching operator found, return NULL if noError is true,
|
|
* raise an error if it is false. pstate and location are used only to report
|
|
* the error position; pass NULL/-1 if not available.
|
|
*
|
|
* NOTE: on success, the returned object is a syscache entry. The caller
|
|
* must ReleaseSysCache() the entry when done with it.
|
|
*/
|
|
Operator
|
|
right_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
|
|
{
|
|
Oid operOid;
|
|
OprCacheKey key;
|
|
bool key_ok;
|
|
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
|
|
HeapTuple tup = NULL;
|
|
|
|
/*
|
|
* Try to find the mapping in the lookaside cache.
|
|
*/
|
|
key_ok = make_oper_cache_key(&key, op, arg, InvalidOid);
|
|
if (key_ok)
|
|
{
|
|
operOid = find_oper_cache_entry(&key);
|
|
if (OidIsValid(operOid))
|
|
{
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
if (HeapTupleIsValid(tup))
|
|
return (Operator) tup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First try for an "exact" match.
|
|
*/
|
|
operOid = OpernameGetOprid(op, arg, InvalidOid);
|
|
if (!OidIsValid(operOid))
|
|
{
|
|
/*
|
|
* Otherwise, search for the most suitable candidate.
|
|
*/
|
|
FuncCandidateList clist;
|
|
|
|
/* Get postfix operators of given name */
|
|
clist = OpernameGetCandidates(op, 'r');
|
|
|
|
/* No operators found? Then fail... */
|
|
if (clist != NULL)
|
|
{
|
|
/*
|
|
* We must run oper_select_candidate even if only one candidate,
|
|
* otherwise we may falsely return a non-type-compatible operator.
|
|
*/
|
|
fdresult = oper_select_candidate(1, &arg, clist, &operOid);
|
|
}
|
|
}
|
|
|
|
if (OidIsValid(operOid))
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
{
|
|
if (key_ok)
|
|
make_oper_cache_entry(&key, operOid);
|
|
}
|
|
else if (!noError)
|
|
op_error(pstate, op, 'r', arg, InvalidOid, fdresult, location);
|
|
|
|
return (Operator) tup;
|
|
}
|
|
|
|
|
|
/* left_oper() -- search for a unary left operator (prefix operator)
|
|
* Given operator name and type of arg, return oper struct.
|
|
*
|
|
* IMPORTANT: the returned operator (if any) is only promised to be
|
|
* coercion-compatible with the input datatype. Do not use this if
|
|
* you need an exact- or binary-compatible match.
|
|
*
|
|
* If no matching operator found, return NULL if noError is true,
|
|
* raise an error if it is false. pstate and location are used only to report
|
|
* the error position; pass NULL/-1 if not available.
|
|
*
|
|
* NOTE: on success, the returned object is a syscache entry. The caller
|
|
* must ReleaseSysCache() the entry when done with it.
|
|
*/
|
|
Operator
|
|
left_oper(ParseState *pstate, List *op, Oid arg, bool noError, int location)
|
|
{
|
|
Oid operOid;
|
|
OprCacheKey key;
|
|
bool key_ok;
|
|
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
|
|
HeapTuple tup = NULL;
|
|
|
|
/*
|
|
* Try to find the mapping in the lookaside cache.
|
|
*/
|
|
key_ok = make_oper_cache_key(&key, op, InvalidOid, arg);
|
|
if (key_ok)
|
|
{
|
|
operOid = find_oper_cache_entry(&key);
|
|
if (OidIsValid(operOid))
|
|
{
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
if (HeapTupleIsValid(tup))
|
|
return (Operator) tup;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* First try for an "exact" match.
|
|
*/
|
|
operOid = OpernameGetOprid(op, InvalidOid, arg);
|
|
if (!OidIsValid(operOid))
|
|
{
|
|
/*
|
|
* Otherwise, search for the most suitable candidate.
|
|
*/
|
|
FuncCandidateList clist;
|
|
|
|
/* Get prefix operators of given name */
|
|
clist = OpernameGetCandidates(op, 'l');
|
|
|
|
/* No operators found? Then fail... */
|
|
if (clist != NULL)
|
|
{
|
|
/*
|
|
* The returned list has args in the form (0, oprright). Move the
|
|
* useful data into args[0] to keep oper_select_candidate simple.
|
|
* XXX we are assuming here that we may scribble on the list!
|
|
*/
|
|
FuncCandidateList clisti;
|
|
|
|
for (clisti = clist; clisti != NULL; clisti = clisti->next)
|
|
{
|
|
clisti->args[0] = clisti->args[1];
|
|
}
|
|
|
|
/*
|
|
* We must run oper_select_candidate even if only one candidate,
|
|
* otherwise we may falsely return a non-type-compatible operator.
|
|
*/
|
|
fdresult = oper_select_candidate(1, &arg, clist, &operOid);
|
|
}
|
|
}
|
|
|
|
if (OidIsValid(operOid))
|
|
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
|
|
|
|
if (HeapTupleIsValid(tup))
|
|
{
|
|
if (key_ok)
|
|
make_oper_cache_entry(&key, operOid);
|
|
}
|
|
else if (!noError)
|
|
op_error(pstate, op, 'l', InvalidOid, arg, fdresult, location);
|
|
|
|
return (Operator) tup;
|
|
}
|
|
|
|
/*
|
|
* op_signature_string
|
|
* Build a string representing an operator name, including arg type(s).
|
|
* The result is something like "integer + integer".
|
|
*
|
|
* This is typically used in the construction of operator-not-found error
|
|
* messages.
|
|
*/
|
|
static const char *
|
|
op_signature_string(List *op, char oprkind, Oid arg1, Oid arg2)
|
|
{
|
|
StringInfoData argbuf;
|
|
|
|
initStringInfo(&argbuf);
|
|
|
|
if (oprkind != 'l')
|
|
appendStringInfo(&argbuf, "%s ", format_type_be(arg1));
|
|
|
|
appendStringInfoString(&argbuf, NameListToString(op));
|
|
|
|
if (oprkind != 'r')
|
|
appendStringInfo(&argbuf, " %s", format_type_be(arg2));
|
|
|
|
return argbuf.data; /* return palloc'd string buffer */
|
|
}
|
|
|
|
/*
|
|
* op_error - utility routine to complain about an unresolvable operator
|
|
*/
|
|
static void
|
|
op_error(ParseState *pstate, List *op, char oprkind,
|
|
Oid arg1, Oid arg2,
|
|
FuncDetailCode fdresult, int location)
|
|
{
|
|
if (fdresult == FUNCDETAIL_MULTIPLE)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_AMBIGUOUS_FUNCTION),
|
|
errmsg("operator is not unique: %s",
|
|
op_signature_string(op, oprkind, arg1, arg2)),
|
|
errhint("Could not choose a best candidate operator. "
|
|
"You might need to add explicit type casts."),
|
|
parser_errposition(pstate, location)));
|
|
else
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operator does not exist: %s",
|
|
op_signature_string(op, oprkind, arg1, arg2)),
|
|
errhint("No operator matches the given name and argument type(s). "
|
|
"You might need to add explicit type casts."),
|
|
parser_errposition(pstate, location)));
|
|
}
|
|
|
|
/*
|
|
* make_op()
|
|
* Operator expression construction.
|
|
*
|
|
* Transform operator expression ensuring type compatibility.
|
|
* This is where some type conversion happens.
|
|
*
|
|
* As with coerce_type, pstate may be NULL if no special unknown-Param
|
|
* processing is wanted.
|
|
*/
|
|
Expr *
|
|
make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
|
|
int location)
|
|
{
|
|
Oid ltypeId,
|
|
rtypeId;
|
|
Operator tup;
|
|
Form_pg_operator opform;
|
|
Oid actual_arg_types[2];
|
|
Oid declared_arg_types[2];
|
|
int nargs;
|
|
List *args;
|
|
Oid rettype;
|
|
OpExpr *result;
|
|
|
|
/* Select the operator */
|
|
if (rtree == NULL)
|
|
{
|
|
/* right operator */
|
|
ltypeId = exprType(ltree);
|
|
rtypeId = InvalidOid;
|
|
tup = right_oper(pstate, opname, ltypeId, false, location);
|
|
}
|
|
else if (ltree == NULL)
|
|
{
|
|
/* left operator */
|
|
rtypeId = exprType(rtree);
|
|
ltypeId = InvalidOid;
|
|
tup = left_oper(pstate, opname, rtypeId, false, location);
|
|
}
|
|
else
|
|
{
|
|
/* otherwise, binary operator */
|
|
ltypeId = exprType(ltree);
|
|
rtypeId = exprType(rtree);
|
|
tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
|
|
}
|
|
|
|
opform = (Form_pg_operator) GETSTRUCT(tup);
|
|
|
|
/* Check it's not a shell */
|
|
if (!RegProcedureIsValid(opform->oprcode))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operator is only a shell: %s",
|
|
op_signature_string(opname,
|
|
opform->oprkind,
|
|
opform->oprleft,
|
|
opform->oprright)),
|
|
parser_errposition(pstate, location)));
|
|
|
|
/* Do typecasting and build the expression tree */
|
|
if (rtree == NULL)
|
|
{
|
|
/* right operator */
|
|
args = list_make1(ltree);
|
|
actual_arg_types[0] = ltypeId;
|
|
declared_arg_types[0] = opform->oprleft;
|
|
nargs = 1;
|
|
}
|
|
else if (ltree == NULL)
|
|
{
|
|
/* left operator */
|
|
args = list_make1(rtree);
|
|
actual_arg_types[0] = rtypeId;
|
|
declared_arg_types[0] = opform->oprright;
|
|
nargs = 1;
|
|
}
|
|
else
|
|
{
|
|
/* otherwise, binary operator */
|
|
args = list_make2(ltree, rtree);
|
|
actual_arg_types[0] = ltypeId;
|
|
actual_arg_types[1] = rtypeId;
|
|
declared_arg_types[0] = opform->oprleft;
|
|
declared_arg_types[1] = opform->oprright;
|
|
nargs = 2;
|
|
}
|
|
|
|
/*
|
|
* enforce consistency with polymorphic argument and return types,
|
|
* possibly adjusting return type or declared_arg_types (which will be
|
|
* used as the cast destination by make_fn_arguments)
|
|
*/
|
|
rettype = enforce_generic_type_consistency(actual_arg_types,
|
|
declared_arg_types,
|
|
nargs,
|
|
opform->oprresult,
|
|
false);
|
|
|
|
/* perform the necessary typecasting of arguments */
|
|
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
|
|
|
|
/* and build the expression node */
|
|
result = makeNode(OpExpr);
|
|
result->opno = oprid(tup);
|
|
result->opfuncid = opform->oprcode;
|
|
result->opresulttype = rettype;
|
|
result->opretset = get_func_retset(opform->oprcode);
|
|
/* opcollid and inputcollid will be set by parse_collate.c */
|
|
result->args = args;
|
|
result->location = location;
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
return (Expr *) result;
|
|
}
|
|
|
|
/*
|
|
* make_scalar_array_op()
|
|
* Build expression tree for "scalar op ANY/ALL (array)" construct.
|
|
*/
|
|
Expr *
|
|
make_scalar_array_op(ParseState *pstate, List *opname,
|
|
bool useOr,
|
|
Node *ltree, Node *rtree,
|
|
int location)
|
|
{
|
|
Oid ltypeId,
|
|
rtypeId,
|
|
atypeId,
|
|
res_atypeId;
|
|
Operator tup;
|
|
Form_pg_operator opform;
|
|
Oid actual_arg_types[2];
|
|
Oid declared_arg_types[2];
|
|
List *args;
|
|
Oid rettype;
|
|
ScalarArrayOpExpr *result;
|
|
|
|
ltypeId = exprType(ltree);
|
|
atypeId = exprType(rtree);
|
|
|
|
/*
|
|
* The right-hand input of the operator will be the element type of the
|
|
* array. However, if we currently have just an untyped literal on the
|
|
* right, stay with that and hope we can resolve the operator.
|
|
*/
|
|
if (atypeId == UNKNOWNOID)
|
|
rtypeId = UNKNOWNOID;
|
|
else
|
|
{
|
|
rtypeId = get_base_element_type(atypeId);
|
|
if (!OidIsValid(rtypeId))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("op ANY/ALL (array) requires array on right side"),
|
|
parser_errposition(pstate, location)));
|
|
}
|
|
|
|
/* Now resolve the operator */
|
|
tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
|
|
opform = (Form_pg_operator) GETSTRUCT(tup);
|
|
|
|
/* Check it's not a shell */
|
|
if (!RegProcedureIsValid(opform->oprcode))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_FUNCTION),
|
|
errmsg("operator is only a shell: %s",
|
|
op_signature_string(opname,
|
|
opform->oprkind,
|
|
opform->oprleft,
|
|
opform->oprright)),
|
|
parser_errposition(pstate, location)));
|
|
|
|
args = list_make2(ltree, rtree);
|
|
actual_arg_types[0] = ltypeId;
|
|
actual_arg_types[1] = rtypeId;
|
|
declared_arg_types[0] = opform->oprleft;
|
|
declared_arg_types[1] = opform->oprright;
|
|
|
|
/*
|
|
* enforce consistency with polymorphic argument and return types,
|
|
* possibly adjusting return type or declared_arg_types (which will be
|
|
* used as the cast destination by make_fn_arguments)
|
|
*/
|
|
rettype = enforce_generic_type_consistency(actual_arg_types,
|
|
declared_arg_types,
|
|
2,
|
|
opform->oprresult,
|
|
false);
|
|
|
|
/*
|
|
* Check that operator result is boolean
|
|
*/
|
|
if (rettype != BOOLOID)
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("op ANY/ALL (array) requires operator to yield boolean"),
|
|
parser_errposition(pstate, location)));
|
|
if (get_func_retset(opform->oprcode))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_WRONG_OBJECT_TYPE),
|
|
errmsg("op ANY/ALL (array) requires operator not to return a set"),
|
|
parser_errposition(pstate, location)));
|
|
|
|
/*
|
|
* Now switch back to the array type on the right, arranging for any
|
|
* needed cast to be applied. Beware of polymorphic operators here;
|
|
* enforce_generic_type_consistency may or may not have replaced a
|
|
* polymorphic type with a real one.
|
|
*/
|
|
if (IsPolymorphicType(declared_arg_types[1]))
|
|
{
|
|
/* assume the actual array type is OK */
|
|
res_atypeId = atypeId;
|
|
}
|
|
else
|
|
{
|
|
res_atypeId = get_array_type(declared_arg_types[1]);
|
|
if (!OidIsValid(res_atypeId))
|
|
ereport(ERROR,
|
|
(errcode(ERRCODE_UNDEFINED_OBJECT),
|
|
errmsg("could not find array type for data type %s",
|
|
format_type_be(declared_arg_types[1])),
|
|
parser_errposition(pstate, location)));
|
|
}
|
|
actual_arg_types[1] = atypeId;
|
|
declared_arg_types[1] = res_atypeId;
|
|
|
|
/* perform the necessary typecasting of arguments */
|
|
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
|
|
|
|
/* and build the expression node */
|
|
result = makeNode(ScalarArrayOpExpr);
|
|
result->opno = oprid(tup);
|
|
result->opfuncid = opform->oprcode;
|
|
result->useOr = useOr;
|
|
/* inputcollid will be set by parse_collate.c */
|
|
result->args = args;
|
|
result->location = location;
|
|
|
|
ReleaseSysCache(tup);
|
|
|
|
return (Expr *) result;
|
|
}
|
|
|
|
|
|
/*
|
|
* Lookaside cache to speed operator lookup. Possibly this should be in
|
|
* a separate module under utils/cache/ ?
|
|
*
|
|
* The idea here is that the mapping from operator name and given argument
|
|
* types is constant for a given search path (or single specified schema OID)
|
|
* so long as the contents of pg_operator and pg_cast don't change. And that
|
|
* mapping is pretty expensive to compute, especially for ambiguous operators;
|
|
* this is mainly because there are a *lot* of instances of popular operator
|
|
* names such as "=", and we have to check each one to see which is the
|
|
* best match. So once we have identified the correct mapping, we save it
|
|
* in a cache that need only be flushed on pg_operator or pg_cast change.
|
|
* (pg_cast must be considered because changes in the set of implicit casts
|
|
* affect the set of applicable operators for any given input datatype.)
|
|
*
|
|
* XXX in principle, ALTER TABLE ... INHERIT could affect the mapping as
|
|
* well, but we disregard that since there's no convenient way to find out
|
|
* about it, and it seems a pretty far-fetched corner-case anyway.
|
|
*
|
|
* Note: at some point it might be worth doing a similar cache for function
|
|
* lookups. However, the potential gain is a lot less since (a) function
|
|
* names are generally not overloaded as heavily as operator names, and
|
|
* (b) we'd have to flush on pg_proc updates, which are probably a good
|
|
* deal more common than pg_operator updates.
|
|
*/
|
|
|
|
/* The operator cache hashtable */
|
|
static HTAB *OprCacheHash = NULL;
|
|
|
|
|
|
/*
|
|
* make_oper_cache_key
|
|
* Fill the lookup key struct given operator name and arg types.
|
|
*
|
|
* Returns TRUE if successful, FALSE if the search_path overflowed
|
|
* (hence no caching is possible).
|
|
*/
|
|
static bool
|
|
make_oper_cache_key(OprCacheKey *key, List *opname, Oid ltypeId, Oid rtypeId)
|
|
{
|
|
char *schemaname;
|
|
char *opername;
|
|
|
|
/* deconstruct the name list */
|
|
DeconstructQualifiedName(opname, &schemaname, &opername);
|
|
|
|
/* ensure zero-fill for stable hashing */
|
|
MemSet(key, 0, sizeof(OprCacheKey));
|
|
|
|
/* save operator name and input types into key */
|
|
strlcpy(key->oprname, opername, NAMEDATALEN);
|
|
key->left_arg = ltypeId;
|
|
key->right_arg = rtypeId;
|
|
|
|
if (schemaname)
|
|
{
|
|
/* search only in exact schema given */
|
|
key->search_path[0] = LookupExplicitNamespace(schemaname);
|
|
}
|
|
else
|
|
{
|
|
/* get the active search path */
|
|
if (fetch_search_path_array(key->search_path,
|
|
MAX_CACHED_PATH_LEN) > MAX_CACHED_PATH_LEN)
|
|
return false; /* oops, didn't fit */
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* find_oper_cache_entry
|
|
*
|
|
* Look for a cache entry matching the given key. If found, return the
|
|
* contained operator OID, else return InvalidOid.
|
|
*/
|
|
static Oid
|
|
find_oper_cache_entry(OprCacheKey *key)
|
|
{
|
|
OprCacheEntry *oprentry;
|
|
|
|
if (OprCacheHash == NULL)
|
|
{
|
|
/* First time through: initialize the hash table */
|
|
HASHCTL ctl;
|
|
|
|
MemSet(&ctl, 0, sizeof(ctl));
|
|
ctl.keysize = sizeof(OprCacheKey);
|
|
ctl.entrysize = sizeof(OprCacheEntry);
|
|
ctl.hash = tag_hash;
|
|
OprCacheHash = hash_create("Operator lookup cache", 256,
|
|
&ctl, HASH_ELEM | HASH_FUNCTION);
|
|
|
|
/* Arrange to flush cache on pg_operator and pg_cast changes */
|
|
CacheRegisterSyscacheCallback(OPERNAMENSP,
|
|
InvalidateOprCacheCallBack,
|
|
(Datum) 0);
|
|
CacheRegisterSyscacheCallback(CASTSOURCETARGET,
|
|
InvalidateOprCacheCallBack,
|
|
(Datum) 0);
|
|
}
|
|
|
|
/* Look for an existing entry */
|
|
oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
|
|
(void *) key,
|
|
HASH_FIND, NULL);
|
|
if (oprentry == NULL)
|
|
return InvalidOid;
|
|
|
|
return oprentry->opr_oid;
|
|
}
|
|
|
|
/*
|
|
* make_oper_cache_entry
|
|
*
|
|
* Insert a cache entry for the given key.
|
|
*/
|
|
static void
|
|
make_oper_cache_entry(OprCacheKey *key, Oid opr_oid)
|
|
{
|
|
OprCacheEntry *oprentry;
|
|
|
|
Assert(OprCacheHash != NULL);
|
|
|
|
oprentry = (OprCacheEntry *) hash_search(OprCacheHash,
|
|
(void *) key,
|
|
HASH_ENTER, NULL);
|
|
oprentry->opr_oid = opr_oid;
|
|
}
|
|
|
|
/*
|
|
* Callback for pg_operator and pg_cast inval events
|
|
*/
|
|
static void
|
|
InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
|
|
{
|
|
HASH_SEQ_STATUS status;
|
|
OprCacheEntry *hentry;
|
|
|
|
Assert(OprCacheHash != NULL);
|
|
|
|
/* Currently we just flush all entries; hard to be smarter ... */
|
|
hash_seq_init(&status, OprCacheHash);
|
|
|
|
while ((hentry = (OprCacheEntry *) hash_seq_search(&status)) != NULL)
|
|
{
|
|
if (hash_search(OprCacheHash,
|
|
(void *) &hentry->key,
|
|
HASH_REMOVE, NULL) == NULL)
|
|
elog(ERROR, "hash table corrupted");
|
|
}
|
|
}
|