diff options
author | Bruce Momjian <bruce@momjian.us> | 1997-11-25 22:07:18 +0000 |
---|---|---|
committer | Bruce Momjian <bruce@momjian.us> | 1997-11-25 22:07:18 +0000 |
commit | 4a5b781d71b61887fd312112d75979f250bf723f (patch) | |
tree | 315803e512d9e978301311a92866a8b6f17a592d /src/backend/parser/parse_oper.c | |
parent | 3aff4011c735faa747ce94d20da6fd9f85144955 (diff) | |
download | postgresql-4a5b781d71b61887fd312112d75979f250bf723f.tar.gz postgresql-4a5b781d71b61887fd312112d75979f250bf723f.zip |
Break parser functions into smaller files, group together.
Diffstat (limited to 'src/backend/parser/parse_oper.c')
-rw-r--r-- | src/backend/parser/parse_oper.c | 613 |
1 files changed, 613 insertions, 0 deletions
diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c new file mode 100644 index 00000000000..d82a46bc2f1 --- /dev/null +++ b/src/backend/parser/parse_oper.c @@ -0,0 +1,613 @@ +/*------------------------------------------------------------------------- + * + * parse_oper.h + * handle operator things for parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.1 1997/11/25 22:05:43 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include <string.h> +#include "postgres.h" +#include <fmgr.h> + +#include <access/heapam.h> +#include <access/relscan.h> +#include <catalog/catname.h> +#include <catalog/pg_operator.h> +#include <catalog/pg_proc.h> +#include <catalog/pg_type.h> +#include <parser/parse_oper.h> +#include <parser/parse_type.h> +#include <storage/bufmgr.h> +#include <utils/syscache.h> + +#ifdef 0 +#include "lib/dllist.h" +#include "utils/datum.h" + +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" + +#include "catalog/pg_inherits.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/indexing.h" +#include "catalog/catname.h" + +#include "access/skey.h" +#include "access/relscan.h" +#include "access/tupdesc.h" +#include "access/htup.h" +#include "access/genam.h" +#include "access/itup.h" +#include "access/tupmacs.h" +#include "storage/buf.h" +#include "utils/lsyscache.h" +#include "storage/lmgr.h" + +#include "port-protos.h" /* strdup() */ +#endif + +Oid +any_ordering_op(int restype) +{ + Operator order_op; + Oid order_opid; + + order_op = oper("<", restype, restype, false); + order_opid = oprid(order_op); + + return order_opid; +} + +/* given operator, return the operator OID */ +Oid +oprid(Operator op) +{ + return (op->t_oid); +} + +/* + * given opname, leftTypeId and rightTypeId, + * find all possible (arg1, arg2) pairs for which an operator named + * opname exists, such that leftTypeId can be coerced to arg1 and + * rightTypeId can be coerced to arg2 + */ +int +binary_oper_get_candidates(char *opname, + Oid leftTypeId, + Oid rightTypeId, + CandidateList *candidates) +{ + CandidateList current_candidate; + Relation pg_operator_desc; + HeapScanDesc pg_operator_scan; + HeapTuple tup; + OperatorTupleForm oper; + Buffer buffer; + int nkeys; + int ncandidates = 0; + ScanKeyData opKey[3]; + + *candidates = NULL; + + ScanKeyEntryInitialize(&opKey[0], 0, + Anum_pg_operator_oprname, + NameEqualRegProcedure, + NameGetDatum(opname)); + + ScanKeyEntryInitialize(&opKey[1], 0, + Anum_pg_operator_oprkind, + CharacterEqualRegProcedure, + CharGetDatum('b')); + + + if (leftTypeId == UNKNOWNOID) + { + if (rightTypeId == UNKNOWNOID) + { + nkeys = 2; + } + else + { + nkeys = 3; + + ScanKeyEntryInitialize(&opKey[2], 0, + Anum_pg_operator_oprright, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(rightTypeId)); + } + } + else if (rightTypeId == UNKNOWNOID) + { + nkeys = 3; + + ScanKeyEntryInitialize(&opKey[2], 0, + Anum_pg_operator_oprleft, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(leftTypeId)); + } + else + /* currently only "unknown" can be coerced */ + return 0; + + pg_operator_desc = heap_openr(OperatorRelationName); + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + true, + nkeys, + opKey); + + do + { + tup = heap_getnext(pg_operator_scan, 0, &buffer); + if (HeapTupleIsValid(tup)) + { + current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) palloc(2 * sizeof(Oid)); + + oper = (OperatorTupleForm) GETSTRUCT(tup); + current_candidate->args[0] = oper->oprleft; + current_candidate->args[1] = oper->oprright; + current_candidate->next = *candidates; + *candidates = current_candidate; + ncandidates++; + ReleaseBuffer(buffer); + } + } while (HeapTupleIsValid(tup)); + + heap_endscan(pg_operator_scan); + heap_close(pg_operator_desc); + + return ncandidates; +} + +/* + * equivalentOpersAfterPromotion - + * checks if a list of candidate operators obtained from + * binary_oper_get_candidates() contain equivalent operators. If + * this routine is called, we have more than 1 candidate and need to + * decided whether to pick one of them. This routine returns true if + * the all the candidates operate on the same data types after + * promotion (int2, int4, float4 -> float8). + */ +bool +equivalentOpersAfterPromotion(CandidateList candidates) +{ + CandidateList result; + CandidateList promotedCandidates = NULL; + Oid leftarg, + rightarg; + + for (result = candidates; result != NULL; result = result->next) + { + CandidateList c; + + c = (CandidateList) palloc(sizeof(*c)); + c->args = (Oid *) palloc(2 * sizeof(Oid)); + switch (result->args[0]) + { + case FLOAT4OID: + case INT4OID: + case INT2OID: + case CASHOID: + c->args[0] = FLOAT8OID; + break; + default: + c->args[0] = result->args[0]; + break; + } + switch (result->args[1]) + { + case FLOAT4OID: + case INT4OID: + case INT2OID: + case CASHOID: + c->args[1] = FLOAT8OID; + break; + default: + c->args[1] = result->args[1]; + break; + } + c->next = promotedCandidates; + promotedCandidates = c; + } + + /* + * if we get called, we have more than 1 candidates so we can do the + * following safely + */ + leftarg = promotedCandidates->args[0]; + rightarg = promotedCandidates->args[1]; + + for (result = promotedCandidates->next; result != NULL; result = result->next) + { + if (result->args[0] != leftarg || result->args[1] != rightarg) + + /* + * this list contains operators that operate on different data + * types even after promotion. Hence we can't decide on which + * one to pick. The user must do explicit type casting. + */ + return FALSE; + } + + /* + * all the candidates are equivalent in the following sense: they + * operate on equivalent data types and picking any one of them is as + * good. + */ + return TRUE; +} + + +/* + * given a choice of argument type pairs for a binary operator, + * try to choose a default pair + */ +CandidateList +binary_oper_select_candidate(Oid arg1, + Oid arg2, + CandidateList candidates) +{ + CandidateList result; + + /* + * if both are "unknown", there is no way to select a candidate + * + * current wisdom holds that the default operator should be one in which + * both operands have the same type (there will only be one such + * operator) + * + * 7.27.93 - I have decided not to do this; it's too hard to justify, and + * it's easy enough to typecast explicitly -avi [the rest of this + * routine were commented out since then -ay] + */ + + if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) + return (NULL); + + /* + * 6/23/95 - I don't complete agree with avi. In particular, casting + * floats is a pain for users. Whatever the rationale behind not doing + * this is, I need the following special case to work. + * + * In the WHERE clause of a query, if a float is specified without + * quotes, we treat it as float8. I added the float48* operators so + * that we can operate on float4 and float8. But now we have more than + * one matching operator if the right arg is unknown (eg. float + * specified with quotes). This break some stuff in the regression + * test where there are floats in quotes not properly casted. Below is + * the solution. In addition to requiring the operator operates on the + * same type for both operands [as in the code Avi originally + * commented out], we also require that the operators be equivalent in + * some sense. (see equivalentOpersAfterPromotion for details.) - ay + * 6/95 + */ + if (!equivalentOpersAfterPromotion(candidates)) + return NULL; + + /* + * if we get here, any one will do but we're more picky and require + * both operands be the same. + */ + for (result = candidates; result != NULL; result = result->next) + { + if (result->args[0] == result->args[1]) + return result; + } + + return (NULL); +} + +/* Given operator, types of arg1, and arg2, return oper struct */ +/* arg1, arg2 --typeids */ +Operator +oper(char *op, Oid arg1, Oid arg2, bool noWarnings) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + if (!arg2) + arg2 = arg1; + if (!arg1) + arg1 = arg2; + + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg1), + ObjectIdGetDatum(arg2), + Int8GetDatum('b')))) + { + ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); + if (ncandidates == 0) + { + + /* + * no operators of the desired types found + */ + if (!noWarnings) + op_error(op, arg1, arg2); + return (NULL); + } + else if (ncandidates == 1) + { + + /* + * exactly one operator of the desired types found + */ + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[1]), + Int8GetDatum('b')); + Assert(HeapTupleIsValid(tup)); + } + else + { + + /* + * multiple operators of the desired types found + */ + candidates = binary_oper_select_candidate(arg1, arg2, candidates); + if (candidates != NULL) + { + /* we chose one of them */ + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[1]), + Int8GetDatum('b')); + Assert(HeapTupleIsValid(tup)); + } + else + { + Type tp1, + tp2; + + /* we chose none of them */ + tp1 = typeidType(arg1); + tp2 = typeidType(arg2); + if (!noWarnings) + { + elog(NOTICE, "there is more than one operator %s for types", op); + elog(NOTICE, "%s and %s. You will have to retype this query", + typeTypeName(tp1), typeTypeName(tp2)); + elog(WARN, "using an explicit cast"); + } + return (NULL); + } + } + } + return ((Operator) tup); +} + +/* + * given opname and typeId, find all possible types for which + * a right/left unary operator named opname exists, + * such that typeId can be coerced to it + */ +int +unary_oper_get_candidates(char *op, + Oid typeId, + CandidateList *candidates, + char rightleft) +{ + CandidateList current_candidate; + Relation pg_operator_desc; + HeapScanDesc pg_operator_scan; + HeapTuple tup; + OperatorTupleForm oper; + Buffer buffer; + int ncandidates = 0; + + static ScanKeyData opKey[2] = { + {0, Anum_pg_operator_oprname, NameEqualRegProcedure}, + {0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure}}; + + *candidates = NULL; + + fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func, + &opKey[0].sk_nargs); + opKey[0].sk_argument = NameGetDatum(op); + fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func, + &opKey[1].sk_nargs); + opKey[1].sk_argument = CharGetDatum(rightleft); + + /* currently, only "unknown" can be coerced */ + + /* + * but we should allow types that are internally the same to be + * "coerced" + */ + if (typeId != UNKNOWNOID) + { + return 0; + } + + pg_operator_desc = heap_openr(OperatorRelationName); + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + true, + 2, + opKey); + + do + { + tup = heap_getnext(pg_operator_scan, 0, &buffer); + if (HeapTupleIsValid(tup)) + { + current_candidate = (CandidateList) palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) palloc(sizeof(Oid)); + + oper = (OperatorTupleForm) GETSTRUCT(tup); + if (rightleft == 'r') + current_candidate->args[0] = oper->oprleft; + else + current_candidate->args[0] = oper->oprright; + current_candidate->next = *candidates; + *candidates = current_candidate; + ncandidates++; + ReleaseBuffer(buffer); + } + } while (HeapTupleIsValid(tup)); + + heap_endscan(pg_operator_scan); + heap_close(pg_operator_desc); + + return ncandidates; +} + +/* Given unary right-side operator (operator on right), return oper struct */ +/* arg-- type id */ +Operator +right_oper(char *op, Oid arg) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + /* + * if (!OpCache) { init_op_cache(); } + */ + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg), + ObjectIdGetDatum(InvalidOid), + Int8GetDatum('r')))) + { + ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r'); + if (ncandidates == 0) + { + elog(WARN, + "Can't find right op: %s for type %d", op, arg); + return (NULL); + } + else if (ncandidates == 1) + { + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(InvalidOid), + Int8GetDatum('r')); + Assert(HeapTupleIsValid(tup)); + } + else + { + elog(NOTICE, "there is more than one right operator %s", op); + elog(NOTICE, "you will have to retype this query"); + elog(WARN, "using an explicit cast"); + return (NULL); + } + } + return ((Operator) tup); +} + +/* Given unary left-side operator (operator on left), return oper struct */ +/* arg--type id */ +Operator +left_oper(char *op, Oid arg) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + /* + * if (!OpCache) { init_op_cache(); } + */ + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(arg), + Int8GetDatum('l')))) + { + ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l'); + if (ncandidates == 0) + { + elog(WARN, + "Can't find left op: %s for type %d", op, arg); + return (NULL); + } + else if (ncandidates == 1) + { + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(candidates->args[0]), + Int8GetDatum('l')); + Assert(HeapTupleIsValid(tup)); + } + else + { + elog(NOTICE, "there is more than one left operator %s", op); + elog(NOTICE, "you will have to retype this query"); + elog(WARN, "using an explicit cast"); + return (NULL); + } + } + return ((Operator) tup); +} + +/* Given a typename and value, returns the ascii form of the value */ + +#ifdef NOT_USED +char * +outstr(char *typename, /* Name of type of value */ + char *value) /* Could be of any type */ +{ + TypeTupleForm tp; + Oid op; + + tp = (TypeTupleForm) GETSTRUCT(type(typename)); + op = tp->typoutput; + return ((char *) fmgr(op, value)); +} + +#endif + +/* + * Give a somewhat useful error message when the operator for two types + * is not found. + */ +void +op_error(char *op, Oid arg1, Oid arg2) +{ + Type tp1 = NULL, + tp2 = NULL; + + if (typeidIsValid(arg1)) + { + tp1 = typeidType(arg1); + } + else + { + elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op); + } + + if (typeidIsValid(arg2)) + { + tp2 = typeidType(arg2); + } + else + { + elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op); + } + + elog(NOTICE, "there is no operator %s for types %s and %s", + op, typeTypeName(tp1), typeTypeName(tp2)); + elog(NOTICE, "You will either have to retype this query using an"); + elog(NOTICE, "explicit cast, or you will have to define the operator"); + elog(WARN, "%s for %s and %s using CREATE OPERATOR", + op, typeTypeName(tp1), typeTypeName(tp2)); +} + |