*
* parse_oper.cpp
* handle operator things for parser
*
* Portions Copyright (c) 1996-2012, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California
*
*
* IDENTIFICATION
* src/common/backend/parser/parse_oper.cpp
*
* -------------------------------------------------------------------------
*/
#include "postgres.h"
#include "knl/knl_variable.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.
*/
#define MAX_CACHED_PATH_LEN 16
typedef struct OprCacheKey {
char oprname[NAMEDATALEN];
Oid left_arg;
Oid right_arg;
Oid search_path[MAX_CACHED_PATH_LEN];
bool use_a_style_coercion;
} OprCacheKey;
typedef struct OprCacheEntry {
OprCacheKey key;
Oid opr_oid;
} OprCacheEntry;
static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion);
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, bool use_a_style_coercion = false);
static Oid find_oper_cache_entry(OprCacheKey* key);
static void make_oper_cache_entry(OprCacheKey* key, Oid opr_oid);
* 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;
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 = NULL;
int cache_flags;
Oid lt_opr;
Oid eq_opr;
Oid gt_opr;
bool hashable = false;
* 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);
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))));
if (ltOpr != NULL)
*ltOpr = lt_opr;
if (eqOpr != NULL)
*eqOpr = eq_opr;
if (gtOpr != NULL)
*gtOpr = gt_opr;
if (isHashable != NULL)
*isHashable = hashable;
}
Oid oprid(Operator op)
{
return HeapTupleGetOid(op);
}
Oid oprfuncid(Operator op)
{
Form_pg_operator pgopform = (Form_pg_operator)GETSTRUCT(op);
return pgopform->oprcode;
}
static Form_pg_operator check_operator_is_shell(List* opname, ParseState* pstate, int location, Operator tup)
{
Form_pg_operator opform = (Form_pg_operator)GETSTRUCT(tup);
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)));
return opform;
}
static HeapTuple find_mapping_in_cache(OprCacheKey key, bool key_ok)
{
HeapTuple tup = NULL;
if (key_ok) {
Oid operOid = find_oper_cache_entry(&key);
if (OidIsValid(operOid))
tup = SearchSysCache1(OPEROID, ObjectIdGetDatum(operOid));
}
return tup;
}
* 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.
*
* If A-style coercion is active, other = unkonw will be treated as
* text = text rather than other = other for other cases.
*/
static Oid binary_oper_exact(List* opname, Oid arg1, Oid arg2, bool use_a_style_coercion)
{
Oid result;
bool was_unknown = false;
if (use_a_style_coercion) {
* For A-style decode,
* decode(<num>, <unknwon>, ...) will be compared as characters
* decode(<unknwon/known string>, <num>, ...) will be compared as numbers
* Note that decode(<num>, <known string type>, ...) categories are not
* handled, because PG-style coercion suffers from blankspace padding of
* bpchar and displaying fractional part of numeric, the behavior is tricky
* to describe.
*/
char arg1_category = get_typecategory(arg1);
char arg2_category = get_typecategory(arg2);
if (arg1_category == TYPCATEGORY_NUMERIC && arg2_category == TYPCATEGORY_UNKNOWN) {
return OpernameGetOprid(opname, TEXTOID, TEXTOID);
} else if (arg2_category == TYPCATEGORY_NUMERIC &&
(arg1_category == TYPCATEGORY_UNKNOWN || arg1_category == TYPCATEGORY_STRING)) {
return OpernameGetOprid(opname, NUMERICOID, NUMERICOID);
}
}
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) {
Oid basetype = getBaseType(arg1);
if (basetype != arg1) {
result = OpernameGetOprid(opname, basetype, basetype);
if (OidIsValid(result))
return result;
}
}
return InvalidOid;
}
* 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)
{
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);
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;
}
* @Description: if the type is a int type that can be converted to numeric
* @in typid - type oid
* @return - return true if the type is a numeric type that can be converted to numeric.
*/
bool IsIntType(Oid typeoid)
{
switch (typeoid) {
case INT1OID:
case INT2OID:
case INT4OID:
case INT8OID:
return true;
default:
return false;
}
}
bool IsTextType(Oid attr_type)
{
return (attr_type == UNKNOWNOID) || IsCharType(attr_type);
}
* 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, bool inNumeric)
{
Oid operOid;
OprCacheKey key;
bool key_ok = false;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup;
bool use_a_style_coercion = false;
if (inNumeric && u_sess->attr.attr_sql.convert_string_to_digit &&
((IsIntType(ltypeId) && IsCharType(rtypeId)) || (IsIntType(rtypeId) && IsCharType(ltypeId)))) {
ltypeId = NUMERICOID;
rtypeId = NUMERICOID;
}
if (DB_IS_CMPT(A_FORMAT) && ACCEPT_FLOAT_STR_AS_INT &&
((IsIntType(ltypeId) && IsTextType(rtypeId)) || (IsIntType(rtypeId) && IsTextType(ltypeId)))) {
ltypeId = NUMERICOID;
rtypeId = NUMERICOID;
}
* In A_FORMAT compatibility and CHAR_COERCE_COMPAT, we choose TEXT-related
* operators for varchar and bpchar.
*/
if (DB_IS_CMPT(A_FORMAT) && CHAR_COERCE_COMPAT && (((ltypeId == VARCHAROID) && (rtypeId == BPCHAROID)) ||
((rtypeId == VARCHAROID) && (ltypeId == BPCHAROID)))) {
ltypeId = TEXTOID;
rtypeId = TEXTOID;
}
* Try to find the mapping in the lookaside cache.
*/
if (pstate != NULL) {
use_a_style_coercion = pstate->p_is_decode && ENABLE_SQL_BETA_FEATURE(A_STYLE_COERCE);
}
key_ok = make_oper_cache_key(&key, opname, ltypeId, rtypeId, use_a_style_coercion);
tup = find_mapping_in_cache(key, key_ok);
if (HeapTupleIsValid(tup))
return (Operator)tup;
* First try for an "exact" match.
*/
operOid = binary_oper_exact(opname, ltypeId, rtypeId, use_a_style_coercion);
if (!OidIsValid(operOid)) {
* Otherwise, search for the most suitable candidate.
*/
FuncCandidateList clist;
clist = OpernameGetCandidates(opname, 'b');
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;
}
* 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;
optup = oper(pstate, op, arg1, arg2, noError, location);
if (optup == (Operator)NULL)
return (Operator)NULL;
opform = (Form_pg_operator)GETSTRUCT(optup);
if (IsBinaryCoercible(arg1, opform->oprleft) && IsBinaryCoercible(arg2, opform->oprright))
return optup;
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;
}
*
* 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;
}
* 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 = false;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup;
* Try to find the mapping in the lookaside cache.
*/
key_ok = make_oper_cache_key(&key, op, arg, InvalidOid);
tup = find_mapping_in_cache(key, key_ok);
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;
clist = OpernameGetCandidates(op, 'r');
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;
}
* 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 = false;
FuncDetailCode fdresult = FUNCDETAIL_NOTFOUND;
HeapTuple tup;
* Try to find the mapping in the lookaside cache.
*/
key_ok = make_oper_cache_key(&key, op, InvalidOid, arg);
tup = find_mapping_in_cache(key, key_ok);
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;
clist = OpernameGetCandidates(op, 'l');
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;
}
* 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)));
}
* parse_get_last_srf
*
* Check pstate, if pstate is NULL, we can't use pstate->p_last_srf directly,
* it's okay to return a NULL value.
*/
Node* parse_get_last_srf(ParseState* pstate)
{
if (pstate != NULL) {
return pstate->p_last_srf;
}
return NULL;
}
* 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, Node* last_srf, int location, bool inNumeric)
{
Oid ltypeId, rtypeId;
Operator tup;
Form_pg_operator opform;
Oid actual_arg_types[2];
Oid declared_arg_types[2];
int nargs;
List* args = NIL;
Oid rettype;
OpExpr* result = NULL;
if (rtree == NULL) {
ltypeId = exprType(ltree);
rtypeId = InvalidOid;
tup = right_oper(pstate, opname, ltypeId, false, location);
} else if (ltree == NULL) {
rtypeId = exprType(rtree);
ltypeId = InvalidOid;
tup = left_oper(pstate, opname, rtypeId, false, location);
} else {
ltypeId = exprType(ltree);
rtypeId = exprType(rtree);
if (IsClientLogicType(ltypeId) || IsClientLogicType(rtypeId)) {
if(pstate != NULL && pstate->p_create_proc_operator_hook) {
pstate->p_create_proc_operator_hook(pstate, ltree, rtree, <ypeId, &rtypeId);
}
}
tup = oper(pstate, opname, ltypeId, rtypeId, false, location, inNumeric);
}
opform = check_operator_is_shell(opname, pstate, location, tup);
if (rtree == NULL) {
args = list_make1(ltree);
actual_arg_types[0] = ltypeId;
declared_arg_types[0] = opform->oprleft;
nargs = 1;
} else if (ltree == NULL) {
args = list_make1(rtree);
actual_arg_types[0] = rtypeId;
declared_arg_types[0] = opform->oprright;
nargs = 1;
} else {
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);
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
result = makeNode(OpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->opresulttype = rettype;
result->opretset = get_func_retset(opform->oprcode);
result->args = args;
result->location = location;
if (result->opretset && pstate && pstate->p_is_flt_frame) {
check_srf_call_placement(pstate, last_srf, location);
pstate->p_last_srf = (Node*)result;
}
ReleaseSysCache(tup);
return (Expr*)result;
}
* 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 = NIL;
Oid rettype;
ScalarArrayOpExpr* result = NULL;
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)));
}
tup = oper(pstate, opname, ltypeId, rtypeId, false, location);
opform = check_operator_is_shell(opname, pstate, location, tup);
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])) {
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;
make_fn_arguments(pstate, args, actual_arg_types, declared_arg_types);
result = makeNode(ScalarArrayOpExpr);
result->opno = oprid(tup);
result->opfuncid = opform->oprcode;
result->hashfuncid = InvalidOid;
result->negfuncid = InvalidOid;
result->useOr = useOr;
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.
*/
* 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, bool use_a_style_coercion)
{
char* schemaname = NULL;
char* opername = NULL;
errno_t rc = EOK;
DeconstructQualifiedName(opname, &schemaname, &opername);
rc = memset_s(key, sizeof(OprCacheKey), 0, sizeof(OprCacheKey));
securec_check(rc, "\0", "\0");
strlcpy(key->oprname, opername, NAMEDATALEN);
key->left_arg = ltypeId;
key->right_arg = rtypeId;
key->use_a_style_coercion = use_a_style_coercion;
if (schemaname != NULL) {
key->search_path[0] = LookupExplicitNamespace(schemaname);
} else {
if (fetch_search_path_array(key->search_path, MAX_CACHED_PATH_LEN) > MAX_CACHED_PATH_LEN)
return false;
}
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 = NULL;
if (u_sess->parser_cxt.opr_cache_hash == NULL) {
HASHCTL ctl;
errno_t rc;
rc = memset_s(&ctl, sizeof(ctl), 0, sizeof(ctl));
securec_check(rc, "\0", "\0");
ctl.keysize = sizeof(OprCacheKey);
ctl.entrysize = sizeof(OprCacheEntry);
ctl.hash = tag_hash;
ctl.hcxt = u_sess->cache_mem_cxt;
u_sess->parser_cxt.opr_cache_hash =
hash_create("Operator lookup cache", 256, &ctl, HASH_ELEM | HASH_FUNCTION | HASH_CONTEXT);
CacheRegisterSessionSyscacheCallback(OPERNAMENSP, InvalidateOprCacheCallBack, (Datum)0);
CacheRegisterSessionSyscacheCallback(CASTSOURCETARGET, InvalidateOprCacheCallBack, (Datum)0);
}
oprentry = (OprCacheEntry*)hash_search(u_sess->parser_cxt.opr_cache_hash, (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 = NULL;
if (unlikely(u_sess->parser_cxt.opr_cache_hash == NULL)) {
ereport(ERROR,
(errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("u_sess->parser_cxt.opr_cache_hash should not be null")));
}
oprentry = (OprCacheEntry*)hash_search(u_sess->parser_cxt.opr_cache_hash, (void*)key, HASH_ENTER, NULL);
oprentry->opr_oid = opr_oid;
}
* Callback for pg_operator and pg_cast inval events
*
* We also use it for invaliding the operator cache when type conversion priority
* changed, e.g. modified parameter convert_string_to_digit.
*/
void InvalidateOprCacheCallBack(Datum arg, int cacheid, uint32 hashvalue)
{
HASH_SEQ_STATUS status;
OprCacheEntry* hentry = NULL;
if (u_sess->parser_cxt.opr_cache_hash == NULL) {
* Check whether InvalidateOprCacheCallBack is called because of pg_operator
* or pg_cast is modified.
*/
if (cacheid == OPERNAMENSP || cacheid == CASTSOURCETARGET)
ereport(ERROR,
(errmodule(MOD_OPT),
errcode(ERRCODE_UNEXPECTED_NULL_VALUE),
errmsg("OprCacheHash is unexpected null")));
else
return;
}
hash_seq_init(&status, u_sess->parser_cxt.opr_cache_hash);
while ((hentry = (OprCacheEntry*)hash_seq_search(&status)) != NULL) {
if (hash_search(u_sess->parser_cxt.opr_cache_hash, (void*)&hentry->key, HASH_REMOVE, NULL) == NULL)
ereport(ERROR, (errcode(ERRCODE_DATA_EXCEPTION), errmsg("hash table corrupted")));
}
}