Add capabilities for automatic type conversion.
This commit is contained in:
parent
54b5577cb6
commit
3ace5fd082
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.74 1998/03/31 23:31:10 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.75 1998/05/09 23:29:52 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -30,6 +30,9 @@
|
||||
#include "parser/parse_target.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/mcxt.h"
|
||||
#ifdef PARSEDEBUG
|
||||
#include "nodes/print.h"
|
||||
#endif
|
||||
|
||||
static Query *transformStmt(ParseState *pstate, Node *stmt);
|
||||
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
|
||||
@ -65,6 +68,10 @@ parse_analyze(List *pl, ParseState *parentParseState)
|
||||
|
||||
while (pl != NIL)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
elog(DEBUG,"parse tree from yacc:\n---\n%s\n---\n", nodeToString(lfirst(pl)));
|
||||
#endif
|
||||
|
||||
pstate = make_parsestate(parentParseState);
|
||||
result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
|
||||
if (pstate->p_target_relation != NULL)
|
||||
|
560
src/backend/parser/parse_coerce.c
Normal file
560
src/backend/parser/parse_coerce.c
Normal file
@ -0,0 +1,560 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* parse_coerce.c
|
||||
* handle type coersions/conversions for parser
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.1 1998/05/09 23:29:53 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#include <string.h>
|
||||
#include "postgres.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "fmgr.h"
|
||||
#include "nodes/makefuncs.h"
|
||||
|
||||
#include "parser/parse_expr.h"
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "parser/parse_target.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
Oid DemoteType(Oid inType);
|
||||
Oid PromoteTypeToNext(Oid inType);
|
||||
|
||||
|
||||
/* coerce_type()
|
||||
* Convert a function argument to a different type.
|
||||
*/
|
||||
Node *
|
||||
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId)
|
||||
{
|
||||
Node *result = NULL;
|
||||
Oid infunc;
|
||||
Datum val;
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: argument types are %d -> %d\n",
|
||||
inputTypeId, targetTypeId);
|
||||
#endif
|
||||
|
||||
if (targetTypeId == InvalidOid)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: apparent NULL target argument; suppress type conversion\n");
|
||||
#endif
|
||||
result = node;
|
||||
}
|
||||
else if (inputTypeId != targetTypeId)
|
||||
{
|
||||
/* one of the known-good transparent conversions? then drop through... */
|
||||
if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: argument type %s is known to be convertible to type %s\n",
|
||||
typeidTypeName(inputTypeId), typeidTypeName(targetTypeId));
|
||||
#endif
|
||||
result = node;
|
||||
}
|
||||
|
||||
/* if not unknown input type, try for explicit conversion using functions... */
|
||||
else if (inputTypeId != UNKNOWNOID)
|
||||
{
|
||||
/* We already know there is a function which will do this, so let's use it */
|
||||
FuncCall *n = makeNode(FuncCall);
|
||||
n->funcname = typeidTypeName(targetTypeId);
|
||||
n->args = lcons(node, NIL);
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: construct function %s(%s)\n",
|
||||
typeidTypeName(targetTypeId), typeidTypeName(inputTypeId));
|
||||
#endif
|
||||
|
||||
result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: node is UNKNOWN type\n");
|
||||
#endif
|
||||
if (nodeTag(node) == T_Const)
|
||||
{
|
||||
Const *con = (Const *) node;
|
||||
|
||||
val = (Datum) textout((struct varlena *)
|
||||
con->constvalue);
|
||||
infunc = typeidInfunc(targetTypeId);
|
||||
con = makeNode(Const);
|
||||
con->consttype = targetTypeId;
|
||||
con->constlen = typeLen(typeidType(targetTypeId));
|
||||
|
||||
/* use "-1" for varchar() type */
|
||||
con->constvalue = (Datum) fmgr(infunc,
|
||||
val,
|
||||
typeidTypElem(targetTypeId),
|
||||
-1);
|
||||
con->constisnull = false;
|
||||
con->constbyval = true;
|
||||
con->constisset = false;
|
||||
result = (Node *) con;
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: should never get here!\n");
|
||||
#endif
|
||||
result = node;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("coerce_type: argument type IDs %d match\n", inputTypeId);
|
||||
#endif
|
||||
|
||||
result = node;
|
||||
}
|
||||
|
||||
return result;
|
||||
} /* coerce_type() */
|
||||
|
||||
|
||||
/* can_coerce_type()
|
||||
* Can input_typeids be coerced to func_typeids?
|
||||
*
|
||||
* There are a few types which are known apriori to be convertible.
|
||||
* We will check for those cases first, and then look for possible
|
||||
* conversion functions.
|
||||
*
|
||||
* Notes:
|
||||
* This uses the same mechanism as the CAST() SQL construct in gram.y.
|
||||
* We should also check the function return type on candidate conversion
|
||||
* routines just to be safe but we do not do that yet...
|
||||
* We need to have a zero-filled OID array here, otherwise the cache lookup fails.
|
||||
* - thomas 1998-03-31
|
||||
*/
|
||||
bool
|
||||
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||
{
|
||||
HeapTuple ftup;
|
||||
int i;
|
||||
Type tp;
|
||||
Oid oid_array[8];
|
||||
|
||||
/* run through argument list... */
|
||||
for (i = 0; i < nargs; i++)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: argument #%d types are %d -> %d\n",
|
||||
i, input_typeids[i], func_typeids[i]);
|
||||
#endif
|
||||
if (input_typeids[i] != func_typeids[i])
|
||||
{
|
||||
/* one of the known-good transparent conversions? then drop through... */
|
||||
if (IS_BINARY_COMPATIBLE(input_typeids[i], func_typeids[i]))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: argument #%d type %s is known to be convertible to type %s\n",
|
||||
i, typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
|
||||
#endif
|
||||
}
|
||||
|
||||
/* don't know what to do for the output type? then quit... */
|
||||
else if (func_typeids[i] == InvalidOid)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: output OID func_typeids[%d] is zero\n", i);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* don't know what to do for the input type? then quit... */
|
||||
else if (input_typeids[i] == InvalidOid)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: input OID input_typeids[%d] is zero\n", i);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
/* if not unknown input type, try for explicit conversion using functions... */
|
||||
else if (input_typeids[i] != UNKNOWNOID)
|
||||
{
|
||||
MemSet(&oid_array[0], 0, 8 * sizeof(Oid));
|
||||
oid_array[0] = input_typeids[i];
|
||||
|
||||
/* look for a single-argument function named with the target type name */
|
||||
ftup = SearchSysCacheTuple(PRONAME,
|
||||
PointerGetDatum(typeidTypeName(func_typeids[i])),
|
||||
Int32GetDatum(1),
|
||||
PointerGetDatum(oid_array),
|
||||
0);
|
||||
|
||||
/* should also check the function return type just to be safe... */
|
||||
if (HeapTupleIsValid(ftup))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: found function %s(%s) to convert argument #%d\n",
|
||||
typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: did not find function %s(%s) to convert argument #%d\n",
|
||||
typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: argument #%d type is %d (UNKNOWN)\n",
|
||||
i, input_typeids[i]);
|
||||
#endif
|
||||
}
|
||||
|
||||
tp = typeidType(input_typeids[i]);
|
||||
if (typeTypeFlag(tp) == 'c')
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: typeTypeFlag for %s is 'c'\n",
|
||||
typeidTypeName(input_typeids[i]));
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: conversion from %s to %s is possible\n",
|
||||
typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("can_coerce_type: argument #%d type IDs %d match\n",
|
||||
i, input_typeids[i]);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
} /* can_coerce_type() */
|
||||
|
||||
|
||||
/* TypeCategory()
|
||||
* Assign a category to the specified OID.
|
||||
*/
|
||||
CATEGORY
|
||||
TypeCategory(Oid inType)
|
||||
{
|
||||
CATEGORY result;
|
||||
|
||||
switch (inType)
|
||||
{
|
||||
case (BOOLOID):
|
||||
result = BOOLEAN_TYPE;
|
||||
break;
|
||||
|
||||
case (CHAROID):
|
||||
case (BPCHAROID):
|
||||
case (VARCHAROID):
|
||||
case (TEXTOID):
|
||||
result = STRING_TYPE;
|
||||
break;
|
||||
|
||||
case (INT2OID):
|
||||
case (INT4OID):
|
||||
case (FLOAT4OID):
|
||||
case (FLOAT8OID):
|
||||
case (CASHOID):
|
||||
result = NUMERIC_TYPE;
|
||||
break;
|
||||
|
||||
case (ABSTIMEOID):
|
||||
case (TIMESTAMPOID):
|
||||
case (DATETIMEOID):
|
||||
result = DATETIME_TYPE;
|
||||
break;
|
||||
|
||||
case (RELTIMEOID):
|
||||
case (TIMESPANOID):
|
||||
result = TIMESPAN_TYPE;
|
||||
break;
|
||||
|
||||
case (POINTOID):
|
||||
case (LSEGOID):
|
||||
case (LINEOID):
|
||||
case (BOXOID):
|
||||
case (PATHOID):
|
||||
case (CIRCLEOID):
|
||||
case (POLYGONOID):
|
||||
result = GEOMETRIC_TYPE;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = USER_TYPE;
|
||||
break;
|
||||
}
|
||||
return (result);
|
||||
} /* TypeCategory() */
|
||||
|
||||
|
||||
/* IsPreferredType()
|
||||
* Assign a category to the specified OID.
|
||||
*/
|
||||
bool
|
||||
IsPreferredType(CATEGORY category, Oid type)
|
||||
{
|
||||
return (type == PreferredType(category, type));
|
||||
} /* IsPreferredType() */
|
||||
|
||||
|
||||
/* PreferredType()
|
||||
* Assign a category to the specified OID.
|
||||
*/
|
||||
Oid
|
||||
PreferredType(CATEGORY category, Oid type)
|
||||
{
|
||||
Oid result;
|
||||
|
||||
switch (category)
|
||||
{
|
||||
case (BOOLEAN_TYPE):
|
||||
result = BOOLOID;
|
||||
break;
|
||||
|
||||
case (STRING_TYPE):
|
||||
result = TEXTOID;
|
||||
break;
|
||||
|
||||
case (NUMERIC_TYPE):
|
||||
result = FLOAT8OID;
|
||||
break;
|
||||
|
||||
case (DATETIME_TYPE):
|
||||
result = DATETIMEOID;
|
||||
break;
|
||||
|
||||
case (TIMESPAN_TYPE):
|
||||
result = TIMESPANOID;
|
||||
break;
|
||||
|
||||
case (GEOMETRIC_TYPE):
|
||||
case (USER_TYPE):
|
||||
result = type;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = UNKNOWNOID;
|
||||
break;
|
||||
}
|
||||
#ifdef PARSEDEBUG
|
||||
printf("PreferredType- (%d) preferred type is %s\n", category, typeidTypeName(result));
|
||||
#endif
|
||||
return (result);
|
||||
} /* PreferredType() */
|
||||
|
||||
|
||||
#if FALSE
|
||||
Oid
|
||||
PromoteTypeToNext(Oid inType)
|
||||
{
|
||||
Oid result;
|
||||
|
||||
switch (inType)
|
||||
{
|
||||
case (CHAROID):
|
||||
case (BPCHAROID):
|
||||
result = VARCHAROID;
|
||||
break;
|
||||
|
||||
case (VARCHAROID):
|
||||
result = TEXTOID;
|
||||
break;
|
||||
|
||||
case (INT2OID):
|
||||
case (CASHOID):
|
||||
result = INT4OID;
|
||||
break;
|
||||
|
||||
case (INT4OID):
|
||||
case (FLOAT4OID):
|
||||
result = FLOAT8OID;
|
||||
break;
|
||||
|
||||
case (DATEOID):
|
||||
case (ABSTIMEOID):
|
||||
case (TIMESTAMPOID):
|
||||
result = DATETIMEOID;
|
||||
break;
|
||||
|
||||
case (TIMEOID):
|
||||
case (RELTIMEOID):
|
||||
result = TIMESPANOID;
|
||||
break;
|
||||
|
||||
case (BOOLOID):
|
||||
case (TEXTOID):
|
||||
case (FLOAT8OID):
|
||||
case (DATETIMEOID):
|
||||
case (TIMESPANOID):
|
||||
default:
|
||||
result = inType;
|
||||
break;
|
||||
}
|
||||
return (result);
|
||||
} /* PromoteTypeToNext() */
|
||||
|
||||
|
||||
Oid
|
||||
DemoteType(Oid inType)
|
||||
{
|
||||
Oid result;
|
||||
|
||||
switch (inType)
|
||||
{
|
||||
case (FLOAT4OID):
|
||||
case (FLOAT8OID):
|
||||
result = INT4OID;
|
||||
break;
|
||||
|
||||
default:
|
||||
result = inType;
|
||||
break;
|
||||
}
|
||||
return (result);
|
||||
} /* DemoteType() */
|
||||
|
||||
|
||||
Oid
|
||||
PromoteLesserType(Oid inType1, Oid inType2, Oid *newType1, Oid *newType2)
|
||||
{
|
||||
Oid result;
|
||||
|
||||
if (inType1 == inType2)
|
||||
{
|
||||
result = PromoteTypeToNext(inType1);
|
||||
inType1 = result;
|
||||
*arg2 = result;
|
||||
return (result);
|
||||
}
|
||||
|
||||
kind1 = ClassifyType(inType1);
|
||||
kind2 = ClassifyType(*arg2);
|
||||
if (kind1 != kind2)
|
||||
{
|
||||
*newType1 = inType1;
|
||||
*newType2 = inType2;
|
||||
result = InvalidOid;
|
||||
}
|
||||
|
||||
isBuiltIn1 = IS_BUILTIN_TYPE(inType1);
|
||||
isBuiltIn2 = IS_BUILTIN_TYPE(*arg2);
|
||||
|
||||
if (isBuiltIn1 && isBuiltIn2)
|
||||
{
|
||||
switch (*arg1)
|
||||
{
|
||||
case (CHAROID):
|
||||
switch (*arg2)
|
||||
{
|
||||
case (BPCHAROID):
|
||||
case (VARCHAROID):
|
||||
case (TEXTOID):
|
||||
|
||||
case (INT2OID):
|
||||
case (INT4OID):
|
||||
case (FLOAT4OID):
|
||||
case (FLOAT8OID):
|
||||
case (CASHOID):
|
||||
|
||||
case (POINTOID):
|
||||
case (LSEGOID):
|
||||
case (LINEOID):
|
||||
case (BOXOID):
|
||||
case (PATHOID):
|
||||
case (CIRCLEOID):
|
||||
case (POLYGONOID):
|
||||
|
||||
case (InvalidOid):
|
||||
case (UNKNOWNOID):
|
||||
case (BOOLOID):
|
||||
default:
|
||||
*arg1 = InvalidOid;
|
||||
*arg2 = InvalidOid;
|
||||
result = InvalidOid;
|
||||
}
|
||||
}
|
||||
else if (isBuiltIn1 && !isBuiltIn2)
|
||||
{
|
||||
if ((promotedType = PromoteBuiltInType(*arg1)) != *arg1)
|
||||
{
|
||||
*arg1 = promotedType;
|
||||
return (promotedType);
|
||||
}
|
||||
else if (CanCoerceType(*arg1, *arg2))
|
||||
{
|
||||
*arg1 = *arg2;
|
||||
return (*arg2);
|
||||
}
|
||||
}
|
||||
else if (!isBuiltIn1 && isBuiltIn2)
|
||||
{
|
||||
if ((promotedType = PromoteBuiltInType(*arg2)) != *arg2)
|
||||
{
|
||||
*arg2 = promotedType;
|
||||
return (promotedType);
|
||||
}
|
||||
else if (CanCoerceType(*arg2, *arg1))
|
||||
{
|
||||
*arg2 = *arg1;
|
||||
return (*arg1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (*arg2 == InvalidOid)
|
||||
return InvalidOid;
|
||||
|
||||
switch (*arg1)
|
||||
{
|
||||
case (CHAROID):
|
||||
switch (*arg2)
|
||||
{
|
||||
case (BPCHAROID):
|
||||
case (VARCHAROID):
|
||||
case (TEXTOID):
|
||||
|
||||
case (INT2OID):
|
||||
case (INT4OID):
|
||||
case (FLOAT4OID):
|
||||
case (FLOAT8OID):
|
||||
case (CASHOID):
|
||||
|
||||
case (POINTOID):
|
||||
case (LSEGOID):
|
||||
case (LINEOID):
|
||||
case (BOXOID):
|
||||
case (PATHOID):
|
||||
case (CIRCLEOID):
|
||||
case (POLYGONOID):
|
||||
|
||||
case (InvalidOid):
|
||||
case (UNKNOWNOID):
|
||||
case (BOOLOID):
|
||||
default:
|
||||
*arg1 = InvalidOid;
|
||||
*arg2 = InvalidOid;
|
||||
result = InvalidOid;
|
||||
}
|
||||
}
|
||||
#endif
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.27 1998/04/26 04:06:45 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.28 1998/05/09 23:29:53 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -301,12 +301,14 @@ transformExpr(ParseState *pstate, Node *expr, int precedence)
|
||||
result = (Node *) expr;
|
||||
break;
|
||||
}
|
||||
/* These nodes do _not_ come from the original parse tree.
|
||||
* They result from parser transformation in this phase.
|
||||
|
||||
/* These nodes do _not_ come from the original parse tree,
|
||||
* but result from parser transformation in this phase.
|
||||
* At least one construct (BETWEEN/AND) puts the same nodes
|
||||
* into two branches of the parse tree. Hence, some nodes
|
||||
* are transformed twice. These nodes come from transforming
|
||||
* a function call. Let's try just passing them through...
|
||||
* into two branches of the parse tree; hence, some nodes
|
||||
* are transformed twice.
|
||||
* These cases below come from transforming function calls.
|
||||
* Let's try just passing them through...
|
||||
* - thomas 1998-03-14
|
||||
*/
|
||||
case T_Expr:
|
||||
@ -506,6 +508,10 @@ parser_typecast(Value *expr, TypeName *typename, int16 atttypmod)
|
||||
return (Node *) adt;
|
||||
}
|
||||
|
||||
|
||||
/* parser_typecast2()
|
||||
* Convert (only) constants to specified type.
|
||||
*/
|
||||
Node *
|
||||
parser_typecast2(Node *expr, Oid exprType, Type tp, int16 atttypmod)
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.14 1998/02/26 04:33:32 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.15 1998/05/09 23:29:53 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -39,7 +39,7 @@ make_operand(char *opname,
|
||||
/*
|
||||
* make_parsestate() --
|
||||
* allocate and initialize a new ParseState.
|
||||
* the CALLERS is responsible for freeing the ParseState* returned
|
||||
* the CALLER is responsible for freeing the ParseState* returned
|
||||
*
|
||||
*/
|
||||
|
||||
@ -57,6 +57,15 @@ make_parsestate(ParseState *parentParseState)
|
||||
return (pstate);
|
||||
}
|
||||
|
||||
|
||||
extern
|
||||
Node *
|
||||
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
|
||||
|
||||
|
||||
/* make_operand()
|
||||
* Ensure argument type match by forcing conversion of constants.
|
||||
*/
|
||||
static Node *
|
||||
make_operand(char *opname,
|
||||
Node *tree,
|
||||
@ -65,35 +74,33 @@ make_operand(char *opname,
|
||||
{
|
||||
Node *result;
|
||||
Type true_type;
|
||||
#if FALSE
|
||||
Datum val;
|
||||
Oid infunc;
|
||||
#endif
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("make_operand: constructing operand for '%s' %s->%s\n",
|
||||
opname, typeidTypeName(orig_typeId), typeidTypeName(true_typeId));
|
||||
#endif
|
||||
if (tree != NULL)
|
||||
{
|
||||
result = tree;
|
||||
true_type = typeidType(true_typeId);
|
||||
disallow_setop(opname, true_type, result);
|
||||
if (true_typeId != orig_typeId)
|
||||
{ /* must coerce */
|
||||
Const *con = (Const *) result;
|
||||
|
||||
Assert(nodeTag(result) == T_Const);
|
||||
val = (Datum) textout((struct varlena *)
|
||||
con->constvalue);
|
||||
infunc = typeidInfunc(true_typeId);
|
||||
con = makeNode(Const);
|
||||
con->consttype = true_typeId;
|
||||
con->constlen = typeLen(true_type);
|
||||
con->constvalue = (Datum) fmgr(infunc,
|
||||
val,
|
||||
typeidTypElem(true_typeId),
|
||||
-1 /* for varchar() type */ );
|
||||
con->constisnull = false;
|
||||
con->constbyval = true;
|
||||
con->constisset = false;
|
||||
result = (Node *) con;
|
||||
/* must coerce? */
|
||||
if (true_typeId != orig_typeId)
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("make_operand: try to convert node from %s to %s\n",
|
||||
typeidTypeName(orig_typeId), typeidTypeName(true_typeId));
|
||||
#endif
|
||||
result = coerce_type(NULL, tree, orig_typeId, true_typeId);
|
||||
}
|
||||
}
|
||||
|
||||
/* otherwise, this is a NULL value */
|
||||
else
|
||||
{
|
||||
Const *con = makeNode(Const);
|
||||
@ -108,7 +115,7 @@ make_operand(char *opname,
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
} /* make_operand() */
|
||||
|
||||
|
||||
static void
|
||||
@ -119,13 +126,49 @@ disallow_setop(char *op, Type optype, Node *operand)
|
||||
|
||||
if (nodeTag(operand) == T_Iter)
|
||||
{
|
||||
elog(NOTICE, "An operand to the '%s' operator returns a set of %s,",
|
||||
op, typeTypeName(optype));
|
||||
elog(ERROR, "but '%s' takes single values, not sets.",
|
||||
op);
|
||||
elog(ERROR, "An operand to the '%s' operator returns a set of %s,"
|
||||
"\n\tbut '%s' takes single values, not sets.",
|
||||
op, typeTypeName(optype), op);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* CoerceType()
|
||||
* Try to force type of node.
|
||||
*/
|
||||
Oid CoerceType(Oid typeId, Node *node);
|
||||
|
||||
Oid
|
||||
CoerceType(Oid typeId, Node *node)
|
||||
{
|
||||
switch (nodeTag(node))
|
||||
{
|
||||
case T_Const:
|
||||
{
|
||||
Const *con = (Const *) node;
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf( "Convert node %d to text\n", nodeTag(node));
|
||||
#endif
|
||||
|
||||
typeId = TEXTOID;
|
||||
con->consttype = typeId;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return typeId;
|
||||
} /* CoerceType() */
|
||||
|
||||
|
||||
/* make_op()
|
||||
* Operator construction.
|
||||
*
|
||||
* Transform operator expression ensuring type compatibility.
|
||||
* This is where some type conversion happens.
|
||||
*/
|
||||
Expr *
|
||||
make_op(char *opname, Node *ltree, Node *rtree)
|
||||
{
|
||||
@ -138,10 +181,9 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
*right;
|
||||
Expr *result;
|
||||
|
||||
/* right operator? */
|
||||
if (rtree == NULL)
|
||||
{
|
||||
|
||||
/* right operator */
|
||||
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
|
||||
temp = right_oper(opname, ltypeId);
|
||||
opform = (OperatorTupleForm) GETSTRUCT(temp);
|
||||
@ -149,25 +191,29 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
right = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* left operator? */
|
||||
else if (ltree == NULL)
|
||||
{
|
||||
|
||||
/* left operator */
|
||||
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
|
||||
temp = left_oper(opname, rtypeId);
|
||||
#ifdef PARSEDEBUG
|
||||
printf("make_op: returned from left_oper() with structure at %p\n", (void *)temp);
|
||||
#endif
|
||||
opform = (OperatorTupleForm) GETSTRUCT(temp);
|
||||
#ifdef PARSEDEBUG
|
||||
printf("make_op: calling make_operand()\n");
|
||||
#endif
|
||||
right = make_operand(opname, rtree, rtypeId, opform->oprright);
|
||||
left = NULL;
|
||||
|
||||
}
|
||||
|
||||
/* otherwise, binary operator */
|
||||
else
|
||||
{
|
||||
char *outstr;
|
||||
Oid infunc,
|
||||
outfunc;
|
||||
Type newtype;
|
||||
|
||||
#define CONVERTABLE_TYPE(t) ( (t) == INT2OID || \
|
||||
#define CONVERTIBLE_TYPE(t) ( (t) == INT2OID || \
|
||||
(t) == INT4OID || \
|
||||
(t) == OIDOID || \
|
||||
(t) == FLOAT4OID || \
|
||||
@ -178,12 +224,32 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
|
||||
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
|
||||
|
||||
#if FALSE
|
||||
/* Both operands of unknown type?
|
||||
* Then they are strings and we should force at least one to text
|
||||
* - thomas 1998-03-16
|
||||
*/
|
||||
ltypeId = exprType(ltree);
|
||||
rtypeId = exprType(rtree);
|
||||
|
||||
if ((ltypeId == UNKNOWNOID)
|
||||
&& (rtypeId == UNKNOWNOID))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf( "Convert left-hand constant to text for node %d\n", nodeTag(ltree));
|
||||
#endif
|
||||
|
||||
ltypeId = CoerceType(TEXTOID, ltree);
|
||||
}
|
||||
#endif
|
||||
|
||||
#if FALSE
|
||||
/*
|
||||
* convert constant when using a const of a numeric type and a
|
||||
* non-const of another numeric type
|
||||
*/
|
||||
if (CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
|
||||
CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
|
||||
if (CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
|
||||
CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
|
||||
!((Const *) rtree)->constiscast)
|
||||
{
|
||||
outfunc = typeidOutfunc(rtypeId);
|
||||
@ -197,8 +263,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
((Const *) rtree)->constbyval = typeByVal(newtype);
|
||||
}
|
||||
|
||||
if (CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
|
||||
CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
|
||||
if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
|
||||
CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
|
||||
!((Const *) ltree)->constiscast)
|
||||
{
|
||||
outfunc = typeidOutfunc(ltypeId);
|
||||
@ -211,6 +277,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
((Const *) ltree)->constlen = typeLen(newtype);
|
||||
((Const *) ltree)->constbyval = typeByVal(newtype);
|
||||
}
|
||||
#endif
|
||||
|
||||
temp = oper(opname, ltypeId, rtypeId, false);
|
||||
opform = (OperatorTupleForm) GETSTRUCT(temp);
|
||||
@ -219,8 +286,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
}
|
||||
|
||||
newop = makeOper(oprid(temp), /* opno */
|
||||
InvalidOid,/* opid */
|
||||
opform->oprresult, /* operator result type */
|
||||
InvalidOid, /* opid */
|
||||
opform->oprresult, /* operator result type */
|
||||
0,
|
||||
NULL);
|
||||
|
||||
@ -239,6 +306,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
Var *
|
||||
make_var(ParseState *pstate, Oid relid, char *refname,
|
||||
char *attrname)
|
||||
@ -356,6 +424,9 @@ make_array_ref(Node *expr,
|
||||
return aref;
|
||||
}
|
||||
|
||||
|
||||
/* make_array_set()
|
||||
*/
|
||||
ArrayRef *
|
||||
make_array_set(Expr *target_expr,
|
||||
List *upperIndexpr,
|
||||
@ -406,10 +477,12 @@ make_array_set(Expr *target_expr,
|
||||
aref->refexpr = (Node *) target_expr;
|
||||
aref->refassgnexpr = (Node *) expr;
|
||||
|
||||
if (lowerIndexpr == NIL) /* accessing a single array element */
|
||||
/* accessing a single array element? */
|
||||
if (lowerIndexpr == NIL)
|
||||
reftype = aref->refelemtype;
|
||||
|
||||
/* otherwise, request to set a part of the array, by another array */
|
||||
else
|
||||
/* request to set a part of the array, by another array */
|
||||
reftype = typearray;
|
||||
|
||||
aref->refelemtype = reftype;
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.10 1998/04/27 04:06:09 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.11 1998/05/09 23:29:53 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -23,9 +23,18 @@
|
||||
#include "fmgr.h"
|
||||
#include "parser/parse_oper.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "parser/parse_coerce.h"
|
||||
#include "storage/bufmgr.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
extern
|
||||
Oid *
|
||||
func_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
|
||||
|
||||
extern
|
||||
Oid *
|
||||
oper_select_candidate(int nargs, Oid *input_typeids, CandidateList candidates);
|
||||
|
||||
static int
|
||||
binary_oper_get_candidates(char *opname,
|
||||
Oid leftTypeId,
|
||||
@ -63,7 +72,8 @@ oprid(Operator op)
|
||||
return (op->t_oid);
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
/* binary_oper_get_candidates()
|
||||
* 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
|
||||
@ -97,7 +107,7 @@ binary_oper_get_candidates(char *opname,
|
||||
F_CHAREQ,
|
||||
CharGetDatum('b'));
|
||||
|
||||
|
||||
#if FALSE
|
||||
if (leftTypeId == UNKNOWNOID)
|
||||
{
|
||||
if (rightTypeId == UNKNOWNOID)
|
||||
@ -110,7 +120,7 @@ binary_oper_get_candidates(char *opname,
|
||||
|
||||
ScanKeyEntryInitialize(&opKey[2], 0,
|
||||
Anum_pg_operator_oprright,
|
||||
F_OIDEQ,
|
||||
ObjectIdEqualRegProcedure,
|
||||
ObjectIdGetDatum(rightTypeId));
|
||||
}
|
||||
}
|
||||
@ -120,12 +130,16 @@ binary_oper_get_candidates(char *opname,
|
||||
|
||||
ScanKeyEntryInitialize(&opKey[2], 0,
|
||||
Anum_pg_operator_oprleft,
|
||||
F_OIDEQ,
|
||||
ObjectIdEqualRegProcedure,
|
||||
ObjectIdGetDatum(leftTypeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* currently only "unknown" can be coerced */
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
nkeys = 2;
|
||||
|
||||
pg_operator_desc = heap_openr(OperatorRelationName);
|
||||
pg_operator_scan = heap_beginscan(pg_operator_desc,
|
||||
@ -156,7 +170,147 @@ binary_oper_get_candidates(char *opname,
|
||||
heap_close(pg_operator_desc);
|
||||
|
||||
return ncandidates;
|
||||
}
|
||||
} /* binary_oper_get_candidates() */
|
||||
|
||||
|
||||
#if FALSE
|
||||
/* BinaryOperCandidates()
|
||||
* 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
|
||||
BinaryOperCandidates(char *opname,
|
||||
Oid lTypeId,
|
||||
Oid rTypeId,
|
||||
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];
|
||||
|
||||
/* Can we promote the lesser type and find a match? */
|
||||
lCandidateTypeId = lTypeId;
|
||||
rCandidateTypeId = rTypeId;
|
||||
higherTypeId = PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
|
||||
if (lTypeId != higherTypeId)
|
||||
lowerTypeId = lTypeId;
|
||||
else
|
||||
lowerTypeId = rTypeId;
|
||||
|
||||
while (lCandidateTypeId != rCandidateTypeId)
|
||||
if ((lCandidateTypeId == InvalidOid) || (rCandidateTypeId == InvalidOid))
|
||||
break;
|
||||
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(lCandidateTypeId),
|
||||
ObjectIdGetDatum(rCandidateTypeId),
|
||||
Int8GetDatum('b'));
|
||||
if (HeapTupleIsValid(tup))
|
||||
return ((Operator) tup);
|
||||
|
||||
PromoteLowerType(&lCandidateTypeId, &rCandidateTypeId);
|
||||
}
|
||||
|
||||
/* Can we promote the lesser type directly to the other? */
|
||||
if (can_coerce_type(lowerTypeId, higherTypeId))
|
||||
{
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(higherTypeId),
|
||||
ObjectIdGetDatum(higherTypeId),
|
||||
Int8GetDatum('b'));
|
||||
if (HeapTupleIsValid(tup))
|
||||
return ((Operator) tup);
|
||||
}
|
||||
|
||||
|
||||
*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 FALSE
|
||||
if (leftTypeId == UNKNOWNOID)
|
||||
{
|
||||
if (rightTypeId == UNKNOWNOID)
|
||||
{
|
||||
nkeys = 2;
|
||||
}
|
||||
else
|
||||
{
|
||||
nkeys = 3;
|
||||
|
||||
ScanKeyEntryInitialize(&opKey[2], 0,
|
||||
Anum_pg_operator_oprright,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(rightTypeId));
|
||||
}
|
||||
}
|
||||
else if (rightTypeId == UNKNOWNOID)
|
||||
{
|
||||
nkeys = 3;
|
||||
|
||||
ScanKeyEntryInitialize(&opKey[2], 0,
|
||||
Anum_pg_operator_oprleft,
|
||||
F_OIDEQ,
|
||||
ObjectIdGetDatum(leftTypeId));
|
||||
}
|
||||
else
|
||||
{
|
||||
/* currently only "unknown" can be coerced */
|
||||
return 0;
|
||||
#endif
|
||||
|
||||
nkeys = 2;
|
||||
|
||||
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;
|
||||
} /* BinaryOperCandidates() */
|
||||
#endif
|
||||
|
||||
|
||||
/*
|
||||
* equivalentOpersAfterPromotion -
|
||||
@ -164,7 +318,7 @@ binary_oper_get_candidates(char *opname,
|
||||
* 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
|
||||
* all the candidates operate on the same data types after
|
||||
* promotion (int2, int4, float4 -> float8).
|
||||
*/
|
||||
static bool
|
||||
@ -237,9 +391,33 @@ equivalentOpersAfterPromotion(CandidateList candidates)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* given a choice of argument type pairs for a binary operator,
|
||||
* try to choose a default pair
|
||||
/* binary_oper_select_candidate()
|
||||
* Given a choice of argument type pairs for a binary operator,
|
||||
* try to choose a default pair.
|
||||
*
|
||||
* 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 was commented out since then - ay]
|
||||
*
|
||||
* 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
|
||||
*/
|
||||
static CandidateList
|
||||
binary_oper_select_candidate(Oid arg1,
|
||||
@ -249,37 +427,11 @@ binary_oper_select_candidate(Oid arg1,
|
||||
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 both are "unknown", there is no way to select a candidate
|
||||
*/
|
||||
|
||||
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;
|
||||
|
||||
@ -296,90 +448,102 @@ binary_oper_select_candidate(Oid arg1,
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Given operator, types of arg1, and arg2, return oper struct */
|
||||
/* arg1, arg2 --typeids */
|
||||
/* oper()
|
||||
* Given operator, types of arg1, and arg2, return oper struct.
|
||||
* Inputs:
|
||||
* arg1, arg2: Type IDs
|
||||
*/
|
||||
Operator
|
||||
oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
|
||||
{
|
||||
HeapTuple tup;
|
||||
CandidateList candidates;
|
||||
int ncandidates;
|
||||
HeapTuple tup;
|
||||
CandidateList candidates;
|
||||
int ncandidates;
|
||||
Oid *targetOids;
|
||||
Oid inputOids[2];
|
||||
|
||||
if (!arg2)
|
||||
/* Unspecified type for one of the arguments? then use the other */
|
||||
if (arg2 == InvalidOid)
|
||||
arg2 = arg1;
|
||||
if (!arg1)
|
||||
if (arg1 == InvalidOid)
|
||||
arg1 = arg2;
|
||||
|
||||
if (!(tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(arg1),
|
||||
ObjectIdGetDatum(arg2),
|
||||
Int8GetDatum('b'))))
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(arg1),
|
||||
ObjectIdGetDatum(arg2),
|
||||
Int8GetDatum('b'));
|
||||
|
||||
/* Did not find anything? then look more carefully... */
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
|
||||
|
||||
/* No operators found? Then throw error or return null... */
|
||||
if (ncandidates == 0)
|
||||
{
|
||||
|
||||
/*
|
||||
* no operators of the desired types found
|
||||
*/
|
||||
if (!noWarnings)
|
||||
op_error(op, arg1, arg2);
|
||||
return (NULL);
|
||||
}
|
||||
|
||||
/* Or found exactly one? Then proceed... */
|
||||
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]),
|
||||
ObjectIdGetDatum(candidates->args[0]),
|
||||
ObjectIdGetDatum(candidates->args[1]),
|
||||
Int8GetDatum('b'));
|
||||
Assert(HeapTupleIsValid(tup));
|
||||
}
|
||||
|
||||
/* Otherwise, multiple operators of the desired types found... */
|
||||
else
|
||||
{
|
||||
|
||||
/*
|
||||
* multiple operators of the desired types found
|
||||
*/
|
||||
#if FALSE
|
||||
candidates = binary_oper_select_candidate(arg1, arg2, candidates);
|
||||
if (candidates != NULL)
|
||||
#endif
|
||||
inputOids[0] = arg1;
|
||||
inputOids[1] = arg2;
|
||||
targetOids = oper_select_candidate(2, inputOids, candidates);
|
||||
#if FALSE
|
||||
targetOids = func_select_candidate(2, inputOids, candidates);
|
||||
#endif
|
||||
if (targetOids != NULL)
|
||||
{
|
||||
/* we chose one of them */
|
||||
#if PARSEDEBUG
|
||||
printf("oper: found candidate\n");
|
||||
#endif
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(candidates->args[0]),
|
||||
ObjectIdGetDatum(candidates->args[1]),
|
||||
ObjectIdGetDatum(targetOids[0]),
|
||||
ObjectIdGetDatum(targetOids[1]),
|
||||
Int8GetDatum('b'));
|
||||
Assert(HeapTupleIsValid(tup));
|
||||
}
|
||||
else
|
||||
{
|
||||
Type tp1,
|
||||
tp2;
|
||||
tup = NULL;
|
||||
}
|
||||
|
||||
/* we chose none of them */
|
||||
tp1 = typeidType(arg1);
|
||||
tp2 = typeidType(arg2);
|
||||
/* Could not choose one, for whatever reason... */
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
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(ERROR, "using an explicit cast");
|
||||
elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'"
|
||||
"\n\tYou will have to retype this query using an explicit cast",
|
||||
op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
|
||||
}
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ((Operator) tup);
|
||||
}
|
||||
} /* oper() */
|
||||
|
||||
/*
|
||||
|
||||
/* unary_oper_get_candidates()
|
||||
* 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
|
||||
@ -409,6 +573,7 @@ unary_oper_get_candidates(char *op,
|
||||
fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
|
||||
opKey[1].sk_argument = CharGetDatum(rightleft);
|
||||
|
||||
#if FALSE
|
||||
/* currently, only "unknown" can be coerced */
|
||||
|
||||
/*
|
||||
@ -419,7 +584,11 @@ unary_oper_get_candidates(char *op,
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("unary_oper_get_candidates: start scan for '%s'\n", op);
|
||||
#endif
|
||||
pg_operator_desc = heap_openr(OperatorRelationName);
|
||||
pg_operator_scan = heap_beginscan(pg_operator_desc,
|
||||
0,
|
||||
@ -442,6 +611,10 @@ unary_oper_get_candidates(char *op,
|
||||
current_candidate->args[0] = oper->oprright;
|
||||
current_candidate->next = *candidates;
|
||||
*candidates = current_candidate;
|
||||
#ifdef PARSEDEBUG
|
||||
printf("unary_oper_get_candidates: found candidate '%s' for type %s\n",
|
||||
op, typeidTypeName(current_candidate->args[0]));
|
||||
#endif
|
||||
ncandidates++;
|
||||
ReleaseBuffer(buffer);
|
||||
}
|
||||
@ -450,32 +623,35 @@ unary_oper_get_candidates(char *op,
|
||||
heap_endscan(pg_operator_scan);
|
||||
heap_close(pg_operator_desc);
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("unary_oper_get_candidates: found %d candidates\n", ncandidates);
|
||||
#endif
|
||||
return ncandidates;
|
||||
}
|
||||
} /* unary_oper_get_candidates() */
|
||||
|
||||
|
||||
/* 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;
|
||||
HeapTuple tup;
|
||||
CandidateList candidates;
|
||||
int ncandidates;
|
||||
Oid *targetOid;
|
||||
|
||||
/*
|
||||
* if (!OpCache) { init_op_cache(); }
|
||||
*/
|
||||
if (!(tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(arg),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
Int8GetDatum('r'))))
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(arg),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
Int8GetDatum('r'));
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
|
||||
if (ncandidates == 0)
|
||||
{
|
||||
elog(ERROR,
|
||||
"Can't find right op: %s for type %d", op, arg);
|
||||
elog(ERROR, "Can't find right op '%s' for type %d", op, arg);
|
||||
return (NULL);
|
||||
}
|
||||
else if (ncandidates == 1)
|
||||
@ -489,38 +665,59 @@ right_oper(char *op, Oid arg)
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(NOTICE, "there is more than one right operator %s", op);
|
||||
elog(NOTICE, "you will have to retype this query");
|
||||
elog(ERROR, "using an explicit cast");
|
||||
return (NULL);
|
||||
#if FALSE
|
||||
elog(ERROR, "There is more than one right operator %s"
|
||||
"\n\tYou will have to retype this query using an explicit cast", op);
|
||||
#endif
|
||||
targetOid = func_select_candidate(1, &arg, candidates);
|
||||
|
||||
if (targetOid != NULL)
|
||||
{
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
ObjectIdGetDatum(*targetOid),
|
||||
Int8GetDatum('r'));
|
||||
}
|
||||
else
|
||||
{
|
||||
tup = NULL;
|
||||
}
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
elog(ERROR, "Unable to convert right operator '%s' from type %s to %s",
|
||||
op, typeidTypeName(arg), typeidTypeName(*targetOid));
|
||||
return (NULL);
|
||||
}
|
||||
}
|
||||
}
|
||||
return ((Operator) tup);
|
||||
}
|
||||
} /* right_oper() */
|
||||
|
||||
|
||||
/* 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;
|
||||
HeapTuple tup;
|
||||
CandidateList candidates;
|
||||
int ncandidates;
|
||||
Oid *targetOid;
|
||||
|
||||
/*
|
||||
* if (!OpCache) { init_op_cache(); }
|
||||
*/
|
||||
if (!(tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
ObjectIdGetDatum(arg),
|
||||
Int8GetDatum('l'))))
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
ObjectIdGetDatum(arg),
|
||||
Int8GetDatum('l'));
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
|
||||
if (ncandidates == 0)
|
||||
{
|
||||
elog(ERROR,
|
||||
"Can't find left op: %s for type %d", op, arg);
|
||||
elog(ERROR, "Can't find left op '%s' for type %d", op, arg);
|
||||
return (NULL);
|
||||
}
|
||||
else if (ncandidates == 1)
|
||||
@ -528,22 +725,44 @@ left_oper(char *op, Oid arg)
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
ObjectIdGetDatum(candidates->args[0]),
|
||||
ObjectIdGetDatum(candidates->args[0]),
|
||||
Int8GetDatum('l'));
|
||||
Assert(HeapTupleIsValid(tup));
|
||||
#ifdef PARSEDEBUG
|
||||
printf("left_oper: searched cache for single left oper candidate '%s %s'\n",
|
||||
op, typeidTypeName((Oid) candidates->args[0]));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(NOTICE, "there is more than one left operator %s", op);
|
||||
elog(NOTICE, "you will have to retype this query");
|
||||
elog(ERROR, "using an explicit cast");
|
||||
return (NULL);
|
||||
#if FALSE
|
||||
elog(ERROR, "There is more than one left operator %s"
|
||||
"\n\tYou will have to retype this query using an explicit cast", op);
|
||||
#endif
|
||||
targetOid = func_select_candidate(1, &arg, candidates);
|
||||
tup = SearchSysCacheTuple(OPRNAME,
|
||||
PointerGetDatum(op),
|
||||
ObjectIdGetDatum(InvalidOid),
|
||||
ObjectIdGetDatum(*targetOid),
|
||||
Int8GetDatum('l'));
|
||||
|
||||
if (!HeapTupleIsValid(tup))
|
||||
{
|
||||
elog(ERROR, "Unable to convert left operator '%s' from type %s to %s",
|
||||
op, typeidTypeName(arg), typeidTypeName(*targetOid));
|
||||
return (NULL);
|
||||
}
|
||||
#ifdef PARSEDEBUG
|
||||
printf("left_oper: searched cache for best left oper candidate '%s %s'\n",
|
||||
op, typeidTypeName(*targetOid));
|
||||
#endif
|
||||
}
|
||||
}
|
||||
return ((Operator) tup);
|
||||
}
|
||||
} /* left_oper() */
|
||||
|
||||
/*
|
||||
|
||||
/* op_error()
|
||||
* Give a somewhat useful error message when the operator for two types
|
||||
* is not found.
|
||||
*/
|
||||
@ -559,7 +778,8 @@ op_error(char *op, Oid arg1, Oid arg2)
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "left hand side of operator %s has an unknown type, probably a bad attribute name", op);
|
||||
elog(ERROR, "Left hand side of operator '%s' has an unknown type"
|
||||
"\n\tProbably a bad attribute name", op);
|
||||
}
|
||||
|
||||
if (typeidIsValid(arg2))
|
||||
@ -568,17 +788,10 @@ op_error(char *op, Oid arg1, Oid arg2)
|
||||
}
|
||||
else
|
||||
{
|
||||
elog(ERROR, "right hand side of operator %s has an unknown type, probably a bad attribute name", op);
|
||||
elog(ERROR, "Right hand side of operator %s has an unknown type"
|
||||
"\n\tProbably a bad attribute name", op);
|
||||
}
|
||||
|
||||
#if FALSE
|
||||
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(ERROR, "%s for %s and %s using CREATE OPERATOR",
|
||||
op, typeTypeName(tp1), typeTypeName(tp2));
|
||||
#endif
|
||||
elog(ERROR, "There is no operator '%s' for types '%s' and '%s'"
|
||||
"\n\tYou will either have to retype this query using an explicit cast,"
|
||||
"\n\tor you will have to define the operator using CREATE OPERATOR",
|
||||
|
@ -7,7 +7,7 @@
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.11 1998/02/26 04:33:35 momjian Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.12 1998/05/09 23:29:54 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -26,6 +26,13 @@
|
||||
#include "parser/parse_target.h"
|
||||
#include "utils/builtins.h"
|
||||
#include "utils/lsyscache.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
extern
|
||||
bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
|
||||
|
||||
extern
|
||||
Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
|
||||
|
||||
static List *expandAllTables(ParseState *pstate);
|
||||
static char *figureColname(Node *expr, Node *resval);
|
||||
@ -34,6 +41,16 @@ make_targetlist_expr(ParseState *pstate,
|
||||
char *colname,
|
||||
Node *expr,
|
||||
List *arrayRef);
|
||||
Node *
|
||||
size_target_expr(ParseState *pstate,
|
||||
Node *expr,
|
||||
Oid attrtype,
|
||||
int16 attrtypmod);
|
||||
Node *
|
||||
coerce_target_expr(ParseState *pstate,
|
||||
Node *expr,
|
||||
Oid type_id,
|
||||
Oid attrtype);
|
||||
|
||||
/*
|
||||
* transformTargetList -
|
||||
@ -110,8 +127,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
Relation rd;
|
||||
Value *constval;
|
||||
|
||||
if (exprType(expr) != UNKNOWNOID ||
|
||||
!IsA(expr, Const))
|
||||
if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
|
||||
elog(ERROR, "yyparse: string constant expected");
|
||||
|
||||
val = (char *) textout((struct varlena *)
|
||||
@ -123,15 +139,15 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
|
||||
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
|
||||
if (!IsA(aind->uidx, Const))
|
||||
elog(ERROR,
|
||||
"Array Index for Append should be a constant");
|
||||
elog(ERROR, "Array Index for Append should be a constant");
|
||||
|
||||
uindx[i] = ((Const *) aind->uidx)->constvalue;
|
||||
if (aind->lidx != NULL)
|
||||
{
|
||||
aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
|
||||
if (!IsA(aind->lidx, Const))
|
||||
elog(ERROR,
|
||||
"Array Index for Append should be a constant");
|
||||
elog(ERROR, "Array Index for Append should be a constant");
|
||||
|
||||
lindx[i] = ((Const *) aind->lidx)->constvalue;
|
||||
}
|
||||
else
|
||||
@ -140,6 +156,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
}
|
||||
if (lindx[i] > uindx[i])
|
||||
elog(ERROR, "yyparse: lower index cannot be greater than upper index");
|
||||
|
||||
sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
|
||||
str += strlen(str);
|
||||
i++;
|
||||
@ -151,11 +168,12 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
ndims = attnumAttNelems(rd, resdomno);
|
||||
if (i != ndims)
|
||||
elog(ERROR, "yyparse: array dimensions do not match");
|
||||
|
||||
constval = makeNode(Value);
|
||||
constval->type = T_String;
|
||||
constval->val.str = save_str;
|
||||
tent = make_targetlist_expr(pstate, res->name,
|
||||
(Node *) make_const(constval),
|
||||
(Node *) make_const(constval),
|
||||
NULL);
|
||||
pfree(save_str);
|
||||
}
|
||||
@ -300,8 +318,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
}
|
||||
default:
|
||||
/* internal error */
|
||||
elog(ERROR,
|
||||
"internal error: do not know how to transform targetlist");
|
||||
elog(ERROR, "internal error: do not know how to transform targetlist");
|
||||
break;
|
||||
}
|
||||
|
||||
@ -321,11 +338,125 @@ transformTargetList(ParseState *pstate, List *targetlist)
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
* make_targetlist_expr -
|
||||
* make a TargetEntry from an expression
|
||||
Node *
|
||||
coerce_target_expr(ParseState *pstate,
|
||||
Node *expr,
|
||||
Oid type_id,
|
||||
Oid attrtype)
|
||||
{
|
||||
if (can_coerce_type(1, &type_id, &attrtype))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: coerce type from %s to %s\n",
|
||||
typeidTypeName(type_id), typeidTypeName(attrtype));
|
||||
#endif
|
||||
expr = coerce_type(pstate, expr, type_id, attrtype);
|
||||
}
|
||||
|
||||
#ifndef DISABLE_STRING_HACKS
|
||||
/* string hacks to get transparent conversions w/o explicit conversions */
|
||||
else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
|
||||
{
|
||||
Oid text_id = TEXTOID;
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: try coercing from %s to %s via text\n",
|
||||
typeidTypeName(type_id), typeidTypeName(attrtype));
|
||||
#endif
|
||||
if (type_id == TEXTOID)
|
||||
{
|
||||
}
|
||||
else if (can_coerce_type(1, &type_id, &text_id))
|
||||
{
|
||||
expr = coerce_type(pstate, expr, type_id, text_id);
|
||||
}
|
||||
else
|
||||
{
|
||||
expr = NULL;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
else
|
||||
{
|
||||
expr = NULL;
|
||||
}
|
||||
|
||||
return expr;
|
||||
} /* coerce_target_expr() */
|
||||
|
||||
|
||||
/* size_target_expr()
|
||||
* Apparently going to a fixed-length string?
|
||||
* Then explicitly size for storage...
|
||||
*/
|
||||
Node *
|
||||
size_target_expr(ParseState *pstate,
|
||||
Node *expr,
|
||||
Oid attrtype,
|
||||
int16 attrtypmod)
|
||||
{
|
||||
int i;
|
||||
HeapTuple ftup;
|
||||
char *funcname;
|
||||
Oid oid_array[8];
|
||||
|
||||
FuncCall *func;
|
||||
A_Const *cons;
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: ensure target fits storage\n");
|
||||
#endif
|
||||
funcname = typeidTypeName(attrtype);
|
||||
oid_array[0] = attrtype;
|
||||
oid_array[1] = INT4OID;
|
||||
for (i = 2; i < 8; i++) oid_array[i] = InvalidOid;
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: look for conversion function %s(%s,%s)\n",
|
||||
funcname, typeidTypeName(attrtype), typeidTypeName(INT4OID));
|
||||
#endif
|
||||
|
||||
/* attempt to find with arguments exactly as specified... */
|
||||
ftup = SearchSysCacheTuple(PRONAME,
|
||||
PointerGetDatum(funcname),
|
||||
Int32GetDatum(2),
|
||||
PointerGetDatum(oid_array),
|
||||
0);
|
||||
|
||||
if (HeapTupleIsValid(ftup))
|
||||
{
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: found conversion function for sizing\n");
|
||||
#endif
|
||||
func = makeNode(FuncCall);
|
||||
func->funcname = funcname;
|
||||
|
||||
cons = makeNode(A_Const);
|
||||
cons->val.type = T_Integer;
|
||||
cons->val.val.ival = attrtypmod;
|
||||
func->args = lappend( lcons(expr,NIL), cons);
|
||||
|
||||
expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
|
||||
}
|
||||
#ifdef PARSEDEBUG
|
||||
else
|
||||
{
|
||||
printf("parse_target: no conversion function for sizing\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
return expr;
|
||||
} /* size_target_expr() */
|
||||
|
||||
|
||||
/* make_targetlist_expr()
|
||||
* Make a TargetEntry from an expression
|
||||
*
|
||||
* arrayRef is a list of transformed A_Indices
|
||||
*
|
||||
* For type mismatches between expressions and targets, use the same
|
||||
* techniques as for function and operator type coersion.
|
||||
* - thomas 1998-05-08
|
||||
*/
|
||||
static TargetEntry *
|
||||
make_targetlist_expr(ParseState *pstate,
|
||||
@ -355,7 +486,6 @@ make_targetlist_expr(ParseState *pstate,
|
||||
/* Processes target columns that will be receiving results */
|
||||
if (pstate->p_is_insert || pstate->p_is_update)
|
||||
{
|
||||
|
||||
/*
|
||||
* insert or update query -- insert, update work only on one
|
||||
* relation, so multiple occurence of same resdomno is bogus
|
||||
@ -368,91 +498,47 @@ make_targetlist_expr(ParseState *pstate,
|
||||
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
|
||||
attrtype = GetArrayElementType(attrtype);
|
||||
attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
|
||||
#if 0
|
||||
if (Input_is_string && Typecast_ok)
|
||||
{
|
||||
Datum val;
|
||||
|
||||
if (type_id == typeTypeId(type("unknown")))
|
||||
{
|
||||
val = (Datum) textout((struct varlena *)
|
||||
((Const) lnext(expr))->constvalue);
|
||||
}
|
||||
else
|
||||
{
|
||||
val = ((Const) lnext(expr))->constvalue;
|
||||
}
|
||||
if (attrisset)
|
||||
{
|
||||
lnext(expr) = makeConst(attrtype,
|
||||
attrlen,
|
||||
val,
|
||||
false,
|
||||
true,
|
||||
true, /* is set */
|
||||
false);
|
||||
}
|
||||
else
|
||||
{
|
||||
lnext(expr) =
|
||||
makeConst(attrtype,
|
||||
attrlen,
|
||||
(Datum) fmgr(typeidInfunc(attrtype),
|
||||
val, typeidTypElem(attrtype), -1),
|
||||
false,
|
||||
true /* Maybe correct-- 80% chance */ ,
|
||||
false, /* is not a set */
|
||||
false);
|
||||
}
|
||||
}
|
||||
else if ((Typecast_ok) && (attrtype != type_id))
|
||||
/* Check for InvalidOid since that seems to indicate a NULL constant... */
|
||||
if (type_id != InvalidOid)
|
||||
{
|
||||
lnext(expr) =
|
||||
parser_typecast2(expr, typeidType(attrtype));
|
||||
}
|
||||
else if (attrtype != type_id)
|
||||
{
|
||||
if ((attrtype == INT2OID) && (type_id == INT4OID))
|
||||
lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */
|
||||
else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID))
|
||||
lfirst(expr) = lispInteger(FLOAT4OID);
|
||||
else
|
||||
elog(ERROR, "unequal type in tlist : %s \n", colname);
|
||||
}
|
||||
/* Mismatch on types? then try to coerce to target... */
|
||||
if (attrtype != type_id)
|
||||
{
|
||||
Oid typelem;
|
||||
|
||||
Input_is_string = false;
|
||||
Input_is_integer = false;
|
||||
Typecast_ok = true;
|
||||
#endif
|
||||
|
||||
if (attrtype != type_id)
|
||||
{
|
||||
if (IsA(expr, Const))
|
||||
{
|
||||
/* try to cast the constant */
|
||||
if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
|
||||
{
|
||||
/* updating a single item */
|
||||
Oid typelem = typeidTypElem(attrtype);
|
||||
|
||||
expr = (Node *) parser_typecast2(expr,
|
||||
type_id,
|
||||
typeidType(typelem),
|
||||
attrtypmod);
|
||||
typelem = typeidTypElem(attrtype);
|
||||
}
|
||||
else
|
||||
expr = (Node *) parser_typecast2(expr,
|
||||
type_id,
|
||||
typeidType(attrtype),
|
||||
attrtypmod);
|
||||
{
|
||||
typelem = attrtype;
|
||||
}
|
||||
|
||||
expr = coerce_target_expr(pstate, expr, type_id, typelem);
|
||||
|
||||
if (!HeapTupleIsValid(expr))
|
||||
{
|
||||
elog(ERROR, "parser: attribute '%s' is of type '%s'"
|
||||
" but expression is of type '%s'"
|
||||
"\n\tYou will need to rewrite or cast the expression",
|
||||
colname,
|
||||
typeidTypeName(attrtype),
|
||||
typeidTypeName(type_id));
|
||||
}
|
||||
}
|
||||
else
|
||||
|
||||
#ifdef PARSEDEBUG
|
||||
printf("parse_target: attrtypmod is %d\n", (int4) attrtypmod);
|
||||
#endif
|
||||
|
||||
/* Apparently going to a fixed-length string?
|
||||
* Then explicitly size for storage...
|
||||
*/
|
||||
if (attrtypmod > 0)
|
||||
{
|
||||
/* currently, we can't handle casting of expressions */
|
||||
elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
|
||||
colname,
|
||||
typeidTypeName(attrtype),
|
||||
typeidTypeName(type_id));
|
||||
expr = size_target_expr(pstate, expr, attrtype, attrtypmod);
|
||||
}
|
||||
}
|
||||
|
||||
@ -467,8 +553,8 @@ make_targetlist_expr(ParseState *pstate,
|
||||
att->relname = pstrdup(RelationGetRelationName(rd)->data);
|
||||
att->attrs = lcons(makeString(colname), NIL);
|
||||
target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
|
||||
&pstate->p_last_resno,
|
||||
EXPR_COLUMN_FIRST);
|
||||
&pstate->p_last_resno,
|
||||
EXPR_COLUMN_FIRST);
|
||||
while (ar != NIL)
|
||||
{
|
||||
A_Indices *ind = lfirst(ar);
|
||||
@ -514,7 +600,8 @@ make_targetlist_expr(ParseState *pstate,
|
||||
tent->expr = expr;
|
||||
|
||||
return tent;
|
||||
}
|
||||
} /* make_targetlist_expr() */
|
||||
|
||||
|
||||
/*
|
||||
* makeTargetNames -
|
||||
@ -564,7 +651,7 @@ makeTargetNames(ParseState *pstate, List *cols)
|
||||
attnameAttNum(pstate->p_target_relation, name);
|
||||
foreach(nxt, lnext(tl))
|
||||
if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
|
||||
elog(ERROR, "Attribute '%s' should be specified only once", name);
|
||||
elog(ERROR, "Attribute '%s' should be specified only once", name);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,13 +1,13 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* parse_type.h
|
||||
* parse_type.c
|
||||
* handle type operations for parser
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
*
|
||||
* IDENTIFICATION
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.8 1998/02/27 19:44:51 scrappy Exp $
|
||||
* $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.9 1998/05/09 23:29:54 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -15,11 +15,17 @@
|
||||
#include "postgres.h"
|
||||
#include "fmgr.h"
|
||||
|
||||
#include "nodes/nodes.h"
|
||||
#include "nodes/parsenodes.h"
|
||||
#include "nodes/primnodes.h"
|
||||
#include "parser/parse_node.h"
|
||||
|
||||
#include "catalog/pg_type.h"
|
||||
#include "parser/parse_target.h"
|
||||
#include "parser/parse_type.h"
|
||||
#include "utils/syscache.h"
|
||||
|
||||
|
||||
/* check to see if a type id is valid,
|
||||
* returns true if it is. By using this call before calling
|
||||
* typeidType or typeidTypeName, more meaningful error messages
|
||||
|
96
src/include/parser/parse_coerce.h
Normal file
96
src/include/parser/parse_coerce.h
Normal file
@ -0,0 +1,96 @@
|
||||
/*-------------------------------------------------------------------------
|
||||
*
|
||||
* parse_coerce.h
|
||||
*
|
||||
*
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_coerce.h,v 1.1 1998/05/09 23:31:34 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
#ifndef PARSE_COERCE_H
|
||||
#define PARSE_COERCE_H
|
||||
|
||||
typedef enum CATEGORY {
|
||||
INVALID_TYPE,
|
||||
UNKNOWN_TYPE,
|
||||
BOOLEAN_TYPE,
|
||||
STRING_TYPE,
|
||||
NUMERIC_TYPE,
|
||||
DATETIME_TYPE,
|
||||
TIMESPAN_TYPE,
|
||||
GEOMETRIC_TYPE,
|
||||
USER_TYPE,
|
||||
MIXED_TYPE
|
||||
} CATEGORY;
|
||||
|
||||
|
||||
#define IS_BUILTIN_TYPE(t) \
|
||||
(((t) == BOOLOID) \
|
||||
|| ((t) == BPCHAROID) \
|
||||
|| ((t) == VARCHAROID) \
|
||||
|| ((t) == TEXTOID) \
|
||||
|| ((t) == CASHOID) \
|
||||
|| ((t) == INT4OID) \
|
||||
|| ((t) == DATETIMEOID) \
|
||||
|| ((t) == FLOAT8OID) \
|
||||
|| ((t) == ABSTIMEOID) \
|
||||
|| ((t) == TIMESTAMPOID) \
|
||||
|| ((t) == RELTIMEOID))
|
||||
|
||||
|
||||
/* IS_BINARY_COMPATIBLE()
|
||||
* Check for types with the same underlying binary representation.
|
||||
* This allows us to cheat and directly exchange values without
|
||||
* going through the trouble of calling a conversion function.
|
||||
*/
|
||||
#define IS_BINARY_COMPATIBLE(a,b) \
|
||||
(((a) == BPCHAROID && (b) == TEXTOID) \
|
||||
|| ((a) == BPCHAROID && (b) == VARCHAROID) \
|
||||
|| ((a) == VARCHAROID && (b) == TEXTOID) \
|
||||
|| ((a) == VARCHAROID && (b) == BPCHAROID) \
|
||||
|| ((a) == TEXTOID && (b) == BPCHAROID) \
|
||||
|| ((a) == TEXTOID && (b) == VARCHAROID) \
|
||||
|| ((a) == CASHOID && (b) == INT4OID) \
|
||||
|| ((a) == INT4OID && (b) == CASHOID) \
|
||||
|| ((a) == DATETIMEOID && (b) == FLOAT8OID) \
|
||||
|| ((a) == FLOAT8OID && (b) == DATETIMEOID) \
|
||||
|| ((a) == ABSTIMEOID && (b) == TIMESTAMPOID) \
|
||||
|| ((a) == TIMESTAMPOID && (b) == ABSTIMEOID) \
|
||||
|| ((a) == ABSTIMEOID && (b) == INT4OID) \
|
||||
|| ((a) == INT4OID && (b) == ABSTIMEOID) \
|
||||
|| ((a) == RELTIMEOID && (b) == INT4OID) \
|
||||
|| ((a) == INT4OID && (b) == RELTIMEOID))
|
||||
|
||||
/* IS_HIGHER_TYPE()
|
||||
* These types are the most general in each of the type categories.
|
||||
*/
|
||||
#define IS_HIGHER_TYPE(t) \
|
||||
(((t) == TEXTOID) \
|
||||
|| ((t) == FLOAT8OID) \
|
||||
|| ((t) == TIMESPANOID) \
|
||||
|| ((t) == DATETIMEOID) \
|
||||
|| ((t) == POLYGONOID))
|
||||
|
||||
/* IS_HIGHEST_TYPE()
|
||||
* These types are the most general in each of the type categories.
|
||||
* Since timespan and datetime overload so many functions, let's
|
||||
* give datetime the preference.
|
||||
* Since text is a generic string type let's leave it out too.
|
||||
*/
|
||||
#define IS_HIGHEST_TYPE(t) \
|
||||
(((t) == FLOAT8OID) \
|
||||
|| ((t) == DATETIMEOID) \
|
||||
|| ((t) == TIMESPANOID))
|
||||
|
||||
|
||||
extern bool IsPreferredType(CATEGORY category, Oid type);
|
||||
extern Oid PreferredType(CATEGORY category, Oid type);
|
||||
extern CATEGORY TypeCategory(Oid type);
|
||||
|
||||
extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
|
||||
extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
|
||||
|
||||
#endif /* PARSE_COERCE_H */
|
@ -6,7 +6,7 @@
|
||||
*
|
||||
* Copyright (c) 1994, Regents of the University of California
|
||||
*
|
||||
* $Id: parse_func.h,v 1.8 1998/02/26 04:42:45 momjian Exp $
|
||||
* $Id: parse_func.h,v 1.9 1998/05/09 23:31:34 thomas Exp $
|
||||
*
|
||||
*-------------------------------------------------------------------------
|
||||
*/
|
||||
@ -49,6 +49,6 @@ extern Node *
|
||||
ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
|
||||
int *curr_resno, int precedence);
|
||||
|
||||
extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes);
|
||||
extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes, char *msg);
|
||||
|
||||
#endif /* PARSE_FUNC_H */
|
||||
|
Loading…
x
Reference in New Issue
Block a user