Add capabilities for automatic type conversion.

This commit is contained in:
Thomas G. Lockhart 1998-05-09 23:31:34 +00:00
parent 54b5577cb6
commit 3ace5fd082
10 changed files with 2051 additions and 395 deletions

View File

@ -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)

View 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

View File

@ -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

View File

@ -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;

View File

@ -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",

View File

@ -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);
}
}

View File

@ -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

View 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 */

View File

@ -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 */