mirror of https://github.com/postgres/postgres
parent
54b5577cb6
commit
3ace5fd082
@ -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 |
||||
File diff suppressed because it is too large
Load Diff
@ -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 */ |
||||
Loading…
Reference in new issue