1
0
mirror of https://github.com/postgres/postgres.git synced 2025-06-13 07:41:39 +03:00

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 * 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 "parser/parse_target.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/mcxt.h" #include "utils/mcxt.h"
#ifdef PARSEDEBUG
#include "nodes/print.h"
#endif
static Query *transformStmt(ParseState *pstate, Node *stmt); static Query *transformStmt(ParseState *pstate, Node *stmt);
static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt);
@ -65,6 +68,10 @@ parse_analyze(List *pl, ParseState *parentParseState)
while (pl != NIL) while (pl != NIL)
{ {
#ifdef PARSEDEBUG
elog(DEBUG,"parse tree from yacc:\n---\n%s\n---\n", nodeToString(lfirst(pl)));
#endif
pstate = make_parsestate(parentParseState); pstate = make_parsestate(parentParseState);
result->qtrees[i++] = transformStmt(pstate, lfirst(pl)); result->qtrees[i++] = transformStmt(pstate, lfirst(pl));
if (pstate->p_target_relation != NULL) 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 * 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; result = (Node *) expr;
break; 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 * At least one construct (BETWEEN/AND) puts the same nodes
* into two branches of the parse tree. Hence, some nodes * into two branches of the parse tree; hence, some nodes
* are transformed twice. These nodes come from transforming * are transformed twice.
* a function call. Let's try just passing them through... * These cases below come from transforming function calls.
* Let's try just passing them through...
* - thomas 1998-03-14 * - thomas 1998-03-14
*/ */
case T_Expr: case T_Expr:
@ -506,6 +508,10 @@ parser_typecast(Value *expr, TypeName *typename, int16 atttypmod)
return (Node *) adt; return (Node *) adt;
} }
/* parser_typecast2()
* Convert (only) constants to specified type.
*/
Node * Node *
parser_typecast2(Node *expr, Oid exprType, Type tp, int16 atttypmod) 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 * 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() -- * make_parsestate() --
* allocate and initialize a new 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); 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 * static Node *
make_operand(char *opname, make_operand(char *opname,
Node *tree, Node *tree,
@ -65,35 +74,33 @@ make_operand(char *opname,
{ {
Node *result; Node *result;
Type true_type; Type true_type;
#if FALSE
Datum val; Datum val;
Oid infunc; 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) if (tree != NULL)
{ {
result = tree; result = tree;
true_type = typeidType(true_typeId); true_type = typeidType(true_typeId);
disallow_setop(opname, true_type, result); disallow_setop(opname, true_type, result);
if (true_typeId != orig_typeId)
{ /* must coerce */
Const *con = (Const *) result;
Assert(nodeTag(result) == T_Const); /* must coerce? */
val = (Datum) textout((struct varlena *) if (true_typeId != orig_typeId)
con->constvalue); {
infunc = typeidInfunc(true_typeId); #ifdef PARSEDEBUG
con = makeNode(Const); printf("make_operand: try to convert node from %s to %s\n",
con->consttype = true_typeId; typeidTypeName(orig_typeId), typeidTypeName(true_typeId));
con->constlen = typeLen(true_type); #endif
con->constvalue = (Datum) fmgr(infunc, result = coerce_type(NULL, tree, orig_typeId, true_typeId);
val,
typeidTypElem(true_typeId),
-1 /* for varchar() type */ );
con->constisnull = false;
con->constbyval = true;
con->constisset = false;
result = (Node *) con;
} }
} }
/* otherwise, this is a NULL value */
else else
{ {
Const *con = makeNode(Const); Const *con = makeNode(Const);
@ -108,7 +115,7 @@ make_operand(char *opname,
} }
return result; return result;
} } /* make_operand() */
static void static void
@ -119,13 +126,49 @@ disallow_setop(char *op, Type optype, Node *operand)
if (nodeTag(operand) == T_Iter) if (nodeTag(operand) == T_Iter)
{ {
elog(NOTICE, "An operand to the '%s' operator returns a set of %s,", elog(ERROR, "An operand to the '%s' operator returns a set of %s,"
op, typeTypeName(optype)); "\n\tbut '%s' takes single values, not sets.",
elog(ERROR, "but '%s' takes single values, not sets.", op, typeTypeName(optype), op);
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 * Expr *
make_op(char *opname, Node *ltree, Node *rtree) make_op(char *opname, Node *ltree, Node *rtree)
{ {
@ -138,10 +181,9 @@ make_op(char *opname, Node *ltree, Node *rtree)
*right; *right;
Expr *result; Expr *result;
/* right operator? */
if (rtree == NULL) if (rtree == NULL)
{ {
/* right operator */
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
temp = right_oper(opname, ltypeId); temp = right_oper(opname, ltypeId);
opform = (OperatorTupleForm) GETSTRUCT(temp); opform = (OperatorTupleForm) GETSTRUCT(temp);
@ -149,25 +191,29 @@ make_op(char *opname, Node *ltree, Node *rtree)
right = NULL; right = NULL;
} }
/* left operator? */
else if (ltree == NULL) else if (ltree == NULL)
{ {
/* left operator */
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree);
temp = left_oper(opname, rtypeId); 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); opform = (OperatorTupleForm) GETSTRUCT(temp);
#ifdef PARSEDEBUG
printf("make_op: calling make_operand()\n");
#endif
right = make_operand(opname, rtree, rtypeId, opform->oprright); right = make_operand(opname, rtree, rtypeId, opform->oprright);
left = NULL; left = NULL;
} }
/* otherwise, binary operator */
else else
{ {
char *outstr;
Oid infunc,
outfunc;
Type newtype;
#define CONVERTABLE_TYPE(t) ( (t) == INT2OID || \ #define CONVERTIBLE_TYPE(t) ( (t) == INT2OID || \
(t) == INT4OID || \ (t) == INT4OID || \
(t) == OIDOID || \ (t) == OIDOID || \
(t) == FLOAT4OID || \ (t) == FLOAT4OID || \
@ -178,12 +224,32 @@ make_op(char *opname, Node *ltree, Node *rtree)
ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree); ltypeId = (ltree == NULL) ? UNKNOWNOID : exprType(ltree);
rtypeId = (rtree == NULL) ? UNKNOWNOID : exprType(rtree); 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 * convert constant when using a const of a numeric type and a
* non-const of another numeric type * non-const of another numeric type
*/ */
if (CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const && if (CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) != T_Const &&
CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const && CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const &&
!((Const *) rtree)->constiscast) !((Const *) rtree)->constiscast)
{ {
outfunc = typeidOutfunc(rtypeId); outfunc = typeidOutfunc(rtypeId);
@ -197,8 +263,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
((Const *) rtree)->constbyval = typeByVal(newtype); ((Const *) rtree)->constbyval = typeByVal(newtype);
} }
if (CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && if (CONVERTIBLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const &&
CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const && CONVERTIBLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const &&
!((Const *) ltree)->constiscast) !((Const *) ltree)->constiscast)
{ {
outfunc = typeidOutfunc(ltypeId); outfunc = typeidOutfunc(ltypeId);
@ -211,6 +277,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
((Const *) ltree)->constlen = typeLen(newtype); ((Const *) ltree)->constlen = typeLen(newtype);
((Const *) ltree)->constbyval = typeByVal(newtype); ((Const *) ltree)->constbyval = typeByVal(newtype);
} }
#endif
temp = oper(opname, ltypeId, rtypeId, false); temp = oper(opname, ltypeId, rtypeId, false);
opform = (OperatorTupleForm) GETSTRUCT(temp); opform = (OperatorTupleForm) GETSTRUCT(temp);
@ -219,8 +286,8 @@ make_op(char *opname, Node *ltree, Node *rtree)
} }
newop = makeOper(oprid(temp), /* opno */ newop = makeOper(oprid(temp), /* opno */
InvalidOid,/* opid */ InvalidOid, /* opid */
opform->oprresult, /* operator result type */ opform->oprresult, /* operator result type */
0, 0,
NULL); NULL);
@ -239,6 +306,7 @@ make_op(char *opname, Node *ltree, Node *rtree)
return result; return result;
} }
Var * Var *
make_var(ParseState *pstate, Oid relid, char *refname, make_var(ParseState *pstate, Oid relid, char *refname,
char *attrname) char *attrname)
@ -356,6 +424,9 @@ make_array_ref(Node *expr,
return aref; return aref;
} }
/* make_array_set()
*/
ArrayRef * ArrayRef *
make_array_set(Expr *target_expr, make_array_set(Expr *target_expr,
List *upperIndexpr, List *upperIndexpr,
@ -406,10 +477,12 @@ make_array_set(Expr *target_expr,
aref->refexpr = (Node *) target_expr; aref->refexpr = (Node *) target_expr;
aref->refassgnexpr = (Node *) expr; aref->refassgnexpr = (Node *) expr;
if (lowerIndexpr == NIL) /* accessing a single array element */ /* accessing a single array element? */
if (lowerIndexpr == NIL)
reftype = aref->refelemtype; reftype = aref->refelemtype;
/* otherwise, request to set a part of the array, by another array */
else else
/* request to set a part of the array, by another array */
reftype = typearray; reftype = typearray;
aref->refelemtype = reftype; aref->refelemtype = reftype;

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 "fmgr.h"
#include "parser/parse_oper.h" #include "parser/parse_oper.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "parser/parse_coerce.h"
#include "storage/bufmgr.h" #include "storage/bufmgr.h"
#include "utils/syscache.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 static int
binary_oper_get_candidates(char *opname, binary_oper_get_candidates(char *opname,
Oid leftTypeId, Oid leftTypeId,
@ -63,7 +72,8 @@ oprid(Operator op)
return (op->t_oid); return (op->t_oid);
} }
/*
/* binary_oper_get_candidates()
* given opname, leftTypeId and rightTypeId, * given opname, leftTypeId and rightTypeId,
* find all possible (arg1, arg2) pairs for which an operator named * find all possible (arg1, arg2) pairs for which an operator named
* opname exists, such that leftTypeId can be coerced to arg1 and * opname exists, such that leftTypeId can be coerced to arg1 and
@ -97,7 +107,7 @@ binary_oper_get_candidates(char *opname,
F_CHAREQ, F_CHAREQ,
CharGetDatum('b')); CharGetDatum('b'));
#if FALSE
if (leftTypeId == UNKNOWNOID) if (leftTypeId == UNKNOWNOID)
{ {
if (rightTypeId == UNKNOWNOID) if (rightTypeId == UNKNOWNOID)
@ -110,7 +120,7 @@ binary_oper_get_candidates(char *opname,
ScanKeyEntryInitialize(&opKey[2], 0, ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprright, Anum_pg_operator_oprright,
F_OIDEQ, ObjectIdEqualRegProcedure,
ObjectIdGetDatum(rightTypeId)); ObjectIdGetDatum(rightTypeId));
} }
} }
@ -120,12 +130,16 @@ binary_oper_get_candidates(char *opname,
ScanKeyEntryInitialize(&opKey[2], 0, ScanKeyEntryInitialize(&opKey[2], 0,
Anum_pg_operator_oprleft, Anum_pg_operator_oprleft,
F_OIDEQ, ObjectIdEqualRegProcedure,
ObjectIdGetDatum(leftTypeId)); ObjectIdGetDatum(leftTypeId));
} }
else else
{
/* currently only "unknown" can be coerced */ /* currently only "unknown" can be coerced */
return 0; return 0;
#endif
nkeys = 2;
pg_operator_desc = heap_openr(OperatorRelationName); pg_operator_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc, pg_operator_scan = heap_beginscan(pg_operator_desc,
@ -156,7 +170,147 @@ binary_oper_get_candidates(char *opname,
heap_close(pg_operator_desc); heap_close(pg_operator_desc);
return ncandidates; 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 - * equivalentOpersAfterPromotion -
@ -164,7 +318,7 @@ binary_oper_get_candidates(char *opname,
* binary_oper_get_candidates() contain equivalent operators. If * binary_oper_get_candidates() contain equivalent operators. If
* this routine is called, we have more than 1 candidate and need to * 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 * 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). * promotion (int2, int4, float4 -> float8).
*/ */
static bool static bool
@ -237,9 +391,33 @@ equivalentOpersAfterPromotion(CandidateList candidates)
} }
/* /* binary_oper_select_candidate()
* given a choice of argument type pairs for a binary operator, * Given a choice of argument type pairs for a binary operator,
* try to choose a default pair * 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 static CandidateList
binary_oper_select_candidate(Oid arg1, binary_oper_select_candidate(Oid arg1,
@ -249,37 +427,11 @@ binary_oper_select_candidate(Oid arg1,
CandidateList result; CandidateList result;
/* /*
* if both are "unknown", there is no way to select a candidate * If both are "unknown", there is no way to select a candidate
*
* current wisdom holds that the default operator should be one in which
* both operands have the same type (there will only be one such
* operator)
*
* 7.27.93 - I have decided not to do this; it's too hard to justify, and
* it's easy enough to typecast explicitly -avi [the rest of this
* routine were commented out since then -ay]
*/ */
if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID)
return (NULL); 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)) if (!equivalentOpersAfterPromotion(candidates))
return NULL; return NULL;
@ -296,90 +448,102 @@ binary_oper_select_candidate(Oid arg1,
return (NULL); return (NULL);
} }
/* Given operator, types of arg1, and arg2, return oper struct */ /* oper()
/* arg1, arg2 --typeids */ * Given operator, types of arg1, and arg2, return oper struct.
* Inputs:
* arg1, arg2: Type IDs
*/
Operator Operator
oper(char *op, Oid arg1, Oid arg2, bool noWarnings) oper(char *op, Oid arg1, Oid arg2, bool noWarnings)
{ {
HeapTuple tup; HeapTuple tup;
CandidateList candidates; CandidateList candidates;
int ncandidates; 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; arg2 = arg1;
if (!arg1) if (arg1 == InvalidOid)
arg1 = arg2; arg1 = arg2;
if (!(tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(arg1), ObjectIdGetDatum(arg1),
ObjectIdGetDatum(arg2), ObjectIdGetDatum(arg2),
Int8GetDatum('b')))) Int8GetDatum('b'));
/* Did not find anything? then look more carefully... */
if (!HeapTupleIsValid(tup))
{ {
ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates);
/* No operators found? Then throw error or return null... */
if (ncandidates == 0) if (ncandidates == 0)
{ {
/*
* no operators of the desired types found
*/
if (!noWarnings) if (!noWarnings)
op_error(op, arg1, arg2); op_error(op, arg1, arg2);
return (NULL); return (NULL);
} }
/* Or found exactly one? Then proceed... */
else if (ncandidates == 1) else if (ncandidates == 1)
{ {
/*
* exactly one operator of the desired types found
*/
tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(candidates->args[0]),
ObjectIdGetDatum(candidates->args[1]), ObjectIdGetDatum(candidates->args[1]),
Int8GetDatum('b')); Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup)); Assert(HeapTupleIsValid(tup));
} }
/* Otherwise, multiple operators of the desired types found... */
else else
{ {
#if FALSE
/*
* multiple operators of the desired types found
*/
candidates = binary_oper_select_candidate(arg1, arg2, candidates); 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, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(targetOids[0]),
ObjectIdGetDatum(candidates->args[1]), ObjectIdGetDatum(targetOids[1]),
Int8GetDatum('b')); Int8GetDatum('b'));
Assert(HeapTupleIsValid(tup));
} }
else else
{ {
Type tp1, tup = NULL;
tp2; }
/* we chose none of them */ /* Could not choose one, for whatever reason... */
tp1 = typeidType(arg1); if (!HeapTupleIsValid(tup))
tp2 = typeidType(arg2); {
if (!noWarnings) if (!noWarnings)
{ {
elog(NOTICE, "there is more than one operator %s for types", op); elog(ERROR, "There is more than one operator '%s' for types '%s' and '%s'"
elog(NOTICE, "%s and %s. You will have to retype this query", "\n\tYou will have to retype this query using an explicit cast",
typeTypeName(tp1), typeTypeName(tp2)); op, typeTypeName(typeidType(arg1)), typeTypeName(typeidType(arg2)));
elog(ERROR, "using an explicit cast");
} }
return (NULL); return (NULL);
} }
} }
} }
return ((Operator) tup); return ((Operator) tup);
} } /* oper() */
/*
/* unary_oper_get_candidates()
* given opname and typeId, find all possible types for which * given opname and typeId, find all possible types for which
* a right/left unary operator named opname exists, * a right/left unary operator named opname exists,
* such that typeId can be coerced to it * 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); fmgr_info(F_CHAREQ, (FmgrInfo *) &opKey[1].sk_func);
opKey[1].sk_argument = CharGetDatum(rightleft); opKey[1].sk_argument = CharGetDatum(rightleft);
#if FALSE
/* currently, only "unknown" can be coerced */ /* currently, only "unknown" can be coerced */
/* /*
@ -419,7 +584,11 @@ unary_oper_get_candidates(char *op,
{ {
return 0; 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_desc = heap_openr(OperatorRelationName);
pg_operator_scan = heap_beginscan(pg_operator_desc, pg_operator_scan = heap_beginscan(pg_operator_desc,
0, 0,
@ -442,6 +611,10 @@ unary_oper_get_candidates(char *op,
current_candidate->args[0] = oper->oprright; current_candidate->args[0] = oper->oprright;
current_candidate->next = *candidates; current_candidate->next = *candidates;
*candidates = current_candidate; *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++; ncandidates++;
ReleaseBuffer(buffer); ReleaseBuffer(buffer);
} }
@ -450,32 +623,35 @@ unary_oper_get_candidates(char *op,
heap_endscan(pg_operator_scan); heap_endscan(pg_operator_scan);
heap_close(pg_operator_desc); heap_close(pg_operator_desc);
#ifdef PARSEDEBUG
printf("unary_oper_get_candidates: found %d candidates\n", ncandidates);
#endif
return ncandidates; return ncandidates;
} } /* unary_oper_get_candidates() */
/* Given unary right-side operator (operator on right), return oper struct */ /* Given unary right-side operator (operator on right), return oper struct */
/* arg-- type id */ /* arg-- type id */
Operator Operator
right_oper(char *op, Oid arg) right_oper(char *op, Oid arg)
{ {
HeapTuple tup; HeapTuple tup;
CandidateList candidates; CandidateList candidates;
int ncandidates; int ncandidates;
Oid *targetOid;
/* tup = SearchSysCacheTuple(OPRNAME,
* if (!OpCache) { init_op_cache(); } PointerGetDatum(op),
*/ ObjectIdGetDatum(arg),
if (!(tup = SearchSysCacheTuple(OPRNAME, ObjectIdGetDatum(InvalidOid),
PointerGetDatum(op), Int8GetDatum('r'));
ObjectIdGetDatum(arg),
ObjectIdGetDatum(InvalidOid), if (!HeapTupleIsValid(tup))
Int8GetDatum('r'))))
{ {
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r'); ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r');
if (ncandidates == 0) if (ncandidates == 0)
{ {
elog(ERROR, elog(ERROR, "Can't find right op '%s' for type %d", op, arg);
"Can't find right op: %s for type %d", op, arg);
return (NULL); return (NULL);
} }
else if (ncandidates == 1) else if (ncandidates == 1)
@ -489,38 +665,59 @@ right_oper(char *op, Oid arg)
} }
else else
{ {
elog(NOTICE, "there is more than one right operator %s", op); #if FALSE
elog(NOTICE, "you will have to retype this query"); elog(ERROR, "There is more than one right operator %s"
elog(ERROR, "using an explicit cast"); "\n\tYou will have to retype this query using an explicit cast", op);
return (NULL); #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); return ((Operator) tup);
} } /* right_oper() */
/* Given unary left-side operator (operator on left), return oper struct */ /* Given unary left-side operator (operator on left), return oper struct */
/* arg--type id */ /* arg--type id */
Operator Operator
left_oper(char *op, Oid arg) left_oper(char *op, Oid arg)
{ {
HeapTuple tup; HeapTuple tup;
CandidateList candidates; CandidateList candidates;
int ncandidates; int ncandidates;
Oid *targetOid;
/* tup = SearchSysCacheTuple(OPRNAME,
* if (!OpCache) { init_op_cache(); } PointerGetDatum(op),
*/ ObjectIdGetDatum(InvalidOid),
if (!(tup = SearchSysCacheTuple(OPRNAME, ObjectIdGetDatum(arg),
PointerGetDatum(op), Int8GetDatum('l'));
ObjectIdGetDatum(InvalidOid),
ObjectIdGetDatum(arg), if (!HeapTupleIsValid(tup))
Int8GetDatum('l'))))
{ {
ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l'); ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l');
if (ncandidates == 0) if (ncandidates == 0)
{ {
elog(ERROR, elog(ERROR, "Can't find left op '%s' for type %d", op, arg);
"Can't find left op: %s for type %d", op, arg);
return (NULL); return (NULL);
} }
else if (ncandidates == 1) else if (ncandidates == 1)
@ -528,22 +725,44 @@ left_oper(char *op, Oid arg)
tup = SearchSysCacheTuple(OPRNAME, tup = SearchSysCacheTuple(OPRNAME,
PointerGetDatum(op), PointerGetDatum(op),
ObjectIdGetDatum(InvalidOid), ObjectIdGetDatum(InvalidOid),
ObjectIdGetDatum(candidates->args[0]), ObjectIdGetDatum(candidates->args[0]),
Int8GetDatum('l')); Int8GetDatum('l'));
Assert(HeapTupleIsValid(tup)); 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 else
{ {
elog(NOTICE, "there is more than one left operator %s", op); #if FALSE
elog(NOTICE, "you will have to retype this query"); elog(ERROR, "There is more than one left operator %s"
elog(ERROR, "using an explicit cast"); "\n\tYou will have to retype this query using an explicit cast", op);
return (NULL); #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); return ((Operator) tup);
} } /* left_oper() */
/*
/* op_error()
* Give a somewhat useful error message when the operator for two types * Give a somewhat useful error message when the operator for two types
* is not found. * is not found.
*/ */
@ -559,7 +778,8 @@ op_error(char *op, Oid arg1, Oid arg2)
} }
else 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)) if (typeidIsValid(arg2))
@ -568,17 +788,10 @@ op_error(char *op, Oid arg1, Oid arg2)
} }
else 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'" 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\tYou will either have to retype this query using an explicit cast,"
"\n\tor you will have to define the operator using CREATE OPERATOR", "\n\tor you will have to define the operator using CREATE OPERATOR",

View File

@ -7,7 +7,7 @@
* *
* *
* IDENTIFICATION * 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 "parser/parse_target.h"
#include "utils/builtins.h" #include "utils/builtins.h"
#include "utils/lsyscache.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 List *expandAllTables(ParseState *pstate);
static char *figureColname(Node *expr, Node *resval); static char *figureColname(Node *expr, Node *resval);
@ -34,6 +41,16 @@ make_targetlist_expr(ParseState *pstate,
char *colname, char *colname,
Node *expr, Node *expr,
List *arrayRef); 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 - * transformTargetList -
@ -110,8 +127,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
Relation rd; Relation rd;
Value *constval; Value *constval;
if (exprType(expr) != UNKNOWNOID || if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
!IsA(expr, Const))
elog(ERROR, "yyparse: string constant expected"); elog(ERROR, "yyparse: string constant expected");
val = (char *) textout((struct varlena *) val = (char *) textout((struct varlena *)
@ -123,15 +139,15 @@ transformTargetList(ParseState *pstate, List *targetlist)
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->uidx, Const)) if (!IsA(aind->uidx, Const))
elog(ERROR, elog(ERROR, "Array Index for Append should be a constant");
"Array Index for Append should be a constant");
uindx[i] = ((Const *) aind->uidx)->constvalue; uindx[i] = ((Const *) aind->uidx)->constvalue;
if (aind->lidx != NULL) if (aind->lidx != NULL)
{ {
aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST); aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->lidx, Const)) if (!IsA(aind->lidx, Const))
elog(ERROR, elog(ERROR, "Array Index for Append should be a constant");
"Array Index for Append should be a constant");
lindx[i] = ((Const *) aind->lidx)->constvalue; lindx[i] = ((Const *) aind->lidx)->constvalue;
} }
else else
@ -140,6 +156,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
} }
if (lindx[i] > uindx[i]) if (lindx[i] > uindx[i])
elog(ERROR, "yyparse: lower index cannot be greater than upper index"); elog(ERROR, "yyparse: lower index cannot be greater than upper index");
sprintf(str, "[%d:%d]", lindx[i], uindx[i]); sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
str += strlen(str); str += strlen(str);
i++; i++;
@ -151,11 +168,12 @@ transformTargetList(ParseState *pstate, List *targetlist)
ndims = attnumAttNelems(rd, resdomno); ndims = attnumAttNelems(rd, resdomno);
if (i != ndims) if (i != ndims)
elog(ERROR, "yyparse: array dimensions do not match"); elog(ERROR, "yyparse: array dimensions do not match");
constval = makeNode(Value); constval = makeNode(Value);
constval->type = T_String; constval->type = T_String;
constval->val.str = save_str; constval->val.str = save_str;
tent = make_targetlist_expr(pstate, res->name, tent = make_targetlist_expr(pstate, res->name,
(Node *) make_const(constval), (Node *) make_const(constval),
NULL); NULL);
pfree(save_str); pfree(save_str);
} }
@ -300,8 +318,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
} }
default: default:
/* internal error */ /* internal error */
elog(ERROR, elog(ERROR, "internal error: do not know how to transform targetlist");
"internal error: do not know how to transform targetlist");
break; break;
} }
@ -321,11 +338,125 @@ transformTargetList(ParseState *pstate, List *targetlist)
} }
/* Node *
* make_targetlist_expr - coerce_target_expr(ParseState *pstate,
* make a TargetEntry from an expression 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 * 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 * static TargetEntry *
make_targetlist_expr(ParseState *pstate, make_targetlist_expr(ParseState *pstate,
@ -355,7 +486,6 @@ make_targetlist_expr(ParseState *pstate,
/* Processes target columns that will be receiving results */ /* Processes target columns that will be receiving results */
if (pstate->p_is_insert || pstate->p_is_update) if (pstate->p_is_insert || pstate->p_is_update)
{ {
/* /*
* insert or update query -- insert, update work only on one * insert or update query -- insert, update work only on one
* relation, so multiple occurence of same resdomno is bogus * relation, so multiple occurence of same resdomno is bogus
@ -368,91 +498,47 @@ make_targetlist_expr(ParseState *pstate,
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
attrtype = GetArrayElementType(attrtype); attrtype = GetArrayElementType(attrtype);
attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod; attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
#if 0
if (Input_is_string && Typecast_ok)
{
Datum val;
if (type_id == typeTypeId(type("unknown"))) /* Check for InvalidOid since that seems to indicate a NULL constant... */
{ if (type_id != InvalidOid)
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))
{ {
lnext(expr) = /* Mismatch on types? then try to coerce to target... */
parser_typecast2(expr, typeidType(attrtype)); if (attrtype != type_id)
} {
else if (attrtype != type_id) Oid typelem;
{
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);
}
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)) if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
{ {
/* updating a single item */ typelem = typeidTypElem(attrtype);
Oid typelem = typeidTypElem(attrtype);
expr = (Node *) parser_typecast2(expr,
type_id,
typeidType(typelem),
attrtypmod);
} }
else else
expr = (Node *) parser_typecast2(expr, {
type_id, typelem = attrtype;
typeidType(attrtype), }
attrtypmod);
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 */ expr = size_target_expr(pstate, expr, attrtype, attrtypmod);
elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
colname,
typeidTypeName(attrtype),
typeidTypeName(type_id));
} }
} }
@ -467,8 +553,8 @@ make_targetlist_expr(ParseState *pstate,
att->relname = pstrdup(RelationGetRelationName(rd)->data); att->relname = pstrdup(RelationGetRelationName(rd)->data);
att->attrs = lcons(makeString(colname), NIL); att->attrs = lcons(makeString(colname), NIL);
target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att, target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
&pstate->p_last_resno, &pstate->p_last_resno,
EXPR_COLUMN_FIRST); EXPR_COLUMN_FIRST);
while (ar != NIL) while (ar != NIL)
{ {
A_Indices *ind = lfirst(ar); A_Indices *ind = lfirst(ar);
@ -514,7 +600,8 @@ make_targetlist_expr(ParseState *pstate,
tent->expr = expr; tent->expr = expr;
return tent; return tent;
} } /* make_targetlist_expr() */
/* /*
* makeTargetNames - * makeTargetNames -
@ -564,7 +651,7 @@ makeTargetNames(ParseState *pstate, List *cols)
attnameAttNum(pstate->p_target_relation, name); attnameAttNum(pstate->p_target_relation, name);
foreach(nxt, lnext(tl)) foreach(nxt, lnext(tl))
if (!strcmp(name, ((Ident *) lfirst(nxt))->name)) 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 * handle type operations for parser
* *
* Copyright (c) 1994, Regents of the University of California * Copyright (c) 1994, Regents of the University of California
* *
* *
* IDENTIFICATION * 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 "postgres.h"
#include "fmgr.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 "catalog/pg_type.h"
#include "parser/parse_target.h" #include "parser/parse_target.h"
#include "parser/parse_type.h" #include "parser/parse_type.h"
#include "utils/syscache.h" #include "utils/syscache.h"
/* check to see if a type id is valid, /* check to see if a type id is valid,
* returns true if it is. By using this call before calling * returns true if it is. By using this call before calling
* typeidType or typeidTypeName, more meaningful error messages * 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 * 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, ParseFuncOrColumn(ParseState *pstate, char *funcname, List *fargs,
int *curr_resno, int precedence); 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 */ #endif /* PARSE_FUNC_H */