mirror of
https://github.com/postgres/postgres.git
synced 2025-04-21 12:05:57 +03:00
1517 lines
39 KiB
C
1517 lines
39 KiB
C
/*-------------------------------------------------------------------------
|
|
*
|
|
* catalog_utils.c--
|
|
*
|
|
* Copyright (c) 1994, Regents of the University of California
|
|
*
|
|
*
|
|
* IDENTIFICATION
|
|
* $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.19 1997/07/24 20:13:01 momjian Exp $
|
|
*
|
|
*-------------------------------------------------------------------------
|
|
*/
|
|
#include <string.h>
|
|
#include "postgres.h"
|
|
|
|
#include "lib/dllist.h"
|
|
#include "utils/datum.h"
|
|
|
|
#include "utils/builtins.h"
|
|
#include "utils/elog.h"
|
|
#include "utils/palloc.h"
|
|
#include "fmgr.h"
|
|
|
|
#include "nodes/pg_list.h"
|
|
#include "nodes/parsenodes.h"
|
|
#include "utils/syscache.h"
|
|
#include "catalog/catname.h"
|
|
|
|
#include "parser/catalog_utils.h"
|
|
#include "catalog/pg_inherits.h"
|
|
#include "catalog/pg_operator.h"
|
|
#include "catalog/pg_type.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/heapam.h"
|
|
#include "access/genam.h"
|
|
#include "access/itup.h"
|
|
#include "access/tupmacs.h"
|
|
|
|
#include "storage/buf.h"
|
|
#include "storage/bufmgr.h"
|
|
#include "utils/lsyscache.h"
|
|
#include "storage/lmgr.h"
|
|
|
|
#include "port-protos.h" /* strdup() */
|
|
|
|
struct {
|
|
char *field;
|
|
int code;
|
|
} special_attr[] = {
|
|
{ "ctid", SelfItemPointerAttributeNumber },
|
|
{ "oid", ObjectIdAttributeNumber },
|
|
{ "xmin", MinTransactionIdAttributeNumber },
|
|
{ "cmin", MinCommandIdAttributeNumber },
|
|
{ "xmax", MaxTransactionIdAttributeNumber },
|
|
{ "cmax", MaxCommandIdAttributeNumber },
|
|
{ "chain", ChainItemPointerAttributeNumber },
|
|
{ "anchor", AnchorItemPointerAttributeNumber },
|
|
{ "tmin", MinAbsoluteTimeAttributeNumber },
|
|
{ "tmax", MaxAbsoluteTimeAttributeNumber },
|
|
{ "vtype", VersionTypeAttributeNumber }
|
|
};
|
|
|
|
#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr))
|
|
|
|
static char *attnum_type[SPECIALS] = {
|
|
"tid",
|
|
"oid",
|
|
"xid",
|
|
"cid",
|
|
"xid",
|
|
"cid",
|
|
"tid",
|
|
"tid",
|
|
"abstime",
|
|
"abstime",
|
|
"char"
|
|
};
|
|
|
|
#define MAXFARGS 8 /* max # args to a c or postquel function */
|
|
|
|
/*
|
|
* This structure is used to explore the inheritance hierarchy above
|
|
* nodes in the type tree in order to disambiguate among polymorphic
|
|
* functions.
|
|
*/
|
|
|
|
typedef struct _InhPaths {
|
|
int nsupers; /* number of superclasses */
|
|
Oid self; /* this class */
|
|
Oid *supervec; /* vector of superclasses */
|
|
} InhPaths;
|
|
|
|
/*
|
|
* This structure holds a list of possible functions or operators that
|
|
* agree with the known name and argument types of the function/operator.
|
|
*/
|
|
typedef struct _CandidateList {
|
|
Oid *args;
|
|
struct _CandidateList *next;
|
|
} *CandidateList;
|
|
|
|
static Oid **argtype_inherit(int nargs, Oid *oid_array);
|
|
static Oid **genxprod(InhPaths *arginh, int nargs);
|
|
static int findsupers(Oid relid, Oid **supervec);
|
|
|
|
/* check to see if a type id is valid,
|
|
* returns true if it is. By using this call before calling
|
|
* get_id_type or get_id_typname, more meaningful error messages
|
|
* can be produced because the caller typically has more context of
|
|
* what's going on - jolly
|
|
*/
|
|
bool
|
|
check_typeid(Oid id)
|
|
{
|
|
return (SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(id),
|
|
0,0,0) != NULL);
|
|
}
|
|
|
|
|
|
/* return a Type structure, given an typid */
|
|
Type
|
|
get_id_type(Oid id)
|
|
{
|
|
HeapTuple tup;
|
|
|
|
if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
|
|
0,0,0))) {
|
|
elog ( WARN, "type id lookup of %ud failed", id);
|
|
return(NULL);
|
|
}
|
|
return((Type) tup);
|
|
}
|
|
|
|
/* return a type name, given a typeid */
|
|
char*
|
|
get_id_typname(Oid id)
|
|
{
|
|
HeapTuple tup;
|
|
TypeTupleForm typetuple;
|
|
|
|
if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id),
|
|
0,0,0))) {
|
|
elog ( WARN, "type id lookup of %ud failed", id);
|
|
return(NULL);
|
|
}
|
|
typetuple = (TypeTupleForm)GETSTRUCT(tup);
|
|
return (typetuple->typname).data;
|
|
}
|
|
|
|
/* return a Type structure, given type name */
|
|
Type
|
|
type(char *s)
|
|
{
|
|
HeapTuple tup;
|
|
|
|
if (s == NULL) {
|
|
elog ( WARN , "type(): Null type" );
|
|
}
|
|
|
|
if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0,0,0))) {
|
|
elog (WARN , "type name lookup of %s failed", s);
|
|
}
|
|
return((Type) tup);
|
|
}
|
|
|
|
/* given attribute id, return type of that attribute */
|
|
/* XXX Special case for pseudo-attributes is a hack */
|
|
Oid
|
|
att_typeid(Relation rd, int attid)
|
|
{
|
|
|
|
if (attid < 0) {
|
|
return(typeid(type(attnum_type[-attid-1])));
|
|
}
|
|
/* -1 because varattno (where attid comes from) returns one
|
|
more than index */
|
|
return(rd->rd_att->attrs[attid-1]->atttypid);
|
|
}
|
|
|
|
|
|
int
|
|
att_attnelems(Relation rd, int attid)
|
|
{
|
|
return(rd->rd_att->attrs[attid-1]->attnelems);
|
|
}
|
|
|
|
/* given type, return the type OID */
|
|
Oid
|
|
typeid(Type tp)
|
|
{
|
|
if (tp == NULL) {
|
|
elog ( WARN , "typeid() called with NULL type struct");
|
|
}
|
|
return(tp->t_oid);
|
|
}
|
|
|
|
/* given type (as type struct), return the length of type */
|
|
int16
|
|
tlen(Type t)
|
|
{
|
|
TypeTupleForm typ;
|
|
|
|
typ = (TypeTupleForm)GETSTRUCT(t);
|
|
return(typ->typlen);
|
|
}
|
|
|
|
/* given type (as type struct), return the value of its 'byval' attribute.*/
|
|
bool
|
|
tbyval(Type t)
|
|
{
|
|
TypeTupleForm typ;
|
|
|
|
typ = (TypeTupleForm)GETSTRUCT(t);
|
|
return(typ->typbyval);
|
|
}
|
|
|
|
/* given type (as type struct), return the name of type */
|
|
char*
|
|
tname(Type t)
|
|
{
|
|
TypeTupleForm typ;
|
|
|
|
typ = (TypeTupleForm)GETSTRUCT(t);
|
|
return (typ->typname).data;
|
|
}
|
|
|
|
/* given type (as type struct), return wether type is passed by value */
|
|
int
|
|
tbyvalue(Type t)
|
|
{
|
|
TypeTupleForm typ;
|
|
|
|
typ = (TypeTupleForm) GETSTRUCT(t);
|
|
return(typ->typbyval);
|
|
}
|
|
|
|
/* given a type, return its typetype ('c' for 'c'atalog types) */
|
|
static char
|
|
typetypetype(Type t)
|
|
{
|
|
TypeTupleForm typ;
|
|
|
|
typ = (TypeTupleForm) GETSTRUCT(t);
|
|
return(typ->typtype);
|
|
}
|
|
|
|
/* 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
|
|
*/
|
|
static 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,
|
|
SelfTimeQual,
|
|
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).
|
|
*/
|
|
static 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:
|
|
c->args[0] = FLOAT8OID;
|
|
break;
|
|
default:
|
|
c->args[0] = result->args[0];
|
|
break;
|
|
}
|
|
switch (result->args[1]) {
|
|
case FLOAT4OID:
|
|
case INT4OID:
|
|
case INT2OID:
|
|
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
|
|
*/
|
|
static 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 = get_id_type(arg1);
|
|
tp2 = get_id_type(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",
|
|
tname(tp1), tname(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
|
|
*/
|
|
static 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 */
|
|
if (typeId != UNKNOWNOID) {
|
|
return 0;
|
|
}
|
|
|
|
pg_operator_desc = heap_openr(OperatorRelationName);
|
|
pg_operator_scan = heap_beginscan(pg_operator_desc,
|
|
0,
|
|
SelfTimeQual,
|
|
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 range variable, return id of variable */
|
|
|
|
int
|
|
varattno(Relation rd, char *a)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < rd->rd_rel->relnatts; i++) {
|
|
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
|
|
return(i+1);
|
|
}
|
|
}
|
|
for (i = 0; i < SPECIALS; i++) {
|
|
if (!strcmp(special_attr[i].field, a)) {
|
|
return(special_attr[i].code);
|
|
}
|
|
}
|
|
|
|
elog(WARN,"Relation %s does not have attribute %s\n",
|
|
RelationGetRelationName(rd), a );
|
|
return(-1);
|
|
}
|
|
|
|
/* Given range variable, return whether attribute of this name
|
|
* is a set.
|
|
* NOTE the ASSUMPTION here that no system attributes are, or ever
|
|
* will be, sets.
|
|
*/
|
|
bool
|
|
varisset(Relation rd, char *name)
|
|
{
|
|
int i;
|
|
|
|
/* First check if this is a system attribute */
|
|
for (i = 0; i < SPECIALS; i++) {
|
|
if (! strcmp(special_attr[i].field, name)) {
|
|
return(false); /* no sys attr is a set */
|
|
}
|
|
}
|
|
return (get_attisset(rd->rd_id, name));
|
|
}
|
|
|
|
/* given range variable, return id of variable */
|
|
int
|
|
nf_varattno(Relation rd, char *a)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < rd->rd_rel->relnatts; i++) {
|
|
if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) {
|
|
return(i+1);
|
|
}
|
|
}
|
|
for (i = 0; i < SPECIALS; i++) {
|
|
if (!strcmp(special_attr[i].field, a)) {
|
|
return(special_attr[i].code);
|
|
}
|
|
}
|
|
return InvalidAttrNumber;
|
|
}
|
|
|
|
/*-------------
|
|
* given an attribute number and a relation, return its relation name
|
|
*/
|
|
char*
|
|
getAttrName(Relation rd, int attrno)
|
|
{
|
|
char *name;
|
|
int i;
|
|
|
|
if (attrno<0) {
|
|
for (i = 0; i < SPECIALS; i++) {
|
|
if (special_attr[i].code == attrno) {
|
|
name = special_attr[i].field;
|
|
return(name);
|
|
}
|
|
}
|
|
elog(WARN, "Illegal attr no %d for relation %s\n",
|
|
attrno, RelationGetRelationName(rd));
|
|
} else if (attrno >=1 && attrno<= RelationGetNumberOfAttributes(rd)) {
|
|
name = (rd->rd_att->attrs[attrno-1]->attname).data;
|
|
return(name);
|
|
} else {
|
|
elog(WARN, "Illegal attr no %d for relation %s\n",
|
|
attrno, RelationGetRelationName(rd));
|
|
}
|
|
|
|
/*
|
|
* Shouldn't get here, but we want lint to be happy...
|
|
*/
|
|
|
|
return(NULL);
|
|
}
|
|
|
|
/* Given a typename and value, returns the ascii form of the value */
|
|
|
|
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));
|
|
}
|
|
|
|
/* Given a Type and a string, return the internal form of that string */
|
|
char *
|
|
instr2(Type tp, char *string, int typlen)
|
|
{
|
|
return(instr1((TypeTupleForm ) GETSTRUCT(tp), string, typlen));
|
|
}
|
|
|
|
/* Given a type structure and a string, returns the internal form of
|
|
that string */
|
|
char *
|
|
instr1(TypeTupleForm tp, char *string, int typlen)
|
|
{
|
|
Oid op;
|
|
Oid typelem;
|
|
|
|
op = tp->typinput;
|
|
typelem = tp->typelem; /* XXX - used for array_in */
|
|
/* typlen is for bpcharin() and varcharin() */
|
|
return((char *) fmgr(op, string, typelem, typlen));
|
|
}
|
|
|
|
/* Given the attribute type of an array return the arrtribute type of
|
|
an element of the array */
|
|
|
|
Oid
|
|
GetArrayElementType(Oid typearray)
|
|
{
|
|
HeapTuple type_tuple;
|
|
TypeTupleForm type_struct_array;
|
|
|
|
type_tuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(typearray),
|
|
0,0,0);
|
|
|
|
if (!HeapTupleIsValid(type_tuple))
|
|
elog(WARN, "GetArrayElementType: Cache lookup failed for type %d\n",
|
|
typearray);
|
|
|
|
/* get the array type struct from the type tuple */
|
|
type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple);
|
|
|
|
if (type_struct_array->typelem == InvalidOid) {
|
|
elog(WARN, "GetArrayElementType: type %s is not an array",
|
|
(Name)&(type_struct_array->typname.data[0]));
|
|
}
|
|
|
|
return(type_struct_array->typelem);
|
|
}
|
|
|
|
Oid
|
|
funcid_get_rettype(Oid funcid)
|
|
{
|
|
HeapTuple func_tuple = NULL;
|
|
Oid funcrettype = (Oid)0;
|
|
|
|
func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid),
|
|
0,0,0);
|
|
|
|
if ( !HeapTupleIsValid ( func_tuple ))
|
|
elog (WARN, "function %d does not exist", funcid);
|
|
|
|
funcrettype = (Oid)
|
|
((Form_pg_proc)GETSTRUCT(func_tuple))->prorettype ;
|
|
|
|
return (funcrettype);
|
|
}
|
|
|
|
/*
|
|
* get a list of all argument type vectors for which a function named
|
|
* funcname taking nargs arguments exists
|
|
*/
|
|
static CandidateList
|
|
func_get_candidates(char *funcname, int nargs)
|
|
{
|
|
Relation heapRelation;
|
|
Relation idesc;
|
|
ScanKeyData skey;
|
|
HeapTuple tuple;
|
|
IndexScanDesc sd;
|
|
RetrieveIndexResult indexRes;
|
|
Buffer buffer;
|
|
Form_pg_proc pgProcP;
|
|
bool bufferUsed = FALSE;
|
|
CandidateList candidates = NULL;
|
|
CandidateList current_candidate;
|
|
int i;
|
|
|
|
heapRelation = heap_openr(ProcedureRelationName);
|
|
ScanKeyEntryInitialize(&skey,
|
|
(bits16)0x0,
|
|
(AttrNumber)1,
|
|
(RegProcedure)NameEqualRegProcedure,
|
|
(Datum)funcname);
|
|
|
|
idesc = index_openr(ProcedureNameIndex);
|
|
|
|
sd = index_beginscan(idesc, false, 1, &skey);
|
|
|
|
do {
|
|
tuple = (HeapTuple)NULL;
|
|
if (bufferUsed) {
|
|
ReleaseBuffer(buffer);
|
|
bufferUsed = FALSE;
|
|
}
|
|
|
|
indexRes = index_getnext(sd, ForwardScanDirection);
|
|
if (indexRes) {
|
|
ItemPointer iptr;
|
|
|
|
iptr = &indexRes->heap_iptr;
|
|
tuple = heap_fetch(heapRelation, NowTimeQual, iptr, &buffer);
|
|
pfree(indexRes);
|
|
if (HeapTupleIsValid(tuple)) {
|
|
pgProcP = (Form_pg_proc)GETSTRUCT(tuple);
|
|
bufferUsed = TRUE;
|
|
if (pgProcP->pronargs == nargs) {
|
|
current_candidate = (CandidateList)
|
|
palloc(sizeof(struct _CandidateList));
|
|
current_candidate->args = (Oid *)
|
|
palloc(8 * sizeof(Oid));
|
|
memset(current_candidate->args, 0, 8 * sizeof(Oid));
|
|
for (i=0; i<nargs; i++) {
|
|
current_candidate->args[i] =
|
|
pgProcP->proargtypes[i];
|
|
}
|
|
|
|
current_candidate->next = candidates;
|
|
candidates = current_candidate;
|
|
}
|
|
}
|
|
}
|
|
} while (indexRes);
|
|
|
|
index_endscan(sd);
|
|
index_close(idesc);
|
|
heap_close(heapRelation);
|
|
|
|
return candidates;
|
|
}
|
|
|
|
/*
|
|
* can input_typeids be coerced to func_typeids?
|
|
*/
|
|
static bool
|
|
can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids)
|
|
{
|
|
int i;
|
|
Type tp;
|
|
|
|
/*
|
|
* right now, we only coerce "unknown", and we cannot coerce it to a
|
|
* relation type
|
|
*/
|
|
for (i=0; i<nargs; i++) {
|
|
if (input_typeids[i] != func_typeids[i]) {
|
|
if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0)
|
|
return false;
|
|
|
|
tp = get_id_type(input_typeids[i]);
|
|
if (typetypetype(tp) == 'c' )
|
|
return false;
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* given a list of possible typeid arrays to a function and an array of
|
|
* input typeids, produce a shortlist of those function typeid arrays
|
|
* that match the input typeids (either exactly or by coercion), and
|
|
* return the number of such arrays
|
|
*/
|
|
static int
|
|
match_argtypes(int nargs,
|
|
Oid *input_typeids,
|
|
CandidateList function_typeids,
|
|
CandidateList *candidates) /* return value */
|
|
{
|
|
CandidateList current_candidate;
|
|
CandidateList matching_candidate;
|
|
Oid *current_typeids;
|
|
int ncandidates = 0;
|
|
|
|
*candidates = NULL;
|
|
|
|
for (current_candidate = function_typeids;
|
|
current_candidate != NULL;
|
|
current_candidate = current_candidate->next) {
|
|
current_typeids = current_candidate->args;
|
|
if (can_coerce(nargs, input_typeids, current_typeids)) {
|
|
matching_candidate = (CandidateList)
|
|
palloc(sizeof(struct _CandidateList));
|
|
matching_candidate->args = current_typeids;
|
|
matching_candidate->next = *candidates;
|
|
*candidates = matching_candidate;
|
|
ncandidates++;
|
|
}
|
|
}
|
|
|
|
return ncandidates;
|
|
}
|
|
|
|
/*
|
|
* given the input argtype array and more than one candidate
|
|
* for the function argtype array, attempt to resolve the conflict.
|
|
* returns the selected argtype array if the conflict can be resolved,
|
|
* otherwise returns NULL
|
|
*/
|
|
static Oid *
|
|
func_select_candidate(int nargs,
|
|
Oid *input_typeids,
|
|
CandidateList candidates)
|
|
{
|
|
/* XXX no conflict resolution implemeneted yet */
|
|
return (NULL);
|
|
}
|
|
|
|
bool
|
|
func_get_detail(char *funcname,
|
|
int nargs,
|
|
Oid *oid_array,
|
|
Oid *funcid, /* return value */
|
|
Oid *rettype, /* return value */
|
|
bool *retset, /* return value */
|
|
Oid **true_typeids) /* return value */
|
|
{
|
|
Oid **input_typeid_vector;
|
|
Oid *current_input_typeids;
|
|
CandidateList function_typeids;
|
|
CandidateList current_function_typeids;
|
|
HeapTuple ftup;
|
|
Form_pg_proc pform;
|
|
|
|
/*
|
|
* attempt to find named function in the system catalogs
|
|
* with arguments exactly as specified - so that the normal
|
|
* case is just as quick as before
|
|
*/
|
|
ftup = SearchSysCacheTuple(PRONAME,
|
|
PointerGetDatum(funcname),
|
|
Int32GetDatum(nargs),
|
|
PointerGetDatum(oid_array),
|
|
0);
|
|
*true_typeids = oid_array;
|
|
|
|
/*
|
|
* If an exact match isn't found :
|
|
* 1) get a vector of all possible input arg type arrays constructed
|
|
* from the superclasses of the original input arg types
|
|
* 2) get a list of all possible argument type arrays to the
|
|
* function with given name and number of arguments
|
|
* 3) for each input arg type array from vector #1 :
|
|
* a) find how many of the function arg type arrays from list #2
|
|
* it can be coerced to
|
|
* b) - if the answer is one, we have our function
|
|
* - if the answer is more than one, attempt to resolve the
|
|
* conflict
|
|
* - if the answer is zero, try the next array from vector #1
|
|
*/
|
|
if (!HeapTupleIsValid(ftup)) {
|
|
function_typeids = func_get_candidates(funcname, nargs);
|
|
|
|
if (function_typeids != NULL) {
|
|
int ncandidates = 0;
|
|
|
|
input_typeid_vector = argtype_inherit(nargs, oid_array);
|
|
current_input_typeids = oid_array;
|
|
|
|
do {
|
|
ncandidates = match_argtypes(nargs, current_input_typeids,
|
|
function_typeids,
|
|
¤t_function_typeids);
|
|
if (ncandidates == 1) {
|
|
*true_typeids = current_function_typeids->args;
|
|
ftup = SearchSysCacheTuple(PRONAME,
|
|
PointerGetDatum(funcname),
|
|
Int32GetDatum(nargs),
|
|
PointerGetDatum(*true_typeids),
|
|
0);
|
|
Assert(HeapTupleIsValid(ftup));
|
|
}
|
|
else if (ncandidates > 1) {
|
|
*true_typeids =
|
|
func_select_candidate(nargs,
|
|
current_input_typeids,
|
|
current_function_typeids);
|
|
if (*true_typeids == NULL) {
|
|
elog(NOTICE, "there is more than one function named \"%s\"",
|
|
funcname);
|
|
elog(NOTICE, "that satisfies the given argument types. you will have to");
|
|
elog(NOTICE, "retype your query using explicit typecasts.");
|
|
func_error("func_get_detail", funcname, nargs, oid_array);
|
|
}
|
|
else {
|
|
ftup = SearchSysCacheTuple(PRONAME,
|
|
PointerGetDatum(funcname),
|
|
Int32GetDatum(nargs),
|
|
PointerGetDatum(*true_typeids),
|
|
0);
|
|
Assert(HeapTupleIsValid(ftup));
|
|
}
|
|
}
|
|
current_input_typeids = *input_typeid_vector++;
|
|
}
|
|
while (current_input_typeids !=
|
|
InvalidOid && ncandidates == 0);
|
|
}
|
|
}
|
|
|
|
if (!HeapTupleIsValid(ftup)) {
|
|
Type tp;
|
|
|
|
if (nargs == 1) {
|
|
tp = get_id_type(oid_array[0]);
|
|
if (typetypetype(tp) == 'c')
|
|
elog(WARN, "no such attribute or function \"%s\"",
|
|
funcname);
|
|
}
|
|
func_error("func_get_detail", funcname, nargs, oid_array);
|
|
} else {
|
|
pform = (Form_pg_proc) GETSTRUCT(ftup);
|
|
*funcid = ftup->t_oid;
|
|
*rettype = pform->prorettype;
|
|
*retset = pform->proretset;
|
|
|
|
return (true);
|
|
}
|
|
/* shouldn't reach here */
|
|
return (false);
|
|
|
|
}
|
|
|
|
/*
|
|
* argtype_inherit() -- Construct an argtype vector reflecting the
|
|
* inheritance properties of the supplied argv.
|
|
*
|
|
* This function is used to disambiguate among functions with the
|
|
* same name but different signatures. It takes an array of eight
|
|
* type ids. For each type id in the array that's a complex type
|
|
* (a class), it walks up the inheritance tree, finding all
|
|
* superclasses of that type. A vector of new Oid type arrays
|
|
* is returned to the caller, reflecting the structure of the
|
|
* inheritance tree above the supplied arguments.
|
|
*
|
|
* The order of this vector is as follows: all superclasses of the
|
|
* rightmost complex class are explored first. The exploration
|
|
* continues from right to left. This policy means that we favor
|
|
* keeping the leftmost argument type as low in the inheritance tree
|
|
* as possible. This is intentional; it is exactly what we need to
|
|
* do for method dispatch. The last type array we return is all
|
|
* zeroes. This will match any functions for which return types are
|
|
* not defined. There are lots of these (mostly builtins) in the
|
|
* catalogs.
|
|
*/
|
|
static Oid **
|
|
argtype_inherit(int nargs, Oid *oid_array)
|
|
{
|
|
Oid relid;
|
|
int i;
|
|
InhPaths arginh[MAXFARGS];
|
|
|
|
for (i = 0; i < MAXFARGS; i++) {
|
|
if (i < nargs) {
|
|
arginh[i].self = oid_array[i];
|
|
if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid) {
|
|
arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec));
|
|
} else {
|
|
arginh[i].nsupers = 0;
|
|
arginh[i].supervec = (Oid *) NULL;
|
|
}
|
|
} else {
|
|
arginh[i].self = InvalidOid;
|
|
arginh[i].nsupers = 0;
|
|
arginh[i].supervec = (Oid *) NULL;
|
|
}
|
|
}
|
|
|
|
/* return an ordered cross-product of the classes involved */
|
|
return (genxprod(arginh, nargs));
|
|
}
|
|
|
|
typedef struct _SuperQE {
|
|
Oid sqe_relid;
|
|
} SuperQE;
|
|
|
|
static int
|
|
findsupers(Oid relid, Oid **supervec)
|
|
{
|
|
Oid *relidvec;
|
|
Relation inhrel;
|
|
HeapScanDesc inhscan;
|
|
ScanKeyData skey;
|
|
HeapTuple inhtup;
|
|
TupleDesc inhtupdesc;
|
|
int nvisited;
|
|
SuperQE *qentry, *vnode;
|
|
Dllist *visited, *queue;
|
|
Dlelem *qe, *elt;
|
|
|
|
Relation rd;
|
|
Buffer buf;
|
|
Datum d;
|
|
bool newrelid;
|
|
char isNull;
|
|
|
|
nvisited = 0;
|
|
queue = DLNewList();
|
|
visited = DLNewList();
|
|
|
|
|
|
inhrel = heap_openr(InheritsRelationName);
|
|
RelationSetLockForRead(inhrel);
|
|
inhtupdesc = RelationGetTupleDescriptor(inhrel);
|
|
|
|
/*
|
|
* Use queue to do a breadth-first traversal of the inheritance
|
|
* graph from the relid supplied up to the root.
|
|
*/
|
|
do {
|
|
ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel,
|
|
ObjectIdEqualRegProcedure,
|
|
ObjectIdGetDatum(relid));
|
|
|
|
inhscan = heap_beginscan(inhrel, 0, NowTimeQual, 1, &skey);
|
|
|
|
while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) {
|
|
qentry = (SuperQE *) palloc(sizeof(SuperQE));
|
|
|
|
d = (Datum) fastgetattr(inhtup, Anum_pg_inherits_inhparent,
|
|
inhtupdesc, &isNull);
|
|
qentry->sqe_relid = DatumGetObjectId(d);
|
|
|
|
/* put this one on the queue */
|
|
DLAddTail(queue, DLNewElem(qentry));
|
|
|
|
ReleaseBuffer(buf);
|
|
}
|
|
|
|
heap_endscan(inhscan);
|
|
|
|
/* pull next unvisited relid off the queue */
|
|
do {
|
|
qe = DLRemHead(queue);
|
|
qentry = qe ? (SuperQE*)DLE_VAL(qe) : NULL;
|
|
|
|
if (qentry == (SuperQE *) NULL)
|
|
break;
|
|
|
|
relid = qentry->sqe_relid;
|
|
newrelid = true;
|
|
|
|
for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
|
|
vnode = (SuperQE*)DLE_VAL(elt);
|
|
if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) {
|
|
newrelid = false;
|
|
break;
|
|
}
|
|
}
|
|
} while (!newrelid);
|
|
|
|
if (qentry != (SuperQE *) NULL) {
|
|
|
|
/* save the type id, rather than the relation id */
|
|
if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL)
|
|
elog(WARN, "relid %d does not exist", qentry->sqe_relid);
|
|
qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data));
|
|
heap_close(rd);
|
|
|
|
DLAddTail(visited, qe);
|
|
|
|
nvisited++;
|
|
}
|
|
} while (qentry != (SuperQE *) NULL);
|
|
|
|
RelationUnsetLockForRead(inhrel);
|
|
heap_close(inhrel);
|
|
|
|
if (nvisited > 0) {
|
|
relidvec = (Oid *) palloc(nvisited * sizeof(Oid));
|
|
*supervec = relidvec;
|
|
|
|
for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) {
|
|
vnode = (SuperQE*)DLE_VAL(elt);
|
|
*relidvec++ = vnode->sqe_relid;
|
|
}
|
|
|
|
} else {
|
|
*supervec = (Oid *) NULL;
|
|
}
|
|
|
|
return (nvisited);
|
|
}
|
|
|
|
static Oid **
|
|
genxprod(InhPaths *arginh, int nargs)
|
|
{
|
|
int nanswers;
|
|
Oid **result, **iter;
|
|
Oid *oneres;
|
|
int i, j;
|
|
int cur[MAXFARGS];
|
|
|
|
nanswers = 1;
|
|
for (i = 0; i < nargs; i++) {
|
|
nanswers *= (arginh[i].nsupers + 2);
|
|
cur[i] = 0;
|
|
}
|
|
|
|
iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers);
|
|
|
|
/* compute the cross product from right to left */
|
|
for (;;) {
|
|
oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid));
|
|
memset(oneres, 0, MAXFARGS * sizeof(Oid));
|
|
|
|
for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--)
|
|
continue;
|
|
|
|
/* if we're done, terminate with NULL pointer */
|
|
if (i < 0) {
|
|
*iter = NULL;
|
|
return (result);
|
|
}
|
|
|
|
/* no, increment this column and zero the ones after it */
|
|
cur[i] = cur[i] + 1;
|
|
for (j = nargs - 1; j > i; j--)
|
|
cur[j] = 0;
|
|
|
|
for (i = 0; i < nargs; i++) {
|
|
if (cur[i] == 0)
|
|
oneres[i] = arginh[i].self;
|
|
else if (cur[i] > arginh[i].nsupers)
|
|
oneres[i] = 0; /* wild card */
|
|
else
|
|
oneres[i] = arginh[i].supervec[cur[i] - 1];
|
|
}
|
|
|
|
*iter++ = oneres;
|
|
}
|
|
}
|
|
|
|
/* Given a type id, returns the in-conversion function of the type */
|
|
Oid
|
|
typeid_get_retinfunc(Oid type_id)
|
|
{
|
|
HeapTuple typeTuple;
|
|
TypeTupleForm type;
|
|
Oid infunc;
|
|
typeTuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(type_id),
|
|
0,0,0);
|
|
if ( !HeapTupleIsValid ( typeTuple ))
|
|
elog(WARN,
|
|
"typeid_get_retinfunc: Invalid type - oid = %u",
|
|
type_id);
|
|
|
|
type = (TypeTupleForm) GETSTRUCT(typeTuple);
|
|
infunc = type->typinput;
|
|
return(infunc);
|
|
}
|
|
|
|
/* Given a type id, returns the out-conversion function of the type */
|
|
Oid
|
|
typeid_get_retoutfunc(Oid type_id)
|
|
{
|
|
HeapTuple typeTuple;
|
|
TypeTupleForm type;
|
|
Oid outfunc;
|
|
typeTuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(type_id),
|
|
0,0,0);
|
|
if ( !HeapTupleIsValid ( typeTuple ))
|
|
elog(WARN,
|
|
"typeid_get_retoutfunc: Invalid type - oid = %u",
|
|
type_id);
|
|
|
|
type = (TypeTupleForm) GETSTRUCT(typeTuple);
|
|
outfunc = type->typoutput;
|
|
return(outfunc);
|
|
}
|
|
|
|
Oid
|
|
typeid_get_relid(Oid type_id)
|
|
{
|
|
HeapTuple typeTuple;
|
|
TypeTupleForm type;
|
|
Oid infunc;
|
|
typeTuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(type_id),
|
|
0,0,0);
|
|
if ( !HeapTupleIsValid ( typeTuple ))
|
|
elog(WARN, "typeid_get_relid: Invalid type - oid = %u ", type_id);
|
|
|
|
type = (TypeTupleForm) GETSTRUCT(typeTuple);
|
|
infunc = type->typrelid;
|
|
return(infunc);
|
|
}
|
|
|
|
Oid
|
|
get_typrelid(Type typ)
|
|
{
|
|
TypeTupleForm typtup;
|
|
|
|
typtup = (TypeTupleForm) GETSTRUCT(typ);
|
|
|
|
return (typtup->typrelid);
|
|
}
|
|
|
|
Oid
|
|
get_typelem(Oid type_id)
|
|
{
|
|
HeapTuple typeTuple;
|
|
TypeTupleForm type;
|
|
|
|
if (!(typeTuple = SearchSysCacheTuple(TYPOID,
|
|
ObjectIdGetDatum(type_id),
|
|
0,0,0))) {
|
|
elog (WARN , "type id lookup of %u failed", type_id);
|
|
}
|
|
type = (TypeTupleForm) GETSTRUCT(typeTuple);
|
|
|
|
return (type->typelem);
|
|
}
|
|
|
|
char
|
|
FindDelimiter(char *typename)
|
|
{
|
|
char delim;
|
|
HeapTuple typeTuple;
|
|
TypeTupleForm type;
|
|
|
|
|
|
if (!(typeTuple = SearchSysCacheTuple(TYPNAME,
|
|
PointerGetDatum(typename),
|
|
0,0,0))) {
|
|
elog (WARN , "type name lookup of %s failed", typename);
|
|
}
|
|
type = (TypeTupleForm) GETSTRUCT(typeTuple);
|
|
|
|
delim = type->typdelim;
|
|
return (delim);
|
|
}
|
|
|
|
/*
|
|
* 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 (check_typeid(arg1)) {
|
|
tp1 = get_id_type(arg1);
|
|
} else {
|
|
elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
|
|
}
|
|
|
|
if (check_typeid(arg2)) {
|
|
tp2 = get_id_type(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, tname(tp1),tname(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, tname(tp1),tname(tp2));
|
|
}
|
|
|
|
/*
|
|
* Error message when function lookup fails that gives details of the
|
|
* argument types
|
|
*/
|
|
void
|
|
func_error(char *caller, char *funcname, int nargs, Oid *argtypes)
|
|
{
|
|
char p[(NAMEDATALEN+2)*MAXFMGRARGS], *ptr;
|
|
int i;
|
|
|
|
ptr = p;
|
|
*ptr = '\0';
|
|
for (i=0; i<nargs; i++) {
|
|
if (i) {
|
|
*ptr++ = ',';
|
|
*ptr++ = ' ';
|
|
}
|
|
if (argtypes[i] != 0) {
|
|
(void) strcpy(ptr, tname(get_id_type(argtypes[i])));
|
|
*(ptr + NAMEDATALEN) = '\0';
|
|
} else
|
|
strcpy(ptr, "opaque");
|
|
ptr += strlen(ptr);
|
|
}
|
|
|
|
elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p);
|
|
}
|
|
|
|
/*
|
|
* Error message when aggregate lookup fails that gives details of the
|
|
* basetype
|
|
*/
|
|
void
|
|
agg_error(char *caller, char *aggname, Oid basetypeID)
|
|
{
|
|
/* basetypeID that is Invalid (zero) means aggregate over all types. (count) */
|
|
|
|
if (basetypeID == InvalidOid) {
|
|
elog(WARN, "%s: aggregate '%s' for all types does not exist", caller, aggname);
|
|
} else {
|
|
elog(WARN, "%s: aggregate '%s' for '%s' does not exist", caller, aggname,
|
|
tname(get_id_type(basetypeID)));
|
|
}
|
|
}
|