postgresql/src/backend/parser/parse_oper.c

614 lines
15 KiB
C
Raw Normal View History

/*-------------------------------------------------------------------------
*
* 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));
}