aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_oper.c
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>1997-11-25 22:07:18 +0000
committerBruce Momjian <bruce@momjian.us>1997-11-25 22:07:18 +0000
commit4a5b781d71b61887fd312112d75979f250bf723f (patch)
tree315803e512d9e978301311a92866a8b6f17a592d /src/backend/parser/parse_oper.c
parent3aff4011c735faa747ce94d20da6fd9f85144955 (diff)
downloadpostgresql-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.c613
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));
+}
+