postgres/src/backend/parser/parse_oper.c
Tom Lane e56bce5d43 Reconsider the handling of procedure OUT parameters.
Commit 2453ea142 redefined pg_proc.proargtypes to include the types of
OUT parameters, for procedures only.  While that had some advantages
for implementing the SQL-spec behavior of DROP PROCEDURE, it was pretty
disastrous from a number of other perspectives.  Notably, since the
primary key of pg_proc is name + proargtypes, this made it possible to
have multiple procedures with identical names + input arguments and
differing output argument types.  That would make it impossible to call
any one of the procedures by writing just NULL (or "?", or any other
data-type-free notation) for the output argument(s).  The change also
seems likely to cause grave confusion for client applications that
examine pg_proc and expect the traditional definition of proargtypes.

Hence, revert the definition of proargtypes to what it was, and
undo a number of complications that had been added to support that.

To support the SQL-spec behavior of DROP PROCEDURE, when there are
no argmode markers in the command's parameter list, we perform the
lookup both ways (that is, matching against both proargtypes and
proallargtypes), succeeding if we get just one unique match.
In principle this could result in ambiguous-function failures
that would not happen when using only one of the two rules.
However, overloading of procedure names is thought to be a pretty
rare usage, so this shouldn't cause many problems in practice.
Postgres-specific code such as pg_dump can defend against any
possibility of such failures by being careful to specify argmodes
for all procedure arguments.

This also fixes a few other bugs in the area of CALL statements
with named parameters, and improves the documentation a little.

catversion bump forced because the representation of procedures
with OUT arguments changes.

Discussion: https://postgr.es/m/3742981.1621533210@sss.pgh.pa.us
2021-06-10 17:11:36 -04:00

1067 lines
30 KiB
C

/*-------------------------------------------------------------------------
*
* parse_oper.c
* handle operator things for parser
*
* Portions Copyright (c) 1996-2021, 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 "access/htup_details.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/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 */
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(ParseState *pstate, OprCacheKey *key,
List *opname, Oid ltypeId, Oid rtypeId,
int location);
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.
*
* 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 = 'b';
else
{
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("postfix operators are not supported"),
parser_errposition(pstate, location)));
oprkind = 0; /* keep compiler quiet */
}
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;
}
/*
* LookupOperWithArgs
* Like LookupOperName, but the argument types are specified by
* a ObjectWithArgs node.
*/
Oid
LookupOperWithArgs(ObjectWithArgs *oper, bool noError)
{
TypeName *oprleft,
*oprright;
Oid leftoid,
rightoid;
Assert(list_length(oper->objargs) == 2);
oprleft = linitial_node(TypeName, oper->objargs);
oprright = lsecond_node(TypeName, oper->objargs);
if (oprleft == NULL)
leftoid = InvalidOid;
else
leftoid = LookupTypeNameOid(NULL, oprleft, noError);
if (oprright == NULL)
rightoid = InvalidOid;
else
rightoid = LookupTypeNameOid(NULL, oprright, noError);
return LookupOperName(NULL, oper->objname, leftoid, rightoid,
noError, -1);
}
/*
* 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 ((Form_pg_operator) GETSTRUCT(op))->oid;
}
/* 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(pstate, &key, opname, ltypeId, rtypeId, location);
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', false);
/* 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;
}
/* 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(pstate, &key, op, InvalidOid, arg, location);
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', false);
/* 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));
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)),
(!arg1 || !arg2) ?
errhint("No operator matches the given name and argument type. "
"You might need to add an explicit type cast.") :
errhint("No operator matches the given name and argument types. "
"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.
*
* last_srf should be a copy of pstate->p_last_srf from just before we
* started transforming the operator's arguments; this is used for nested-SRF
* detection. If the caller will throw an error anyway for a set-returning
* expression, it's okay to cheat and just pass pstate->p_last_srf.
*/
Expr *
make_op(ParseState *pstate, List *opname, Node *ltree, Node *rtree,
Node *last_srf, 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;
/* Check it's not a postfix operator */
if (rtree == NULL)
ereport(ERROR,
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("postfix operators are not supported")));
/* Select the operator */
if (ltree == NULL)
{
/* prefix 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 (ltree == NULL)
{
/* prefix 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;
/* if it returns a set, check that's OK */
if (result->opretset)
{
check_srf_call_placement(pstate, last_srf, location);
/* ... and remember it for error checks at higher levels */
pstate->p_last_srf = (Node *) result;
}
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->hashfuncid = InvalidOid;
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).
*
* pstate/location are used only to report the error position; pass NULL/-1
* if not available.
*/
static bool
make_oper_cache_key(ParseState *pstate, OprCacheKey *key, List *opname,
Oid ltypeId, Oid rtypeId, int location)
{
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)
{
ParseCallbackState pcbstate;
/* search only in exact schema given */
setup_parser_errposition_callback(&pcbstate, pstate, location);
key->search_path[0] = LookupExplicitNamespace(schemaname, false);
cancel_parser_errposition_callback(&pcbstate);
}
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;
ctl.keysize = sizeof(OprCacheKey);
ctl.entrysize = sizeof(OprCacheEntry);
OprCacheHash = hash_create("Operator lookup cache", 256,
&ctl, HASH_ELEM | HASH_BLOBS);
/* 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");
}
}