aboutsummaryrefslogtreecommitdiff
path: root/src/backend/parser/parse_target.c
diff options
context:
space:
mode:
authorThomas G. Lockhart <lockhart@fourpalms.org>1998-05-09 23:31:34 +0000
committerThomas G. Lockhart <lockhart@fourpalms.org>1998-05-09 23:31:34 +0000
commit3ace5fd08247726756d695156e8ccb075d5f76d5 (patch)
tree640d4b580e65f500f0894a7c9b65bb5d8bd974c7 /src/backend/parser/parse_target.c
parent54b5577cb6822d35256e2e3a36b6c59e7fda1409 (diff)
downloadpostgresql-3ace5fd08247726756d695156e8ccb075d5f76d5.tar.gz
postgresql-3ace5fd08247726756d695156e8ccb075d5f76d5.zip
Add capabilities for automatic type conversion.
Diffstat (limited to 'src/backend/parser/parse_target.c')
-rw-r--r--src/backend/parser/parse_target.c275
1 files changed, 181 insertions, 94 deletions
diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c
index b9ab916d882..e3d5654ec4a 100644
--- a/src/backend/parser/parse_target.c
+++ b/src/backend/parser/parse_target.c
@@ -7,7 +7,7 @@
*
*
* IDENTIFICATION
- * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.11 1998/02/26 04:33:35 momjian Exp $
+ * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.12 1998/05/09 23:29:54 thomas Exp $
*
*-------------------------------------------------------------------------
*/
@@ -26,6 +26,13 @@
#include "parser/parse_target.h"
#include "utils/builtins.h"
#include "utils/lsyscache.h"
+#include "utils/syscache.h"
+
+extern
+bool can_coerce_type(int nargs, Oid *input_typeids, Oid *func_typeids);
+
+extern
+Node *coerce_type(ParseState *pstate, Node *node, Oid inputTypeId, Oid targetTypeId);
static List *expandAllTables(ParseState *pstate);
static char *figureColname(Node *expr, Node *resval);
@@ -34,6 +41,16 @@ make_targetlist_expr(ParseState *pstate,
char *colname,
Node *expr,
List *arrayRef);
+Node *
+size_target_expr(ParseState *pstate,
+ Node *expr,
+ Oid attrtype,
+ int16 attrtypmod);
+Node *
+coerce_target_expr(ParseState *pstate,
+ Node *expr,
+ Oid type_id,
+ Oid attrtype);
/*
* transformTargetList -
@@ -110,8 +127,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
Relation rd;
Value *constval;
- if (exprType(expr) != UNKNOWNOID ||
- !IsA(expr, Const))
+ if (exprType(expr) != UNKNOWNOID || !IsA(expr, Const))
elog(ERROR, "yyparse: string constant expected");
val = (char *) textout((struct varlena *)
@@ -123,15 +139,15 @@ transformTargetList(ParseState *pstate, List *targetlist)
aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->uidx, Const))
- elog(ERROR,
- "Array Index for Append should be a constant");
+ elog(ERROR, "Array Index for Append should be a constant");
+
uindx[i] = ((Const *) aind->uidx)->constvalue;
if (aind->lidx != NULL)
{
aind->lidx = transformExpr(pstate, aind->lidx, EXPR_COLUMN_FIRST);
if (!IsA(aind->lidx, Const))
- elog(ERROR,
- "Array Index for Append should be a constant");
+ elog(ERROR, "Array Index for Append should be a constant");
+
lindx[i] = ((Const *) aind->lidx)->constvalue;
}
else
@@ -140,6 +156,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
}
if (lindx[i] > uindx[i])
elog(ERROR, "yyparse: lower index cannot be greater than upper index");
+
sprintf(str, "[%d:%d]", lindx[i], uindx[i]);
str += strlen(str);
i++;
@@ -151,11 +168,12 @@ transformTargetList(ParseState *pstate, List *targetlist)
ndims = attnumAttNelems(rd, resdomno);
if (i != ndims)
elog(ERROR, "yyparse: array dimensions do not match");
+
constval = makeNode(Value);
constval->type = T_String;
constval->val.str = save_str;
tent = make_targetlist_expr(pstate, res->name,
- (Node *) make_const(constval),
+ (Node *) make_const(constval),
NULL);
pfree(save_str);
}
@@ -300,8 +318,7 @@ transformTargetList(ParseState *pstate, List *targetlist)
}
default:
/* internal error */
- elog(ERROR,
- "internal error: do not know how to transform targetlist");
+ elog(ERROR, "internal error: do not know how to transform targetlist");
break;
}
@@ -321,11 +338,125 @@ transformTargetList(ParseState *pstate, List *targetlist)
}
-/*
- * make_targetlist_expr -
- * make a TargetEntry from an expression
+Node *
+coerce_target_expr(ParseState *pstate,
+ Node *expr,
+ Oid type_id,
+ Oid attrtype)
+{
+ if (can_coerce_type(1, &type_id, &attrtype))
+ {
+#ifdef PARSEDEBUG
+printf("parse_target: coerce type from %s to %s\n",
+ typeidTypeName(type_id), typeidTypeName(attrtype));
+#endif
+ expr = coerce_type(pstate, expr, type_id, attrtype);
+ }
+
+#ifndef DISABLE_STRING_HACKS
+ /* string hacks to get transparent conversions w/o explicit conversions */
+ else if ((attrtype == BPCHAROID) || (attrtype == VARCHAROID))
+ {
+ Oid text_id = TEXTOID;
+#ifdef PARSEDEBUG
+printf("parse_target: try coercing from %s to %s via text\n",
+ typeidTypeName(type_id), typeidTypeName(attrtype));
+#endif
+ if (type_id == TEXTOID)
+ {
+ }
+ else if (can_coerce_type(1, &type_id, &text_id))
+ {
+ expr = coerce_type(pstate, expr, type_id, text_id);
+ }
+ else
+ {
+ expr = NULL;
+ }
+ }
+#endif
+
+ else
+ {
+ expr = NULL;
+ }
+
+ return expr;
+} /* coerce_target_expr() */
+
+
+/* size_target_expr()
+ * Apparently going to a fixed-length string?
+ * Then explicitly size for storage...
+ */
+Node *
+size_target_expr(ParseState *pstate,
+ Node *expr,
+ Oid attrtype,
+ int16 attrtypmod)
+{
+ int i;
+ HeapTuple ftup;
+ char *funcname;
+ Oid oid_array[8];
+
+ FuncCall *func;
+ A_Const *cons;
+
+#ifdef PARSEDEBUG
+printf("parse_target: ensure target fits storage\n");
+#endif
+ funcname = typeidTypeName(attrtype);
+ oid_array[0] = attrtype;
+ oid_array[1] = INT4OID;
+ for (i = 2; i < 8; i++) oid_array[i] = InvalidOid;
+
+#ifdef PARSEDEBUG
+printf("parse_target: look for conversion function %s(%s,%s)\n",
+ funcname, typeidTypeName(attrtype), typeidTypeName(INT4OID));
+#endif
+
+ /* attempt to find with arguments exactly as specified... */
+ ftup = SearchSysCacheTuple(PRONAME,
+ PointerGetDatum(funcname),
+ Int32GetDatum(2),
+ PointerGetDatum(oid_array),
+ 0);
+
+ if (HeapTupleIsValid(ftup))
+ {
+#ifdef PARSEDEBUG
+printf("parse_target: found conversion function for sizing\n");
+#endif
+ func = makeNode(FuncCall);
+ func->funcname = funcname;
+
+ cons = makeNode(A_Const);
+ cons->val.type = T_Integer;
+ cons->val.val.ival = attrtypmod;
+ func->args = lappend( lcons(expr,NIL), cons);
+
+ expr = transformExpr(pstate, (Node *) func, EXPR_COLUMN_FIRST);
+ }
+#ifdef PARSEDEBUG
+ else
+ {
+printf("parse_target: no conversion function for sizing\n");
+ }
+#endif
+
+ return expr;
+} /* size_target_expr() */
+
+
+/* make_targetlist_expr()
+ * Make a TargetEntry from an expression
*
* arrayRef is a list of transformed A_Indices
+ *
+ * For type mismatches between expressions and targets, use the same
+ * techniques as for function and operator type coersion.
+ * - thomas 1998-05-08
*/
static TargetEntry *
make_targetlist_expr(ParseState *pstate,
@@ -355,7 +486,6 @@ make_targetlist_expr(ParseState *pstate,
/* Processes target columns that will be receiving results */
if (pstate->p_is_insert || pstate->p_is_update)
{
-
/*
* insert or update query -- insert, update work only on one
* relation, so multiple occurence of same resdomno is bogus
@@ -368,91 +498,47 @@ make_targetlist_expr(ParseState *pstate,
if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL))
attrtype = GetArrayElementType(attrtype);
attrtypmod = rd->rd_att->attrs[resdomno - 1]->atttypmod;
-#if 0
- if (Input_is_string && Typecast_ok)
- {
- Datum val;
- if (type_id == typeTypeId(type("unknown")))
- {
- val = (Datum) textout((struct varlena *)
- ((Const) lnext(expr))->constvalue);
- }
- else
- {
- val = ((Const) lnext(expr))->constvalue;
- }
- if (attrisset)
- {
- lnext(expr) = makeConst(attrtype,
- attrlen,
- val,
- false,
- true,
- true, /* is set */
- false);
- }
- else
- {
- lnext(expr) =
- makeConst(attrtype,
- attrlen,
- (Datum) fmgr(typeidInfunc(attrtype),
- val, typeidTypElem(attrtype), -1),
- false,
- true /* Maybe correct-- 80% chance */ ,
- false, /* is not a set */
- false);
- }
- }
- else if ((Typecast_ok) && (attrtype != type_id))
- {
- lnext(expr) =
- parser_typecast2(expr, typeidType(attrtype));
- }
- else if (attrtype != type_id)
+ /* Check for InvalidOid since that seems to indicate a NULL constant... */
+ if (type_id != InvalidOid)
{
- 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))
+ /* Mismatch on types? then try to coerce to target... */
+ if (attrtype != type_id)
{
- /* try to cast the constant */
+ Oid typelem;
+
if (arrayRef && !(((A_Indices *) lfirst(arrayRef))->lidx))
{
- /* updating a single item */
- Oid typelem = typeidTypElem(attrtype);
-
- expr = (Node *) parser_typecast2(expr,
- type_id,
- typeidType(typelem),
- attrtypmod);
+ typelem = typeidTypElem(attrtype);
}
else
- expr = (Node *) parser_typecast2(expr,
- type_id,
- typeidType(attrtype),
- attrtypmod);
+ {
+ typelem = attrtype;
+ }
+
+ expr = coerce_target_expr(pstate, expr, type_id, typelem);
+
+ if (!HeapTupleIsValid(expr))
+ {
+ elog(ERROR, "parser: attribute '%s' is of type '%s'"
+ " but expression is of type '%s'"
+ "\n\tYou will need to rewrite or cast the expression",
+ colname,
+ typeidTypeName(attrtype),
+ typeidTypeName(type_id));
+ }
}
- else
+
+#ifdef PARSEDEBUG
+printf("parse_target: attrtypmod is %d\n", (int4) attrtypmod);
+#endif
+
+ /* Apparently going to a fixed-length string?
+ * Then explicitly size for storage...
+ */
+ if (attrtypmod > 0)
{
- /* currently, we can't handle casting of expressions */
- elog(ERROR, "parser: attribute '%s' is of type '%s' but expression is of type '%s'",
- colname,
- typeidTypeName(attrtype),
- typeidTypeName(type_id));
+ expr = size_target_expr(pstate, expr, attrtype, attrtypmod);
}
}
@@ -467,8 +553,8 @@ make_targetlist_expr(ParseState *pstate,
att->relname = pstrdup(RelationGetRelationName(rd)->data);
att->attrs = lcons(makeString(colname), NIL);
target_expr = (Expr *) ParseNestedFuncOrColumn(pstate, att,
- &pstate->p_last_resno,
- EXPR_COLUMN_FIRST);
+ &pstate->p_last_resno,
+ EXPR_COLUMN_FIRST);
while (ar != NIL)
{
A_Indices *ind = lfirst(ar);
@@ -514,7 +600,8 @@ make_targetlist_expr(ParseState *pstate,
tent->expr = expr;
return tent;
-}
+} /* make_targetlist_expr() */
+
/*
* makeTargetNames -
@@ -564,7 +651,7 @@ makeTargetNames(ParseState *pstate, List *cols)
attnameAttNum(pstate->p_target_relation, name);
foreach(nxt, lnext(tl))
if (!strcmp(name, ((Ident *) lfirst(nxt))->name))
- elog(ERROR, "Attribute '%s' should be specified only once", name);
+ elog(ERROR, "Attribute '%s' should be specified only once", name);
}
}