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:
@ -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)
|
||||||
|
560
src/backend/parser/parse_coerce.c
Normal file
560
src/backend/parser/parse_coerce.c
Normal file
@ -0,0 +1,560 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* parse_coerce.c
|
||||||
|
* handle type coersions/conversions for parser
|
||||||
|
*
|
||||||
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* IDENTIFICATION
|
||||||
|
* $Header: /cvsroot/pgsql/src/backend/parser/parse_coerce.c,v 2.1 1998/05/09 23:29:53 thomas Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#include <string.h>
|
||||||
|
#include "postgres.h"
|
||||||
|
#include "utils/builtins.h"
|
||||||
|
#include "fmgr.h"
|
||||||
|
#include "nodes/makefuncs.h"
|
||||||
|
|
||||||
|
#include "parser/parse_expr.h"
|
||||||
|
|
||||||
|
#include "catalog/pg_type.h"
|
||||||
|
#include "parser/parse_type.h"
|
||||||
|
#include "parser/parse_target.h"
|
||||||
|
#include "parser/parse_coerce.h"
|
||||||
|
#include "utils/syscache.h"
|
||||||
|
|
||||||
|
Oid DemoteType(Oid inType);
|
||||||
|
Oid PromoteTypeToNext(Oid inType);
|
||||||
|
|
||||||
|
|
||||||
|
/* coerce_type()
|
||||||
|
* Convert a function argument to a different type.
|
||||||
|
*/
|
||||||
|
Node *
|
||||||
|
coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId)
|
||||||
|
{
|
||||||
|
Node *result = NULL;
|
||||||
|
Oid infunc;
|
||||||
|
Datum val;
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: argument types are %d -> %d\n",
|
||||||
|
inputTypeId, targetTypeId);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (targetTypeId == InvalidOid)
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: apparent NULL target argument; suppress type conversion\n");
|
||||||
|
#endif
|
||||||
|
result = node;
|
||||||
|
}
|
||||||
|
else if (inputTypeId != targetTypeId)
|
||||||
|
{
|
||||||
|
/* one of the known-good transparent conversions? then drop through... */
|
||||||
|
if (IS_BINARY_COMPATIBLE(inputTypeId, targetTypeId))
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: argument type %s is known to be convertible to type %s\n",
|
||||||
|
typeidTypeName(inputTypeId), typeidTypeName(targetTypeId));
|
||||||
|
#endif
|
||||||
|
result = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if not unknown input type, try for explicit conversion using functions... */
|
||||||
|
else if (inputTypeId != UNKNOWNOID)
|
||||||
|
{
|
||||||
|
/* We already know there is a function which will do this, so let's use it */
|
||||||
|
FuncCall *n = makeNode(FuncCall);
|
||||||
|
n->funcname = typeidTypeName(targetTypeId);
|
||||||
|
n->args = lcons(node, NIL);
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: construct function %s(%s)\n",
|
||||||
|
typeidTypeName(targetTypeId), typeidTypeName(inputTypeId));
|
||||||
|
#endif
|
||||||
|
|
||||||
|
result = transformExpr(pstate, (Node *) n, EXPR_COLUMN_FIRST);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: node is UNKNOWN type\n");
|
||||||
|
#endif
|
||||||
|
if (nodeTag(node) == T_Const)
|
||||||
|
{
|
||||||
|
Const *con = (Const *) node;
|
||||||
|
|
||||||
|
val = (Datum) textout((struct varlena *)
|
||||||
|
con->constvalue);
|
||||||
|
infunc = typeidInfunc(targetTypeId);
|
||||||
|
con = makeNode(Const);
|
||||||
|
con->consttype = targetTypeId;
|
||||||
|
con->constlen = typeLen(typeidType(targetTypeId));
|
||||||
|
|
||||||
|
/* use "-1" for varchar() type */
|
||||||
|
con->constvalue = (Datum) fmgr(infunc,
|
||||||
|
val,
|
||||||
|
typeidTypElem(targetTypeId),
|
||||||
|
-1);
|
||||||
|
con->constisnull = false;
|
||||||
|
con->constbyval = true;
|
||||||
|
con->constisset = false;
|
||||||
|
result = (Node *) con;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: should never get here!\n");
|
||||||
|
#endif
|
||||||
|
result = node;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("coerce_type: argument type IDs %d match\n", inputTypeId);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
result = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
} /* coerce_type() */
|
||||||
|
|
||||||
|
|
||||||
|
/* can_coerce_type()
|
||||||
|
* Can input_typeids be coerced to func_typeids?
|
||||||
|
*
|
||||||
|
* There are a few types which are known apriori to be convertible.
|
||||||
|
* We will check for those cases first, and then look for possible
|
||||||
|
* conversion functions.
|
||||||
|
*
|
||||||
|
* Notes:
|
||||||
|
* This uses the same mechanism as the CAST() SQL construct in gram.y.
|
||||||
|
* We should also check the function return type on candidate conversion
|
||||||
|
* routines just to be safe but we do not do that yet...
|
||||||
|
* We need to have a zero-filled OID array here, otherwise the cache lookup fails.
|
||||||
|
* - thomas 1998-03-31
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids)
|
||||||
|
{
|
||||||
|
HeapTuple ftup;
|
||||||
|
int i;
|
||||||
|
Type tp;
|
||||||
|
Oid oid_array[8];
|
||||||
|
|
||||||
|
/* run through argument list... */
|
||||||
|
for (i = 0; i < nargs; i++)
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: argument #%d types are %d -> %d\n",
|
||||||
|
i, input_typeids[i], func_typeids[i]);
|
||||||
|
#endif
|
||||||
|
if (input_typeids[i] != func_typeids[i])
|
||||||
|
{
|
||||||
|
/* one of the known-good transparent conversions? then drop through... */
|
||||||
|
if (IS_BINARY_COMPATIBLE(input_typeids[i], func_typeids[i]))
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: argument #%d type %s is known to be convertible to type %s\n",
|
||||||
|
i, typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't know what to do for the output type? then quit... */
|
||||||
|
else if (func_typeids[i] == InvalidOid)
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: output OID func_typeids[%d] is zero\n", i);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't know what to do for the input type? then quit... */
|
||||||
|
else if (input_typeids[i] == InvalidOid)
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: input OID input_typeids[%d] is zero\n", i);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* if not unknown input type, try for explicit conversion using functions... */
|
||||||
|
else if (input_typeids[i] != UNKNOWNOID)
|
||||||
|
{
|
||||||
|
MemSet(&oid_array[0], 0, 8 * sizeof(Oid));
|
||||||
|
oid_array[0] = input_typeids[i];
|
||||||
|
|
||||||
|
/* look for a single-argument function named with the target type name */
|
||||||
|
ftup = SearchSysCacheTuple(PRONAME,
|
||||||
|
PointerGetDatum(typeidTypeName(func_typeids[i])),
|
||||||
|
Int32GetDatum(1),
|
||||||
|
PointerGetDatum(oid_array),
|
||||||
|
0);
|
||||||
|
|
||||||
|
/* should also check the function return type just to be safe... */
|
||||||
|
if (HeapTupleIsValid(ftup))
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: found function %s(%s) to convert argument #%d\n",
|
||||||
|
typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: did not find function %s(%s) to convert argument #%d\n",
|
||||||
|
typeidTypeName(func_typeids[i]), typeidTypeName(input_typeids[i]), i);
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: argument #%d type is %d (UNKNOWN)\n",
|
||||||
|
i, input_typeids[i]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
tp = typeidType(input_typeids[i]);
|
||||||
|
if (typeTypeFlag(tp) == 'c')
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: typeTypeFlag for %s is 'c'\n",
|
||||||
|
typeidTypeName(input_typeids[i]));
|
||||||
|
#endif
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: conversion from %s to %s is possible\n",
|
||||||
|
typeidTypeName(input_typeids[i]), typeidTypeName(func_typeids[i]));
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("can_coerce_type: argument #%d type IDs %d match\n",
|
||||||
|
i, input_typeids[i]);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
} /* can_coerce_type() */
|
||||||
|
|
||||||
|
|
||||||
|
/* TypeCategory()
|
||||||
|
* Assign a category to the specified OID.
|
||||||
|
*/
|
||||||
|
CATEGORY
|
||||||
|
TypeCategory(Oid inType)
|
||||||
|
{
|
||||||
|
CATEGORY result;
|
||||||
|
|
||||||
|
switch (inType)
|
||||||
|
{
|
||||||
|
case (BOOLOID):
|
||||||
|
result = BOOLEAN_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (CHAROID):
|
||||||
|
case (BPCHAROID):
|
||||||
|
case (VARCHAROID):
|
||||||
|
case (TEXTOID):
|
||||||
|
result = STRING_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (INT2OID):
|
||||||
|
case (INT4OID):
|
||||||
|
case (FLOAT4OID):
|
||||||
|
case (FLOAT8OID):
|
||||||
|
case (CASHOID):
|
||||||
|
result = NUMERIC_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (ABSTIMEOID):
|
||||||
|
case (TIMESTAMPOID):
|
||||||
|
case (DATETIMEOID):
|
||||||
|
result = DATETIME_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (RELTIMEOID):
|
||||||
|
case (TIMESPANOID):
|
||||||
|
result = TIMESPAN_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (POINTOID):
|
||||||
|
case (LSEGOID):
|
||||||
|
case (LINEOID):
|
||||||
|
case (BOXOID):
|
||||||
|
case (PATHOID):
|
||||||
|
case (CIRCLEOID):
|
||||||
|
case (POLYGONOID):
|
||||||
|
result = GEOMETRIC_TYPE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = USER_TYPE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (result);
|
||||||
|
} /* TypeCategory() */
|
||||||
|
|
||||||
|
|
||||||
|
/* IsPreferredType()
|
||||||
|
* Assign a category to the specified OID.
|
||||||
|
*/
|
||||||
|
bool
|
||||||
|
IsPreferredType(CATEGORY category, Oid type)
|
||||||
|
{
|
||||||
|
return (type == PreferredType(category, type));
|
||||||
|
} /* IsPreferredType() */
|
||||||
|
|
||||||
|
|
||||||
|
/* PreferredType()
|
||||||
|
* Assign a category to the specified OID.
|
||||||
|
*/
|
||||||
|
Oid
|
||||||
|
PreferredType(CATEGORY category, Oid type)
|
||||||
|
{
|
||||||
|
Oid result;
|
||||||
|
|
||||||
|
switch (category)
|
||||||
|
{
|
||||||
|
case (BOOLEAN_TYPE):
|
||||||
|
result = BOOLOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (STRING_TYPE):
|
||||||
|
result = TEXTOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (NUMERIC_TYPE):
|
||||||
|
result = FLOAT8OID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (DATETIME_TYPE):
|
||||||
|
result = DATETIMEOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (TIMESPAN_TYPE):
|
||||||
|
result = TIMESPANOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (GEOMETRIC_TYPE):
|
||||||
|
case (USER_TYPE):
|
||||||
|
result = type;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = UNKNOWNOID;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
#ifdef PARSEDEBUG
|
||||||
|
printf("PreferredType- (%d) preferred type is %s\n", category, typeidTypeName(result));
|
||||||
|
#endif
|
||||||
|
return (result);
|
||||||
|
} /* PreferredType() */
|
||||||
|
|
||||||
|
|
||||||
|
#if FALSE
|
||||||
|
Oid
|
||||||
|
PromoteTypeToNext(Oid inType)
|
||||||
|
{
|
||||||
|
Oid result;
|
||||||
|
|
||||||
|
switch (inType)
|
||||||
|
{
|
||||||
|
case (CHAROID):
|
||||||
|
case (BPCHAROID):
|
||||||
|
result = VARCHAROID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (VARCHAROID):
|
||||||
|
result = TEXTOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (INT2OID):
|
||||||
|
case (CASHOID):
|
||||||
|
result = INT4OID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (INT4OID):
|
||||||
|
case (FLOAT4OID):
|
||||||
|
result = FLOAT8OID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (DATEOID):
|
||||||
|
case (ABSTIMEOID):
|
||||||
|
case (TIMESTAMPOID):
|
||||||
|
result = DATETIMEOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (TIMEOID):
|
||||||
|
case (RELTIMEOID):
|
||||||
|
result = TIMESPANOID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case (BOOLOID):
|
||||||
|
case (TEXTOID):
|
||||||
|
case (FLOAT8OID):
|
||||||
|
case (DATETIMEOID):
|
||||||
|
case (TIMESPANOID):
|
||||||
|
default:
|
||||||
|
result = inType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (result);
|
||||||
|
} /* PromoteTypeToNext() */
|
||||||
|
|
||||||
|
|
||||||
|
Oid
|
||||||
|
DemoteType(Oid inType)
|
||||||
|
{
|
||||||
|
Oid result;
|
||||||
|
|
||||||
|
switch (inType)
|
||||||
|
{
|
||||||
|
case (FLOAT4OID):
|
||||||
|
case (FLOAT8OID):
|
||||||
|
result = INT4OID;
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
result = inType;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
return (result);
|
||||||
|
} /* DemoteType() */
|
||||||
|
|
||||||
|
|
||||||
|
Oid
|
||||||
|
PromoteLesserType(Oid inType1, Oid inType2, Oid *newType1, Oid *newType2)
|
||||||
|
{
|
||||||
|
Oid result;
|
||||||
|
|
||||||
|
if (inType1 == inType2)
|
||||||
|
{
|
||||||
|
result = PromoteTypeToNext(inType1);
|
||||||
|
inType1 = result;
|
||||||
|
*arg2 = result;
|
||||||
|
return (result);
|
||||||
|
}
|
||||||
|
|
||||||
|
kind1 = ClassifyType(inType1);
|
||||||
|
kind2 = ClassifyType(*arg2);
|
||||||
|
if (kind1 != kind2)
|
||||||
|
{
|
||||||
|
*newType1 = inType1;
|
||||||
|
*newType2 = inType2;
|
||||||
|
result = InvalidOid;
|
||||||
|
}
|
||||||
|
|
||||||
|
isBuiltIn1 = IS_BUILTIN_TYPE(inType1);
|
||||||
|
isBuiltIn2 = IS_BUILTIN_TYPE(*arg2);
|
||||||
|
|
||||||
|
if (isBuiltIn1 && isBuiltIn2)
|
||||||
|
{
|
||||||
|
switch (*arg1)
|
||||||
|
{
|
||||||
|
case (CHAROID):
|
||||||
|
switch (*arg2)
|
||||||
|
{
|
||||||
|
case (BPCHAROID):
|
||||||
|
case (VARCHAROID):
|
||||||
|
case (TEXTOID):
|
||||||
|
|
||||||
|
case (INT2OID):
|
||||||
|
case (INT4OID):
|
||||||
|
case (FLOAT4OID):
|
||||||
|
case (FLOAT8OID):
|
||||||
|
case (CASHOID):
|
||||||
|
|
||||||
|
case (POINTOID):
|
||||||
|
case (LSEGOID):
|
||||||
|
case (LINEOID):
|
||||||
|
case (BOXOID):
|
||||||
|
case (PATHOID):
|
||||||
|
case (CIRCLEOID):
|
||||||
|
case (POLYGONOID):
|
||||||
|
|
||||||
|
case (InvalidOid):
|
||||||
|
case (UNKNOWNOID):
|
||||||
|
case (BOOLOID):
|
||||||
|
default:
|
||||||
|
*arg1 = InvalidOid;
|
||||||
|
*arg2 = InvalidOid;
|
||||||
|
result = InvalidOid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (isBuiltIn1 && !isBuiltIn2)
|
||||||
|
{
|
||||||
|
if ((promotedType = PromoteBuiltInType(*arg1)) != *arg1)
|
||||||
|
{
|
||||||
|
*arg1 = promotedType;
|
||||||
|
return (promotedType);
|
||||||
|
}
|
||||||
|
else if (CanCoerceType(*arg1, *arg2))
|
||||||
|
{
|
||||||
|
*arg1 = *arg2;
|
||||||
|
return (*arg2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!isBuiltIn1 && isBuiltIn2)
|
||||||
|
{
|
||||||
|
if ((promotedType = PromoteBuiltInType(*arg2)) != *arg2)
|
||||||
|
{
|
||||||
|
*arg2 = promotedType;
|
||||||
|
return (promotedType);
|
||||||
|
}
|
||||||
|
else if (CanCoerceType(*arg2, *arg1))
|
||||||
|
{
|
||||||
|
*arg2 = *arg1;
|
||||||
|
return (*arg1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
if (*arg2 == InvalidOid)
|
||||||
|
return InvalidOid;
|
||||||
|
|
||||||
|
switch (*arg1)
|
||||||
|
{
|
||||||
|
case (CHAROID):
|
||||||
|
switch (*arg2)
|
||||||
|
{
|
||||||
|
case (BPCHAROID):
|
||||||
|
case (VARCHAROID):
|
||||||
|
case (TEXTOID):
|
||||||
|
|
||||||
|
case (INT2OID):
|
||||||
|
case (INT4OID):
|
||||||
|
case (FLOAT4OID):
|
||||||
|
case (FLOAT8OID):
|
||||||
|
case (CASHOID):
|
||||||
|
|
||||||
|
case (POINTOID):
|
||||||
|
case (LSEGOID):
|
||||||
|
case (LINEOID):
|
||||||
|
case (BOXOID):
|
||||||
|
case (PATHOID):
|
||||||
|
case (CIRCLEOID):
|
||||||
|
case (POLYGONOID):
|
||||||
|
|
||||||
|
case (InvalidOid):
|
||||||
|
case (UNKNOWNOID):
|
||||||
|
case (BOOLOID):
|
||||||
|
default:
|
||||||
|
*arg1 = InvalidOid;
|
||||||
|
*arg2 = InvalidOid;
|
||||||
|
result = InvalidOid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
@ -7,7 +7,7 @@
|
|||||||
*
|
*
|
||||||
*
|
*
|
||||||
* IDENTIFICATION
|
* 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
@ -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;
|
||||||
|
@ -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",
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
96
src/include/parser/parse_coerce.h
Normal file
96
src/include/parser/parse_coerce.h
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
/*-------------------------------------------------------------------------
|
||||||
|
*
|
||||||
|
* parse_coerce.h
|
||||||
|
*
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* Copyright (c) 1994, Regents of the University of California
|
||||||
|
*
|
||||||
|
* $Id: parse_coerce.h,v 1.1 1998/05/09 23:31:34 thomas Exp $
|
||||||
|
*
|
||||||
|
*-------------------------------------------------------------------------
|
||||||
|
*/
|
||||||
|
#ifndef PARSE_COERCE_H
|
||||||
|
#define PARSE_COERCE_H
|
||||||
|
|
||||||
|
typedef enum CATEGORY {
|
||||||
|
INVALID_TYPE,
|
||||||
|
UNKNOWN_TYPE,
|
||||||
|
BOOLEAN_TYPE,
|
||||||
|
STRING_TYPE,
|
||||||
|
NUMERIC_TYPE,
|
||||||
|
DATETIME_TYPE,
|
||||||
|
TIMESPAN_TYPE,
|
||||||
|
GEOMETRIC_TYPE,
|
||||||
|
USER_TYPE,
|
||||||
|
MIXED_TYPE
|
||||||
|
} CATEGORY;
|
||||||
|
|
||||||
|
|
||||||
|
#define IS_BUILTIN_TYPE(t) \
|
||||||
|
(((t) == BOOLOID) \
|
||||||
|
|| ((t) == BPCHAROID) \
|
||||||
|
|| ((t) == VARCHAROID) \
|
||||||
|
|| ((t) == TEXTOID) \
|
||||||
|
|| ((t) == CASHOID) \
|
||||||
|
|| ((t) == INT4OID) \
|
||||||
|
|| ((t) == DATETIMEOID) \
|
||||||
|
|| ((t) == FLOAT8OID) \
|
||||||
|
|| ((t) == ABSTIMEOID) \
|
||||||
|
|| ((t) == TIMESTAMPOID) \
|
||||||
|
|| ((t) == RELTIMEOID))
|
||||||
|
|
||||||
|
|
||||||
|
/* IS_BINARY_COMPATIBLE()
|
||||||
|
* Check for types with the same underlying binary representation.
|
||||||
|
* This allows us to cheat and directly exchange values without
|
||||||
|
* going through the trouble of calling a conversion function.
|
||||||
|
*/
|
||||||
|
#define IS_BINARY_COMPATIBLE(a,b) \
|
||||||
|
(((a) == BPCHAROID && (b) == TEXTOID) \
|
||||||
|
|| ((a) == BPCHAROID && (b) == VARCHAROID) \
|
||||||
|
|| ((a) == VARCHAROID && (b) == TEXTOID) \
|
||||||
|
|| ((a) == VARCHAROID && (b) == BPCHAROID) \
|
||||||
|
|| ((a) == TEXTOID && (b) == BPCHAROID) \
|
||||||
|
|| ((a) == TEXTOID && (b) == VARCHAROID) \
|
||||||
|
|| ((a) == CASHOID && (b) == INT4OID) \
|
||||||
|
|| ((a) == INT4OID && (b) == CASHOID) \
|
||||||
|
|| ((a) == DATETIMEOID && (b) == FLOAT8OID) \
|
||||||
|
|| ((a) == FLOAT8OID && (b) == DATETIMEOID) \
|
||||||
|
|| ((a) == ABSTIMEOID && (b) == TIMESTAMPOID) \
|
||||||
|
|| ((a) == TIMESTAMPOID && (b) == ABSTIMEOID) \
|
||||||
|
|| ((a) == ABSTIMEOID && (b) == INT4OID) \
|
||||||
|
|| ((a) == INT4OID && (b) == ABSTIMEOID) \
|
||||||
|
|| ((a) == RELTIMEOID && (b) == INT4OID) \
|
||||||
|
|| ((a) == INT4OID && (b) == RELTIMEOID))
|
||||||
|
|
||||||
|
/* IS_HIGHER_TYPE()
|
||||||
|
* These types are the most general in each of the type categories.
|
||||||
|
*/
|
||||||
|
#define IS_HIGHER_TYPE(t) \
|
||||||
|
(((t) == TEXTOID) \
|
||||||
|
|| ((t) == FLOAT8OID) \
|
||||||
|
|| ((t) == TIMESPANOID) \
|
||||||
|
|| ((t) == DATETIMEOID) \
|
||||||
|
|| ((t) == POLYGONOID))
|
||||||
|
|
||||||
|
/* IS_HIGHEST_TYPE()
|
||||||
|
* These types are the most general in each of the type categories.
|
||||||
|
* Since timespan and datetime overload so many functions, let's
|
||||||
|
* give datetime the preference.
|
||||||
|
* Since text is a generic string type let's leave it out too.
|
||||||
|
*/
|
||||||
|
#define IS_HIGHEST_TYPE(t) \
|
||||||
|
(((t) == FLOAT8OID) \
|
||||||
|
|| ((t) == DATETIMEOID) \
|
||||||
|
|| ((t) == TIMESPANOID))
|
||||||
|
|
||||||
|
|
||||||
|
extern bool IsPreferredType(CATEGORY category, Oid type);
|
||||||
|
extern Oid PreferredType(CATEGORY category, Oid type);
|
||||||
|
extern CATEGORY TypeCategory(Oid type);
|
||||||
|
|
||||||
|
extern bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
|
||||||
|
extern Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
|
||||||
|
|
||||||
|
#endif /* PARSE_COERCE_H */
|
@ -6,7 +6,7 @@
|
|||||||
*
|
*
|
||||||
* Copyright (c) 1994, Regents of the University of California
|
* 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 */
|
||||||
|
Reference in New Issue
Block a user