diff --git a/src/backend/access/common/tupdesc.c b/src/backend/access/common/tupdesc.c index 04622a1362..1a8d2975cc 100644 --- a/src/backend/access/common/tupdesc.c +++ b/src/backend/access/common/tupdesc.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.28 1997/11/24 05:07:42 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/access/common/tupdesc.c,v 1.29 1997/11/25 21:58:35 momjian Exp $ * * NOTES * some of the executor utility code such as "ExecTypeFromTL" should be @@ -20,8 +20,9 @@ #include -#include +#include #include +#include #include #include #include @@ -377,10 +378,10 @@ TupleDescInitEntry(TupleDesc desc, */ if (attisset) { - Type t = type("oid"); + Type t = typeidType(OIDOID); - att->attlen = tlen(t); - att->attbyval = tbyval(t); + att->attlen = typeLen(t); + att->attbyval = typeByVal(t); } else { @@ -410,12 +411,12 @@ TupleDescMakeSelfReference(TupleDesc desc, char *relname) { AttributeTupleForm att; - Type t = type("oid"); + Type t = typeidType(OIDOID); att = desc->attrs[attnum - 1]; att->atttypid = TypeShellMake(relname); - att->attlen = tlen(t); - att->attbyval = tbyval(t); + att->attlen = typeLen(t); + att->attbyval = typeByVal(t); att->attnelems = 0; } diff --git a/src/backend/catalog/heap.c b/src/backend/catalog/heap.c index 6a150a01b2..512e2a0fe3 100644 --- a/src/backend/catalog/heap.c +++ b/src/backend/catalog/heap.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.33 1997/11/24 05:08:07 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/heap.c,v 1.34 1997/11/25 21:58:40 momjian Exp $ * * INTERFACE ROUTINES * heap_creatr() - Create an uncataloged heap relation @@ -42,11 +42,12 @@ #include #include #include +#include +#include +#include #include #include #include -#include -#include #include #include #include @@ -722,8 +723,8 @@ addNewRelationType(char *typeName, Oid new_rel_oid) */ new_type_oid = TypeCreate(typeName, /* type name */ new_rel_oid, /* relation oid */ - tlen(type("oid")), /* internal size */ - tlen(type("oid")), /* external size */ + typeLen(typeidType(OIDOID)), /* internal size */ + typeLen(typeidType(OIDOID)), /* external size */ 'c', /* type-type (catalog) */ ',', /* default array delimiter */ "int4in", /* input procedure */ diff --git a/src/backend/catalog/index.c b/src/backend/catalog/index.c index 418123a0aa..deb908e24f 100644 --- a/src/backend/catalog/index.c +++ b/src/backend/catalog/index.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.29 1997/11/24 05:08:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/index.c,v 1.30 1997/11/25 21:58:43 momjian Exp $ * * * INTERFACE ROUTINES @@ -30,26 +30,27 @@ #include #include #include -#include -#include +#include #include -#include -#include -#include -#include -#include -#include -#include #include #include #include #include #include #include +#include #include +#include #include #include -#include +#include +#include +#include +#include +#include +#include +#include +#include #ifndef HAVE_MEMMOVE #include diff --git a/src/backend/catalog/pg_operator.c b/src/backend/catalog/pg_operator.c index a4b4919417..bbdd23701a 100644 --- a/src/backend/catalog/pg_operator.c +++ b/src/backend/catalog/pg_operator.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.16 1997/11/24 05:08:15 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_operator.c,v 1.17 1997/11/25 21:58:46 momjian Exp $ * * NOTES * these routines moved here from commands/define.c and somewhat cleaned up. @@ -20,9 +20,10 @@ #include #include #include -#include #include #include +#include +#include #include #include #include diff --git a/src/backend/catalog/pg_proc.c b/src/backend/catalog/pg_proc.c index 9cbf4e3d7c..e08bb549c6 100644 --- a/src/backend/catalog/pg_proc.c +++ b/src/backend/catalog/pg_proc.c @@ -7,28 +7,28 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.9 1997/09/18 20:20:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_proc.c,v 1.10 1997/11/25 21:58:48 momjian Exp $ * *------------------------------------------------------------------------- */ #include +#include +#include #include #include #include #include -#include -#include -#include #include #include -#include +#include +#include #include -#include +#include +#include +#include #include #include -#include -#include #ifndef HAVE_MEMMOVE #include #else @@ -200,7 +200,7 @@ ProcedureCreate(char *procedureName, if (parameterCount == 1 && (toid = TypeGet(strVal(lfirst(argList)), &defined)) && defined && - (relid = typeid_get_relid(toid)) != 0 && + (relid = typeidTypeRelid(toid)) != 0 && get_attnum(relid, procedureName) != InvalidAttrNumber) elog(WARN, "method %s already an attribute of type %s", procedureName, strVal(lfirst(argList))); diff --git a/src/backend/catalog/pg_type.c b/src/backend/catalog/pg_type.c index 2dc80bbbe0..1cd0fa936c 100644 --- a/src/backend/catalog/pg_type.c +++ b/src/backend/catalog/pg_type.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.13 1997/11/24 05:08:17 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/catalog/pg_type.c,v 1.14 1997/11/25 21:58:50 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,9 +20,10 @@ #include #include #include -#include #include #include +#include +#include #include #include #ifndef HAVE_MEMMOVE diff --git a/src/backend/commands/_deadcode/version.c b/src/backend/commands/_deadcode/version.c index 0059405767..829f9241ee 100644 --- a/src/backend/commands/_deadcode/version.c +++ b/src/backend/commands/_deadcode/version.c @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.7 1997/09/08 02:22:18 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/_deadcode/Attic/version.c,v 1.8 1997/11/25 21:59:11 momjian Exp $ * * NOTES * At the point the version is defined, 2 physical relations are created @@ -30,6 +30,7 @@ #include #include #include /* for GetCurrentXactStartTime */ +#include #include #define MAX_QUERY_LEN 1024 diff --git a/src/backend/commands/explain.c b/src/backend/commands/explain.c index c0a7412276..5c2c1a2859 100644 --- a/src/backend/commands/explain.c +++ b/src/backend/commands/explain.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.14 1997/09/18 20:20:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/explain.c,v 1.15 1997/11/25 21:58:53 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,12 +16,11 @@ #include -#include -#include /* for MakeTimeRange() */ #include #include #include #include +#include #include #include diff --git a/src/backend/commands/recipe.c b/src/backend/commands/recipe.c index 9e5d2819e0..5b77b732a8 100644 --- a/src/backend/commands/recipe.c +++ b/src/backend/commands/recipe.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.12 1997/11/21 18:09:51 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/recipe.c,v 1.13 1997/11/25 21:59:00 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -20,9 +20,9 @@ #include #include #include +#include #include #include /* for RelationNameGetRelation */ -#include #include #include #include @@ -488,7 +488,7 @@ tg_replaceNumberedParam(Node *expression, * "result" attribute from the tee relation */ - isRel = (typeid_get_relid(p->paramtype) != 0); + isRel = (typeidTypeRelid(p->paramtype) != 0); if (isRel) { newVar = makeVar(rt_ind, diff --git a/src/backend/commands/remove.c b/src/backend/commands/remove.c index aaf2127e46..3bfdfb0962 100644 --- a/src/backend/commands/remove.c +++ b/src/backend/commands/remove.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.16 1997/11/20 23:21:13 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/Attic/remove.c,v 1.17 1997/11/25 21:59:03 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -24,7 +24,8 @@ #include #include #include -#include +#include +#include #include #include #ifndef HAVE_MEMMOVE diff --git a/src/backend/commands/vacuum.c b/src/backend/commands/vacuum.c index 7ae8fa1256..2de53600ce 100644 --- a/src/backend/commands/vacuum.c +++ b/src/backend/commands/vacuum.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.52 1997/11/21 19:59:34 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/vacuum.c,v 1.53 1997/11/25 21:59:09 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include @@ -44,7 +45,6 @@ #include #include #include -#include #include #include "storage/shmem.h" #ifndef HAVE_GETRUSAGE diff --git a/src/backend/commands/view.c b/src/backend/commands/view.c index 64b4de2ef4..a752e3020c 100644 --- a/src/backend/commands/view.c +++ b/src/backend/commands/view.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.15 1997/11/21 18:09:58 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/commands/view.c,v 1.16 1997/11/25 21:59:12 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,8 +21,8 @@ #include #include #include -#include -#include +#include +#include #include #include #include @@ -72,7 +72,7 @@ DefineVirtualRelation(char *relname, List *tlist) entry = lfirst(t); res = entry->resdom; resname = res->resname; - restypename = tname(get_id_type(res->restype)); + restypename = typeidTypeName(res->restype); typename = makeNode(TypeName); diff --git a/src/backend/executor/execTuples.c b/src/backend/executor/execTuples.c index f513354e49..d541f29d9e 100644 --- a/src/backend/executor/execTuples.c +++ b/src/backend/executor/execTuples.c @@ -14,7 +14,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.10 1997/09/18 20:20:32 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/execTuples.c,v 1.11 1997/11/25 21:59:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -125,11 +125,11 @@ #undef ExecStoreTuple #include "access/tupdesc.h" +#include "catalog/pg_type.h" +#include "parser/parse_type.h" +#include "storage/bufmgr.h" #include "utils/palloc.h" #include "utils/lsyscache.h" -#include "storage/bufmgr.h" -#include "parser/catalog_utils.h" -#include "catalog/pg_type.h" static TupleTableSlot *NodeGetResultTupleSlot(Plan *node); diff --git a/src/backend/executor/functions.c b/src/backend/executor/functions.c index a2f143d862..2f0e0b91e2 100644 --- a/src/backend/executor/functions.c +++ b/src/backend/executor/functions.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.12 1997/09/18 20:20:37 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/executor/functions.c,v 1.13 1997/11/25 21:59:19 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -21,7 +21,6 @@ #include "nodes/plannodes.h" #include "catalog/pg_proc.h" -#include "parser/parse_query.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" diff --git a/src/backend/executor/nodeAgg.c b/src/backend/executor/nodeAgg.c index b9d7999573..accc7b1fb7 100644 --- a/src/backend/executor/nodeAgg.c +++ b/src/backend/executor/nodeAgg.c @@ -23,12 +23,12 @@ #include "access/heapam.h" #include "catalog/pg_aggregate.h" #include "catalog/catalog.h" +#include "parser/parse_type.h" #include "executor/executor.h" #include "executor/nodeAgg.h" #include "storage/bufmgr.h" #include "utils/palloc.h" #include "utils/syscache.h" -#include "parser/catalog_utils.h" /* * AggFuncInfo - @@ -172,7 +172,7 @@ ExecAgg(Agg *node) if (!HeapTupleIsValid(aggTuple)) elog(WARN, "ExecAgg: cache lookup failed for aggregate \"%s\"(%s)", aggname, - tname(get_id_type(agg->basetype))); + typeidTypeName(agg->basetype)); aggp = (Form_pg_aggregate) GETSTRUCT(aggTuple); xfn1_oid = aggp->aggtransfn1; diff --git a/src/backend/executor/spi.c b/src/backend/executor/spi.c index 16a8ee3212..f954dbe432 100644 --- a/src/backend/executor/spi.c +++ b/src/backend/executor/spi.c @@ -6,6 +6,7 @@ *------------------------------------------------------------------------- */ #include "executor/spi.h" +#include "catalog/pg_type.h" #include "access/printtup.h" #include "fmgr.h" diff --git a/src/backend/nodes/copyfuncs.c b/src/backend/nodes/copyfuncs.c index 62b72ab834..5d848df5ab 100644 --- a/src/backend/nodes/copyfuncs.c +++ b/src/backend/nodes/copyfuncs.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.15 1997/11/20 23:21:40 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/copyfuncs.c,v 1.16 1997/11/25 21:59:40 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,6 @@ #include "nodes/parsenodes.h" #include "nodes/primnodes.h" #include "nodes/relation.h" -#include "parser/parse_query.h" #include "utils/syscache.h" #include "utils/builtins.h" /* for namecpy */ diff --git a/src/backend/nodes/print.c b/src/backend/nodes/print.c index d674263a32..6b62064de9 100644 --- a/src/backend/nodes/print.c +++ b/src/backend/nodes/print.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.10 1997/10/25 01:09:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/nodes/print.c,v 1.11 1997/11/25 21:59:44 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -26,11 +26,11 @@ #include "nodes/parsenodes.h" #include "nodes/print.h" #include "parser/parsetree.h" -#include "parser/catalog_utils.h" #include "access/heapam.h" #include "utils/lsyscache.h" #include "nodes/nodes.h" #include "nodes/plannodes.h" +#include "parser/parse_relation.h" #include "optimizer/clauses.h" static char *plannode_type(Plan *p); @@ -194,7 +194,7 @@ print_expr(Node *expr, List *rtable) r = heap_openr(relname); if (rt->refname) relname = rt->refname; /* table renamed */ - attname = getAttrName(r, var->varattno); + attname = attnumAttName(r, var->varattno); heap_close(r); } break; diff --git a/src/backend/optimizer/path/xfunc.c b/src/backend/optimizer/path/xfunc.c index 39d4131979..0f6894eec4 100644 --- a/src/backend/optimizer/path/xfunc.c +++ b/src/backend/optimizer/path/xfunc.c @@ -9,7 +9,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.6 1997/09/08 21:45:10 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/path/Attic/xfunc.c,v 1.7 1997/11/25 21:59:50 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -641,10 +641,10 @@ xfunc_width(LispValue clause) } else if (IsA(clause, Param)) { - if (typeid_get_relid(get_paramtype((Param) clause))) + if (typeidTypeRelid(get_paramtype((Param) clause))) { /* Param node returns a tuple. Find its width */ - rd = heap_open(typeid_get_relid(get_paramtype((Param) clause))); + rd = heap_open(typeidTypeRelid(get_paramtype((Param) clause))); retval = xfunc_tuple_width(rd); heap_close(rd); } @@ -659,7 +659,7 @@ xfunc_width(LispValue clause) else { /* Param node returns a base type */ - retval = tlen(get_id_type(get_paramtype((Param) clause))); + retval = typeLen(typeidType(get_paramtype((Param) clause))); } goto exit; } @@ -1324,9 +1324,9 @@ xfunc_func_width(RegProcedure funcid, LispValue args) proc = (Form_pg_proc) GETSTRUCT(tupl); /* if function returns a tuple, get the width of that */ - if (typeid_get_relid(proc->prorettype)) + if (typeidTypeRelid(proc->prorettype)) { - rd = heap_open(typeid_get_relid(proc->prorettype)); + rd = heap_open(typeidTypeRelid(proc->prorettype)); retval = xfunc_tuple_width(rd); heap_close(rd); goto exit; diff --git a/src/backend/optimizer/plan/createplan.c b/src/backend/optimizer/plan/createplan.c index 11d777e834..00898ded09 100644 --- a/src/backend/optimizer/plan/createplan.c +++ b/src/backend/optimizer/plan/createplan.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.15 1997/09/08 21:45:13 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/createplan.c,v 1.16 1997/11/25 21:59:56 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -31,7 +31,6 @@ #include "utils/palloc.h" #include "utils/builtins.h" -#include "parser/parse_query.h" #include "optimizer/clauseinfo.h" #include "optimizer/clauses.h" #include "optimizer/planmain.h" diff --git a/src/backend/optimizer/plan/planner.c b/src/backend/optimizer/plan/planner.c index e10e36bdcd..ceddbbe2d6 100644 --- a/src/backend/optimizer/plan/planner.c +++ b/src/backend/optimizer/plan/planner.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.10 1997/11/21 18:10:26 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/plan/planner.c,v 1.11 1997/11/25 21:59:59 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -19,9 +19,8 @@ #include "nodes/plannodes.h" #include "nodes/parsenodes.h" #include "nodes/relation.h" +#include "parser/parse_expr.h" -#include "parser/catalog_utils.h" -#include "parser/parse_query.h" #include "utils/elog.h" #include "utils/lsyscache.h" #include "access/heapam.h" @@ -310,7 +309,7 @@ pg_checkretval(Oid rettype, QueryTreeList *queryTreeList) } /* by here, the function is declared to return some type */ - if ((typ = (Type) get_id_type(rettype)) == NULL) + if ((typ = typeidType(rettype)) == NULL) elog(WARN, "can't find return type %d for function\n", rettype); /* @@ -318,21 +317,21 @@ pg_checkretval(Oid rettype, QueryTreeList *queryTreeList) * final query had better be a retrieve. */ if (cmd != CMD_SELECT) - elog(WARN, "function declared to return type %s, but final query is not a retrieve", tname(typ)); + elog(WARN, "function declared to return type %s, but final query is not a retrieve", typeTypeName(typ)); /* * test 4: for base type returns, the target list should have exactly * one entry, and its type should agree with what the user declared. */ - if (get_typrelid(typ) == InvalidOid) + if (typeTypeRelid(typ) == InvalidOid) { if (exec_tlist_length(tlist) > 1) - elog(WARN, "function declared to return %s returns multiple values in final retrieve", tname(typ)); + elog(WARN, "function declared to return %s returns multiple values in final retrieve", typeTypeName(typ)); resnode = (Resdom *) ((TargetEntry *) lfirst(tlist))->resdom; if (resnode->restype != rettype) - elog(WARN, "return type mismatch in function: declared to return %s, returns %s", tname(typ), tname(get_id_type(resnode->restype))); + elog(WARN, "return type mismatch in function: declared to return %s, returns %s", typeTypeName(typ), typeidTypeName(resnode->restype)); /* by here, base return types match */ return; @@ -358,16 +357,16 @@ pg_checkretval(Oid rettype, QueryTreeList *queryTreeList) * declared return type, and be sure that attributes 1 .. n in the * target list match the declared types. */ - reln = heap_open(get_typrelid(typ)); + reln = heap_open(typeTypeRelid(typ)); if (!RelationIsValid(reln)) - elog(WARN, "cannot open relation relid %d", get_typrelid(typ)); + elog(WARN, "cannot open relation relid %d", typeTypeRelid(typ)); relid = reln->rd_id; relnatts = reln->rd_rel->relnatts; if (exec_tlist_length(tlist) != relnatts) - elog(WARN, "function declared to return type %s does not retrieve (%s.*)", tname(typ), tname(typ)); + elog(WARN, "function declared to return type %s does not retrieve (%s.*)", typeTypeName(typ), typeTypeName(typ)); /* expect attributes 1 .. n in order */ for (i = 1; i <= relnatts; i++) @@ -397,14 +396,14 @@ pg_checkretval(Oid rettype, QueryTreeList *queryTreeList) else if (IsA(thenode, Func)) tletype = (Oid) get_functype((Func *) thenode); else - elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ)); + elog(WARN, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ)); } else - elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ)); + elog(WARN, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ)); #endif /* reach right in there, why don't you? */ if (tletype != reln->rd_att->attrs[i - 1]->atttypid) - elog(WARN, "function declared to return type %s does not retrieve (%s.all)", tname(typ), tname(typ)); + elog(WARN, "function declared to return type %s does not retrieve (%s.all)", typeTypeName(typ), typeTypeName(typ)); } heap_close(reln); diff --git a/src/backend/optimizer/prep/preptlist.c b/src/backend/optimizer/prep/preptlist.c index 0010c69d6a..faf281985e 100644 --- a/src/backend/optimizer/prep/preptlist.c +++ b/src/backend/optimizer/prep/preptlist.c @@ -7,13 +7,14 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.5 1997/09/08 21:45:36 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/preptlist.c,v 1.6 1997/11/25 22:00:06 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include "postgres.h" +#include "catalog/pg_type.h" #include "nodes/pg_list.h" #include "nodes/relation.h" #include "nodes/primnodes.h" @@ -24,9 +25,9 @@ #include "utils/builtins.h" #include "utils/lsyscache.h" #include "utils/palloc.h" +#include "parser/parse_type.h" #include "parser/parsetree.h" /* for getrelid() */ -#include "parser/catalog_utils.h" #include "optimizer/internal.h" #include "optimizer/prep.h" @@ -278,7 +279,7 @@ new_relation_targetlist(Oid relid, Index rt_index, NodeTag node_type) attisset = get_attisset( /* type_id, */ relid, attname); if (attisset) { - typlen = tlen(type("oid")); + typlen = typeLen(typeidType(OIDOID)); } else { diff --git a/src/backend/optimizer/prep/prepunion.c b/src/backend/optimizer/prep/prepunion.c index a686f94e01..00dd407978 100644 --- a/src/backend/optimizer/prep/prepunion.c +++ b/src/backend/optimizer/prep/prepunion.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.8 1997/11/21 18:10:44 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/prep/prepunion.c,v 1.9 1997/11/25 22:00:10 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -22,7 +22,6 @@ #include "nodes/plannodes.h" #include "nodes/relation.h" -#include "parser/parse_query.h" #include "parser/parsetree.h" #include "utils/elog.h" diff --git a/src/backend/optimizer/util/tlist.c b/src/backend/optimizer/util/tlist.c index 089633033b..aa867f4b5a 100644 --- a/src/backend/optimizer/util/tlist.c +++ b/src/backend/optimizer/util/tlist.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.7 1997/09/08 21:45:55 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/optimizer/util/tlist.c,v 1.8 1997/11/25 22:00:16 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -26,7 +26,6 @@ #include "optimizer/clauses.h" #include "nodes/makefuncs.h" -#include "parser/catalog_utils.h" static Node *flatten_tlistentry(Node *tlistentry, List *flat_tlist); diff --git a/src/backend/parser/Makefile b/src/backend/parser/Makefile index 8559100ee3..7ab45e4bee 100644 --- a/src/backend/parser/Makefile +++ b/src/backend/parser/Makefile @@ -4,7 +4,7 @@ # Makefile for parser # # IDENTIFICATION -# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.11 1997/11/24 05:20:57 momjian Exp $ +# $Header: /cvsroot/pgsql/src/backend/parser/Makefile,v 1.12 1997/11/25 22:00:21 momjian Exp $ # #------------------------------------------------------------------------- @@ -22,8 +22,9 @@ CFLAGS+= -Wno-error endif -OBJS= analyze.o catalog_utils.o gram.o \ - keywords.o parser.o parse_query.o scan.o scansup.o +OBJS= analyze.o gram.o keywords.o parser.o parse_agg.o parse_clause.o \ + parse_expr.o parse_func.o parse_node.o parse_oper.o parse_relation.o \ + parse_type.o parse_target.o scan.o scansup.o all: SUBSYS.o diff --git a/src/backend/parser/analyze.c b/src/backend/parser/analyze.c index bee5132a8b..4a3800a8a4 100644 --- a/src/backend/parser/analyze.c +++ b/src/backend/parser/analyze.c @@ -7,46 +7,30 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.49 1997/11/20 23:22:11 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/analyze.c,v 1.50 1997/11/25 22:00:27 momjian Exp $ * *------------------------------------------------------------------------- */ + #include #include #include #include "postgres.h" -#include "nodes/nodes.h" -#include "nodes/params.h" -#include "nodes/primnodes.h" -#include "nodes/parsenodes.h" -#include "nodes/relation.h" -#include "parse.h" /* for AND, OR, etc. */ -#include "catalog/pg_aggregate.h" -#include "catalog/pg_type.h" /* for INT4OID, etc. */ -#include "catalog/pg_proc.h" -#include "utils/elog.h" -#include "utils/builtins.h" /* namecmp(), textout() */ -#include "utils/lsyscache.h" -#include "utils/palloc.h" -#include "utils/mcxt.h" -#include "utils/syscache.h" -#include "utils/acl.h" -#include "parser/parse_query.h" -#include "parser/parse_state.h" -#include "nodes/makefuncs.h" /* for makeResdom(), etc. */ -#include "nodes/nodeFuncs.h" -#include "commands/sequence.h" -#include "optimizer/clauses.h" #include "access/heapam.h" +#include "nodes/makefuncs.h" +#include "nodes/memnodes.h" +#include "nodes/pg_list.h" +#include "parser/analyze.h" +#include "parser/parse_agg.h" +#include "parser/parse_node.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "parser/parse_clause.h" +#include "utils/builtins.h" +#include "utils/mcxt.h" -#include "miscadmin.h" - -#include "port-protos.h" /* strdup() */ - -/* convert the parse tree into a query tree */ static Query *transformStmt(ParseState *pstate, Node *stmt); - static Query *transformDeleteStmt(ParseState *pstate, DeleteStmt *stmt); static Query *transformInsertStmt(ParseState *pstate, AppendStmt *stmt); static Query *transformIndexStmt(ParseState *pstate, IndexStmt *stmt); @@ -55,74 +39,7 @@ static Query *transformRuleStmt(ParseState *query, RuleStmt *stmt); static Query *transformSelectStmt(ParseState *pstate, RetrieveStmt *stmt); static Query *transformUpdateStmt(ParseState *pstate, ReplaceStmt *stmt); static Query *transformCursorStmt(ParseState *pstate, CursorStmt *stmt); -static Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno); -#define EXPR_COLUMN_FIRST 1 -#define EXPR_RELATION_FIRST 2 -static Node *transformExpr(ParseState *pstate, Node *expr, int precedence); -static Node *transformIdent(ParseState *pstate, Node *expr, int precedence); - -static void makeRangeTable(ParseState *pstate, char *relname, List *frmList); -static List *expandAllTables(ParseState *pstate); -static char *figureColname(Node *expr, Node *resval); -static List *makeTargetNames(ParseState *pstate, List *cols); -static List *transformTargetList(ParseState *pstate, List *targetlist); -static TargetEntry *make_targetlist_expr(ParseState *pstate, - char *colname, Node *expr, - List *arrayRef); -static bool inWhereClause = false; -static Node *transformWhereClause(ParseState *pstate, Node *a_expr); -static List *transformGroupClause(ParseState *pstate, List *grouplist, - List *targetlist); -static List *transformSortClause(ParseState *pstate, - List *orderlist, List *targetlist, - char *uniqueFlag); - -static void parseFromClause(ParseState *pstate, List *frmList); -static Node *ParseFunc(ParseState *pstate, char *funcname, - List *fargs, int *curr_resno); -static List *setup_tlist(char *attname, Oid relid); -static List *setup_base_tlist(Oid typeid); -static void make_arguments(int nargs, List *fargs, Oid *input_typeids, - Oid *function_typeids); -static void AddAggToParseState(ParseState *pstate, Aggreg *aggreg); -static void finalizeAggregates(ParseState *pstate, Query *qry); -static void parseCheckAggregates(ParseState *pstate, Query *qry); -static ParseState *makeParseState(void); -static Node *parser_typecast(Value *expr, TypeName *typename, int typlen); -static Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen); -static Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target); - -/***************************************************************************** - * - *****************************************************************************/ - -/* - * makeParseState() -- - * allocate and initialize a new ParseState. - * the CALLERS is responsible for freeing the ParseState* returned - * - */ - -static ParseState * -makeParseState(void) -{ - ParseState *pstate; - - pstate = malloc(sizeof(ParseState)); - pstate->p_last_resno = 1; - pstate->p_rtable = NIL; - pstate->p_numAgg = 0; - pstate->p_aggs = NIL; - pstate->p_is_insert = false; - pstate->p_insert_columns = NIL; - pstate->p_is_update = false; - pstate->p_is_rule = false; - pstate->p_target_relation = NULL; - pstate->p_target_rangetblentry = NULL; - - return (pstate); -} /* * parse_analyze - @@ -144,11 +61,9 @@ parse_analyze(List *pl) result->len = length(pl); result->qtrees = (Query **) malloc(result->len * sizeof(Query *)); - inWhereClause = false; /* to avoid nextval(sequence) in WHERE */ - while (pl != NIL) { - pstate = makeParseState(); + pstate = make_parsestate(); result->qtrees[i++] = transformStmt(pstate, lfirst(pl)); pl = lnext(pl); if (pstate->p_target_relation != NULL) @@ -620,2580 +535,3 @@ transformCursorStmt(ParseState *pstate, CursorStmt *stmt) return (Query *) qry; } - -/***************************************************************************** - * - * Transform Exprs, Aggs, etc. - * - *****************************************************************************/ - -/* - * transformExpr - - * analyze and transform expressions. Type checking and type casting is - * done here. The optimizer and the executor cannot handle the original - * (raw) expressions collected by the parse tree. Hence the transformation - * here. - */ -static Node * -transformExpr(ParseState *pstate, Node *expr, int precedence) -{ - Node *result = NULL; - - if (expr == NULL) - return NULL; - - switch (nodeTag(expr)) - { - case T_Attr: - { - Attr *att = (Attr *) expr; - Node *temp; - - /* what if att.attrs == "*"?? */ - temp = handleNestedDots(pstate, att, &pstate->p_last_resno); - if (att->indirection != NIL) - { - List *idx = att->indirection; - - while (idx != NIL) - { - A_Indices *ai = (A_Indices *) lfirst(idx); - Node *lexpr = NULL, - *uexpr; - - uexpr = transformExpr(pstate, ai->uidx, precedence); /* must exists */ - if (exprType(uexpr) != INT4OID) - elog(WARN, "array index expressions must be int4's"); - if (ai->lidx != NULL) - { - lexpr = transformExpr(pstate, ai->lidx, precedence); - if (exprType(lexpr) != INT4OID) - elog(WARN, "array index expressions must be int4's"); - } -#if 0 - pfree(ai->uidx); - if (ai->lidx != NULL) - pfree(ai->lidx); -#endif - ai->lidx = lexpr; - ai->uidx = uexpr; - - /* - * note we reuse the list of indices, make sure we - * don't free them! Otherwise, make a new list - * here - */ - idx = lnext(idx); - } - result = (Node *) make_array_ref(temp, att->indirection); - } - else - { - result = temp; - } - break; - } - case T_A_Const: - { - A_Const *con = (A_Const *) expr; - Value *val = &con->val; - - if (con->typename != NULL) - { - result = parser_typecast(val, con->typename, -1); - } - else - { - result = (Node *) make_const(val); - } - break; - } - case T_ParamNo: - { - ParamNo *pno = (ParamNo *) expr; - Oid toid; - int paramno; - Param *param; - - paramno = pno->number; - toid = param_type(paramno); - if (!OidIsValid(toid)) - { - elog(WARN, "Parameter '$%d' is out of range", - paramno); - } - param = makeNode(Param); - param->paramkind = PARAM_NUM; - param->paramid = (AttrNumber) paramno; - param->paramname = ""; - param->paramtype = (Oid) toid; - param->param_tlist = (List *) NULL; - - result = (Node *) param; - break; - } - case T_A_Expr: - { - A_Expr *a = (A_Expr *) expr; - - switch (a->oper) - { - case OP: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - result = (Node *) make_op(a->opname, lexpr, rexpr); - } - break; - case ISNULL: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - - result = ParseFunc(pstate, - "nullvalue", lcons(lexpr, NIL), - &pstate->p_last_resno); - } - break; - case NOTNULL: - { - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - - result = ParseFunc(pstate, - "nonnullvalue", lcons(lexpr, NIL), - &pstate->p_last_resno); - } - break; - case AND: - { - Expr *expr = makeNode(Expr); - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(lexpr) != BOOLOID) - elog(WARN, - "left-hand side of AND is type '%s', not bool", - tname(get_id_type(exprType(lexpr)))); - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "right-hand side of AND is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = AND_EXPR; - expr->args = makeList(lexpr, rexpr, -1); - result = (Node *) expr; - } - break; - case OR: - { - Expr *expr = makeNode(Expr); - Node *lexpr = transformExpr(pstate, a->lexpr, precedence); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(lexpr) != BOOLOID) - elog(WARN, - "left-hand side of OR is type '%s', not bool", - tname(get_id_type(exprType(lexpr)))); - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "right-hand side of OR is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = OR_EXPR; - expr->args = makeList(lexpr, rexpr, -1); - result = (Node *) expr; - } - break; - case NOT: - { - Expr *expr = makeNode(Expr); - Node *rexpr = transformExpr(pstate, a->rexpr, precedence); - - if (exprType(rexpr) != BOOLOID) - elog(WARN, - "argument to NOT is type '%s', not bool", - tname(get_id_type(exprType(rexpr)))); - expr->typeOid = BOOLOID; - expr->opType = NOT_EXPR; - expr->args = makeList(rexpr, -1); - result = (Node *) expr; - } - break; - } - break; - } - case T_Ident: - { - - /* - * look for a column name or a relation name (the default - * behavior) - */ - result = transformIdent(pstate, expr, precedence); - break; - } - case T_FuncCall: - { - FuncCall *fn = (FuncCall *) expr; - List *args; - - /* transform the list of arguments */ - foreach(args, fn->args) - lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence); - result = ParseFunc(pstate, - fn->funcname, fn->args, &pstate->p_last_resno); - break; - } - default: - /* should not reach here */ - elog(WARN, "transformExpr: does not know how to transform %d\n", - nodeTag(expr)); - break; - } - - return result; -} - -static Node * -transformIdent(ParseState *pstate, Node *expr, int precedence) -{ - Ident *ident = (Ident *) expr; - RangeTblEntry *rte; - Node *column_result, - *relation_result, - *result; - - column_result = relation_result = result = 0; - /* try to find the ident as a column */ - if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL) - { - Attr *att = makeNode(Attr); - - att->relname = rte->refname; - att->attrs = lcons(makeString(ident->name), NIL); - column_result = - (Node *) handleNestedDots(pstate, att, &pstate->p_last_resno); - } - - /* try to find the ident as a relation */ - if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL) - { - ident->isRel = TRUE; - relation_result = (Node *) ident; - } - - /* choose the right result based on the precedence */ - if (precedence == EXPR_COLUMN_FIRST) - { - if (column_result) - result = column_result; - else - result = relation_result; - } - else - { - if (relation_result) - result = relation_result; - else - result = column_result; - } - - if (result == NULL) - elog(WARN, "attribute '%s' not found", ident->name); - - return result; -} - -/***************************************************************************** - * - * From Clause - * - *****************************************************************************/ - -/* - * parseFromClause - - * turns the table references specified in the from-clause into a - * range table. The range table may grow as we transform the expressions - * in the target list. (Note that this happens because in POSTQUEL, we - * allow references to relations not specified in the from-clause. We - * also allow that in our POST-SQL) - * - */ -static void -parseFromClause(ParseState *pstate, List *frmList) -{ - List *fl; - - foreach(fl, frmList) - { - RangeVar *r = lfirst(fl); - RelExpr *baserel = r->relExpr; - char *relname = baserel->relname; - char *refname = r->name; - RangeTblEntry *rte; - - if (refname == NULL) - refname = relname; - - /* - * marks this entry to indicate it comes from the FROM clause. In - * SQL, the target list can only refer to range variables - * specified in the from clause but we follow the more powerful - * POSTQUEL semantics and automatically generate the range - * variable if not specified. However there are times we need to - * know whether the entries are legitimate. - * - * eg. select * from foo f where f.x = 1; will generate wrong answer - * if we expand * to foo.x. - */ - rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE); - } -} - -/* - * makeRangeTable - - * make a range table with the specified relation (optional) and the - * from-clause. - */ -static void -makeRangeTable(ParseState *pstate, char *relname, List *frmList) -{ - RangeTblEntry *rte; - - parseFromClause(pstate, frmList); - - if (relname == NULL) - return; - - if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1) - rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE); - else - rte = refnameRangeTableEntry(pstate->p_rtable, relname); - - pstate->p_target_rangetblentry = rte; - Assert(pstate->p_target_relation == NULL); - pstate->p_target_relation = heap_open(rte->relid); - Assert(pstate->p_target_relation != NULL); - /* will close relation later */ -} - -/* - * exprType - - * returns the Oid of the type of the expression. (Used for typechecking.) - */ -Oid -exprType(Node *expr) -{ - Oid type = (Oid) 0; - - switch (nodeTag(expr)) - { - case T_Func: - type = ((Func *) expr)->functype; - break; - case T_Iter: - type = ((Iter *) expr)->itertype; - break; - case T_Var: - type = ((Var *) expr)->vartype; - break; - case T_Expr: - type = ((Expr *) expr)->typeOid; - break; - case T_Const: - type = ((Const *) expr)->consttype; - break; - case T_ArrayRef: - type = ((ArrayRef *) expr)->refelemtype; - break; - case T_Aggreg: - type = ((Aggreg *) expr)->aggtype; - break; - case T_Param: - type = ((Param *) expr)->paramtype; - break; - case T_Ident: - /* is this right? */ - type = UNKNOWNOID; - break; - default: - elog(WARN, "exprType: don't know how to get type for %d node", - nodeTag(expr)); - break; - } - return type; -} - -/* - * expandAllTables - - * turns '*' (in the target list) into a list of attributes - * (of all relations in the range table) - */ -static List * -expandAllTables(ParseState *pstate) -{ - List *target = NIL; - List *legit_rtable = NIL; - List *rt, - *rtable; - - rtable = pstate->p_rtable; - if (pstate->p_is_rule) - { - - /* - * skip first two entries, "*new*" and "*current*" - */ - rtable = lnext(lnext(pstate->p_rtable)); - } - - /* this should not happen */ - if (rtable == NULL) - elog(WARN, "cannot expand: null p_rtable"); - - /* - * go through the range table and make a list of range table entries - * which we will expand. - */ - foreach(rt, rtable) - { - RangeTblEntry *rte = lfirst(rt); - - /* - * we only expand those specify in the from clause. (This will - * also prevent us from using the wrong table in inserts: eg. - * tenk2 in "insert into tenk2 select * from tenk1;") - */ - if (!rte->inFromCl) - continue; - legit_rtable = lappend(legit_rtable, rte); - } - - foreach(rt, legit_rtable) - { - RangeTblEntry *rte = lfirst(rt); - List *temp = target; - - if (temp == NIL) - target = expandAll(pstate, rte->relname, rte->refname, - &pstate->p_last_resno); - else - { - while (temp != NIL && lnext(temp) != NIL) - temp = lnext(temp); - lnext(temp) = expandAll(pstate, rte->relname, rte->refname, - &pstate->p_last_resno); - } - } - return target; -} - - -/* - * figureColname - - * if the name of the resulting column is not specified in the target - * list, we have to guess. - * - */ -static char * -figureColname(Node *expr, Node *resval) -{ - switch (nodeTag(expr)) - { - case T_Aggreg: - return (char *) /* XXX */ - ((Aggreg *) expr)->aggname; - case T_Expr: - if (((Expr *) expr)->opType == FUNC_EXPR) - { - if (nodeTag(resval) == T_FuncCall) - return ((FuncCall *) resval)->funcname; - } - break; - default: - break; - } - - return "?column?"; -} - -/***************************************************************************** - * - * Target list - * - *****************************************************************************/ - -/* - * makeTargetNames - - * generate a list of column names if not supplied or - * test supplied column names to make sure they are in target table - * (used exclusively for inserts) - */ -static List * -makeTargetNames(ParseState *pstate, List *cols) -{ - List *tl = NULL; - - /* Generate ResTarget if not supplied */ - - if (cols == NIL) - { - int numcol; - int i; - AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs; - - numcol = pstate->p_target_relation->rd_rel->relnatts; - for (i = 0; i < numcol; i++) - { - Ident *id = makeNode(Ident); - - id->name = palloc(NAMEDATALEN); - StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN); - id->indirection = NIL; - id->isRel = false; - if (tl == NIL) - cols = tl = lcons(id, NIL); - else - { - lnext(tl) = lcons(id, NIL); - tl = lnext(tl); - } - } - } - else - { - foreach(tl, cols) - { - List *nxt; - char *name = ((Ident *) lfirst(tl))->name; - - /* elog on failure */ - varattno(pstate->p_target_relation, name); - foreach(nxt, lnext(tl)) - if (!strcmp(name, ((Ident *) lfirst(nxt))->name)) - elog (WARN, "Attribute '%s' should be specified only once", name); - } - } - - return cols; -} - -/* - * transformTargetList - - * turns a list of ResTarget's into a list of TargetEntry's - */ -static List * -transformTargetList(ParseState *pstate, List *targetlist) -{ - List *p_target = NIL; - List *tail_p_target = NIL; - - while (targetlist != NIL) - { - ResTarget *res = (ResTarget *) lfirst(targetlist); - TargetEntry *tent = makeNode(TargetEntry); - - switch (nodeTag(res->val)) - { - case T_Ident: - { - Node *expr; - Oid type_id; - int type_len; - char *identname; - char *resname; - - identname = ((Ident *) res->val)->name; - handleTargetColname(pstate, &res->name, NULL, identname); - - /* - * here we want to look for column names only, not relation - * names (even though they can be stored in Ident nodes, too) - */ - expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - type_id = exprType(expr); - type_len = tlen(get_id_type(type_id)); - resname = (res->name) ? res->name : identname; - tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - (Size) type_len, - resname, - (Index) 0, - (Oid) 0, - 0); - - tent->expr = expr; - break; - } - case T_ParamNo: - case T_FuncCall: - case T_A_Const: - case T_A_Expr: - { - Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); - - handleTargetColname(pstate, &res->name, NULL, NULL); - /* note indirection has not been transformed */ - if (pstate->p_is_insert && res->indirection != NIL) - { - /* this is an array assignment */ - char *val; - char *str, - *save_str; - List *elt; - int i = 0, - ndims; - int lindx[MAXDIM], - uindx[MAXDIM]; - int resdomno; - Relation rd; - Value *constval; - - if (exprType(expr) != UNKNOWNOID || - !IsA(expr, Const)) - elog(WARN, "yyparse: string constant expected"); - - val = (char *) textout((struct varlena *) - ((Const *) expr)->constvalue); - str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2); - foreach(elt, res->indirection) - { - A_Indices *aind = (A_Indices *) lfirst(elt); - - aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); - if (!IsA(aind->uidx, Const)) - elog(WARN, - "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(WARN, - "Array Index for Append should be a constant"); - lindx[i] = ((Const *) aind->lidx)->constvalue; - } - else - { - lindx[i] = 1; - } - if (lindx[i] > uindx[i]) - elog(WARN, "yyparse: lower index cannot be greater than upper index"); - sprintf(str, "[%d:%d]", lindx[i], uindx[i]); - str += strlen(str); - i++; - } - sprintf(str, "=%s", val); - rd = pstate->p_target_relation; - Assert(rd != NULL); - resdomno = varattno(rd, res->name); - ndims = att_attnelems(rd, resdomno); - if (i != ndims) - elog(WARN, "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), - NULL); - pfree(save_str); - } - else - { - char *colname = res->name; - - /* this is not an array assignment */ - if (colname == NULL) - { - - /* - * if you're wondering why this is here, look - * at the yacc grammar for why a name can be - * missing. -ay - */ - colname = figureColname(expr, res->val); - } - if (res->indirection) - { - List *ilist = res->indirection; - - while (ilist != NIL) - { - A_Indices *ind = lfirst(ilist); - - ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); - ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); - ilist = lnext(ilist); - } - } - res->name = colname; - tent = make_targetlist_expr(pstate, res->name, expr, - res->indirection); - } - break; - } - case T_Attr: - { - Oid type_id; - int type_len; - Attr *att = (Attr *) res->val; - Node *result; - char *attrname; - char *resname; - Resdom *resnode; - List *attrs = att->attrs; - - /* - * Target item is a single '*', expand all tables (eg. - * SELECT * FROM emp) - */ - if (att->relname != NULL && !strcmp(att->relname, "*")) - { - if (tail_p_target == NIL) - p_target = tail_p_target = expandAllTables(pstate); - else - lnext(tail_p_target) = expandAllTables(pstate); - - while (lnext(tail_p_target) != NIL) - /* make sure we point to the last target entry */ - tail_p_target = lnext(tail_p_target); - - /* - * skip rest of while loop - */ - targetlist = lnext(targetlist); - continue; - } - - /* - * Target item is relation.*, expand the table (eg. - * SELECT emp.*, dname FROM emp, dept) - */ - attrname = strVal(lfirst(att->attrs)); - if (att->attrs != NIL && !strcmp(attrname, "*")) - { - - /* - * tail_p_target is the target list we're building - * in the while loop. Make sure we fix it after - * appending more nodes. - */ - if (tail_p_target == NIL) - p_target = tail_p_target = expandAll(pstate, att->relname, - att->relname, &pstate->p_last_resno); - else - lnext(tail_p_target) = - expandAll(pstate, att->relname, att->relname, - &pstate->p_last_resno); - while (lnext(tail_p_target) != NIL) - /* make sure we point to the last target entry */ - tail_p_target = lnext(tail_p_target); - - /* - * skip the rest of the while loop - */ - targetlist = lnext(targetlist); - continue; - } - - - /* - * Target item is fully specified: ie. - * relation.attribute - */ - result = handleNestedDots(pstate, att, &pstate->p_last_resno); - handleTargetColname(pstate, &res->name, att->relname, attrname); - if (att->indirection != NIL) - { - List *ilist = att->indirection; - - while (ilist != NIL) - { - A_Indices *ind = lfirst(ilist); - - ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); - ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); - ilist = lnext(ilist); - } - result = (Node *) make_array_ref(result, att->indirection); - } - type_id = exprType(result); - type_len = tlen(get_id_type(type_id)); - /* move to last entry */ - while (lnext(attrs) != NIL) - attrs = lnext(attrs); - resname = (res->name) ? res->name : strVal(lfirst(attrs)); - resnode = makeResdom((AttrNumber) pstate->p_last_resno++, - (Oid) type_id, - (Size) type_len, - resname, - (Index) 0, - (Oid) 0, - 0); - tent->resdom = resnode; - tent->expr = result; - break; - } - default: - /* internal error */ - elog(WARN, - "internal error: do not know how to transform targetlist"); - break; - } - - if (p_target == NIL) - { - p_target = tail_p_target = lcons(tent, NIL); - } - else - { - lnext(tail_p_target) = lcons(tent, NIL); - tail_p_target = lnext(tail_p_target); - } - targetlist = lnext(targetlist); - } - - return p_target; -} - - -/* - * make_targetlist_expr - - * make a TargetEntry from an expression - * - * arrayRef is a list of transformed A_Indices - */ -static TargetEntry * -make_targetlist_expr(ParseState *pstate, - char *colname, - Node *expr, - List *arrayRef) -{ - Oid type_id, - attrtype; - int type_len, - attrlen; - int resdomno; - Relation rd; - bool attrisset; - TargetEntry *tent; - Resdom *resnode; - - if (expr == NULL) - elog(WARN, "make_targetlist_expr: invalid use of NULL expression"); - - type_id = exprType(expr); - if (type_id == InvalidOid) - { - type_len = 0; - } - else - type_len = tlen(get_id_type(type_id)); - - /* I have no idea what the following does! */ - /* It appears to process target columns that will be receiving results */ - if (pstate->p_is_insert || pstate->p_is_update) - { - - /* - * append or replace query -- append, replace work only on one - * relation, so multiple occurence of same resdomno is bogus - */ - rd = pstate->p_target_relation; - Assert(rd != NULL); - resdomno = varattno(rd, colname); - attrisset = varisset(rd, colname); - attrtype = att_typeid(rd, resdomno); - if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) - attrtype = GetArrayElementType(attrtype); - if (attrtype == BPCHAROID || attrtype == VARCHAROID) - { - attrlen = rd->rd_att->attrs[resdomno - 1]->attlen; - } - else - { - attrlen = tlen(get_id_type(attrtype)); - } -#if 0 - if (Input_is_string && Typecast_ok) - { - Datum val; - - if (type_id == typeid(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(typeid_get_retinfunc(attrtype), - val, get_typelem(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, get_id_type(attrtype)); - } - else if (attrtype != type_id) - { - if ((attrtype == INT2OID) && (type_id == INT4OID)) - lfirst(expr) = lispInteger(INT2OID); /* handle CASHOID too */ - else if ((attrtype == FLOAT4OID) && (type_id == FLOAT8OID)) - lfirst(expr) = lispInteger(FLOAT4OID); - else - elog(WARN, "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)) - { - /* updating a single item */ - Oid typelem = get_typelem(attrtype); - - expr = (Node *) parser_typecast2(expr, - type_id, - get_id_type(typelem), - attrlen); - } - else - expr = (Node *) parser_typecast2(expr, - type_id, - get_id_type(attrtype), - attrlen); - } - else - { - /* currently, we can't handle casting of expressions */ - elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'", - colname, - get_id_typname(attrtype), - get_id_typname(type_id)); - } - } - - if (arrayRef != NIL) - { - Expr *target_expr; - Attr *att = makeNode(Attr); - List *ar = arrayRef; - List *upperIndexpr = NIL; - List *lowerIndexpr = NIL; - - att->relname = pstrdup(RelationGetRelationName(rd)->data); - att->attrs = lcons(makeString(colname), NIL); - target_expr = (Expr *) handleNestedDots(pstate, att, - &pstate->p_last_resno); - while (ar != NIL) - { - A_Indices *ind = lfirst(ar); - - if (lowerIndexpr || (!upperIndexpr && ind->lidx)) - { - - /* - * XXX assume all lowerIndexpr is non-null in this - * case - */ - lowerIndexpr = lappend(lowerIndexpr, ind->lidx); - } - upperIndexpr = lappend(upperIndexpr, ind->uidx); - ar = lnext(ar); - } - - expr = (Node *) make_array_set(target_expr, - upperIndexpr, - lowerIndexpr, - (Expr *) expr); - attrtype = att_typeid(rd, resdomno); - attrlen = tlen(get_id_type(attrtype)); - } - } - else - { - resdomno = pstate->p_last_resno++; - attrtype = type_id; - attrlen = type_len; - } - tent = makeNode(TargetEntry); - - resnode = makeResdom((AttrNumber) resdomno, - (Oid) attrtype, - (Size) attrlen, - colname, - (Index) 0, - (Oid) 0, - 0); - - tent->resdom = resnode; - tent->expr = expr; - - return tent; -} - - -/***************************************************************************** - * - * Where Clause - * - *****************************************************************************/ - -/* - * transformWhereClause - - * transforms the qualification and make sure it is of type Boolean - * - */ -static Node * -transformWhereClause(ParseState *pstate, Node *a_expr) -{ - Node *qual; - - if (a_expr == NULL) - return (Node *) NULL; /* no qualifiers */ - - inWhereClause = true; - qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST); - inWhereClause = false; - if (exprType(qual) != BOOLOID) - { - elog(WARN, - "where clause must return type bool, not %s", - tname(get_id_type(exprType(qual)))); - } - return qual; -} - -/***************************************************************************** - * - * Sort Clause - * - *****************************************************************************/ - -/* - * find_targetlist_entry - - * returns the Resdom in the target list matching the specified varname - * and range - * - */ -static TargetEntry * -find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) -{ - List *i; - int real_rtable_pos = 0, - target_pos = 0; - TargetEntry *target_result = NULL; - - if (sortgroupby->range) - real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable, - sortgroupby->range); - - foreach(i, tlist) - { - TargetEntry *target = (TargetEntry *) lfirst(i); - Resdom *resnode = target->resdom; - Var *var = (Var *) target->expr; - char *resname = resnode->resname; - int test_rtable_pos = var->varno; - -#ifdef PARSEDEBUG - printf("find_targetlist_entry- target name is %s, position %d, resno %d\n", - (sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno); -#endif - - if (!sortgroupby->name) - { - if (sortgroupby->resno == ++target_pos) - { - target_result = target; - break; - } - } - else - { - if (!strcmp(resname, sortgroupby->name)) - { - if (sortgroupby->range) - { - if (real_rtable_pos == test_rtable_pos) - { - if (target_result != NULL) - elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); - else - target_result = target; - } - } - else - { - if (target_result != NULL) - elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); - else - target_result = target; - } - } - } - } - return target_result; -} - -static Oid -any_ordering_op(int restype) -{ - Operator order_op; - Oid order_opid; - - order_op = oper("<", restype, restype, false); - order_opid = oprid(order_op); - - return order_opid; -} - -/* - * transformGroupClause - - * transform a Group By clause - * - */ -static List * -transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) -{ - List *glist = NIL, - *gl = NIL; - - while (grouplist != NIL) - { - GroupClause *grpcl = makeNode(GroupClause); - TargetEntry *restarget; - Resdom *resdom; - - restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist); - - if (restarget == NULL) - elog(WARN, "The field being grouped by must appear in the target list"); - - grpcl->entry = restarget; - resdom = restarget->resdom; - grpcl->grpOpoid = oprid(oper("<", - resdom->restype, - resdom->restype, false)); - if (glist == NIL) - gl = glist = lcons(grpcl, NIL); - else - { - List *i; - - foreach (i, glist) - { - GroupClause *gcl = (GroupClause *) lfirst (i); - - if ( gcl->entry == grpcl->entry ) - break; - } - if ( i == NIL ) /* not in grouplist already */ - { - lnext(gl) = lcons(grpcl, NIL); - gl = lnext(gl); - } - else - pfree (grpcl); /* get rid of this */ - } - grouplist = lnext(grouplist); - } - - return glist; -} - -/* - * transformSortClause - - * transform an Order By clause - * - */ -static List * -transformSortClause(ParseState *pstate, - List *orderlist, List *targetlist, - char *uniqueFlag) -{ - List *sortlist = NIL; - List *s = NIL; - - while (orderlist != NIL) - { - SortGroupBy *sortby = lfirst(orderlist); - SortClause *sortcl = makeNode(SortClause); - TargetEntry *restarget; - Resdom *resdom; - - restarget = find_targetlist_entry(pstate, sortby, targetlist); - if (restarget == NULL) - elog(WARN, "The field being ordered by must appear in the target list"); - - sortcl->resdom = resdom = restarget->resdom; - sortcl->opoid = oprid(oper(sortby->useOp, - resdom->restype, - resdom->restype, false)); - if (sortlist == NIL) - { - s = sortlist = lcons(sortcl, NIL); - } - else - { - List *i; - - foreach (i, sortlist) - { - SortClause *scl = (SortClause *) lfirst (i); - - if ( scl->resdom == sortcl->resdom ) - break; - } - if ( i == NIL ) /* not in sortlist already */ - { - lnext(s) = lcons(sortcl, NIL); - s = lnext(s); - } - else - pfree (sortcl); /* get rid of this */ - } - orderlist = lnext(orderlist); - } - - if (uniqueFlag) - { - List *i; - - if (uniqueFlag[0] == '*') - { - - /* - * concatenate all elements from target list that are not - * already in the sortby list - */ - foreach(i, targetlist) - { - TargetEntry *tlelt = (TargetEntry *) lfirst(i); - - s = sortlist; - while (s != NIL) - { - SortClause *sortcl = lfirst(s); - - if (sortcl->resdom == tlelt->resdom) - break; - s = lnext(s); - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } - } - } - else - { - TargetEntry *tlelt = NULL; - char *uniqueAttrName = uniqueFlag; - - /* only create sort clause with the specified unique attribute */ - foreach(i, targetlist) - { - tlelt = (TargetEntry *) lfirst(i); - if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0) - break; - } - if (i == NIL) - { - elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist"); - } - s = sortlist; - foreach(s, sortlist) - { - SortClause *sortcl = lfirst(s); - - if (sortcl->resdom == tlelt->resdom) - break; - } - if (s == NIL) - { - /* not a member of the sortclauses yet */ - SortClause *sortcl = makeNode(SortClause); - - sortcl->resdom = tlelt->resdom; - sortcl->opoid = any_ordering_op(tlelt->resdom->restype); - - sortlist = lappend(sortlist, sortcl); - } - } - - } - - return sortlist; -} - -/* - ** HandleNestedDots -- - ** Given a nested dot expression (i.e. (relation func ... attr), build up - ** a tree with of Iter and Func nodes. - */ -static Node * -handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno) -{ - List *mutator_iter; - Node *retval = NULL; - - if (attr->paramNo != NULL) - { - Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST); - - retval = - ParseFunc(pstate, strVal(lfirst(attr->attrs)), - lcons(param, NIL), - curr_resno); - } - else - { - Ident *ident = makeNode(Ident); - - ident->name = attr->relname; - ident->isRel = TRUE; - retval = - ParseFunc(pstate, strVal(lfirst(attr->attrs)), - lcons(ident, NIL), - curr_resno); - } - - foreach(mutator_iter, lnext(attr->attrs)) - { - retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)), - lcons(retval, NIL), - curr_resno); - } - - return (retval); -} - -/* - ** make_arguments -- - ** Given the number and types of arguments to a function, and the - ** actual arguments and argument types, do the necessary typecasting. - */ -static void -make_arguments(int nargs, - List *fargs, - Oid *input_typeids, - Oid *function_typeids) -{ - - /* - * there are two ways an input typeid can differ from a function - * typeid : either the input type inherits the function type, so no - * typecasting is necessary, or the input type can be typecast into - * the function type. right now, we only typecast unknowns, and that - * is all we check for. - */ - - List *current_fargs; - int i; - - for (i = 0, current_fargs = fargs; - i < nargs; - i++, current_fargs = lnext(current_fargs)) - { - - if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid) - { - lfirst(current_fargs) = - parser_typecast2(lfirst(current_fargs), - input_typeids[i], - get_id_type(function_typeids[i]), - -1); - } - } -} - -/* - ** setup_tlist -- - ** Build a tlist that says which attribute to project to. - ** This routine is called by ParseFunc() to set up a target list - ** on a tuple parameter or return value. Due to a bug in 4.0, - ** it's not possible to refer to system attributes in this case. - */ -static List * -setup_tlist(char *attname, Oid relid) -{ - TargetEntry *tle; - Resdom *resnode; - Var *varnode; - Oid typeid; - int attno; - - attno = get_attnum(relid, attname); - if (attno < 0) - elog(WARN, "cannot reference attribute '%s' of tuple params/return values for functions", attname); - - typeid = find_atttype(relid, attname); - resnode = makeResdom(1, - typeid, - tlen(get_id_type(typeid)), - get_attname(relid, attno), - 0, - (Oid) 0, - 0); - varnode = makeVar(-1, attno, typeid, -1, attno); - - tle = makeNode(TargetEntry); - tle->resdom = resnode; - tle->expr = (Node *) varnode; - return (lcons(tle, NIL)); -} - -/* - ** setup_base_tlist -- - ** Build a tlist that extracts a base type from the tuple - ** returned by the executor. - */ -static List * -setup_base_tlist(Oid typeid) -{ - TargetEntry *tle; - Resdom *resnode; - Var *varnode; - - resnode = makeResdom(1, - typeid, - tlen(get_id_type(typeid)), - "", - 0, - (Oid) 0, - 0); - varnode = makeVar(-1, 1, typeid, -1, 1); - tle = makeNode(TargetEntry); - tle->resdom = resnode; - tle->expr = (Node *) varnode; - - return (lcons(tle, NIL)); -} - -/* - * ParseComplexProjection - - * handles function calls with a single argument that is of complex type. - * This routine returns NULL if it can't handle the projection (eg. sets). - */ -static Node * -ParseComplexProjection(ParseState *pstate, - char *funcname, - Node *first_arg, - bool *attisset) -{ - Oid argtype; - Oid argrelid; - Name relname; - Relation rd; - Oid relid; - int attnum; - - switch (nodeTag(first_arg)) - { - case T_Iter: - { - Func *func; - Iter *iter; - - iter = (Iter *) first_arg; - func = (Func *) ((Expr *) iter->iterexpr)->oper; - argtype = funcid_get_rettype(func->funcid); - argrelid = typeid_get_relid(argtype); - if (argrelid && - ((attnum = get_attnum(argrelid, funcname)) - != InvalidAttrNumber)) - { - - /* - * the argument is a function returning a tuple, so - * funcname may be a projection - */ - - /* add a tlist to the func node and return the Iter */ - rd = heap_openr(tname(get_id_type(argtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd)) - { - func->func_tlist = - setup_tlist(funcname, argrelid); - iter->itertype = att_typeid(rd, attnum); - return ((Node *) iter); - } - else - { - elog(WARN, - "Function '%s' has bad returntype %d", - funcname, argtype); - } - } - else - { - /* drop through */ - ; - } - break; - } - case T_Var: - { - - /* - * The argument is a set, so this is either a projection - * or a function call on this set. - */ - *attisset = true; - break; - } - case T_Expr: - { - Expr *expr = (Expr *) first_arg; - Func *funcnode; - - if (expr->opType != FUNC_EXPR) - break; - - funcnode = (Func *) expr->oper; - argtype = funcid_get_rettype(funcnode->funcid); - argrelid = typeid_get_relid(argtype); - - /* - * the argument is a function returning a tuple, so - * funcname may be a projection - */ - if (argrelid && - (attnum = get_attnum(argrelid, funcname)) - != InvalidAttrNumber) - { - - /* add a tlist to the func node */ - rd = heap_openr(tname(get_id_type(argtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd)) - { - Expr *newexpr; - - funcnode->func_tlist = - setup_tlist(funcname, argrelid); - funcnode->functype = att_typeid(rd, attnum); - - newexpr = makeNode(Expr); - newexpr->typeOid = funcnode->functype; - newexpr->opType = FUNC_EXPR; - newexpr->oper = (Node *) funcnode; - newexpr->args = lcons(first_arg, NIL); - - return ((Node *) newexpr); - } - - } - - elog(WARN, "Function '%s' has bad returntype %d", - funcname, argtype); - break; - } - case T_Param: - { - Param *param = (Param *) first_arg; - - /* - * If the Param is a complex type, this could be a - * projection - */ - rd = heap_openr(tname(get_id_type(param->paramtype))); - if (RelationIsValid(rd)) - { - relid = RelationGetRelationId(rd); - relname = RelationGetRelationName(rd); - heap_close(rd); - } - if (RelationIsValid(rd) && - (attnum = get_attnum(relid, funcname)) - != InvalidAttrNumber) - { - - param->paramtype = att_typeid(rd, attnum); - param->param_tlist = setup_tlist(funcname, relid); - return ((Node *) param); - } - break; - } - default: - break; - } - - return NULL; -} - -static Node * -ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno) -{ - Oid rettype = (Oid) 0; - Oid argrelid = (Oid) 0; - Oid funcid = (Oid) 0; - List *i = NIL; - Node *first_arg = NULL; - char *relname = NULL; - char *refname = NULL; - Relation rd; - Oid relid; - int nargs; - Func *funcnode; - Oid oid_array[8]; - Oid *true_oid_array; - Node *retval; - bool retset; - bool exists; - bool attisset = false; - Oid toid = (Oid) 0; - Expr *expr; - - if (fargs) - { - first_arg = lfirst(fargs); - if (first_arg == NULL) - elog(WARN, "function '%s' does not allow NULL input", funcname); - } - - /* - * * check for projection methods: if function takes one argument, and * - * that argument is a relation, param, or PQ function returning a - * complex * type, then the function could be a projection. - */ - if (length(fargs) == 1) - { - - if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel) - { - RangeTblEntry *rte; - Ident *ident = (Ident *) first_arg; - - /* - * first arg is a relation. This could be a projection. - */ - refname = ident->name; - - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - if (rte == NULL) - rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); - - relname = rte->relname; - relid = rte->relid; - - /* - * If the attr isn't a set, just make a var for it. If it is - * a set, treat it like a function and drop through. - */ - if (get_attnum(relid, funcname) != InvalidAttrNumber) - { - Oid dummyTypeId; - - return - ((Node *) make_var(pstate, - refname, - funcname, - &dummyTypeId)); - } - else - { - /* drop through - attr is a set */ - ; - } - } - else if (ISCOMPLEX(exprType(first_arg))) - { - - /* - * Attempt to handle projection of a complex argument. If - * ParseComplexProjection can't handle the projection, we have - * to keep going. - */ - retval = ParseComplexProjection(pstate, - funcname, - first_arg, - &attisset); - if (attisset) - { - toid = exprType(first_arg); - rd = heap_openr(tname(get_id_type(toid))); - if (RelationIsValid(rd)) - { - relname = RelationGetRelationName(rd)->data; - heap_close(rd); - } - else - elog(WARN, - "Type '%s' is not a relation type", - tname(get_id_type(toid))); - argrelid = typeid_get_relid(toid); - - /* - * A projection contains either an attribute name or the - * "*". - */ - if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) - && strcmp(funcname, "*")) - { - elog(WARN, "Functions on sets are not yet supported"); - } - } - - if (retval) - return retval; - } - else - { - - /* - * Parsing aggregates. - */ - Oid basetype; - - /* - * the aggregate count is a special case, ignore its base - * type. Treat it as zero - */ - if (strcmp(funcname, "count") == 0) - basetype = 0; - else - basetype = exprType(lfirst(fargs)); - if (SearchSysCacheTuple(AGGNAME, - PointerGetDatum(funcname), - ObjectIdGetDatum(basetype), - 0, 0)) - { - Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs)); - - AddAggToParseState(pstate, aggreg); - return (Node *) aggreg; - } - } - } - - - /* - * * If we dropped through to here it's really a function (or a set, - * which * is implemented as a function.) * extract arg type info and - * transform relation name arguments into * varnodes of the - * appropriate form. - */ - MemSet(&oid_array[0], 0, 8 * sizeof(Oid)); - - nargs = 0; - foreach(i, fargs) - { - int vnum; - RangeTblEntry *rte; - Node *pair = lfirst(i); - - if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel) - { - - /* - * a relation - */ - refname = ((Ident *) pair)->name; - - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - if (rte == NULL) - rte = addRangeTableEntry(pstate, refname, refname, - FALSE, FALSE); - relname = rte->relname; - - vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname); - - /* - * for func(relname), the param to the function is the tuple - * under consideration. we build a special VarNode to reflect - * this -- it has varno set to the correct range table entry, - * but has varattno == 0 to signal that the whole tuple is the - * argument. - */ - toid = typeid(type(relname)); - /* replace it in the arg list */ - lfirst(fargs) = - makeVar(vnum, 0, toid, vnum, 0); - } - else if (!attisset) - { /* set functions don't have parameters */ - - /* - * any functiona args which are typed "unknown", but aren't - * constants, we don't know what to do with, because we can't - * cast them - jolly - */ - if (exprType(pair) == UNKNOWNOID && - !IsA(pair, Const)) - { - elog(WARN, "ParseFunc: no function named '%s' that takes in an unknown type as argument #%d", funcname, nargs); - } - else - toid = exprType(pair); - } - - oid_array[nargs++] = toid; - } - - /* - * func_get_detail looks up the function in the catalogs, does - * disambiguation for polymorphic functions, handles inheritance, and - * returns the funcid and type and set or singleton status of the - * function's return value. it also returns the true argument types - * to the function. if func_get_detail returns true, the function - * exists. otherwise, there was an error. - */ - if (attisset) - { /* we know all of these fields already */ - - /* - * We create a funcnode with a placeholder function SetEval. - * SetEval() never actually gets executed. When the function - * evaluation routines see it, they use the funcid projected out - * from the relation as the actual function to call. Example: - * retrieve (emp.mgr.name) The plan for this will scan the emp - * relation, projecting out the mgr attribute, which is a funcid. - * This function is then called (instead of SetEval) and "name" is - * projected from its result. - */ - funcid = SetEvalRegProcedure; - rettype = toid; - retset = true; - true_oid_array = oid_array; - exists = true; - } - else - { - exists = func_get_detail(funcname, nargs, oid_array, &funcid, - &rettype, &retset, &true_oid_array); - } - - if (!exists) - elog(WARN, "no such attribute or function '%s'", funcname); - - /* got it */ - funcnode = makeNode(Func); - funcnode->funcid = funcid; - funcnode->functype = rettype; - funcnode->funcisindex = false; - funcnode->funcsize = 0; - funcnode->func_fcache = NULL; - funcnode->func_tlist = NIL; - funcnode->func_planlist = NIL; - - /* perform the necessary typecasting */ - make_arguments(nargs, fargs, oid_array, true_oid_array); - - /* - * for functions returning base types, we want to project out the - * return value. set up a target list to do that. the executor will - * ignore these for c functions, and do the right thing for postquel - * functions. - */ - - if (typeid_get_relid(rettype) == InvalidOid) - funcnode->func_tlist = setup_base_tlist(rettype); - - /* - * For sets, we want to make a targetlist to project out this - * attribute of the set tuples. - */ - if (attisset) - { - if (!strcmp(funcname, "*")) - { - funcnode->func_tlist = - expandAll(pstate, relname, refname, curr_resno); - } - else - { - funcnode->func_tlist = setup_tlist(funcname, argrelid); - rettype = find_atttype(argrelid, funcname); - } - } - - /* - * Sequence handling. - */ - if (funcid == SeqNextValueRegProcedure || - funcid == SeqCurrValueRegProcedure) - { - Const *seq; - char *seqrel; - text *seqname; - int32 aclcheck_result = -1; - extern text *lower (text *string); - - Assert(length(fargs) == 1); - seq = (Const *) lfirst(fargs); - if (!IsA((Node *) seq, Const)) - elog(WARN, "%s: only constant sequence names are acceptable", funcname); - seqname = lower ((text*)DatumGetPointer(seq->constvalue)); - pfree (DatumGetPointer(seq->constvalue)); - seq->constvalue = PointerGetDatum (seqname); - seqrel = textout(seqname); - - if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(), - ((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD))) - != ACLCHECK_OK) - elog(WARN, "%s.%s: %s", - seqrel, funcname, aclcheck_error_strings[aclcheck_result]); - - pfree(seqrel); - - if (funcid == SeqNextValueRegProcedure && inWhereClause) - elog(WARN, "nextval of a sequence in WHERE disallowed"); - } - - expr = makeNode(Expr); - expr->typeOid = rettype; - expr->opType = FUNC_EXPR; - expr->oper = (Node *) funcnode; - expr->args = fargs; - retval = (Node *) expr; - - /* - * if the function returns a set of values, then we need to iterate - * over all the returned values in the executor, so we stick an iter - * node here. if it returns a singleton, then we don't need the iter - * node. - */ - - if (retset) - { - Iter *iter = makeNode(Iter); - - iter->itertype = rettype; - iter->iterexpr = retval; - retval = (Node *) iter; - } - - return (retval); -} - -/***************************************************************************** - * - *****************************************************************************/ - -/* - * AddAggToParseState - - * add the aggregate to the list of unique aggregates in pstate. - * - * SIDE EFFECT: aggno in target list entry will be modified - */ -static void -AddAggToParseState(ParseState *pstate, Aggreg *aggreg) -{ - List *ag; - int i; - - /* - * see if we have the aggregate already (we only need to record the - * aggregate once) - */ - i = 0; - foreach(ag, pstate->p_aggs) - { - Aggreg *a = lfirst(ag); - - if (!strcmp(a->aggname, aggreg->aggname) && - equal(a->target, aggreg->target)) - { - - /* fill in the aggno and we're done */ - aggreg->aggno = i; - return; - } - i++; - } - - /* not found, new aggregate */ - aggreg->aggno = i; - pstate->p_numAgg++; - pstate->p_aggs = lappend(pstate->p_aggs, aggreg); - return; -} - -/* - * finalizeAggregates - - * fill in qry_aggs from pstate. Also checks to make sure that aggregates - * are used in the proper place. - */ -static void -finalizeAggregates(ParseState *pstate, Query *qry) -{ - List *l; - int i; - - parseCheckAggregates(pstate, qry); - - qry->qry_numAgg = pstate->p_numAgg; - qry->qry_aggs = - (Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg); - i = 0; - foreach(l, pstate->p_aggs) - qry->qry_aggs[i++] = (Aggreg *) lfirst(l); -} - -/* - * contain_agg_clause-- - * Recursively find aggreg nodes from a clause. - * - * Returns true if any aggregate found. - */ -static bool -contain_agg_clause(Node *clause) -{ - if (clause == NULL) - return FALSE; - else if (IsA(clause, Aggreg)) - return TRUE; - else if (IsA(clause, Iter)) - return contain_agg_clause(((Iter *) clause)->iterexpr); - else if (single_node(clause)) - return FALSE; - else if (or_clause(clause)) - { - List *temp; - - foreach(temp, ((Expr *) clause)->args) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - return FALSE; - } - else if (is_funcclause(clause)) - { - List *temp; - - foreach(temp, ((Expr *) clause)->args) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - return FALSE; - } - else if (IsA(clause, ArrayRef)) - { - List *temp; - - foreach(temp, ((ArrayRef *) clause)->refupperindexpr) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) - if (contain_agg_clause(lfirst(temp))) - return TRUE; - if (contain_agg_clause(((ArrayRef *) clause)->refexpr)) - return TRUE; - if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr)) - return TRUE; - return FALSE; - } - else if (not_clause(clause)) - return contain_agg_clause((Node *) get_notclausearg((Expr *) clause)); - else if (is_opclause(clause)) - return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) || - contain_agg_clause((Node *) get_rightop((Expr *) clause))); - - return FALSE; -} - -/* - * exprIsAggOrGroupCol - - * returns true if the expression does not contain non-group columns. - */ -static bool -exprIsAggOrGroupCol(Node *expr, List *groupClause) -{ - List *gl; - - if (expr == NULL || IsA(expr, Const) || - IsA(expr, Param) ||IsA(expr, Aggreg)) - return TRUE; - - foreach(gl, groupClause) - { - GroupClause *grpcl = lfirst(gl); - - if (equal(expr, grpcl->entry->expr)) - return TRUE; - } - - if (IsA(expr, Expr)) - { - List *temp; - - foreach(temp, ((Expr *) expr)->args) - if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) - return FALSE; - return TRUE; - } - - return FALSE; -} - -/* - * tleIsAggOrGroupCol - - * returns true if the TargetEntry is Agg or GroupCol. - */ -static bool -tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause) -{ - Node *expr = tle->expr; - List *gl; - - if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param)) - return TRUE; - - foreach(gl, groupClause) - { - GroupClause *grpcl = lfirst(gl); - - if (tle->resdom->resno == grpcl->entry->resdom->resno) - { - if (contain_agg_clause((Node *) expr)) - elog(WARN, "parser: aggregates not allowed in GROUP BY clause"); - return TRUE; - } - } - - if (IsA(expr, Aggreg)) - return TRUE; - - if (IsA(expr, Expr)) - { - List *temp; - - foreach(temp, ((Expr *) expr)->args) - if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) - return FALSE; - return TRUE; - } - - return FALSE; -} - -/* - * parseCheckAggregates - - * this should really be done earlier but the current grammar - * cannot differentiate functions from aggregates. So we have do check - * here when the target list and the qualifications are finalized. - */ -static void -parseCheckAggregates(ParseState *pstate, Query *qry) -{ - List *tl; - - Assert(pstate->p_numAgg > 0); - - /* - * aggregates never appear in WHERE clauses. (we have to check where - * clause first because if there is an aggregate, the check for - * non-group column in target list may fail.) - */ - if (contain_agg_clause(qry->qual)) - elog(WARN, "parser: aggregates not allowed in WHERE clause"); - - /* - * the target list can only contain aggregates, group columns and - * functions thereof. - */ - foreach(tl, qry->targetList) - { - TargetEntry *tle = lfirst(tl); - - if (!tleIsAggOrGroupCol(tle, qry->groupClause)) - elog(WARN, - "parser: illegal use of aggregates or non-group column in target list"); - } - - /* - * the expression specified in the HAVING clause has the same - * restriction as those in the target list. - */ -/* - * Need to change here when we get HAVING works. Currently - * qry->havingQual is NULL. - vadim 04/05/97 - if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) - elog(WARN, - "parser: illegal use of aggregates or non-group column in HAVING clause"); - */ - return; -} - -/* not used -#define PSIZE(PTR) (*((int32 *)(PTR) - 1)) -*/ - -static Node * -parser_typecast(Value *expr, TypeName *typename, int typlen) -{ - /* check for passing non-ints */ - Const *adt; - Datum lcp; - Type tp; - char type_string[NAMEDATALEN]; - int32 len; - char *cp = NULL; - char *const_string = NULL; - bool string_palloced = false; - - switch (nodeTag(expr)) - { - case T_String: - const_string = DatumGetPointer(expr->val.str); - break; - case T_Integer: - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%ld", expr->val.ival); - break; - default: - elog(WARN, - "parser_typecast: cannot cast this expression to type \"%s\"", - typename->name); - } - - if (typename->arrayBounds != NIL) - { - sprintf(type_string, "_%s", typename->name); - tp = (Type) type(type_string); - } - else - { - tp = (Type) type(typename->name); - } - - len = tlen(tp); - -#if 0 /* fix me */ - switch (CInteger(lfirst(expr))) - { - case INT4OID: /* int4 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue); - break; - - case NAMEOID: /* char16 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue); - break; - - case CHAROID: /* char */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue); - break; - - case FLOAT8OID: /* float8 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue); - break; - - case CASHOID: /* money */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", - (int) ((Const *) expr)->constvalue); - break; - - case TEXTOID: /* text */ - const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - - case UNKNOWNOID: /* unknown */ - const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - - default: - elog(WARN, "unknown type %d", CInteger(lfirst(expr))); - } -#endif - - cp = instr2(tp, const_string, typlen); - - if (!tbyvalue(tp)) - { -/* - if (len >= 0 && len != PSIZE(cp)) { - char *pp; - pp = (char *) palloc(len); - memmove(pp, cp, len); - cp = pp; - } -*/ - lcp = PointerGetDatum(cp); - } - else - { - switch (len) - { - case 1: - lcp = Int8GetDatum(cp); - break; - case 2: - lcp = Int16GetDatum(cp); - break; - case 4: - lcp = Int32GetDatum(cp); - break; - default: - lcp = PointerGetDatum(cp); - break; - } - } - - adt = makeConst(typeid(tp), - len, - (Datum) lcp, - false, - tbyvalue(tp), - false, /* not a set */ - true /* is cast */ ); - - if (string_palloced) - pfree(const_string); - - return (Node *) adt; -} - -static Node * -parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen) -{ - /* check for passing non-ints */ - Const *adt; - Datum lcp; - int32 len = tlen(tp); - char *cp = NULL; - - char *const_string = NULL; - bool string_palloced = false; - - Assert(IsA(expr, Const)); - - switch (exprType) - { - case 0: /* NULL */ - break; - case INT4OID: /* int4 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%d", - (int) ((Const *) expr)->constvalue); - break; - case NAMEOID: /* char16 */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%s", - (char *) ((Const *) expr)->constvalue); - break; - case CHAROID: /* char */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%c", - (char) ((Const *) expr)->constvalue); - break; - case FLOAT4OID: /* float4 */ - { - float32 floatVal = - DatumGetFloat32(((Const *) expr)->constvalue); - - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", *floatVal); - break; - } - case FLOAT8OID: /* float8 */ - { - float64 floatVal = - DatumGetFloat64(((Const *) expr)->constvalue); - - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%f", *floatVal); - break; - } - case CASHOID: /* money */ - const_string = (char *) palloc(256); - string_palloced = true; - sprintf(const_string, "%ld", - (long) ((Const *) expr)->constvalue); - break; - case TEXTOID: /* text */ - const_string = - DatumGetPointer(((Const *) expr)->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - case UNKNOWNOID: /* unknown */ - const_string = - DatumGetPointer(((Const *) expr)->constvalue); - const_string = (char *) textout((struct varlena *) const_string); - break; - default: - elog(WARN, "unknown type %u ", exprType); - } - - if (!exprType) - { - adt = makeConst(typeid(tp), - (Size) 0, - (Datum) NULL, - true, /* isnull */ - false, /* was omitted */ - false, /* not a set */ - true /* is cast */ ); - return ((Node *) adt); - } - - cp = instr2(tp, const_string, typlen); - - - if (!tbyvalue(tp)) - { -/* - if (len >= 0 && len != PSIZE(cp)) { - char *pp; - pp = (char *) palloc(len); - memmove(pp, cp, len); - cp = pp; - } -*/ - lcp = PointerGetDatum(cp); - } - else - { - switch (len) - { - case 1: - lcp = Int8GetDatum(cp); - break; - case 2: - lcp = Int16GetDatum(cp); - break; - case 4: - lcp = Int32GetDatum(cp); - break; - default: - lcp = PointerGetDatum(cp); - break; - } - } - - adt = makeConst(typeid(tp), - (Size) len, - (Datum) lcp, - false, - false, /* was omitted */ - false, /* not a set */ - true /* is cast */ ); - - /* - * printf("adt %s : %u %d %d\n",CString(expr),typeid(tp) , len,cp); - */ - if (string_palloced) - pfree(const_string); - - return ((Node *) adt); -} - -static Aggreg * -ParseAgg(char *aggname, Oid basetype, Node *target) -{ - Oid fintype; - Oid vartype; - Oid xfn1; - Form_pg_aggregate aggform; - Aggreg *aggreg; - HeapTuple theAggTuple; - - theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname), - ObjectIdGetDatum(basetype), - 0, 0); - if (!HeapTupleIsValid(theAggTuple)) - { - elog(WARN, "aggregate %s does not exist", aggname); - } - - aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple); - fintype = aggform->aggfinaltype; - xfn1 = aggform->aggtransfn1; - - if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr) - elog(WARN, "parser: aggregate can only be applied on an attribute or expression"); - - /* only aggregates with transfn1 need a base type */ - if (OidIsValid(xfn1)) - { - basetype = aggform->aggbasetype; - if (nodeTag(target) == T_Var) - vartype = ((Var *) target)->vartype; - else - vartype = ((Expr *) target)->typeOid; - - if (basetype != vartype) - { - Type tp1, - tp2; - - tp1 = get_id_type(basetype); - tp2 = get_id_type(vartype); - elog(NOTICE, "Aggregate type mismatch:"); - elog(WARN, "%s works on %s, not %s", aggname, - tname(tp1), tname(tp2)); - } - } - - aggreg = makeNode(Aggreg); - aggreg->aggname = pstrdup(aggname); - aggreg->basetype = aggform->aggbasetype; - aggreg->aggtype = fintype; - - aggreg->target = target; - - return aggreg; -} diff --git a/src/backend/parser/catalog_utils.c b/src/backend/parser/catalog_utils.c deleted file mode 100644 index b119c7dbb5..0000000000 --- a/src/backend/parser/catalog_utils.c +++ /dev/null @@ -1,1686 +0,0 @@ -/*------------------------------------------------------------------------- - * - * catalog_utils.c-- - * - * Copyright (c) 1994, Regents of the University of California - * - * - * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/Attic/catalog_utils.c,v 1.30 1997/11/20 23:22:14 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#include -#include "postgres.h" - -#include "lib/dllist.h" -#include "utils/datum.h" - -#include "utils/builtins.h" -#include "utils/elog.h" -#include "utils/palloc.h" -#include "fmgr.h" - -#include "nodes/pg_list.h" -#include "nodes/parsenodes.h" -#include "utils/syscache.h" -#include "catalog/catname.h" - -#include "parser/catalog_utils.h" -#include "catalog/pg_inherits.h" -#include "catalog/pg_operator.h" -#include "catalog/pg_type.h" -#include "catalog/pg_proc.h" -#include "catalog/indexing.h" -#include "catalog/catname.h" - -#include "access/skey.h" -#include "access/relscan.h" -#include "access/tupdesc.h" -#include "access/htup.h" -#include "access/heapam.h" -#include "access/genam.h" -#include "access/itup.h" -#include "access/tupmacs.h" - -#include "storage/buf.h" -#include "storage/bufmgr.h" -#include "utils/lsyscache.h" -#include "storage/lmgr.h" - -#include "port-protos.h" /* strdup() */ - -struct -{ - char *field; - int code; -} special_attr[] = - -{ - { - "ctid", SelfItemPointerAttributeNumber - }, - { - "oid", ObjectIdAttributeNumber - }, - { - "xmin", MinTransactionIdAttributeNumber - }, - { - "cmin", MinCommandIdAttributeNumber - }, - { - "xmax", MaxTransactionIdAttributeNumber - }, - { - "cmax", MaxCommandIdAttributeNumber - }, -}; - -#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr)) - -static char *attnum_type[SPECIALS] = { - "tid", - "oid", - "xid", - "cid", - "xid", - "cid", -}; - -#define MAXFARGS 8 /* max # args to a c or postquel function */ - -/* - * This structure is used to explore the inheritance hierarchy above - * nodes in the type tree in order to disambiguate among polymorphic - * functions. - */ - -typedef struct _InhPaths -{ - int nsupers; /* number of superclasses */ - Oid self; /* this class */ - Oid *supervec; /* vector of superclasses */ -} InhPaths; - -/* - * This structure holds a list of possible functions or operators that - * agree with the known name and argument types of the function/operator. - */ -typedef struct _CandidateList -{ - Oid *args; - struct _CandidateList *next; -} *CandidateList; - -static Oid **argtype_inherit(int nargs, Oid *oid_array); -static Oid **genxprod(InhPaths *arginh, int nargs); -static int findsupers(Oid relid, Oid **supervec); -static bool check_typeid(Oid id); -static char *instr1(TypeTupleForm tp, char *string, int typlen); -static void op_error(char *op, Oid arg1, Oid arg2); - -/* check to see if a type id is valid, - * returns true if it is. By using this call before calling - * get_id_type or get_id_typname, more meaningful error messages - * can be produced because the caller typically has more context of - * what's going on - jolly - */ -static bool -check_typeid(Oid id) -{ - return (SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(id), - 0, 0, 0) != NULL); -} - - -/* return a Type structure, given an typid */ -Type -get_id_type(Oid id) -{ - HeapTuple tup; - - if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id), - 0, 0, 0))) - { - elog(WARN, "type id lookup of %ud failed", id); - return (NULL); - } - return ((Type) tup); -} - -/* return a type name, given a typeid */ -char * -get_id_typname(Oid id) -{ - HeapTuple tup; - TypeTupleForm typetuple; - - if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id), - 0, 0, 0))) - { - elog(WARN, "type id lookup of %ud failed", id); - return (NULL); - } - typetuple = (TypeTupleForm) GETSTRUCT(tup); - return (typetuple->typname).data; -} - -/* return a Type structure, given type name */ -Type -type(char *s) -{ - HeapTuple tup; - - if (s == NULL) - { - elog(WARN, "type(): Null type"); - } - - if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0, 0, 0))) - { - elog(WARN, "type name lookup of %s failed", s); - } - return ((Type) tup); -} - -/* given attribute id, return type of that attribute */ -/* XXX Special case for pseudo-attributes is a hack */ -Oid -att_typeid(Relation rd, int attid) -{ - - if (attid < 0) - { - return (typeid(type(attnum_type[-attid - 1]))); - } - - /* - * -1 because varattno (where attid comes from) returns one more than - * index - */ - return (rd->rd_att->attrs[attid - 1]->atttypid); -} - - -int -att_attnelems(Relation rd, int attid) -{ - return (rd->rd_att->attrs[attid - 1]->attnelems); -} - -/* given type, return the type OID */ -Oid -typeid(Type tp) -{ - if (tp == NULL) - { - elog(WARN, "typeid() called with NULL type struct"); - } - return (tp->t_oid); -} - -/* given type (as type struct), return the length of type */ -int16 -tlen(Type t) -{ - TypeTupleForm typ; - - typ = (TypeTupleForm) GETSTRUCT(t); - return (typ->typlen); -} - -/* given type (as type struct), return the value of its 'byval' attribute.*/ -bool -tbyval(Type t) -{ - TypeTupleForm typ; - - typ = (TypeTupleForm) GETSTRUCT(t); - return (typ->typbyval); -} - -/* given type (as type struct), return the name of type */ -char * -tname(Type t) -{ - TypeTupleForm typ; - - typ = (TypeTupleForm) GETSTRUCT(t); - return (typ->typname).data; -} - -/* given type (as type struct), return wether type is passed by value */ -int -tbyvalue(Type t) -{ - TypeTupleForm typ; - - typ = (TypeTupleForm) GETSTRUCT(t); - return (typ->typbyval); -} - -/* given a type, return its typetype ('c' for 'c'atalog types) */ -static char -typetypetype(Type t) -{ - TypeTupleForm typ; - - typ = (TypeTupleForm) GETSTRUCT(t); - return (typ->typtype); -} - -/* given operator, return the operator OID */ -Oid -oprid(Operator op) -{ - return (op->t_oid); -} - -/* - * 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 -binary_oper_get_candidates(char *opname, - Oid leftTypeId, - Oid rightTypeId, - 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]; - - *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 (leftTypeId == UNKNOWNOID) - { - if (rightTypeId == UNKNOWNOID) - { - nkeys = 2; - } - else - { - nkeys = 3; - - ScanKeyEntryInitialize(&opKey[2], 0, - Anum_pg_operator_oprright, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(rightTypeId)); - } - } - else if (rightTypeId == UNKNOWNOID) - { - nkeys = 3; - - ScanKeyEntryInitialize(&opKey[2], 0, - Anum_pg_operator_oprleft, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(leftTypeId)); - } - else - { - /* currently only "unknown" can be coerced */ - return 0; - } - - 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; -} - -/* - * equivalentOpersAfterPromotion - - * checks if a list of candidate operators obtained from - * binary_oper_get_candidates() contain equivalent operators. If - * this routine is called, we have more than 1 candidate and need to - * decided whether to pick one of them. This routine returns true if - * the all the candidates operate on the same data types after - * promotion (int2, int4, float4 -> float8). - */ -static bool -equivalentOpersAfterPromotion(CandidateList candidates) -{ - CandidateList result; - CandidateList promotedCandidates = NULL; - Oid leftarg, - rightarg; - - for (result = candidates; result != NULL; result = result->next) - { - CandidateList c; - - c = (CandidateList) palloc(sizeof(*c)); - c->args = (Oid *) palloc(2 * sizeof(Oid)); - switch (result->args[0]) - { - case FLOAT4OID: - case INT4OID: - case INT2OID: - case CASHOID: - c->args[0] = FLOAT8OID; - break; - default: - c->args[0] = result->args[0]; - break; - } - switch (result->args[1]) - { - case FLOAT4OID: - case INT4OID: - case INT2OID: - case CASHOID: - c->args[1] = FLOAT8OID; - break; - default: - c->args[1] = result->args[1]; - break; - } - c->next = promotedCandidates; - promotedCandidates = c; - } - - /* - * if we get called, we have more than 1 candidates so we can do the - * following safely - */ - leftarg = promotedCandidates->args[0]; - rightarg = promotedCandidates->args[1]; - - for (result = promotedCandidates->next; result != NULL; result = result->next) - { - if (result->args[0] != leftarg || result->args[1] != rightarg) - - /* - * this list contains operators that operate on different data - * types even after promotion. Hence we can't decide on which - * one to pick. The user must do explicit type casting. - */ - return FALSE; - } - - /* - * all the candidates are equivalent in the following sense: they - * operate on equivalent data types and picking any one of them is as - * good. - */ - return TRUE; -} - - -/* - * given a choice of argument type pairs for a binary operator, - * try to choose a default pair - */ -static CandidateList -binary_oper_select_candidate(Oid arg1, - Oid arg2, - CandidateList candidates) -{ - CandidateList result; - - /* - * if both are "unknown", there is no way to select a candidate - * - * current wisdom holds that the default operator should be one in which - * both operands have the same type (there will only be one such - * operator) - * - * 7.27.93 - I have decided not to do this; it's too hard to justify, and - * it's easy enough to typecast explicitly -avi [the rest of this - * routine were commented out since then -ay] - */ - - if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) - return (NULL); - - /* - * 6/23/95 - I don't complete agree with avi. In particular, casting - * floats is a pain for users. Whatever the rationale behind not doing - * this is, I need the following special case to work. - * - * In the WHERE clause of a query, if a float is specified without - * quotes, we treat it as float8. I added the float48* operators so - * that we can operate on float4 and float8. But now we have more than - * one matching operator if the right arg is unknown (eg. float - * specified with quotes). This break some stuff in the regression - * test where there are floats in quotes not properly casted. Below is - * the solution. In addition to requiring the operator operates on the - * same type for both operands [as in the code Avi originally - * commented out], we also require that the operators be equivalent in - * some sense. (see equivalentOpersAfterPromotion for details.) - ay - * 6/95 - */ - if (!equivalentOpersAfterPromotion(candidates)) - return NULL; - - /* - * if we get here, any one will do but we're more picky and require - * both operands be the same. - */ - for (result = candidates; result != NULL; result = result->next) - { - if (result->args[0] == result->args[1]) - return result; - } - - return (NULL); -} - -/* Given operator, types of arg1, and arg2, return oper struct */ -/* arg1, arg2 --typeids */ -Operator -oper(char *op, Oid arg1, Oid arg2, bool noWarnings) -{ - HeapTuple tup; - CandidateList candidates; - int ncandidates; - - if (!arg2) - arg2 = arg1; - if (!arg1) - arg1 = arg2; - - if (!(tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(arg1), - ObjectIdGetDatum(arg2), - Int8GetDatum('b')))) - { - ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); - if (ncandidates == 0) - { - - /* - * no operators of the desired types found - */ - if (!noWarnings) - op_error(op, arg1, arg2); - return (NULL); - } - else if (ncandidates == 1) - { - - /* - * exactly one operator of the desired types found - */ - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(candidates->args[0]), - ObjectIdGetDatum(candidates->args[1]), - Int8GetDatum('b')); - Assert(HeapTupleIsValid(tup)); - } - else - { - - /* - * multiple operators of the desired types found - */ - candidates = binary_oper_select_candidate(arg1, arg2, candidates); - if (candidates != NULL) - { - /* we chose one of them */ - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(candidates->args[0]), - ObjectIdGetDatum(candidates->args[1]), - Int8GetDatum('b')); - Assert(HeapTupleIsValid(tup)); - } - else - { - Type tp1, - tp2; - - /* we chose none of them */ - tp1 = get_id_type(arg1); - tp2 = get_id_type(arg2); - if (!noWarnings) - { - elog(NOTICE, "there is more than one operator %s for types", op); - elog(NOTICE, "%s and %s. You will have to retype this query", - tname(tp1), tname(tp2)); - elog(WARN, "using an explicit cast"); - } - return (NULL); - } - } - } - return ((Operator) tup); -} - -/* - * given opname and typeId, find all possible types for which - * a right/left unary operator named opname exists, - * such that typeId can be coerced to it - */ -static int -unary_oper_get_candidates(char *op, - Oid typeId, - CandidateList *candidates, - char rightleft) -{ - CandidateList current_candidate; - Relation pg_operator_desc; - HeapScanDesc pg_operator_scan; - HeapTuple tup; - OperatorTupleForm oper; - Buffer buffer; - int ncandidates = 0; - - static ScanKeyData opKey[2] = { - {0, Anum_pg_operator_oprname, NameEqualRegProcedure}, - {0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure}}; - - *candidates = NULL; - - fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func, - &opKey[0].sk_nargs); - opKey[0].sk_argument = NameGetDatum(op); - fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func, - &opKey[1].sk_nargs); - opKey[1].sk_argument = CharGetDatum(rightleft); - - /* currently, only "unknown" can be coerced */ - - /* - * but we should allow types that are internally the same to be - * "coerced" - */ - if (typeId != UNKNOWNOID) - { - return 0; - } - - pg_operator_desc = heap_openr(OperatorRelationName); - pg_operator_scan = heap_beginscan(pg_operator_desc, - 0, - true, - 2, - 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(sizeof(Oid)); - - oper = (OperatorTupleForm) GETSTRUCT(tup); - if (rightleft == 'r') - current_candidate->args[0] = oper->oprleft; - else - current_candidate->args[0] = 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; -} - -/* Given unary right-side operator (operator on right), return oper struct */ -/* arg-- type id */ -Operator -right_oper(char *op, Oid arg) -{ - HeapTuple tup; - CandidateList candidates; - int ncandidates; - - /* - * if (!OpCache) { init_op_cache(); } - */ - if (!(tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(arg), - ObjectIdGetDatum(InvalidOid), - Int8GetDatum('r')))) - { - ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r'); - if (ncandidates == 0) - { - elog(WARN, - "Can't find right op: %s for type %d", op, arg); - return (NULL); - } - else if (ncandidates == 1) - { - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(candidates->args[0]), - ObjectIdGetDatum(InvalidOid), - Int8GetDatum('r')); - Assert(HeapTupleIsValid(tup)); - } - else - { - elog(NOTICE, "there is more than one right operator %s", op); - elog(NOTICE, "you will have to retype this query"); - elog(WARN, "using an explicit cast"); - return (NULL); - } - } - return ((Operator) tup); -} - -/* Given unary left-side operator (operator on left), return oper struct */ -/* arg--type id */ -Operator -left_oper(char *op, Oid arg) -{ - HeapTuple tup; - CandidateList candidates; - int ncandidates; - - /* - * if (!OpCache) { init_op_cache(); } - */ - if (!(tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(arg), - Int8GetDatum('l')))) - { - ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l'); - if (ncandidates == 0) - { - elog(WARN, - "Can't find left op: %s for type %d", op, arg); - return (NULL); - } - else if (ncandidates == 1) - { - tup = SearchSysCacheTuple(OPRNAME, - PointerGetDatum(op), - ObjectIdGetDatum(InvalidOid), - ObjectIdGetDatum(candidates->args[0]), - Int8GetDatum('l')); - Assert(HeapTupleIsValid(tup)); - } - else - { - elog(NOTICE, "there is more than one left operator %s", op); - elog(NOTICE, "you will have to retype this query"); - elog(WARN, "using an explicit cast"); - return (NULL); - } - } - return ((Operator) tup); -} - -/* given range variable, return id of variable */ - -int -varattno(Relation rd, char *a) -{ - int i; - - for (i = 0; i < rd->rd_rel->relnatts; i++) - { - if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) - { - return (i + 1); - } - } - for (i = 0; i < SPECIALS; i++) - { - if (!strcmp(special_attr[i].field, a)) - { - return (special_attr[i].code); - } - } - - elog(WARN, "Relation %s does not have attribute %s", - RelationGetRelationName(rd), a); - return (-1); -} - -/* Given range variable, return whether attribute of this name - * is a set. - * NOTE the ASSUMPTION here that no system attributes are, or ever - * will be, sets. - */ -bool -varisset(Relation rd, char *name) -{ - int i; - - /* First check if this is a system attribute */ - for (i = 0; i < SPECIALS; i++) - { - if (!strcmp(special_attr[i].field, name)) - { - return (false); /* no sys attr is a set */ - } - } - return (get_attisset(rd->rd_id, name)); -} - -/* given range variable, return id of variable */ -int -nf_varattno(Relation rd, char *a) -{ - int i; - - for (i = 0; i < rd->rd_rel->relnatts; i++) - { - if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) - { - return (i + 1); - } - } - for (i = 0; i < SPECIALS; i++) - { - if (!strcmp(special_attr[i].field, a)) - { - return (special_attr[i].code); - } - } - return InvalidAttrNumber; -} - -/*------------- - * given an attribute number and a relation, return its relation name - */ -char * -getAttrName(Relation rd, int attrno) -{ - char *name; - int i; - - if (attrno < 0) - { - for (i = 0; i < SPECIALS; i++) - { - if (special_attr[i].code == attrno) - { - name = special_attr[i].field; - return (name); - } - } - elog(WARN, "Illegal attr no %d for relation %s", - attrno, RelationGetRelationName(rd)); - } - else if (attrno >= 1 && attrno <= RelationGetNumberOfAttributes(rd)) - { - name = (rd->rd_att->attrs[attrno - 1]->attname).data; - return (name); - } - else - { - elog(WARN, "Illegal attr no %d for relation %s", - attrno, RelationGetRelationName(rd)); - } - - /* - * Shouldn't get here, but we want lint to be happy... - */ - - return (NULL); -} - -/* Given a typename and value, returns the ascii form of the value */ - -#ifdef NOT_USED -char * -outstr(char *typename, /* Name of type of value */ - char *value) /* Could be of any type */ -{ - TypeTupleForm tp; - Oid op; - - tp = (TypeTupleForm) GETSTRUCT(type(typename)); - op = tp->typoutput; - return ((char *) fmgr(op, value)); -} - -#endif - -/* Given a Type and a string, return the internal form of that string */ -char * -instr2(Type tp, char *string, int typlen) -{ - return (instr1((TypeTupleForm) GETSTRUCT(tp), string, typlen)); -} - -/* Given a type structure and a string, returns the internal form of - that string */ -static char * -instr1(TypeTupleForm tp, char *string, int typlen) -{ - Oid op; - Oid typelem; - - op = tp->typinput; - typelem = tp->typelem; /* XXX - used for array_in */ - /* typlen is for bpcharin() and varcharin() */ - return ((char *) fmgr(op, string, typelem, typlen)); -} - -/* Given the attribute type of an array return the arrtribute type of - an element of the array */ - -Oid -GetArrayElementType(Oid typearray) -{ - HeapTuple type_tuple; - TypeTupleForm type_struct_array; - - type_tuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(typearray), - 0, 0, 0); - - if (!HeapTupleIsValid(type_tuple)) - elog(WARN, "GetArrayElementType: Cache lookup failed for type %d", - typearray); - - /* get the array type struct from the type tuple */ - type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple); - - if (type_struct_array->typelem == InvalidOid) - { - elog(WARN, "GetArrayElementType: type %s is not an array", - (Name) &(type_struct_array->typname.data[0])); - } - - return (type_struct_array->typelem); -} - -Oid -funcid_get_rettype(Oid funcid) -{ - HeapTuple func_tuple = NULL; - Oid funcrettype = (Oid) 0; - - func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), - 0, 0, 0); - - if (!HeapTupleIsValid(func_tuple)) - elog(WARN, "function %d does not exist", funcid); - - funcrettype = (Oid) - ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype; - - return (funcrettype); -} - -/* - * get a list of all argument type vectors for which a function named - * funcname taking nargs arguments exists - */ -static CandidateList -func_get_candidates(char *funcname, int nargs) -{ - Relation heapRelation; - Relation idesc; - ScanKeyData skey; - HeapTuple tuple; - IndexScanDesc sd; - RetrieveIndexResult indexRes; - Buffer buffer; - Form_pg_proc pgProcP; - bool bufferUsed = FALSE; - CandidateList candidates = NULL; - CandidateList current_candidate; - int i; - - heapRelation = heap_openr(ProcedureRelationName); - ScanKeyEntryInitialize(&skey, - (bits16) 0x0, - (AttrNumber) 1, - (RegProcedure) NameEqualRegProcedure, - (Datum) funcname); - - idesc = index_openr(ProcedureNameIndex); - - sd = index_beginscan(idesc, false, 1, &skey); - - do - { - tuple = (HeapTuple) NULL; - if (bufferUsed) - { - ReleaseBuffer(buffer); - bufferUsed = FALSE; - } - - indexRes = index_getnext(sd, ForwardScanDirection); - if (indexRes) - { - ItemPointer iptr; - - iptr = &indexRes->heap_iptr; - tuple = heap_fetch(heapRelation, false, iptr, &buffer); - pfree(indexRes); - if (HeapTupleIsValid(tuple)) - { - pgProcP = (Form_pg_proc) GETSTRUCT(tuple); - bufferUsed = TRUE; - if (pgProcP->pronargs == nargs) - { - current_candidate = (CandidateList) - palloc(sizeof(struct _CandidateList)); - current_candidate->args = (Oid *) - palloc(8 * sizeof(Oid)); - MemSet(current_candidate->args, 0, 8 * sizeof(Oid)); - for (i = 0; i < nargs; i++) - { - current_candidate->args[i] = - pgProcP->proargtypes[i]; - } - - current_candidate->next = candidates; - candidates = current_candidate; - } - } - } - } while (indexRes); - - index_endscan(sd); - index_close(idesc); - heap_close(heapRelation); - - return candidates; -} - -/* - * can input_typeids be coerced to func_typeids? - */ -static bool -can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids) -{ - int i; - Type tp; - - /* - * right now, we only coerce "unknown", and we cannot coerce it to a - * relation type - */ - for (i = 0; i < nargs; i++) - { - if (input_typeids[i] != func_typeids[i]) - { - if ((input_typeids[i] == BPCHAROID && func_typeids[i] == TEXTOID) || - (input_typeids[i] == BPCHAROID && func_typeids[i] == VARCHAROID) || - (input_typeids[i] == VARCHAROID && func_typeids[i] == TEXTOID) || - (input_typeids[i] == VARCHAROID && func_typeids[i] == BPCHAROID) || - (input_typeids[i] == CASHOID && func_typeids[i] == INT4OID) || - (input_typeids[i] == INT4OID && func_typeids[i] == CASHOID)) - ; /* these are OK */ - else if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0) - return false; - - tp = get_id_type(input_typeids[i]); - if (typetypetype(tp) == 'c') - return false; - } - } - - return true; -} - -/* - * given a list of possible typeid arrays to a function and an array of - * input typeids, produce a shortlist of those function typeid arrays - * that match the input typeids (either exactly or by coercion), and - * return the number of such arrays - */ -static int -match_argtypes(int nargs, - Oid *input_typeids, - CandidateList function_typeids, - CandidateList *candidates) /* return value */ -{ - CandidateList current_candidate; - CandidateList matching_candidate; - Oid *current_typeids; - int ncandidates = 0; - - *candidates = NULL; - - for (current_candidate = function_typeids; - current_candidate != NULL; - current_candidate = current_candidate->next) - { - current_typeids = current_candidate->args; - if (can_coerce(nargs, input_typeids, current_typeids)) - { - matching_candidate = (CandidateList) - palloc(sizeof(struct _CandidateList)); - matching_candidate->args = current_typeids; - matching_candidate->next = *candidates; - *candidates = matching_candidate; - ncandidates++; - } - } - - return ncandidates; -} - -/* - * given the input argtype array and more than one candidate - * for the function argtype array, attempt to resolve the conflict. - * returns the selected argtype array if the conflict can be resolved, - * otherwise returns NULL - */ -static Oid * -func_select_candidate(int nargs, - Oid *input_typeids, - CandidateList candidates) -{ - /* XXX no conflict resolution implemeneted yet */ - return (NULL); -} - -bool -func_get_detail(char *funcname, - int nargs, - Oid *oid_array, - Oid *funcid, /* return value */ - Oid *rettype, /* return value */ - bool *retset, /* return value */ - Oid **true_typeids) /* return value */ -{ - Oid **input_typeid_vector; - Oid *current_input_typeids; - CandidateList function_typeids; - CandidateList current_function_typeids; - HeapTuple ftup; - Form_pg_proc pform; - - /* - * attempt to find named function in the system catalogs with - * arguments exactly as specified - so that the normal case is just as - * quick as before - */ - ftup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(funcname), - Int32GetDatum(nargs), - PointerGetDatum(oid_array), - 0); - *true_typeids = oid_array; - - /* - * If an exact match isn't found : 1) get a vector of all possible - * input arg type arrays constructed from the superclasses of the - * original input arg types 2) get a list of all possible argument - * type arrays to the function with given name and number of arguments - * 3) for each input arg type array from vector #1 : a) find how many - * of the function arg type arrays from list #2 it can be coerced to - * b) - if the answer is one, we have our function - if the answer is - * more than one, attempt to resolve the conflict - if the answer is - * zero, try the next array from vector #1 - */ - if (!HeapTupleIsValid(ftup)) - { - function_typeids = func_get_candidates(funcname, nargs); - - if (function_typeids != NULL) - { - int ncandidates = 0; - - input_typeid_vector = argtype_inherit(nargs, oid_array); - current_input_typeids = oid_array; - - do - { - ncandidates = match_argtypes(nargs, current_input_typeids, - function_typeids, - ¤t_function_typeids); - if (ncandidates == 1) - { - *true_typeids = current_function_typeids->args; - ftup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(funcname), - Int32GetDatum(nargs), - PointerGetDatum(*true_typeids), - 0); - Assert(HeapTupleIsValid(ftup)); - } - else if (ncandidates > 1) - { - *true_typeids = - func_select_candidate(nargs, - current_input_typeids, - current_function_typeids); - if (*true_typeids == NULL) - { - elog(NOTICE, "there is more than one function named \"%s\"", - funcname); - elog(NOTICE, "that satisfies the given argument types. you will have to"); - elog(NOTICE, "retype your query using explicit typecasts."); - func_error("func_get_detail", funcname, nargs, oid_array); - } - else - { - ftup = SearchSysCacheTuple(PRONAME, - PointerGetDatum(funcname), - Int32GetDatum(nargs), - PointerGetDatum(*true_typeids), - 0); - Assert(HeapTupleIsValid(ftup)); - } - } - current_input_typeids = *input_typeid_vector++; - } - while (current_input_typeids != - InvalidOid && ncandidates == 0); - } - } - - if (!HeapTupleIsValid(ftup)) - { - Type tp; - - if (nargs == 1) - { - tp = get_id_type(oid_array[0]); - if (typetypetype(tp) == 'c') - elog(WARN, "no such attribute or function \"%s\"", - funcname); - } - func_error("func_get_detail", funcname, nargs, oid_array); - } - else - { - pform = (Form_pg_proc) GETSTRUCT(ftup); - *funcid = ftup->t_oid; - *rettype = pform->prorettype; - *retset = pform->proretset; - - return (true); - } -/* shouldn't reach here */ - return (false); - -} - -/* - * argtype_inherit() -- Construct an argtype vector reflecting the - * inheritance properties of the supplied argv. - * - * This function is used to disambiguate among functions with the - * same name but different signatures. It takes an array of eight - * type ids. For each type id in the array that's a complex type - * (a class), it walks up the inheritance tree, finding all - * superclasses of that type. A vector of new Oid type arrays - * is returned to the caller, reflecting the structure of the - * inheritance tree above the supplied arguments. - * - * The order of this vector is as follows: all superclasses of the - * rightmost complex class are explored first. The exploration - * continues from right to left. This policy means that we favor - * keeping the leftmost argument type as low in the inheritance tree - * as possible. This is intentional; it is exactly what we need to - * do for method dispatch. The last type array we return is all - * zeroes. This will match any functions for which return types are - * not defined. There are lots of these (mostly builtins) in the - * catalogs. - */ -static Oid ** -argtype_inherit(int nargs, Oid *oid_array) -{ - Oid relid; - int i; - InhPaths arginh[MAXFARGS]; - - for (i = 0; i < MAXFARGS; i++) - { - if (i < nargs) - { - arginh[i].self = oid_array[i]; - if ((relid = typeid_get_relid(oid_array[i])) != InvalidOid) - { - arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec)); - } - else - { - arginh[i].nsupers = 0; - arginh[i].supervec = (Oid *) NULL; - } - } - else - { - arginh[i].self = InvalidOid; - arginh[i].nsupers = 0; - arginh[i].supervec = (Oid *) NULL; - } - } - - /* return an ordered cross-product of the classes involved */ - return (genxprod(arginh, nargs)); -} - -typedef struct _SuperQE -{ - Oid sqe_relid; -} SuperQE; - -static int -findsupers(Oid relid, Oid **supervec) -{ - Oid *relidvec; - Relation inhrel; - HeapScanDesc inhscan; - ScanKeyData skey; - HeapTuple inhtup; - TupleDesc inhtupdesc; - int nvisited; - SuperQE *qentry, - *vnode; - Dllist *visited, - *queue; - Dlelem *qe, - *elt; - - Relation rd; - Buffer buf; - Datum d; - bool newrelid; - char isNull; - - nvisited = 0; - queue = DLNewList(); - visited = DLNewList(); - - - inhrel = heap_openr(InheritsRelationName); - RelationSetLockForRead(inhrel); - inhtupdesc = RelationGetTupleDescriptor(inhrel); - - /* - * Use queue to do a breadth-first traversal of the inheritance graph - * from the relid supplied up to the root. - */ - do - { - ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel, - ObjectIdEqualRegProcedure, - ObjectIdGetDatum(relid)); - - inhscan = heap_beginscan(inhrel, 0, false, 1, &skey); - - while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) - { - qentry = (SuperQE *) palloc(sizeof(SuperQE)); - - d = fastgetattr(inhtup, Anum_pg_inherits_inhparent, - inhtupdesc, &isNull); - qentry->sqe_relid = DatumGetObjectId(d); - - /* put this one on the queue */ - DLAddTail(queue, DLNewElem(qentry)); - - ReleaseBuffer(buf); - } - - heap_endscan(inhscan); - - /* pull next unvisited relid off the queue */ - do - { - qe = DLRemHead(queue); - qentry = qe ? (SuperQE *) DLE_VAL(qe) : NULL; - - if (qentry == (SuperQE *) NULL) - break; - - relid = qentry->sqe_relid; - newrelid = true; - - for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) - { - vnode = (SuperQE *) DLE_VAL(elt); - if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) - { - newrelid = false; - break; - } - } - } while (!newrelid); - - if (qentry != (SuperQE *) NULL) - { - - /* save the type id, rather than the relation id */ - if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL) - elog(WARN, "relid %d does not exist", qentry->sqe_relid); - qentry->sqe_relid = typeid(type(RelationGetRelationName(rd)->data)); - heap_close(rd); - - DLAddTail(visited, qe); - - nvisited++; - } - } while (qentry != (SuperQE *) NULL); - - RelationUnsetLockForRead(inhrel); - heap_close(inhrel); - - if (nvisited > 0) - { - relidvec = (Oid *) palloc(nvisited * sizeof(Oid)); - *supervec = relidvec; - - for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) - { - vnode = (SuperQE *) DLE_VAL(elt); - *relidvec++ = vnode->sqe_relid; - } - - } - else - { - *supervec = (Oid *) NULL; - } - - return (nvisited); -} - -static Oid ** -genxprod(InhPaths *arginh, int nargs) -{ - int nanswers; - Oid **result, - **iter; - Oid *oneres; - int i, - j; - int cur[MAXFARGS]; - - nanswers = 1; - for (i = 0; i < nargs; i++) - { - nanswers *= (arginh[i].nsupers + 2); - cur[i] = 0; - } - - iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers); - - /* compute the cross product from right to left */ - for (;;) - { - oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid)); - MemSet(oneres, 0, MAXFARGS * sizeof(Oid)); - - for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--) - continue; - - /* if we're done, terminate with NULL pointer */ - if (i < 0) - { - *iter = NULL; - return (result); - } - - /* no, increment this column and zero the ones after it */ - cur[i] = cur[i] + 1; - for (j = nargs - 1; j > i; j--) - cur[j] = 0; - - for (i = 0; i < nargs; i++) - { - if (cur[i] == 0) - oneres[i] = arginh[i].self; - else if (cur[i] > arginh[i].nsupers) - oneres[i] = 0; /* wild card */ - else - oneres[i] = arginh[i].supervec[cur[i] - 1]; - } - - *iter++ = oneres; - } -} - -/* Given a type id, returns the in-conversion function of the type */ -Oid -typeid_get_retinfunc(Oid type_id) -{ - HeapTuple typeTuple; - TypeTupleForm type; - Oid infunc; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_id), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(WARN, "typeid_get_retinfunc: Invalid type - oid = %u", type_id); - - type = (TypeTupleForm) GETSTRUCT(typeTuple); - infunc = type->typinput; - return (infunc); -} - -/* Given a type id, returns the out-conversion function of the type */ -Oid -typeid_get_retoutfunc(Oid type_id) -{ - HeapTuple typeTuple; - TypeTupleForm type; - Oid outfunc; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_id), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(WARN, "typeid_get_retoutfunc: Invalid type - oid = %u", type_id); - - type = (TypeTupleForm) GETSTRUCT(typeTuple); - outfunc = type->typoutput; - return (outfunc); -} - -Oid -typeid_get_relid(Oid type_id) -{ - HeapTuple typeTuple; - TypeTupleForm type; - Oid infunc; - - typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_id), - 0, 0, 0); - if (!HeapTupleIsValid(typeTuple)) - elog(WARN, "typeid_get_relid: Invalid type - oid = %u", type_id); - - type = (TypeTupleForm) GETSTRUCT(typeTuple); - infunc = type->typrelid; - return (infunc); -} - -Oid -get_typrelid(Type typ) -{ - TypeTupleForm typtup; - - typtup = (TypeTupleForm) GETSTRUCT(typ); - - return (typtup->typrelid); -} - -Oid -get_typelem(Oid type_id) -{ - HeapTuple typeTuple; - TypeTupleForm type; - - if (!(typeTuple = SearchSysCacheTuple(TYPOID, - ObjectIdGetDatum(type_id), - 0, 0, 0))) - { - elog(WARN, "type id lookup of %u failed", type_id); - } - type = (TypeTupleForm) GETSTRUCT(typeTuple); - - return (type->typelem); -} - -#ifdef NOT_USED -char -FindDelimiter(char *typename) -{ - char delim; - HeapTuple typeTuple; - TypeTupleForm type; - - - if (!(typeTuple = SearchSysCacheTuple(TYPNAME, - PointerGetDatum(typename), - 0, 0, 0))) - { - elog(WARN, "type name lookup of %s failed", typename); - } - type = (TypeTupleForm) GETSTRUCT(typeTuple); - - delim = type->typdelim; - return (delim); -} - -#endif - -/* - * Give a somewhat useful error message when the operator for two types - * is not found. - */ -static void -op_error(char *op, Oid arg1, Oid arg2) -{ - Type tp1 = NULL, - tp2 = NULL; - - if (check_typeid(arg1)) - { - tp1 = get_id_type(arg1); - } - else - { - elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op); - } - - if (check_typeid(arg2)) - { - tp2 = get_id_type(arg2); - } - else - { - elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op); - } - - elog(NOTICE, "there is no operator %s for types %s and %s", - op, tname(tp1), tname(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(WARN, "%s for %s and %s using CREATE OPERATOR", - op, tname(tp1), tname(tp2)); -} - -/* - * Error message when function lookup fails that gives details of the - * argument types - */ -void -func_error(char *caller, char *funcname, int nargs, Oid *argtypes) -{ - char p[(NAMEDATALEN + 2) * MAXFMGRARGS], - *ptr; - int i; - - ptr = p; - *ptr = '\0'; - for (i = 0; i < nargs; i++) - { - if (i) - { - *ptr++ = ','; - *ptr++ = ' '; - } - if (argtypes[i] != 0) - { - strcpy(ptr, tname(get_id_type(argtypes[i]))); - *(ptr + NAMEDATALEN) = '\0'; - } - else - strcpy(ptr, "opaque"); - ptr += strlen(ptr); - } - - elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p); -} - -/* - * Error message when aggregate lookup fails that gives details of the - * basetype - */ -void -agg_error(char *caller, char *aggname, Oid basetypeID) -{ - - /* - * basetypeID that is Invalid (zero) means aggregate over all types. - * (count) - */ - - if (basetypeID == InvalidOid) - { - elog(WARN, "%s: aggregate '%s' for all types does not exist", caller, aggname); - } - else - { - elog(WARN, "%s: aggregate '%s' for '%s' does not exist", caller, aggname, - tname(get_id_type(basetypeID))); - } -} diff --git a/src/backend/parser/gram.y b/src/backend/parser/gram.y index abaedbb57b..d146b6e4ce 100644 --- a/src/backend/parser/gram.y +++ b/src/backend/parser/gram.y @@ -10,7 +10,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.71 1997/11/24 16:55:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/gram.y,v 1.72 1997/11/25 22:05:29 momjian Exp $ * * HISTORY * AUTHOR DATE MAJOR EVENT @@ -39,8 +39,6 @@ #include "nodes/parsenodes.h" #include "nodes/print.h" #include "parser/gramparse.h" -#include "parser/catalog_utils.h" -#include "parser/parse_query.h" #include "utils/acl.h" #include "catalog/catname.h" #include "utils/elog.h" @@ -49,8 +47,11 @@ static char saved_relname[NAMEDATALEN]; /* need this for complex attributes */ static bool QueryIsRule = FALSE; static Node *saved_In_Expr; +static Oid *param_type_info; +static int pfunc_num_args; extern List *parsetree; + /* * If you need access to certain yacc-generated variables and find that * they're static by default, uncomment the next line. (this is not a @@ -64,6 +65,9 @@ static List *makeConstantList( A_Const *node); static char *FlattenStringList(List *list); static char *fmtId(char *rawid); static Node *makeIndexable(char *opname, Node *lexpr, Node *rexpr); +static void param_type_init(Oid *typev, int nargs); + +Oid param_type(int t); /* used in parse_expr.c */ /* old versions of flex define this as a macro */ #if defined(yywrap) @@ -2324,7 +2328,7 @@ Typename: Array opt_array_bounds * emp(name=text,mgr=emp) */ $$->setof = TRUE; - else if (get_typrelid((Type)type($$->name)) != InvalidOid) + else if (typeTypeRelid(typenameType($$->name)) != InvalidOid) /* (Eventually add in here that the set can only * contain one element.) */ @@ -3690,4 +3694,24 @@ printf("fmtId- %sconvert %s to %s\n", ((cp == rawid)? "do not ": ""), rawid, cp) #endif return(cp); -} /* fmtId() */ +} + +/* + * param_type_init() + * + * keep enough information around fill out the type of param nodes + * used in postquel functions + */ +static void +param_type_init(Oid *typev, int nargs) +{ + pfunc_num_args = nargs; + param_type_info = typev; +} + +Oid param_type(int t) +{ + if ((t > pfunc_num_args) || (t == 0)) + return InvalidOid; + return param_type_info[t - 1]; +} diff --git a/src/backend/parser/keywords.c b/src/backend/parser/keywords.c index 0312e0251b..1c4b63c44e 100644 --- a/src/backend/parser/keywords.c +++ b/src/backend/parser/keywords.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.24 1997/11/24 05:32:28 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/keywords.c,v 1.25 1997/11/25 22:05:32 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -18,8 +18,8 @@ #include "nodes/pg_list.h" #include "nodes/parsenodes.h" #include "parse.h" -#include "utils/elog.h" #include "parser/keywords.h" +#include "utils/elog.h" /* * List of (keyword-name, keyword-token-value) pairs. diff --git a/src/backend/parser/parse_agg.c b/src/backend/parser/parse_agg.c new file mode 100644 index 0000000000..b64b92079e --- /dev/null +++ b/src/backend/parser/parse_agg.c @@ -0,0 +1,371 @@ +/*------------------------------------------------------------------------- + * + * parse_agg.c-- + * handle aggregates in parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_agg.c,v 1.1 1997/11/25 22:05:34 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include + +#include "postgres.h" +#include "access/heapam.h" +#include "catalog/pg_aggregate.h" +#include "nodes/nodeFuncs.h" +#include "nodes/primnodes.h" +#include "nodes/relation.h" +#include "optimizer/clauses.h" +#include "parser/parse_agg.h" +#include "parser/parse_node.h" +#include "parser/parse_target.h" +#include "utils/syscache.h" + +#ifdef 0 +#include "nodes/nodes.h" +#include "nodes/params.h" +#include "parse.h" /* for AND, OR, etc. */ +#include "catalog/pg_type.h" /* for INT4OID, etc. */ +#include "catalog/pg_proc.h" +#include "utils/elog.h" +#include "utils/builtins.h" /* namecmp(), textout() */ +#include "utils/lsyscache.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "utils/acl.h" +#include "nodes/makefuncs.h" /* for makeResdom(), etc. */ +#include "commands/sequence.h" + +#endif + +/* + * AddAggToParseState - + * add the aggregate to the list of unique aggregates in pstate. + * + * SIDE EFFECT: aggno in target list entry will be modified + */ +void +AddAggToParseState(ParseState *pstate, Aggreg *aggreg) +{ + List *ag; + int i; + + /* + * see if we have the aggregate already (we only need to record the + * aggregate once) + */ + i = 0; + foreach(ag, pstate->p_aggs) + { + Aggreg *a = lfirst(ag); + + if (!strcmp(a->aggname, aggreg->aggname) && + equal(a->target, aggreg->target)) + { + + /* fill in the aggno and we're done */ + aggreg->aggno = i; + return; + } + i++; + } + + /* not found, new aggregate */ + aggreg->aggno = i; + pstate->p_numAgg++; + pstate->p_aggs = lappend(pstate->p_aggs, aggreg); + return; +} + +/* + * finalizeAggregates - + * fill in qry_aggs from pstate. Also checks to make sure that aggregates + * are used in the proper place. + */ +void +finalizeAggregates(ParseState *pstate, Query *qry) +{ + List *l; + int i; + + parseCheckAggregates(pstate, qry); + + qry->qry_numAgg = pstate->p_numAgg; + qry->qry_aggs = + (Aggreg **) palloc(sizeof(Aggreg *) * qry->qry_numAgg); + i = 0; + foreach(l, pstate->p_aggs) + qry->qry_aggs[i++] = (Aggreg *) lfirst(l); +} + +/* + * contain_agg_clause-- + * Recursively find aggreg nodes from a clause. + * + * Returns true if any aggregate found. + */ +bool +contain_agg_clause(Node *clause) +{ + if (clause == NULL) + return FALSE; + else if (IsA(clause, Aggreg)) + return TRUE; + else if (IsA(clause, Iter)) + return contain_agg_clause(((Iter *) clause)->iterexpr); + else if (single_node(clause)) + return FALSE; + else if (or_clause(clause)) + { + List *temp; + + foreach(temp, ((Expr *) clause)->args) + if (contain_agg_clause(lfirst(temp))) + return TRUE; + return FALSE; + } + else if (is_funcclause(clause)) + { + List *temp; + + foreach(temp, ((Expr *) clause)->args) + if (contain_agg_clause(lfirst(temp))) + return TRUE; + return FALSE; + } + else if (IsA(clause, ArrayRef)) + { + List *temp; + + foreach(temp, ((ArrayRef *) clause)->refupperindexpr) + if (contain_agg_clause(lfirst(temp))) + return TRUE; + foreach(temp, ((ArrayRef *) clause)->reflowerindexpr) + if (contain_agg_clause(lfirst(temp))) + return TRUE; + if (contain_agg_clause(((ArrayRef *) clause)->refexpr)) + return TRUE; + if (contain_agg_clause(((ArrayRef *) clause)->refassgnexpr)) + return TRUE; + return FALSE; + } + else if (not_clause(clause)) + return contain_agg_clause((Node *) get_notclausearg((Expr *) clause)); + else if (is_opclause(clause)) + return (contain_agg_clause((Node *) get_leftop((Expr *) clause)) || + contain_agg_clause((Node *) get_rightop((Expr *) clause))); + + return FALSE; +} + +/* + * exprIsAggOrGroupCol - + * returns true if the expression does not contain non-group columns. + */ +bool +exprIsAggOrGroupCol(Node *expr, List *groupClause) +{ + List *gl; + + if (expr == NULL || IsA(expr, Const) || + IsA(expr, Param) ||IsA(expr, Aggreg)) + return TRUE; + + foreach(gl, groupClause) + { + GroupClause *grpcl = lfirst(gl); + + if (equal(expr, grpcl->entry->expr)) + return TRUE; + } + + if (IsA(expr, Expr)) + { + List *temp; + + foreach(temp, ((Expr *) expr)->args) + if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) + return FALSE; + return TRUE; + } + + return FALSE; +} + +/* + * tleIsAggOrGroupCol - + * returns true if the TargetEntry is Agg or GroupCol. + */ +bool +tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause) +{ + Node *expr = tle->expr; + List *gl; + + if (expr == NULL || IsA(expr, Const) ||IsA(expr, Param)) + return TRUE; + + foreach(gl, groupClause) + { + GroupClause *grpcl = lfirst(gl); + + if (tle->resdom->resno == grpcl->entry->resdom->resno) + { + if (contain_agg_clause((Node *) expr)) + elog(WARN, "parser: aggregates not allowed in GROUP BY clause"); + return TRUE; + } + } + + if (IsA(expr, Aggreg)) + return TRUE; + + if (IsA(expr, Expr)) + { + List *temp; + + foreach(temp, ((Expr *) expr)->args) + if (!exprIsAggOrGroupCol(lfirst(temp), groupClause)) + return FALSE; + return TRUE; + } + + return FALSE; +} + +/* + * parseCheckAggregates - + * this should really be done earlier but the current grammar + * cannot differentiate functions from aggregates. So we have do check + * here when the target list and the qualifications are finalized. + */ +void +parseCheckAggregates(ParseState *pstate, Query *qry) +{ + List *tl; + + Assert(pstate->p_numAgg > 0); + + /* + * aggregates never appear in WHERE clauses. (we have to check where + * clause first because if there is an aggregate, the check for + * non-group column in target list may fail.) + */ + if (contain_agg_clause(qry->qual)) + elog(WARN, "parser: aggregates not allowed in WHERE clause"); + + /* + * the target list can only contain aggregates, group columns and + * functions thereof. + */ + foreach(tl, qry->targetList) + { + TargetEntry *tle = lfirst(tl); + + if (!tleIsAggOrGroupCol(tle, qry->groupClause)) + elog(WARN, + "parser: illegal use of aggregates or non-group column in target list"); + } + + /* + * the expression specified in the HAVING clause has the same + * restriction as those in the target list. + */ +/* + * Need to change here when we get HAVING works. Currently + * qry->havingQual is NULL. - vadim 04/05/97 + if (!exprIsAggOrGroupCol(qry->havingQual, qry->groupClause)) + elog(WARN, + "parser: illegal use of aggregates or non-group column in HAVING clause"); + */ + return; +} + + +Aggreg * +ParseAgg(char *aggname, Oid basetype, Node *target) +{ + Oid fintype; + Oid vartype; + Oid xfn1; + Form_pg_aggregate aggform; + Aggreg *aggreg; + HeapTuple theAggTuple; + + theAggTuple = SearchSysCacheTuple(AGGNAME, PointerGetDatum(aggname), + ObjectIdGetDatum(basetype), + 0, 0); + if (!HeapTupleIsValid(theAggTuple)) + { + elog(WARN, "aggregate %s does not exist", aggname); + } + + aggform = (Form_pg_aggregate) GETSTRUCT(theAggTuple); + fintype = aggform->aggfinaltype; + xfn1 = aggform->aggtransfn1; + + if (nodeTag(target) != T_Var && nodeTag(target) != T_Expr) + elog(WARN, "parser: aggregate can only be applied on an attribute or expression"); + + /* only aggregates with transfn1 need a base type */ + if (OidIsValid(xfn1)) + { + basetype = aggform->aggbasetype; + if (nodeTag(target) == T_Var) + vartype = ((Var *) target)->vartype; + else + vartype = ((Expr *) target)->typeOid; + + if (basetype != vartype) + { + Type tp1, + tp2; + + tp1 = typeidType(basetype); + tp2 = typeidType(vartype); + elog(NOTICE, "Aggregate type mismatch:"); + elog(WARN, "%s works on %s, not %s", aggname, + typeTypeName(tp1), typeTypeName(tp2)); + } + } + + aggreg = makeNode(Aggreg); + aggreg->aggname = pstrdup(aggname); + aggreg->basetype = aggform->aggbasetype; + aggreg->aggtype = fintype; + + aggreg->target = target; + + return aggreg; +} + +/* + * Error message when aggregate lookup fails that gives details of the + * basetype + */ +void +agg_error(char *caller, char *aggname, Oid basetypeID) +{ + + /* + * basetypeID that is Invalid (zero) means aggregate over all types. + * (count) + */ + + if (basetypeID == InvalidOid) + { + elog(WARN, "%s: aggregate '%s' for all types does not exist", caller, aggname); + } + else + { + elog(WARN, "%s: aggregate '%s' for '%s' does not exist", caller, aggname, + typeidTypeName(basetypeID)); + } +} + diff --git a/src/backend/parser/parse_clause.c b/src/backend/parser/parse_clause.c new file mode 100644 index 0000000000..8e08e00a2e --- /dev/null +++ b/src/backend/parser/parse_clause.c @@ -0,0 +1,407 @@ +/*------------------------------------------------------------------------- + * + * parse_clause.c-- + * handle clauses in parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_clause.c,v 1.1 1997/11/25 22:05:35 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include "postgres.h" +#include "access/heapam.h" +#include "parser/parse_clause.h" +#include "parser/parse_expr.h" +#include "parser/parse_node.h" +#include "parser/parse_oper.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "catalog/pg_type.h" + +#ifdef 0 +#include "nodes/nodes.h" +#include "nodes/params.h" +#include "nodes/primnodes.h" +#include "nodes/parsenodes.h" +#include "nodes/relation.h" +#include "parse.h" /* for AND, OR, etc. */ +#include "catalog/pg_aggregate.h" +#include "catalog/pg_proc.h" +#include "utils/elog.h" +#include "utils/builtins.h" /* namecmp(), textout() */ +#include "utils/lsyscache.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "utils/syscache.h" +#include "utils/acl.h" +#include "nodes/makefuncs.h" /* for makeResdom(), etc. */ +#include "nodes/nodeFuncs.h" +#include "commands/sequence.h" + +#include "optimizer/clauses.h" + +#include "miscadmin.h" + +#include "port-protos.h" /* strdup() */ +#endif + +/* + * parseFromClause - + * turns the table references specified in the from-clause into a + * range table. The range table may grow as we transform the expressions + * in the target list. (Note that this happens because in POSTQUEL, we + * allow references to relations not specified in the from-clause. We + * also allow that in our POST-SQL) + * + */ +void +parseFromClause(ParseState *pstate, List *frmList) +{ + List *fl; + + foreach(fl, frmList) + { + RangeVar *r = lfirst(fl); + RelExpr *baserel = r->relExpr; + char *relname = baserel->relname; + char *refname = r->name; + RangeTblEntry *rte; + + if (refname == NULL) + refname = relname; + + /* + * marks this entry to indicate it comes from the FROM clause. In + * SQL, the target list can only refer to range variables + * specified in the from clause but we follow the more powerful + * POSTQUEL semantics and automatically generate the range + * variable if not specified. However there are times we need to + * know whether the entries are legitimate. + * + * eg. select * from foo f where f.x = 1; will generate wrong answer + * if we expand * to foo.x. + */ + rte = addRangeTableEntry(pstate, relname, refname, baserel->inh, TRUE); + } +} + +/* + * makeRangeTable - + * make a range table with the specified relation (optional) and the + * from-clause. + */ +void +makeRangeTable(ParseState *pstate, char *relname, List *frmList) +{ + RangeTblEntry *rte; + + parseFromClause(pstate, frmList); + + if (relname == NULL) + return; + + if (refnameRangeTablePosn(pstate->p_rtable, relname) < 1) + rte = addRangeTableEntry(pstate, relname, relname, FALSE, FALSE); + else + rte = refnameRangeTableEntry(pstate->p_rtable, relname); + + pstate->p_target_rangetblentry = rte; + Assert(pstate->p_target_relation == NULL); + pstate->p_target_relation = heap_open(rte->relid); + Assert(pstate->p_target_relation != NULL); + /* will close relation later */ +} + +/***************************************************************************** + * + * Where Clause + * + *****************************************************************************/ + +/* + * transformWhereClause - + * transforms the qualification and make sure it is of type Boolean + * + */ +Node * +transformWhereClause(ParseState *pstate, Node *a_expr) +{ + Node *qual; + + if (a_expr == NULL) + return (Node *) NULL; /* no qualifiers */ + + pstate->p_in_where_clause = true; + qual = transformExpr(pstate, a_expr, EXPR_COLUMN_FIRST); + pstate->p_in_where_clause = false; + if (exprType(qual) != BOOLOID) + { + elog(WARN, + "where clause must return type bool, not %s", + typeidTypeName(exprType(qual))); + } + return qual; +} + +/***************************************************************************** + * + * Sort Clause + * + *****************************************************************************/ + +/* + * find_targetlist_entry - + * returns the Resdom in the target list matching the specified varname + * and range + * + */ +TargetEntry * +find_targetlist_entry(ParseState *pstate, SortGroupBy *sortgroupby, List *tlist) +{ + List *i; + int real_rtable_pos = 0, + target_pos = 0; + TargetEntry *target_result = NULL; + + if (sortgroupby->range) + real_rtable_pos = refnameRangeTablePosn(pstate->p_rtable, + sortgroupby->range); + + foreach(i, tlist) + { + TargetEntry *target = (TargetEntry *) lfirst(i); + Resdom *resnode = target->resdom; + Var *var = (Var *) target->expr; + char *resname = resnode->resname; + int test_rtable_pos = var->varno; + +#ifdef PARSEDEBUG + printf("find_targetlist_entry- target name is %s, position %d, resno %d\n", + (sortgroupby->name ? sortgroupby->name : "(null)"), target_pos + 1, sortgroupby->resno); +#endif + + if (!sortgroupby->name) + { + if (sortgroupby->resno == ++target_pos) + { + target_result = target; + break; + } + } + else + { + if (!strcmp(resname, sortgroupby->name)) + { + if (sortgroupby->range) + { + if (real_rtable_pos == test_rtable_pos) + { + if (target_result != NULL) + elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); + else + target_result = target; + } + } + else + { + if (target_result != NULL) + elog(WARN, "Order/Group By '%s' is ambiguous", sortgroupby->name); + else + target_result = target; + } + } + } + } + return target_result; +} + +/* + * transformGroupClause - + * transform a Group By clause + * + */ +List * +transformGroupClause(ParseState *pstate, List *grouplist, List *targetlist) +{ + List *glist = NIL, + *gl = NIL; + + while (grouplist != NIL) + { + GroupClause *grpcl = makeNode(GroupClause); + TargetEntry *restarget; + Resdom *resdom; + + restarget = find_targetlist_entry(pstate, lfirst(grouplist), targetlist); + + if (restarget == NULL) + elog(WARN, "The field being grouped by must appear in the target list"); + + grpcl->entry = restarget; + resdom = restarget->resdom; + grpcl->grpOpoid = oprid(oper("<", + resdom->restype, + resdom->restype, false)); + if (glist == NIL) + gl = glist = lcons(grpcl, NIL); + else + { + List *i; + + foreach (i, glist) + { + GroupClause *gcl = (GroupClause *) lfirst (i); + + if ( gcl->entry == grpcl->entry ) + break; + } + if ( i == NIL ) /* not in grouplist already */ + { + lnext(gl) = lcons(grpcl, NIL); + gl = lnext(gl); + } + else + pfree (grpcl); /* get rid of this */ + } + grouplist = lnext(grouplist); + } + + return glist; +} + +/* + * transformSortClause - + * transform an Order By clause + * + */ +List * +transformSortClause(ParseState *pstate, + List *orderlist, List *targetlist, + char *uniqueFlag) +{ + List *sortlist = NIL; + List *s = NIL; + + while (orderlist != NIL) + { + SortGroupBy *sortby = lfirst(orderlist); + SortClause *sortcl = makeNode(SortClause); + TargetEntry *restarget; + Resdom *resdom; + + restarget = find_targetlist_entry(pstate, sortby, targetlist); + if (restarget == NULL) + elog(WARN, "The field being ordered by must appear in the target list"); + + sortcl->resdom = resdom = restarget->resdom; + sortcl->opoid = oprid(oper(sortby->useOp, + resdom->restype, + resdom->restype, false)); + if (sortlist == NIL) + { + s = sortlist = lcons(sortcl, NIL); + } + else + { + List *i; + + foreach (i, sortlist) + { + SortClause *scl = (SortClause *) lfirst (i); + + if ( scl->resdom == sortcl->resdom ) + break; + } + if ( i == NIL ) /* not in sortlist already */ + { + lnext(s) = lcons(sortcl, NIL); + s = lnext(s); + } + else + pfree (sortcl); /* get rid of this */ + } + orderlist = lnext(orderlist); + } + + if (uniqueFlag) + { + List *i; + + if (uniqueFlag[0] == '*') + { + + /* + * concatenate all elements from target list that are not + * already in the sortby list + */ + foreach(i, targetlist) + { + TargetEntry *tlelt = (TargetEntry *) lfirst(i); + + s = sortlist; + while (s != NIL) + { + SortClause *sortcl = lfirst(s); + + if (sortcl->resdom == tlelt->resdom) + break; + s = lnext(s); + } + if (s == NIL) + { + /* not a member of the sortclauses yet */ + SortClause *sortcl = makeNode(SortClause); + + sortcl->resdom = tlelt->resdom; + sortcl->opoid = any_ordering_op(tlelt->resdom->restype); + + sortlist = lappend(sortlist, sortcl); + } + } + } + else + { + TargetEntry *tlelt = NULL; + char *uniqueAttrName = uniqueFlag; + + /* only create sort clause with the specified unique attribute */ + foreach(i, targetlist) + { + tlelt = (TargetEntry *) lfirst(i); + if (strcmp(tlelt->resdom->resname, uniqueAttrName) == 0) + break; + } + if (i == NIL) + { + elog(WARN, "The field specified in the UNIQUE ON clause is not in the targetlist"); + } + s = sortlist; + foreach(s, sortlist) + { + SortClause *sortcl = lfirst(s); + + if (sortcl->resdom == tlelt->resdom) + break; + } + if (s == NIL) + { + /* not a member of the sortclauses yet */ + SortClause *sortcl = makeNode(SortClause); + + sortcl->resdom = tlelt->resdom; + sortcl->opoid = any_ordering_op(tlelt->resdom->restype); + + sortlist = lappend(sortlist, sortcl); + } + } + + } + + return sortlist; +} diff --git a/src/backend/parser/parse_expr.c b/src/backend/parser/parse_expr.c new file mode 100644 index 0000000000..fe00b2b48a --- /dev/null +++ b/src/backend/parser/parse_expr.c @@ -0,0 +1,694 @@ +/*------------------------------------------------------------------------- + * + * parse_expr.c + * handle expressions in parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_expr.c,v 1.1 1997/11/25 22:05:39 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include + +#include "postgres.h" +#include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "nodes/nodes.h" +#include "nodes/params.h" +#include "nodes/relation.h" +#include "parser/parse_expr.h" +#include "parser/parse_func.h" +#include "parser/parse_node.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "parse.h" +#include "utils/builtins.h" + +#ifdef 0 +#include "nodes/primnodes.h" +#include "nodes/parsenodes.h" +#include "catalog/pg_aggregate.h" +#include "catalog/pg_proc.h" +#include "utils/elog.h" +#include "utils/lsyscache.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "utils/syscache.h" +#include "utils/acl.h" +#include "nodes/nodeFuncs.h" +#include "commands/sequence.h" + +#include "optimizer/clauses.h" +#include "access/heapam.h" + +#include "miscadmin.h" +#endif + +Oid param_type(int t); /* from gram.y */ + +/* + * transformExpr - + * analyze and transform expressions. Type checking and type casting is + * done here. The optimizer and the executor cannot handle the original + * (raw) expressions collected by the parse tree. Hence the transformation + * here. + */ +Node * +transformExpr(ParseState *pstate, Node *expr, int precedence) +{ + Node *result = NULL; + + if (expr == NULL) + return NULL; + + switch (nodeTag(expr)) + { + case T_Attr: + { + Attr *att = (Attr *) expr; + Node *temp; + + /* what if att.attrs == "*"?? */ + temp = handleNestedDots(pstate, att, &pstate->p_last_resno); + if (att->indirection != NIL) + { + List *idx = att->indirection; + + while (idx != NIL) + { + A_Indices *ai = (A_Indices *) lfirst(idx); + Node *lexpr = NULL, + *uexpr; + + uexpr = transformExpr(pstate, ai->uidx, precedence); /* must exists */ + if (exprType(uexpr) != INT4OID) + elog(WARN, "array index expressions must be int4's"); + if (ai->lidx != NULL) + { + lexpr = transformExpr(pstate, ai->lidx, precedence); + if (exprType(lexpr) != INT4OID) + elog(WARN, "array index expressions must be int4's"); + } +#if 0 + pfree(ai->uidx); + if (ai->lidx != NULL) + pfree(ai->lidx); +#endif + ai->lidx = lexpr; + ai->uidx = uexpr; + + /* + * note we reuse the list of indices, make sure we + * don't free them! Otherwise, make a new list + * here + */ + idx = lnext(idx); + } + result = (Node *) make_array_ref(temp, att->indirection); + } + else + { + result = temp; + } + break; + } + case T_A_Const: + { + A_Const *con = (A_Const *) expr; + Value *val = &con->val; + + if (con->typename != NULL) + { + result = parser_typecast(val, con->typename, -1); + } + else + { + result = (Node *) make_const(val); + } + break; + } + case T_ParamNo: + { + ParamNo *pno = (ParamNo *) expr; + Oid toid; + int paramno; + Param *param; + + paramno = pno->number; + toid = param_type(paramno); + if (!OidIsValid(toid)) + { + elog(WARN, "Parameter '$%d' is out of range", + paramno); + } + param = makeNode(Param); + param->paramkind = PARAM_NUM; + param->paramid = (AttrNumber) paramno; + param->paramname = ""; + param->paramtype = (Oid) toid; + param->param_tlist = (List *) NULL; + + result = (Node *) param; + break; + } + case T_A_Expr: + { + A_Expr *a = (A_Expr *) expr; + + switch (a->oper) + { + case OP: + { + Node *lexpr = transformExpr(pstate, a->lexpr, precedence); + Node *rexpr = transformExpr(pstate, a->rexpr, precedence); + + result = (Node *) make_op(a->opname, lexpr, rexpr); + } + break; + case ISNULL: + { + Node *lexpr = transformExpr(pstate, a->lexpr, precedence); + + result = ParseFunc(pstate, + "nullvalue", lcons(lexpr, NIL), + &pstate->p_last_resno); + } + break; + case NOTNULL: + { + Node *lexpr = transformExpr(pstate, a->lexpr, precedence); + + result = ParseFunc(pstate, + "nonnullvalue", lcons(lexpr, NIL), + &pstate->p_last_resno); + } + break; + case AND: + { + Expr *expr = makeNode(Expr); + Node *lexpr = transformExpr(pstate, a->lexpr, precedence); + Node *rexpr = transformExpr(pstate, a->rexpr, precedence); + + if (exprType(lexpr) != BOOLOID) + elog(WARN, + "left-hand side of AND is type '%s', not bool", + typeidTypeName(exprType(lexpr))); + if (exprType(rexpr) != BOOLOID) + elog(WARN, + "right-hand side of AND is type '%s', not bool", + typeidTypeName(exprType(rexpr))); + expr->typeOid = BOOLOID; + expr->opType = AND_EXPR; + expr->args = makeList(lexpr, rexpr, -1); + result = (Node *) expr; + } + break; + case OR: + { + Expr *expr = makeNode(Expr); + Node *lexpr = transformExpr(pstate, a->lexpr, precedence); + Node *rexpr = transformExpr(pstate, a->rexpr, precedence); + + if (exprType(lexpr) != BOOLOID) + elog(WARN, + "left-hand side of OR is type '%s', not bool", + typeidTypeName(exprType(lexpr))); + if (exprType(rexpr) != BOOLOID) + elog(WARN, + "right-hand side of OR is type '%s', not bool", + typeidTypeName(exprType(rexpr))); + expr->typeOid = BOOLOID; + expr->opType = OR_EXPR; + expr->args = makeList(lexpr, rexpr, -1); + result = (Node *) expr; + } + break; + case NOT: + { + Expr *expr = makeNode(Expr); + Node *rexpr = transformExpr(pstate, a->rexpr, precedence); + + if (exprType(rexpr) != BOOLOID) + elog(WARN, + "argument to NOT is type '%s', not bool", + typeidTypeName(exprType(rexpr))); + expr->typeOid = BOOLOID; + expr->opType = NOT_EXPR; + expr->args = makeList(rexpr, -1); + result = (Node *) expr; + } + break; + } + break; + } + case T_Ident: + { + + /* + * look for a column name or a relation name (the default + * behavior) + */ + result = transformIdent(pstate, expr, precedence); + break; + } + case T_FuncCall: + { + FuncCall *fn = (FuncCall *) expr; + List *args; + + /* transform the list of arguments */ + foreach(args, fn->args) + lfirst(args) = transformExpr(pstate, (Node *) lfirst(args), precedence); + result = ParseFunc(pstate, + fn->funcname, fn->args, &pstate->p_last_resno); + break; + } + default: + /* should not reach here */ + elog(WARN, "transformExpr: does not know how to transform %d\n", + nodeTag(expr)); + break; + } + + return result; +} + +Node * +transformIdent(ParseState *pstate, Node *expr, int precedence) +{ + Ident *ident = (Ident *) expr; + RangeTblEntry *rte; + Node *column_result, + *relation_result, + *result; + + column_result = relation_result = result = 0; + /* try to find the ident as a column */ + if ((rte = colnameRangeTableEntry(pstate, ident->name)) != NULL) + { + Attr *att = makeNode(Attr); + + att->relname = rte->refname; + att->attrs = lcons(makeString(ident->name), NIL); + column_result = + (Node *) handleNestedDots(pstate, att, &pstate->p_last_resno); + } + + /* try to find the ident as a relation */ + if (refnameRangeTableEntry(pstate->p_rtable, ident->name) != NULL) + { + ident->isRel = TRUE; + relation_result = (Node *) ident; + } + + /* choose the right result based on the precedence */ + if (precedence == EXPR_COLUMN_FIRST) + { + if (column_result) + result = column_result; + else + result = relation_result; + } + else + { + if (relation_result) + result = relation_result; + else + result = column_result; + } + + if (result == NULL) + elog(WARN, "attribute '%s' not found", ident->name); + + return result; +} + +/* + * exprType - + * returns the Oid of the type of the expression. (Used for typechecking.) + */ +Oid +exprType(Node *expr) +{ + Oid type = (Oid) 0; + + switch (nodeTag(expr)) + { + case T_Func: + type = ((Func *) expr)->functype; + break; + case T_Iter: + type = ((Iter *) expr)->itertype; + break; + case T_Var: + type = ((Var *) expr)->vartype; + break; + case T_Expr: + type = ((Expr *) expr)->typeOid; + break; + case T_Const: + type = ((Const *) expr)->consttype; + break; + case T_ArrayRef: + type = ((ArrayRef *) expr)->refelemtype; + break; + case T_Aggreg: + type = ((Aggreg *) expr)->aggtype; + break; + case T_Param: + type = ((Param *) expr)->paramtype; + break; + case T_Ident: + /* is this right? */ + type = UNKNOWNOID; + break; + default: + elog(WARN, "exprType: don't know how to get type for %d node", + nodeTag(expr)); + break; + } + return type; +} + +/* + ** HandleNestedDots -- + ** Given a nested dot expression (i.e. (relation func ... attr), build up + ** a tree with of Iter and Func nodes. + */ +Node * +handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno) +{ + List *mutator_iter; + Node *retval = NULL; + + if (attr->paramNo != NULL) + { + Param *param = (Param *) transformExpr(pstate, (Node *) attr->paramNo, EXPR_RELATION_FIRST); + + retval = + ParseFunc(pstate, strVal(lfirst(attr->attrs)), + lcons(param, NIL), + curr_resno); + } + else + { + Ident *ident = makeNode(Ident); + + ident->name = attr->relname; + ident->isRel = TRUE; + retval = + ParseFunc(pstate, strVal(lfirst(attr->attrs)), + lcons(ident, NIL), + curr_resno); + } + + foreach(mutator_iter, lnext(attr->attrs)) + { + retval = ParseFunc(pstate, strVal(lfirst(mutator_iter)), + lcons(retval, NIL), + curr_resno); + } + + return (retval); +} + +Node * +parser_typecast(Value *expr, TypeName *typename, int typlen) +{ + /* check for passing non-ints */ + Const *adt; + Datum lcp; + Type tp; + char type_string[NAMEDATALEN]; + int32 len; + char *cp = NULL; + char *const_string = NULL; + bool string_palloced = false; + + switch (nodeTag(expr)) + { + case T_String: + const_string = DatumGetPointer(expr->val.str); + break; + case T_Integer: + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%ld", expr->val.ival); + break; + default: + elog(WARN, + "parser_typecast: cannot cast this expression to type \"%s\"", + typename->name); + } + + if (typename->arrayBounds != NIL) + { + sprintf(type_string, "_%s", typename->name); + tp = (Type) typenameType(type_string); + } + else + { + tp = (Type) typenameType(typename->name); + } + + len = typeLen(tp); + +#if 0 /* fix me */ + switch (CInteger(lfirst(expr))) + { + case INT4OID: /* int4 */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%d", ((Const *) lnext(expr))->constvalue); + break; + + case NAMEOID: /* char16 */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%s", ((Const *) lnext(expr))->constvalue); + break; + + case CHAROID: /* char */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%c", ((Const) lnext(expr))->constvalue); + break; + + case FLOAT8OID: /* float8 */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%f", ((Const) lnext(expr))->constvalue); + break; + + case CASHOID: /* money */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%d", + (int) ((Const *) expr)->constvalue); + break; + + case TEXTOID: /* text */ + const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); + const_string = (char *) textout((struct varlena *) const_string); + break; + + case UNKNOWNOID: /* unknown */ + const_string = DatumGetPointer(((Const) lnext(expr))->constvalue); + const_string = (char *) textout((struct varlena *) const_string); + break; + + default: + elog(WARN, "unknown type %d", CInteger(lfirst(expr))); + } +#endif + + cp = stringTypeString(tp, const_string, typlen); + + if (!typeByVal(tp)) + { +/* + if (len >= 0 && len != PSIZE(cp)) { + char *pp; + pp = (char *) palloc(len); + memmove(pp, cp, len); + cp = pp; + } +*/ + lcp = PointerGetDatum(cp); + } + else + { + switch (len) + { + case 1: + lcp = Int8GetDatum(cp); + break; + case 2: + lcp = Int16GetDatum(cp); + break; + case 4: + lcp = Int32GetDatum(cp); + break; + default: + lcp = PointerGetDatum(cp); + break; + } + } + + adt = makeConst(typeTypeId(tp), + len, + (Datum) lcp, + false, + typeByVal(tp), + false, /* not a set */ + true /* is cast */ ); + + if (string_palloced) + pfree(const_string); + + return (Node *) adt; +} + +Node * +parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen) +{ + /* check for passing non-ints */ + Const *adt; + Datum lcp; + int32 len = typeLen(tp); + char *cp = NULL; + + char *const_string = NULL; + bool string_palloced = false; + + Assert(IsA(expr, Const)); + + switch (exprType) + { + case 0: /* NULL */ + break; + case INT4OID: /* int4 */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%d", + (int) ((Const *) expr)->constvalue); + break; + case NAMEOID: /* char16 */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%s", + (char *) ((Const *) expr)->constvalue); + break; + case CHAROID: /* char */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%c", + (char) ((Const *) expr)->constvalue); + break; + case FLOAT4OID: /* float4 */ + { + float32 floatVal = + DatumGetFloat32(((Const *) expr)->constvalue); + + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%f", *floatVal); + break; + } + case FLOAT8OID: /* float8 */ + { + float64 floatVal = + DatumGetFloat64(((Const *) expr)->constvalue); + + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%f", *floatVal); + break; + } + case CASHOID: /* money */ + const_string = (char *) palloc(256); + string_palloced = true; + sprintf(const_string, "%ld", + (long) ((Const *) expr)->constvalue); + break; + case TEXTOID: /* text */ + const_string = + DatumGetPointer(((Const *) expr)->constvalue); + const_string = (char *) textout((struct varlena *) const_string); + break; + case UNKNOWNOID: /* unknown */ + const_string = + DatumGetPointer(((Const *) expr)->constvalue); + const_string = (char *) textout((struct varlena *) const_string); + break; + default: + elog(WARN, "unknown type %u ", exprType); + } + + if (!exprType) + { + adt = makeConst(typeTypeId(tp), + (Size) 0, + (Datum) NULL, + true, /* isnull */ + false, /* was omitted */ + false, /* not a set */ + true /* is cast */ ); + return ((Node *) adt); + } + + cp = stringTypeString(tp, const_string, typlen); + + + if (!typeByVal(tp)) + { +/* + if (len >= 0 && len != PSIZE(cp)) { + char *pp; + pp = (char *) palloc(len); + memmove(pp, cp, len); + cp = pp; + } +*/ + lcp = PointerGetDatum(cp); + } + else + { + switch (len) + { + case 1: + lcp = Int8GetDatum(cp); + break; + case 2: + lcp = Int16GetDatum(cp); + break; + case 4: + lcp = Int32GetDatum(cp); + break; + default: + lcp = PointerGetDatum(cp); + break; + } + } + + adt = makeConst(typeTypeId(tp), + (Size) len, + (Datum) lcp, + false, + false, /* was omitted */ + false, /* not a set */ + true /* is cast */ ); + + /* + * printf("adt %s : %u %d %d\n",CString(expr),typeTypeId(tp) , len,cp); + */ + if (string_palloced) + pfree(const_string); + + return ((Node *) adt); +} diff --git a/src/backend/parser/parse_func.c b/src/backend/parser/parse_func.c new file mode 100644 index 0000000000..bb2a7773fd --- /dev/null +++ b/src/backend/parser/parse_func.c @@ -0,0 +1,1264 @@ +/*------------------------------------------------------------------------- + * + * parse_func.c + * handle function calls in parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_func.c,v 1.1 1997/11/25 22:05:41 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include "postgres.h" +#include "fmgr.h" +#include "miscadmin.h" +#include "access/genam.h" +#include "access/heapam.h" +#include "access/itup.h" +#include "access/relscan.h" +#include "access/sdir.h" +#include "catalog/catname.h" +#include "catalog/indexing.h" +#include "catalog/pg_inherits.h" +#include "catalog/pg_proc.h" +#include "catalog/pg_type.h" +#include "lib/dllist.h" +#include "nodes/makefuncs.h" +#include "nodes/relation.h" +#include "parser/parse_agg.h" +#include "parser/parse_expr.h" +#include "parser/parse_func.h" +#include "parser/parse_node.h" +#include "parser/parse_relation.h" +#include "parser/parse_type.h" +#include "storage/bufmgr.h" +#include "storage/lmgr.h" +#include "utils/acl.h" +#include "utils/builtins.h" +#include "utils/lsyscache.h" +#include "utils/syscache.h" + +#ifdef 0 +#include "utils/datum.h" + +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" + +#include "catalog/pg_operator.h" +#include "catalog/catname.h" + +#include "access/skey.h" +#include "access/tupdesc.h" +#include "access/htup.h" +#include "access/genam.h" +#include "access/itup.h" +#include "access/tupmacs.h" + +#include "storage/buf.h" +#endif + +#define ISCOMPLEX(type) (typeidTypeRelid(type) ? true : false) + +#define MAXFARGS 8 /* max # args to a c or postquel function */ + +typedef struct _SuperQE +{ + Oid sqe_relid; +} SuperQE; + +/* + * parse function + */ + +Node * +ParseFunc(ParseState *pstate, char *funcname, List *fargs, int *curr_resno) +{ + Oid rettype = (Oid) 0; + Oid argrelid = (Oid) 0; + Oid funcid = (Oid) 0; + List *i = NIL; + Node *first_arg = NULL; + char *relname = NULL; + char *refname = NULL; + Relation rd; + Oid relid; + int nargs; + Func *funcnode; + Oid oid_array[8]; + Oid *true_oid_array; + Node *retval; + bool retset; + bool exists; + bool attisset = false; + Oid toid = (Oid) 0; + Expr *expr; + + if (fargs) + { + first_arg = lfirst(fargs); + if (first_arg == NULL) + elog(WARN, "function '%s' does not allow NULL input", funcname); + } + + /* + * check for projection methods: if function takes one argument, and + * that argument is a relation, param, or PQ function returning a + * complex * type, then the function could be a projection. + */ + if (length(fargs) == 1) + { + + if (nodeTag(first_arg) == T_Ident && ((Ident *) first_arg)->isRel) + { + RangeTblEntry *rte; + Ident *ident = (Ident *) first_arg; + + /* + * first arg is a relation. This could be a projection. + */ + refname = ident->name; + + rte = refnameRangeTableEntry(pstate->p_rtable, refname); + if (rte == NULL) + rte = addRangeTableEntry(pstate, refname, refname, FALSE, FALSE); + + relname = rte->relname; + relid = rte->relid; + + /* + * If the attr isn't a set, just make a var for it. If it is + * a set, treat it like a function and drop through. + */ + if (get_attnum(relid, funcname) != InvalidAttrNumber) + { + Oid dummyTypeId; + + return + ((Node *) make_var(pstate, + refname, + funcname, + &dummyTypeId)); + } + else + { + /* drop through - attr is a set */ + ; + } + } + else if (ISCOMPLEX(exprType(first_arg))) + { + + /* + * Attempt to handle projection of a complex argument. If + * ParseComplexProjection can't handle the projection, we have + * to keep going. + */ + retval = ParseComplexProjection(pstate, + funcname, + first_arg, + &attisset); + if (attisset) + { + toid = exprType(first_arg); + rd = heap_openr(typeidTypeName(toid)); + if (RelationIsValid(rd)) + { + relname = RelationGetRelationName(rd)->data; + heap_close(rd); + } + else + elog(WARN, + "Type '%s' is not a relation type", + typeidTypeName(toid)); + argrelid = typeidTypeRelid(toid); + + /* + * A projection contains either an attribute name or the + * "*". + */ + if ((get_attnum(argrelid, funcname) == InvalidAttrNumber) + && strcmp(funcname, "*")) + { + elog(WARN, "Functions on sets are not yet supported"); + } + } + + if (retval) + return retval; + } + else + { + + /* + * Parsing aggregates. + */ + Oid basetype; + + /* + * the aggregate count is a special case, ignore its base + * type. Treat it as zero + */ + if (strcmp(funcname, "count") == 0) + basetype = 0; + else + basetype = exprType(lfirst(fargs)); + if (SearchSysCacheTuple(AGGNAME, + PointerGetDatum(funcname), + ObjectIdGetDatum(basetype), + 0, 0)) + { + Aggreg *aggreg = ParseAgg(funcname, basetype, lfirst(fargs)); + + AddAggToParseState(pstate, aggreg); + return (Node *) aggreg; + } + } + } + + + /* + * * If we dropped through to here it's really a function (or a set, + * which * is implemented as a function.) * extract arg type info and + * transform relation name arguments into * varnodes of the + * appropriate form. + */ + MemSet(&oid_array[0], 0, 8 * sizeof(Oid)); + + nargs = 0; + foreach(i, fargs) + { + int vnum; + RangeTblEntry *rte; + Node *pair = lfirst(i); + + if (nodeTag(pair) == T_Ident && ((Ident *) pair)->isRel) + { + + /* + * a relation + */ + refname = ((Ident *) pair)->name; + + rte = refnameRangeTableEntry(pstate->p_rtable, refname); + if (rte == NULL) + rte = addRangeTableEntry(pstate, refname, refname, + FALSE, FALSE); + relname = rte->relname; + + vnum = refnameRangeTablePosn(pstate->p_rtable, rte->refname); + + /* + * for func(relname), the param to the function is the tuple + * under consideration. we build a special VarNode to reflect + * this -- it has varno set to the correct range table entry, + * but has varattno == 0 to signal that the whole tuple is the + * argument. + */ + toid = typeTypeId(typenameType(relname)); + /* replace it in the arg list */ + lfirst(fargs) = + makeVar(vnum, 0, toid, vnum, 0); + } + else if (!attisset) + { /* set functions don't have parameters */ + + /* + * any functiona args which are typed "unknown", but aren't + * constants, we don't know what to do with, because we can't + * cast them - jolly + */ + if (exprType(pair) == UNKNOWNOID && + !IsA(pair, Const)) + { + elog(WARN, "ParseFunc: no function named '%s' that takes in an unknown type as argument #%d", funcname, nargs); + } + else + toid = exprType(pair); + } + + oid_array[nargs++] = toid; + } + + /* + * func_get_detail looks up the function in the catalogs, does + * disambiguation for polymorphic functions, handles inheritance, and + * returns the funcid and type and set or singleton status of the + * function's return value. it also returns the true argument types + * to the function. if func_get_detail returns true, the function + * exists. otherwise, there was an error. + */ + if (attisset) + { /* we know all of these fields already */ + + /* + * We create a funcnode with a placeholder function SetEval. + * SetEval() never actually gets executed. When the function + * evaluation routines see it, they use the funcid projected out + * from the relation as the actual function to call. Example: + * retrieve (emp.mgr.name) The plan for this will scan the emp + * relation, projecting out the mgr attribute, which is a funcid. + * This function is then called (instead of SetEval) and "name" is + * projected from its result. + */ + funcid = SetEvalRegProcedure; + rettype = toid; + retset = true; + true_oid_array = oid_array; + exists = true; + } + else + { + exists = func_get_detail(funcname, nargs, oid_array, &funcid, + &rettype, &retset, &true_oid_array); + } + + if (!exists) + elog(WARN, "no such attribute or function '%s'", funcname); + + /* got it */ + funcnode = makeNode(Func); + funcnode->funcid = funcid; + funcnode->functype = rettype; + funcnode->funcisindex = false; + funcnode->funcsize = 0; + funcnode->func_fcache = NULL; + funcnode->func_tlist = NIL; + funcnode->func_planlist = NIL; + + /* perform the necessary typecasting */ + make_arguments(nargs, fargs, oid_array, true_oid_array); + + /* + * for functions returning base types, we want to project out the + * return value. set up a target list to do that. the executor will + * ignore these for c functions, and do the right thing for postquel + * functions. + */ + + if (typeidTypeRelid(rettype) == InvalidOid) + funcnode->func_tlist = setup_base_tlist(rettype); + + /* + * For sets, we want to make a targetlist to project out this + * attribute of the set tuples. + */ + if (attisset) + { + if (!strcmp(funcname, "*")) + { + funcnode->func_tlist = + expandAll(pstate, relname, refname, curr_resno); + } + else + { + funcnode->func_tlist = setup_tlist(funcname, argrelid); + rettype = attnameTypeId(argrelid, funcname); + } + } + + /* + * Sequence handling. + */ + if (funcid == SeqNextValueRegProcedure || + funcid == SeqCurrValueRegProcedure) + { + Const *seq; + char *seqrel; + text *seqname; + int32 aclcheck_result = -1; + extern text *lower (text *string); + + Assert(length(fargs) == 1); + seq = (Const *) lfirst(fargs); + if (!IsA((Node *) seq, Const)) + elog(WARN, "%s: only constant sequence names are acceptable", funcname); + seqname = lower ((text*)DatumGetPointer(seq->constvalue)); + pfree (DatumGetPointer(seq->constvalue)); + seq->constvalue = PointerGetDatum (seqname); + seqrel = textout(seqname); + + if ((aclcheck_result = pg_aclcheck(seqrel, GetPgUserName(), + ((funcid == SeqNextValueRegProcedure) ? ACL_WR : ACL_RD))) + != ACLCHECK_OK) + elog(WARN, "%s.%s: %s", + seqrel, funcname, aclcheck_error_strings[aclcheck_result]); + + pfree(seqrel); + + if (funcid == SeqNextValueRegProcedure && pstate->p_in_where_clause) + elog(WARN, "nextval of a sequence in WHERE disallowed"); + } + + expr = makeNode(Expr); + expr->typeOid = rettype; + expr->opType = FUNC_EXPR; + expr->oper = (Node *) funcnode; + expr->args = fargs; + retval = (Node *) expr; + + /* + * if the function returns a set of values, then we need to iterate + * over all the returned values in the executor, so we stick an iter + * node here. if it returns a singleton, then we don't need the iter + * node. + */ + + if (retset) + { + Iter *iter = makeNode(Iter); + + iter->itertype = rettype; + iter->iterexpr = retval; + retval = (Node *) iter; + } + + return (retval); +} + +Oid +funcid_get_rettype(Oid funcid) +{ + HeapTuple func_tuple = NULL; + Oid funcrettype = (Oid) 0; + + func_tuple = SearchSysCacheTuple(PROOID, ObjectIdGetDatum(funcid), + 0, 0, 0); + + if (!HeapTupleIsValid(func_tuple)) + elog(WARN, "function %d does not exist", funcid); + + funcrettype = (Oid) + ((Form_pg_proc) GETSTRUCT(func_tuple))->prorettype; + + return (funcrettype); +} + +/* + * get a list of all argument type vectors for which a function named + * funcname taking nargs arguments exists + */ +CandidateList +func_get_candidates(char *funcname, int nargs) +{ + Relation heapRelation; + Relation idesc; + ScanKeyData skey; + HeapTuple tuple; + IndexScanDesc sd; + RetrieveIndexResult indexRes; + Buffer buffer; + Form_pg_proc pgProcP; + bool bufferUsed = FALSE; + CandidateList candidates = NULL; + CandidateList current_candidate; + int i; + + heapRelation = heap_openr(ProcedureRelationName); + ScanKeyEntryInitialize(&skey, + (bits16) 0x0, + (AttrNumber) 1, + (RegProcedure) NameEqualRegProcedure, + (Datum) funcname); + + idesc = index_openr(ProcedureNameIndex); + + sd = index_beginscan(idesc, false, 1, &skey); + + do + { + tuple = (HeapTuple) NULL; + if (bufferUsed) + { + ReleaseBuffer(buffer); + bufferUsed = FALSE; + } + + indexRes = index_getnext(sd, ForwardScanDirection); + if (indexRes) + { + ItemPointer iptr; + + iptr = &indexRes->heap_iptr; + tuple = heap_fetch(heapRelation, false, iptr, &buffer); + pfree(indexRes); + if (HeapTupleIsValid(tuple)) + { + pgProcP = (Form_pg_proc) GETSTRUCT(tuple); + bufferUsed = TRUE; + if (pgProcP->pronargs == nargs) + { + current_candidate = (CandidateList) + palloc(sizeof(struct _CandidateList)); + current_candidate->args = (Oid *) + palloc(8 * sizeof(Oid)); + MemSet(current_candidate->args, 0, 8 * sizeof(Oid)); + for (i = 0; i < nargs; i++) + { + current_candidate->args[i] = + pgProcP->proargtypes[i]; + } + + current_candidate->next = candidates; + candidates = current_candidate; + } + } + } + } while (indexRes); + + index_endscan(sd); + index_close(idesc); + heap_close(heapRelation); + + return candidates; +} + +/* + * can input_typeids be coerced to func_typeids? + */ +bool +can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids) +{ + int i; + Type tp; + + /* + * right now, we only coerce "unknown", and we cannot coerce it to a + * relation type + */ + for (i = 0; i < nargs; i++) + { + if (input_typeids[i] != func_typeids[i]) + { + if ((input_typeids[i] == BPCHAROID && func_typeids[i] == TEXTOID) || + (input_typeids[i] == BPCHAROID && func_typeids[i] == VARCHAROID) || + (input_typeids[i] == VARCHAROID && func_typeids[i] == TEXTOID) || + (input_typeids[i] == VARCHAROID && func_typeids[i] == BPCHAROID) || + (input_typeids[i] == CASHOID && func_typeids[i] == INT4OID) || + (input_typeids[i] == INT4OID && func_typeids[i] == CASHOID)) + ; /* these are OK */ + else if (input_typeids[i] != UNKNOWNOID || func_typeids[i] == 0) + return false; + + tp = typeidType(input_typeids[i]); + if (typeTypeFlag(tp) == 'c') + return false; + } + } + + return true; +} + +/* + * given a list of possible typeid arrays to a function and an array of + * input typeids, produce a shortlist of those function typeid arrays + * that match the input typeids (either exactly or by coercion), and + * return the number of such arrays + */ +int +match_argtypes(int nargs, + Oid *input_typeids, + CandidateList function_typeids, + CandidateList *candidates) /* return value */ +{ + CandidateList current_candidate; + CandidateList matching_candidate; + Oid *current_typeids; + int ncandidates = 0; + + *candidates = NULL; + + for (current_candidate = function_typeids; + current_candidate != NULL; + current_candidate = current_candidate->next) + { + current_typeids = current_candidate->args; + if (can_coerce(nargs, input_typeids, current_typeids)) + { + matching_candidate = (CandidateList) + palloc(sizeof(struct _CandidateList)); + matching_candidate->args = current_typeids; + matching_candidate->next = *candidates; + *candidates = matching_candidate; + ncandidates++; + } + } + + return ncandidates; +} + +/* + * given the input argtype array and more than one candidate + * for the function argtype array, attempt to resolve the conflict. + * returns the selected argtype array if the conflict can be resolved, + * otherwise returns NULL + */ +Oid * +func_select_candidate(int nargs, + Oid *input_typeids, + CandidateList candidates) +{ + /* XXX no conflict resolution implemeneted yet */ + return (NULL); +} + +bool +func_get_detail(char *funcname, + int nargs, + Oid *oid_array, + Oid *funcid, /* return value */ + Oid *rettype, /* return value */ + bool *retset, /* return value */ + Oid **true_typeids) /* return value */ +{ + Oid **input_typeid_vector; + Oid *current_input_typeids; + CandidateList function_typeids; + CandidateList current_function_typeids; + HeapTuple ftup; + Form_pg_proc pform; + + /* + * attempt to find named function in the system catalogs with + * arguments exactly as specified - so that the normal case is just as + * quick as before + */ + ftup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(funcname), + Int32GetDatum(nargs), + PointerGetDatum(oid_array), + 0); + *true_typeids = oid_array; + + /* + * If an exact match isn't found : 1) get a vector of all possible + * input arg type arrays constructed from the superclasses of the + * original input arg types 2) get a list of all possible argument + * type arrays to the function with given name and number of arguments + * 3) for each input arg type array from vector #1 : a) find how many + * of the function arg type arrays from list #2 it can be coerced to + * b) - if the answer is one, we have our function - if the answer is + * more than one, attempt to resolve the conflict - if the answer is + * zero, try the next array from vector #1 + */ + if (!HeapTupleIsValid(ftup)) + { + function_typeids = func_get_candidates(funcname, nargs); + + if (function_typeids != NULL) + { + int ncandidates = 0; + + input_typeid_vector = argtype_inherit(nargs, oid_array); + current_input_typeids = oid_array; + + do + { + ncandidates = match_argtypes(nargs, current_input_typeids, + function_typeids, + ¤t_function_typeids); + if (ncandidates == 1) + { + *true_typeids = current_function_typeids->args; + ftup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(funcname), + Int32GetDatum(nargs), + PointerGetDatum(*true_typeids), + 0); + Assert(HeapTupleIsValid(ftup)); + } + else if (ncandidates > 1) + { + *true_typeids = + func_select_candidate(nargs, + current_input_typeids, + current_function_typeids); + if (*true_typeids == NULL) + { + elog(NOTICE, "there is more than one function named \"%s\"", + funcname); + elog(NOTICE, "that satisfies the given argument types. you will have to"); + elog(NOTICE, "retype your query using explicit typecasts."); + func_error("func_get_detail", funcname, nargs, oid_array); + } + else + { + ftup = SearchSysCacheTuple(PRONAME, + PointerGetDatum(funcname), + Int32GetDatum(nargs), + PointerGetDatum(*true_typeids), + 0); + Assert(HeapTupleIsValid(ftup)); + } + } + current_input_typeids = *input_typeid_vector++; + } + while (current_input_typeids != + InvalidOid && ncandidates == 0); + } + } + + if (!HeapTupleIsValid(ftup)) + { + Type tp; + + if (nargs == 1) + { + tp = typeidType(oid_array[0]); + if (typeTypeFlag(tp) == 'c') + elog(WARN, "no such attribute or function \"%s\"", + funcname); + } + func_error("func_get_detail", funcname, nargs, oid_array); + } + else + { + pform = (Form_pg_proc) GETSTRUCT(ftup); + *funcid = ftup->t_oid; + *rettype = pform->prorettype; + *retset = pform->proretset; + + return (true); + } +/* shouldn't reach here */ + return (false); + +} + +/* + * argtype_inherit() -- Construct an argtype vector reflecting the + * inheritance properties of the supplied argv. + * + * This function is used to disambiguate among functions with the + * same name but different signatures. It takes an array of eight + * type ids. For each type id in the array that's a complex type + * (a class), it walks up the inheritance tree, finding all + * superclasses of that type. A vector of new Oid type arrays + * is returned to the caller, reflecting the structure of the + * inheritance tree above the supplied arguments. + * + * The order of this vector is as follows: all superclasses of the + * rightmost complex class are explored first. The exploration + * continues from right to left. This policy means that we favor + * keeping the leftmost argument type as low in the inheritance tree + * as possible. This is intentional; it is exactly what we need to + * do for method dispatch. The last type array we return is all + * zeroes. This will match any functions for which return types are + * not defined. There are lots of these (mostly builtins) in the + * catalogs. + */ +Oid ** +argtype_inherit(int nargs, Oid *oid_array) +{ + Oid relid; + int i; + InhPaths arginh[MAXFARGS]; + + for (i = 0; i < MAXFARGS; i++) + { + if (i < nargs) + { + arginh[i].self = oid_array[i]; + if ((relid = typeidTypeRelid(oid_array[i])) != InvalidOid) + { + arginh[i].nsupers = findsupers(relid, &(arginh[i].supervec)); + } + else + { + arginh[i].nsupers = 0; + arginh[i].supervec = (Oid *) NULL; + } + } + else + { + arginh[i].self = InvalidOid; + arginh[i].nsupers = 0; + arginh[i].supervec = (Oid *) NULL; + } + } + + /* return an ordered cross-product of the classes involved */ + return (genxprod(arginh, nargs)); +} + +int findsupers(Oid relid, Oid **supervec) +{ + Oid *relidvec; + Relation inhrel; + HeapScanDesc inhscan; + ScanKeyData skey; + HeapTuple inhtup; + TupleDesc inhtupdesc; + int nvisited; + SuperQE *qentry, + *vnode; + Dllist *visited, + *queue; + Dlelem *qe, + *elt; + + Relation rd; + Buffer buf; + Datum d; + bool newrelid; + char isNull; + + nvisited = 0; + queue = DLNewList(); + visited = DLNewList(); + + + inhrel = heap_openr(InheritsRelationName); + RelationSetLockForRead(inhrel); + inhtupdesc = RelationGetTupleDescriptor(inhrel); + + /* + * Use queue to do a breadth-first traversal of the inheritance graph + * from the relid supplied up to the root. + */ + do + { + ScanKeyEntryInitialize(&skey, 0x0, Anum_pg_inherits_inhrel, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(relid)); + + inhscan = heap_beginscan(inhrel, 0, false, 1, &skey); + + while (HeapTupleIsValid(inhtup = heap_getnext(inhscan, 0, &buf))) + { + qentry = (SuperQE *) palloc(sizeof(SuperQE)); + + d = fastgetattr(inhtup, Anum_pg_inherits_inhparent, + inhtupdesc, &isNull); + qentry->sqe_relid = DatumGetObjectId(d); + + /* put this one on the queue */ + DLAddTail(queue, DLNewElem(qentry)); + + ReleaseBuffer(buf); + } + + heap_endscan(inhscan); + + /* pull next unvisited relid off the queue */ + do + { + qe = DLRemHead(queue); + qentry = qe ? (SuperQE *) DLE_VAL(qe) : NULL; + + if (qentry == (SuperQE *) NULL) + break; + + relid = qentry->sqe_relid; + newrelid = true; + + for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) + { + vnode = (SuperQE *) DLE_VAL(elt); + if (vnode && (qentry->sqe_relid == vnode->sqe_relid)) + { + newrelid = false; + break; + } + } + } while (!newrelid); + + if (qentry != (SuperQE *) NULL) + { + + /* save the type id, rather than the relation id */ + if ((rd = heap_open(qentry->sqe_relid)) == (Relation) NULL) + elog(WARN, "relid %d does not exist", qentry->sqe_relid); + qentry->sqe_relid = typeTypeId(typenameType(RelationGetRelationName(rd)->data)); + heap_close(rd); + + DLAddTail(visited, qe); + + nvisited++; + } + } while (qentry != (SuperQE *) NULL); + + RelationUnsetLockForRead(inhrel); + heap_close(inhrel); + + if (nvisited > 0) + { + relidvec = (Oid *) palloc(nvisited * sizeof(Oid)); + *supervec = relidvec; + + for (elt = DLGetHead(visited); elt; elt = DLGetSucc(elt)) + { + vnode = (SuperQE *) DLE_VAL(elt); + *relidvec++ = vnode->sqe_relid; + } + + } + else + { + *supervec = (Oid *) NULL; + } + + return (nvisited); +} + +Oid ** +genxprod(InhPaths *arginh, int nargs) +{ + int nanswers; + Oid **result, + **iter; + Oid *oneres; + int i, + j; + int cur[MAXFARGS]; + + nanswers = 1; + for (i = 0; i < nargs; i++) + { + nanswers *= (arginh[i].nsupers + 2); + cur[i] = 0; + } + + iter = result = (Oid **) palloc(sizeof(Oid *) * nanswers); + + /* compute the cross product from right to left */ + for (;;) + { + oneres = (Oid *) palloc(MAXFARGS * sizeof(Oid)); + MemSet(oneres, 0, MAXFARGS * sizeof(Oid)); + + for (i = nargs - 1; i >= 0 && cur[i] > arginh[i].nsupers; i--) + continue; + + /* if we're done, terminate with NULL pointer */ + if (i < 0) + { + *iter = NULL; + return (result); + } + + /* no, increment this column and zero the ones after it */ + cur[i] = cur[i] + 1; + for (j = nargs - 1; j > i; j--) + cur[j] = 0; + + for (i = 0; i < nargs; i++) + { + if (cur[i] == 0) + oneres[i] = arginh[i].self; + else if (cur[i] > arginh[i].nsupers) + oneres[i] = 0; /* wild card */ + else + oneres[i] = arginh[i].supervec[cur[i] - 1]; + } + + *iter++ = oneres; + } +} + + +/* + ** make_arguments -- + ** Given the number and types of arguments to a function, and the + ** actual arguments and argument types, do the necessary typecasting. + */ +void +make_arguments(int nargs, + List *fargs, + Oid *input_typeids, + Oid *function_typeids) +{ + + /* + * there are two ways an input typeid can differ from a function + * typeid : either the input type inherits the function type, so no + * typecasting is necessary, or the input type can be typecast into + * the function type. right now, we only typecast unknowns, and that + * is all we check for. + */ + + List *current_fargs; + int i; + + for (i = 0, current_fargs = fargs; + i < nargs; + i++, current_fargs = lnext(current_fargs)) + { + + if (input_typeids[i] == UNKNOWNOID && function_typeids[i] != InvalidOid) + { + lfirst(current_fargs) = + parser_typecast2(lfirst(current_fargs), + input_typeids[i], + typeidType(function_typeids[i]), + -1); + } + } +} + +/* + ** setup_tlist -- + ** Build a tlist that says which attribute to project to. + ** This routine is called by ParseFunc() to set up a target list + ** on a tuple parameter or return value. Due to a bug in 4.0, + ** it's not possible to refer to system attributes in this case. + */ +List * +setup_tlist(char *attname, Oid relid) +{ + TargetEntry *tle; + Resdom *resnode; + Var *varnode; + Oid typeid; + int attno; + + attno = get_attnum(relid, attname); + if (attno < 0) + elog(WARN, "cannot reference attribute '%s' of tuple params/return values for functions", attname); + + typeid = attnameTypeId(relid, attname); + resnode = makeResdom(1, + typeid, + typeLen(typeidType(typeid)), + get_attname(relid, attno), + 0, + (Oid) 0, + 0); + varnode = makeVar(-1, attno, typeid, -1, attno); + + tle = makeNode(TargetEntry); + tle->resdom = resnode; + tle->expr = (Node *) varnode; + return (lcons(tle, NIL)); +} + +/* + ** setup_base_tlist -- + ** Build a tlist that extracts a base type from the tuple + ** returned by the executor. + */ +List * +setup_base_tlist(Oid typeid) +{ + TargetEntry *tle; + Resdom *resnode; + Var *varnode; + + resnode = makeResdom(1, + typeid, + typeLen(typeidType(typeid)), + "", + 0, + (Oid) 0, + 0); + varnode = makeVar(-1, 1, typeid, -1, 1); + tle = makeNode(TargetEntry); + tle->resdom = resnode; + tle->expr = (Node *) varnode; + + return (lcons(tle, NIL)); +} + +/* + * ParseComplexProjection - + * handles function calls with a single argument that is of complex type. + * This routine returns NULL if it can't handle the projection (eg. sets). + */ +Node * +ParseComplexProjection(ParseState *pstate, + char *funcname, + Node *first_arg, + bool *attisset) +{ + Oid argtype; + Oid argrelid; + Name relname; + Relation rd; + Oid relid; + int attnum; + + switch (nodeTag(first_arg)) + { + case T_Iter: + { + Func *func; + Iter *iter; + + iter = (Iter *) first_arg; + func = (Func *) ((Expr *) iter->iterexpr)->oper; + argtype = funcid_get_rettype(func->funcid); + argrelid = typeidTypeRelid(argtype); + if (argrelid && + ((attnum = get_attnum(argrelid, funcname)) + != InvalidAttrNumber)) + { + + /* + * the argument is a function returning a tuple, so + * funcname may be a projection + */ + + /* add a tlist to the func node and return the Iter */ + rd = heap_openr(typeidTypeName(argtype)); + if (RelationIsValid(rd)) + { + relid = RelationGetRelationId(rd); + relname = RelationGetRelationName(rd); + heap_close(rd); + } + if (RelationIsValid(rd)) + { + func->func_tlist = + setup_tlist(funcname, argrelid); + iter->itertype = attnumTypeId(rd, attnum); + return ((Node *) iter); + } + else + { + elog(WARN, + "Function '%s' has bad returntype %d", + funcname, argtype); + } + } + else + { + /* drop through */ + ; + } + break; + } + case T_Var: + { + + /* + * The argument is a set, so this is either a projection + * or a function call on this set. + */ + *attisset = true; + break; + } + case T_Expr: + { + Expr *expr = (Expr *) first_arg; + Func *funcnode; + + if (expr->opType != FUNC_EXPR) + break; + + funcnode = (Func *) expr->oper; + argtype = funcid_get_rettype(funcnode->funcid); + argrelid = typeidTypeRelid(argtype); + + /* + * the argument is a function returning a tuple, so + * funcname may be a projection + */ + if (argrelid && + (attnum = get_attnum(argrelid, funcname)) + != InvalidAttrNumber) + { + + /* add a tlist to the func node */ + rd = heap_openr(typeidTypeName(argtype)); + if (RelationIsValid(rd)) + { + relid = RelationGetRelationId(rd); + relname = RelationGetRelationName(rd); + heap_close(rd); + } + if (RelationIsValid(rd)) + { + Expr *newexpr; + + funcnode->func_tlist = + setup_tlist(funcname, argrelid); + funcnode->functype = attnumTypeId(rd, attnum); + + newexpr = makeNode(Expr); + newexpr->typeOid = funcnode->functype; + newexpr->opType = FUNC_EXPR; + newexpr->oper = (Node *) funcnode; + newexpr->args = lcons(first_arg, NIL); + + return ((Node *) newexpr); + } + + } + + elog(WARN, "Function '%s' has bad returntype %d", + funcname, argtype); + break; + } + case T_Param: + { + Param *param = (Param *) first_arg; + + /* + * If the Param is a complex type, this could be a + * projection + */ + rd = heap_openr(typeidTypeName(param->paramtype)); + if (RelationIsValid(rd)) + { + relid = RelationGetRelationId(rd); + relname = RelationGetRelationName(rd); + heap_close(rd); + } + if (RelationIsValid(rd) && + (attnum = get_attnum(relid, funcname)) + != InvalidAttrNumber) + { + + param->paramtype = attnumTypeId(rd, attnum); + param->param_tlist = setup_tlist(funcname, relid); + return ((Node *) param); + } + break; + } + default: + break; + } + + return NULL; +} + +/* + * Error message when function lookup fails that gives details of the + * argument types + */ +void +func_error(char *caller, char *funcname, int nargs, Oid *argtypes) +{ + char p[(NAMEDATALEN + 2) * MAXFMGRARGS], + *ptr; + int i; + + ptr = p; + *ptr = '\0'; + for (i = 0; i < nargs; i++) + { + if (i) + { + *ptr++ = ','; + *ptr++ = ' '; + } + if (argtypes[i] != 0) + { + strcpy(ptr, typeidTypeName(argtypes[i])); + *(ptr + NAMEDATALEN) = '\0'; + } + else + strcpy(ptr, "opaque"); + ptr += strlen(ptr); + } + + elog(WARN, "%s: function %s(%s) does not exist", caller, funcname, p); +} + + + diff --git a/src/backend/parser/parse_query.c b/src/backend/parser/parse_node.c similarity index 56% rename from src/backend/parser/parse_query.c rename to src/backend/parser/parse_node.c index c47feeaa11..c06e00888b 100644 --- a/src/backend/parser/parse_query.c +++ b/src/backend/parser/parse_node.c @@ -1,259 +1,76 @@ /*------------------------------------------------------------------------- * - * parse_query.c-- - * take an "optimizable" stmt and make the query tree that - * the planner requires. + * parse_node.c-- + * various routines that make nodes for query plans * * Copyright (c) 1994, Regents of the University of California * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/Attic/parse_query.c,v 1.25 1997/11/24 05:08:27 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parse_node.c,v 1.1 1997/11/25 22:05:42 momjian Exp $ * *------------------------------------------------------------------------- */ #include #include -#include "postgres.h" +#include "postgres.h" #include "fmgr.h" #include "access/heapam.h" -#include "access/tupmacs.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "parser/parse_expr.h" +#include "parser/parse_oper.h" +#include "parser/parse_node.h" +#include "parser/parse_relation.h" +#include "parser/parse_type.h" #include "utils/builtins.h" +#include "utils/syscache.h" + +#ifdef 0 +#include "access/tupmacs.h" #include "utils/elog.h" #include "utils/palloc.h" #include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */ #include "utils/rel.h" /* Relation stuff */ #include "utils/syscache.h" -#include "catalog/pg_type.h" -#include "catalog/pg_operator.h" -#include "parser/catalog_utils.h" -#include "parser/parse_query.h" -#include "utils/lsyscache.h" #include "nodes/pg_list.h" #include "nodes/primnodes.h" #include "nodes/parsenodes.h" -#include "nodes/makefuncs.h" - -static void -checkTargetTypes(ParseState *pstate, char *target_colname, - char *refname, char *colname); - -Oid *param_type_info; -int pfunc_num_args; - -/* given refname, return a pointer to the range table entry */ -RangeTblEntry * -refnameRangeTableEntry(List *rtable, char *refname) -{ - List *temp; - - foreach(temp, rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (!strcmp(rte->refname, refname)) - return rte; - } - return NULL; -} - -/* given refname, return id of variable; position starts with 1 */ -int -refnameRangeTablePosn(List *rtable, char *refname) -{ - int index; - List *temp; - - index = 1; - foreach(temp, rtable) - { - RangeTblEntry *rte = lfirst(temp); - - if (!strcmp(rte->refname, refname)) - return index; - index++; - } - return (0); -} +#endif /* - * returns range entry if found, else NULL + * make_parsestate() -- + * allocate and initialize a new ParseState. + * the CALLERS is responsible for freeing the ParseState* returned + * */ -RangeTblEntry * -colnameRangeTableEntry(ParseState *pstate, char *colname) + +ParseState * +make_parsestate(void) { - List *et; - List *rtable; - RangeTblEntry *rte_result; + ParseState *pstate; - if (pstate->p_is_rule) - rtable = lnext(lnext(pstate->p_rtable)); - else - rtable = pstate->p_rtable; + pstate = malloc(sizeof(ParseState)); + pstate->p_last_resno = 1; + pstate->p_rtable = NIL; + pstate->p_numAgg = 0; + pstate->p_aggs = NIL; + pstate->p_is_insert = false; + pstate->p_insert_columns = NIL; + pstate->p_is_update = false; + pstate->p_is_rule = false; + pstate->p_in_where_clause = false; + pstate->p_target_relation = NULL; + pstate->p_target_rangetblentry = NULL; - rte_result = NULL; - foreach(et, rtable) - { - RangeTblEntry *rte = lfirst(et); - - /* only entries on outer(non-function?) scope */ - if (!rte->inFromCl && rte != pstate->p_target_rangetblentry) - continue; - - if (get_attnum(rte->relid, colname) != InvalidAttrNumber) - { - if (rte_result != NULL) - { - if (!pstate->p_is_insert || - rte != pstate->p_target_rangetblentry) - elog(WARN, "Column %s is ambiguous", colname); - } - else - rte_result = rte; - } - } - return rte_result; + return (pstate); } -/* - * put new entry in pstate p_rtable structure, or return pointer - * if pstate null -*/ -RangeTblEntry * -addRangeTableEntry(ParseState *pstate, - char *relname, - char *refname, - bool inh, - bool inFromCl) -{ - Relation relation; - RangeTblEntry *rte = makeNode(RangeTblEntry); - - if (pstate != NULL && - refnameRangeTableEntry(pstate->p_rtable, refname) != NULL) - elog(WARN, "Table name %s specified more than once", refname); - - rte->relname = pstrdup(relname); - rte->refname = pstrdup(refname); - - relation = heap_openr(relname); - if (relation == NULL) - { - elog(WARN, "%s: %s", - relname, aclcheck_error_strings[ACLCHECK_NO_CLASS]); - } - - /* - * Flags - zero or more from inheritance,union,version or - * recursive (transitive closure) [we don't support them all -- ay - * 9/94 ] - */ - rte->inh = inh; - - /* RelOID */ - rte->relid = RelationGetRelationId(relation); - - rte->inFromCl = inFromCl; - - /* - * close the relation we're done with it for now. - */ - if (pstate != NULL) - pstate->p_rtable = lappend(pstate->p_rtable, rte); - - heap_close(relation); - - return rte; -} - -/* - * expandAll - - * makes a list of attributes - * assumes reldesc caching works - */ -List * -expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) -{ - Relation rdesc; - List *te_tail = NIL, - *te_head = NIL; - Var *varnode; - int varattno, - maxattrs; - Oid type_id; - int type_len; - RangeTblEntry *rte; - - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - if (rte == NULL) - rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE); - - rdesc = heap_open(rte->relid); - - if (rdesc == NULL) - { - elog(WARN, "Unable to expand all -- heap_open failed on %s", - rte->refname); - return NIL; - } - maxattrs = RelationGetNumberOfAttributes(rdesc); - - for (varattno = 0; varattno <= maxattrs - 1; varattno++) - { - char *attrname; - char *resname = NULL; - TargetEntry *te = makeNode(TargetEntry); - - attrname = pstrdup((rdesc->rd_att->attrs[varattno]->attname).data); - varnode = (Var *) make_var(pstate, refname, attrname, &type_id); - type_len = (int) tlen(get_id_type(type_id)); - - handleTargetColname(pstate, &resname, refname, attrname); - if (resname != NULL) - attrname = resname; - - /* - * Even if the elements making up a set are complex, the set - * itself is not. - */ - - te->resdom = makeResdom((AttrNumber) (*this_resno)++, - type_id, - (Size) type_len, - attrname, - (Index) 0, - (Oid) 0, - 0); - te->expr = (Node *) varnode; - if (te_head == NIL) - te_head = te_tail = lcons(te, NIL); - else - te_tail = lappend(te_tail, te); - } - - heap_close(rdesc); - return (te_head); -} - -static void -disallow_setop(char *op, Type optype, Node *operand) -{ - if (operand == NULL) - return; - - if (nodeTag(operand) == T_Iter) - { - elog(NOTICE, "An operand to the '%s' operator returns a set of %s,", - op, tname(optype)); - elog(WARN, "but '%s' takes single values, not sets.", - op); - } -} - -static Node * +Node * make_operand(char *opname, Node *tree, Oid orig_typeId, @@ -267,7 +84,7 @@ make_operand(char *opname, if (tree != NULL) { result = tree; - true_type = get_id_type(true_typeId); + true_type = typeidType(true_typeId); disallow_setop(opname, true_type, result); if (true_typeId != orig_typeId) { /* must coerce */ @@ -276,13 +93,13 @@ make_operand(char *opname, Assert(nodeTag(result) == T_Const); val = (Datum) textout((struct varlena *) con->constvalue); - infunc = typeid_get_retinfunc(true_typeId); + infunc = typeidRetinfunc(true_typeId); con = makeNode(Const); con->consttype = true_typeId; - con->constlen = tlen(true_type); + con->constlen = typeLen(true_type); con->constvalue = (Datum) fmgr(infunc, val, - get_typelem(true_typeId), + typeidTypElem(true_typeId), -1 /* for varchar() type */ ); con->constisnull = false; con->constbyval = true; @@ -307,6 +124,21 @@ make_operand(char *opname, } +void +disallow_setop(char *op, Type optype, Node *operand) +{ + if (operand == NULL) + return; + + if (nodeTag(operand) == T_Iter) + { + elog(NOTICE, "An operand to the '%s' operator returns a set of %s,", + op, typeTypeName(optype)); + elog(WARN, "but '%s' takes single values, not sets.", + op); + } +} + Expr * make_op(char *opname, Node *ltree, Node *rtree) { @@ -367,30 +199,30 @@ make_op(char *opname, Node *ltree, Node *rtree) CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) == T_Const && !((Const *) rtree)->constiscast) { - outfunc = typeid_get_retoutfunc(rtypeId); - infunc = typeid_get_retinfunc(ltypeId); + outfunc = typeidRetoutfunc(rtypeId); + infunc = typeidRetinfunc(ltypeId); outstr = (char *) fmgr(outfunc, ((Const *) rtree)->constvalue); ((Const *) rtree)->constvalue = (Datum) fmgr(infunc, outstr); pfree(outstr); ((Const *) rtree)->consttype = rtypeId = ltypeId; - newtype = get_id_type(rtypeId); - ((Const *) rtree)->constlen = tlen(newtype); - ((Const *) rtree)->constbyval = tbyval(newtype); + newtype = typeidType(rtypeId); + ((Const *) rtree)->constlen = typeLen(newtype); + ((Const *) rtree)->constbyval = typeByVal(newtype); } if (CONVERTABLE_TYPE(rtypeId) && nodeTag(rtree) != T_Const && CONVERTABLE_TYPE(ltypeId) && nodeTag(ltree) == T_Const && !((Const *) ltree)->constiscast) { - outfunc = typeid_get_retoutfunc(ltypeId); - infunc = typeid_get_retinfunc(rtypeId); + outfunc = typeidRetoutfunc(ltypeId); + infunc = typeidRetinfunc(rtypeId); outstr = (char *) fmgr(outfunc, ((Const *) ltree)->constvalue); ((Const *) ltree)->constvalue = (Datum) fmgr(infunc, outstr); pfree(outstr); ((Const *) ltree)->consttype = ltypeId = rtypeId; - newtype = get_id_type(ltypeId); - ((Const *) ltree)->constlen = tlen(newtype); - ((Const *) ltree)->constbyval = tbyval(newtype); + newtype = typeidType(ltypeId); + ((Const *) ltree)->constlen = typeLen(newtype); + ((Const *) ltree)->constbyval = typeByVal(newtype); } temp = oper(opname, ltypeId, rtypeId, false); @@ -426,38 +258,6 @@ make_op(char *opname, Node *ltree, Node *rtree) return result; } -Oid -find_atttype(Oid relid, char *attrname) -{ - int attid; - Oid vartype; - Relation rd; - - rd = heap_open(relid); - if (!RelationIsValid(rd)) - { - rd = heap_openr(tname(get_id_type(relid))); - if (!RelationIsValid(rd)) - elog(WARN, "cannot compute type of att %s for relid %d", - attrname, relid); - } - - attid = nf_varattno(rd, attrname); - - if (attid == InvalidAttrNumber) - elog(WARN, "Invalid attribute %s\n", attrname); - - vartype = att_typeid(rd, attid); - - /* - * close relation we're done with it now - */ - heap_close(rd); - - return (vartype); -} - - Var * make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id) { @@ -476,10 +276,8 @@ make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id) rd = heap_open(rte->relid); - attid = nf_varattno(rd, attrname); - if (attid == InvalidAttrNumber) - elog(WARN, "Invalid attribute %s\n", attrname); - vartypeid = att_typeid(rd, attid); + attid = attnameAttNum(rd, attrname); /* could elog(WARN) */ + vartypeid = attnumTypeId(rd, attid); varnode = makeVar(vnum, attid, vartypeid, vnum, attid); @@ -667,7 +465,7 @@ make_const(Value *value) switch (nodeTag(value)) { case T_Integer: - tp = type("int4"); + tp = typeidType(INT4OID); val = Int32GetDatum(intVal(value)); break; @@ -675,7 +473,7 @@ make_const(Value *value) { float64 dummy; - tp = type("float8"); + tp = typeidType(FLOAT8OID); dummy = (float64) palloc(sizeof(float64data)); *dummy = floatVal(value); @@ -685,7 +483,7 @@ make_const(Value *value) break; case T_String: - tp = type("unknown"); /* unknown for now, will be type + tp = typeidType(UNKNOWNOID); /* unknown for now, will be type * coerced */ val = PointerGetDatum(textin(strVal(value))); break; @@ -702,111 +500,14 @@ make_const(Value *value) } } - con = makeConst(typeid(tp), - tlen(tp), + con = makeConst(typeTypeId(tp), + typeLen(tp), val, false, - tbyval(tp), + typeByVal(tp), false, /* not a set */ false); return (con); } -/* - * param_type_init() - * - * keep enough information around fill out the type of param nodes - * used in postquel functions - */ -void -param_type_init(Oid *typev, int nargs) -{ - pfunc_num_args = nargs; - param_type_info = typev; -} - -Oid -param_type(int t) -{ - if ((t > pfunc_num_args) || (t == 0)) - return InvalidOid; - return param_type_info[t - 1]; -} - -/* - * handleTargetColname - - * use column names from insert - */ -void -handleTargetColname(ParseState *pstate, char **resname, - char *refname, char *colname) -{ - if (pstate->p_is_insert) - { - if (pstate->p_insert_columns != NIL) - { - Ident *id = lfirst(pstate->p_insert_columns); - - *resname = id->name; - pstate->p_insert_columns = lnext(pstate->p_insert_columns); - } - else - elog(WARN, "insert: more expressions than target columns"); - } - if (pstate->p_is_insert || pstate->p_is_update) - checkTargetTypes(pstate, *resname, refname, colname); -} - -/* - * checkTargetTypes - - * checks value and target column types - */ -static void -checkTargetTypes(ParseState *pstate, char *target_colname, - char *refname, char *colname) -{ - Oid attrtype_id, - attrtype_target; - int resdomno_id, - resdomno_target; - Relation rd; - RangeTblEntry *rte; - - if (target_colname == NULL || colname == NULL) - return; - - if (refname != NULL) - rte = refnameRangeTableEntry(pstate->p_rtable, refname); - else - { - rte = colnameRangeTableEntry(pstate, colname); - if (rte == (RangeTblEntry *) NULL) - elog(WARN, "attribute %s not found", colname); - refname = rte->refname; - } - -/* - if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry) - elog(WARN, "%s not available in this context", colname); -*/ - rd = heap_open(rte->relid); - - resdomno_id = varattno(rd, colname); - attrtype_id = att_typeid(rd, resdomno_id); - - resdomno_target = varattno(pstate->p_target_relation, target_colname); - attrtype_target = att_typeid(pstate->p_target_relation, resdomno_target); - - if (attrtype_id != attrtype_target) - elog(WARN, "Type of %s does not match target column %s", - colname, target_colname); - - if ((attrtype_id == BPCHAROID || attrtype_id == VARCHAROID) && - rd->rd_att->attrs[resdomno_id - 1]->attlen != - pstate->p_target_relation->rd_att->attrs[resdomno_target - 1]->attlen) - elog(WARN, "Length of %s does not match length of target column %s", - colname, target_colname); - - heap_close(rd); -} diff --git a/src/backend/parser/parse_oper.c b/src/backend/parser/parse_oper.c new file mode 100644 index 0000000000..d82a46bc2f --- /dev/null +++ b/src/backend/parser/parse_oper.c @@ -0,0 +1,613 @@ +/*------------------------------------------------------------------------- + * + * parse_oper.h + * handle operator things for parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_oper.c,v 1.1 1997/11/25 22:05:43 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include "postgres.h" +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef 0 +#include "lib/dllist.h" +#include "utils/datum.h" + +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" + +#include "catalog/pg_inherits.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/indexing.h" +#include "catalog/catname.h" + +#include "access/skey.h" +#include "access/relscan.h" +#include "access/tupdesc.h" +#include "access/htup.h" +#include "access/genam.h" +#include "access/itup.h" +#include "access/tupmacs.h" +#include "storage/buf.h" +#include "utils/lsyscache.h" +#include "storage/lmgr.h" + +#include "port-protos.h" /* strdup() */ +#endif + +Oid +any_ordering_op(int restype) +{ + Operator order_op; + Oid order_opid; + + order_op = oper("<", restype, restype, false); + order_opid = oprid(order_op); + + return order_opid; +} + +/* given operator, return the operator OID */ +Oid +oprid(Operator op) +{ + return (op->t_oid); +} + +/* + * 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 + */ +int +binary_oper_get_candidates(char *opname, + Oid leftTypeId, + Oid rightTypeId, + 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]; + + *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 (leftTypeId == UNKNOWNOID) + { + if (rightTypeId == UNKNOWNOID) + { + nkeys = 2; + } + else + { + nkeys = 3; + + ScanKeyEntryInitialize(&opKey[2], 0, + Anum_pg_operator_oprright, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(rightTypeId)); + } + } + else if (rightTypeId == UNKNOWNOID) + { + nkeys = 3; + + ScanKeyEntryInitialize(&opKey[2], 0, + Anum_pg_operator_oprleft, + ObjectIdEqualRegProcedure, + ObjectIdGetDatum(leftTypeId)); + } + else + /* currently only "unknown" can be coerced */ + return 0; + + 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; +} + +/* + * equivalentOpersAfterPromotion - + * checks if a list of candidate operators obtained from + * binary_oper_get_candidates() contain equivalent operators. If + * this routine is called, we have more than 1 candidate and need to + * decided whether to pick one of them. This routine returns true if + * the all the candidates operate on the same data types after + * promotion (int2, int4, float4 -> float8). + */ +bool +equivalentOpersAfterPromotion(CandidateList candidates) +{ + CandidateList result; + CandidateList promotedCandidates = NULL; + Oid leftarg, + rightarg; + + for (result = candidates; result != NULL; result = result->next) + { + CandidateList c; + + c = (CandidateList) palloc(sizeof(*c)); + c->args = (Oid *) palloc(2 * sizeof(Oid)); + switch (result->args[0]) + { + case FLOAT4OID: + case INT4OID: + case INT2OID: + case CASHOID: + c->args[0] = FLOAT8OID; + break; + default: + c->args[0] = result->args[0]; + break; + } + switch (result->args[1]) + { + case FLOAT4OID: + case INT4OID: + case INT2OID: + case CASHOID: + c->args[1] = FLOAT8OID; + break; + default: + c->args[1] = result->args[1]; + break; + } + c->next = promotedCandidates; + promotedCandidates = c; + } + + /* + * if we get called, we have more than 1 candidates so we can do the + * following safely + */ + leftarg = promotedCandidates->args[0]; + rightarg = promotedCandidates->args[1]; + + for (result = promotedCandidates->next; result != NULL; result = result->next) + { + if (result->args[0] != leftarg || result->args[1] != rightarg) + + /* + * this list contains operators that operate on different data + * types even after promotion. Hence we can't decide on which + * one to pick. The user must do explicit type casting. + */ + return FALSE; + } + + /* + * all the candidates are equivalent in the following sense: they + * operate on equivalent data types and picking any one of them is as + * good. + */ + return TRUE; +} + + +/* + * given a choice of argument type pairs for a binary operator, + * try to choose a default pair + */ +CandidateList +binary_oper_select_candidate(Oid arg1, + Oid arg2, + CandidateList candidates) +{ + CandidateList result; + + /* + * if both are "unknown", there is no way to select a candidate + * + * current wisdom holds that the default operator should be one in which + * both operands have the same type (there will only be one such + * operator) + * + * 7.27.93 - I have decided not to do this; it's too hard to justify, and + * it's easy enough to typecast explicitly -avi [the rest of this + * routine were commented out since then -ay] + */ + + if (arg1 == UNKNOWNOID && arg2 == UNKNOWNOID) + return (NULL); + + /* + * 6/23/95 - I don't complete agree with avi. In particular, casting + * floats is a pain for users. Whatever the rationale behind not doing + * this is, I need the following special case to work. + * + * In the WHERE clause of a query, if a float is specified without + * quotes, we treat it as float8. I added the float48* operators so + * that we can operate on float4 and float8. But now we have more than + * one matching operator if the right arg is unknown (eg. float + * specified with quotes). This break some stuff in the regression + * test where there are floats in quotes not properly casted. Below is + * the solution. In addition to requiring the operator operates on the + * same type for both operands [as in the code Avi originally + * commented out], we also require that the operators be equivalent in + * some sense. (see equivalentOpersAfterPromotion for details.) - ay + * 6/95 + */ + if (!equivalentOpersAfterPromotion(candidates)) + return NULL; + + /* + * if we get here, any one will do but we're more picky and require + * both operands be the same. + */ + for (result = candidates; result != NULL; result = result->next) + { + if (result->args[0] == result->args[1]) + return result; + } + + return (NULL); +} + +/* Given operator, types of arg1, and arg2, return oper struct */ +/* arg1, arg2 --typeids */ +Operator +oper(char *op, Oid arg1, Oid arg2, bool noWarnings) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + if (!arg2) + arg2 = arg1; + if (!arg1) + arg1 = arg2; + + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg1), + ObjectIdGetDatum(arg2), + Int8GetDatum('b')))) + { + ncandidates = binary_oper_get_candidates(op, arg1, arg2, &candidates); + if (ncandidates == 0) + { + + /* + * no operators of the desired types found + */ + if (!noWarnings) + op_error(op, arg1, arg2); + return (NULL); + } + else if (ncandidates == 1) + { + + /* + * exactly one operator of the desired types found + */ + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[1]), + Int8GetDatum('b')); + Assert(HeapTupleIsValid(tup)); + } + else + { + + /* + * multiple operators of the desired types found + */ + candidates = binary_oper_select_candidate(arg1, arg2, candidates); + if (candidates != NULL) + { + /* we chose one of them */ + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(candidates->args[1]), + Int8GetDatum('b')); + Assert(HeapTupleIsValid(tup)); + } + else + { + Type tp1, + tp2; + + /* we chose none of them */ + tp1 = typeidType(arg1); + tp2 = typeidType(arg2); + if (!noWarnings) + { + elog(NOTICE, "there is more than one operator %s for types", op); + elog(NOTICE, "%s and %s. You will have to retype this query", + typeTypeName(tp1), typeTypeName(tp2)); + elog(WARN, "using an explicit cast"); + } + return (NULL); + } + } + } + return ((Operator) tup); +} + +/* + * given opname and typeId, find all possible types for which + * a right/left unary operator named opname exists, + * such that typeId can be coerced to it + */ +int +unary_oper_get_candidates(char *op, + Oid typeId, + CandidateList *candidates, + char rightleft) +{ + CandidateList current_candidate; + Relation pg_operator_desc; + HeapScanDesc pg_operator_scan; + HeapTuple tup; + OperatorTupleForm oper; + Buffer buffer; + int ncandidates = 0; + + static ScanKeyData opKey[2] = { + {0, Anum_pg_operator_oprname, NameEqualRegProcedure}, + {0, Anum_pg_operator_oprkind, CharacterEqualRegProcedure}}; + + *candidates = NULL; + + fmgr_info(NameEqualRegProcedure, (func_ptr *) &opKey[0].sk_func, + &opKey[0].sk_nargs); + opKey[0].sk_argument = NameGetDatum(op); + fmgr_info(CharacterEqualRegProcedure, (func_ptr *) &opKey[1].sk_func, + &opKey[1].sk_nargs); + opKey[1].sk_argument = CharGetDatum(rightleft); + + /* currently, only "unknown" can be coerced */ + + /* + * but we should allow types that are internally the same to be + * "coerced" + */ + if (typeId != UNKNOWNOID) + { + return 0; + } + + pg_operator_desc = heap_openr(OperatorRelationName); + pg_operator_scan = heap_beginscan(pg_operator_desc, + 0, + true, + 2, + 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(sizeof(Oid)); + + oper = (OperatorTupleForm) GETSTRUCT(tup); + if (rightleft == 'r') + current_candidate->args[0] = oper->oprleft; + else + current_candidate->args[0] = 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; +} + +/* Given unary right-side operator (operator on right), return oper struct */ +/* arg-- type id */ +Operator +right_oper(char *op, Oid arg) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + /* + * if (!OpCache) { init_op_cache(); } + */ + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(arg), + ObjectIdGetDatum(InvalidOid), + Int8GetDatum('r')))) + { + ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'r'); + if (ncandidates == 0) + { + elog(WARN, + "Can't find right op: %s for type %d", op, arg); + return (NULL); + } + else if (ncandidates == 1) + { + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(candidates->args[0]), + ObjectIdGetDatum(InvalidOid), + Int8GetDatum('r')); + Assert(HeapTupleIsValid(tup)); + } + else + { + elog(NOTICE, "there is more than one right operator %s", op); + elog(NOTICE, "you will have to retype this query"); + elog(WARN, "using an explicit cast"); + return (NULL); + } + } + return ((Operator) tup); +} + +/* Given unary left-side operator (operator on left), return oper struct */ +/* arg--type id */ +Operator +left_oper(char *op, Oid arg) +{ + HeapTuple tup; + CandidateList candidates; + int ncandidates; + + /* + * if (!OpCache) { init_op_cache(); } + */ + if (!(tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(arg), + Int8GetDatum('l')))) + { + ncandidates = unary_oper_get_candidates(op, arg, &candidates, 'l'); + if (ncandidates == 0) + { + elog(WARN, + "Can't find left op: %s for type %d", op, arg); + return (NULL); + } + else if (ncandidates == 1) + { + tup = SearchSysCacheTuple(OPRNAME, + PointerGetDatum(op), + ObjectIdGetDatum(InvalidOid), + ObjectIdGetDatum(candidates->args[0]), + Int8GetDatum('l')); + Assert(HeapTupleIsValid(tup)); + } + else + { + elog(NOTICE, "there is more than one left operator %s", op); + elog(NOTICE, "you will have to retype this query"); + elog(WARN, "using an explicit cast"); + return (NULL); + } + } + return ((Operator) tup); +} + +/* Given a typename and value, returns the ascii form of the value */ + +#ifdef NOT_USED +char * +outstr(char *typename, /* Name of type of value */ + char *value) /* Could be of any type */ +{ + TypeTupleForm tp; + Oid op; + + tp = (TypeTupleForm) GETSTRUCT(type(typename)); + op = tp->typoutput; + return ((char *) fmgr(op, value)); +} + +#endif + +/* + * Give a somewhat useful error message when the operator for two types + * is not found. + */ +void +op_error(char *op, Oid arg1, Oid arg2) +{ + Type tp1 = NULL, + tp2 = NULL; + + if (typeidIsValid(arg1)) + { + tp1 = typeidType(arg1); + } + else + { + elog(WARN, "left hand side of operator %s has an unknown type, probably a bad attribute name", op); + } + + if (typeidIsValid(arg2)) + { + tp2 = typeidType(arg2); + } + else + { + elog(WARN, "right hand side of operator %s has an unknown type, probably a bad attribute name", op); + } + + 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(WARN, "%s for %s and %s using CREATE OPERATOR", + op, typeTypeName(tp1), typeTypeName(tp2)); +} + diff --git a/src/backend/parser/parse_relation.c b/src/backend/parser/parse_relation.c new file mode 100644 index 0000000000..dd3fa2787a --- /dev/null +++ b/src/backend/parser/parse_relation.c @@ -0,0 +1,480 @@ +/*------------------------------------------------------------------------- + * + * parse_relation.c-- + * parser support routines dealing with relations + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_relation.c,v 1.1 1997/11/25 22:05:45 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include + +#include "postgres.h" +#include "access/heapam.h" +#include +#include +#include "nodes/makefuncs.h" +#include +#include +#include "utils/builtins.h" +#include + +#ifdef 0 +#include "fmgr.h" +#include "access/tupmacs.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/acl.h" /* for ACL_NO_PRIV_WARNING */ + +#include "utils/syscache.h" +#include "catalog/pg_operator.h" + +#include "nodes/pg_list.h" +#include "nodes/primnodes.h" +#include "nodes/parsenodes.h" +#endif + +struct +{ + char *field; + int code; +} special_attr[] = + +{ + { + "ctid", SelfItemPointerAttributeNumber + }, + { + "oid", ObjectIdAttributeNumber + }, + { + "xmin", MinTransactionIdAttributeNumber + }, + { + "cmin", MinCommandIdAttributeNumber + }, + { + "xmax", MaxTransactionIdAttributeNumber + }, + { + "cmax", MaxCommandIdAttributeNumber + }, +}; + +#define SPECIALS (sizeof(special_attr)/sizeof(*special_attr)) + +static char *attnum_type[SPECIALS] = { + "tid", + "oid", + "xid", + "cid", + "xid", + "cid", +}; + +/* given refname, return a pointer to the range table entry */ +RangeTblEntry * +refnameRangeTableEntry(List *rtable, char *refname) +{ + List *temp; + + foreach(temp, rtable) + { + RangeTblEntry *rte = lfirst(temp); + + if (!strcmp(rte->refname, refname)) + return rte; + } + return NULL; +} + +/* given refname, return id of variable; position starts with 1 */ +int +refnameRangeTablePosn(List *rtable, char *refname) +{ + int index; + List *temp; + + index = 1; + foreach(temp, rtable) + { + RangeTblEntry *rte = lfirst(temp); + + if (!strcmp(rte->refname, refname)) + return index; + index++; + } + return (0); +} + +/* + * returns range entry if found, else NULL + */ +RangeTblEntry * +colnameRangeTableEntry(ParseState *pstate, char *colname) +{ + List *et; + List *rtable; + RangeTblEntry *rte_result; + + if (pstate->p_is_rule) + rtable = lnext(lnext(pstate->p_rtable)); + else + rtable = pstate->p_rtable; + + rte_result = NULL; + foreach(et, rtable) + { + RangeTblEntry *rte = lfirst(et); + + /* only entries on outer(non-function?) scope */ + if (!rte->inFromCl && rte != pstate->p_target_rangetblentry) + continue; + + if (get_attnum(rte->relid, colname) != InvalidAttrNumber) + { + if (rte_result != NULL) + { + if (!pstate->p_is_insert || + rte != pstate->p_target_rangetblentry) + elog(WARN, "Column %s is ambiguous", colname); + } + else + rte_result = rte; + } + } + return rte_result; +} + +/* + * put new entry in pstate p_rtable structure, or return pointer + * if pstate null +*/ +RangeTblEntry * +addRangeTableEntry(ParseState *pstate, + char *relname, + char *refname, + bool inh, + bool inFromCl) +{ + Relation relation; + RangeTblEntry *rte = makeNode(RangeTblEntry); + + if (pstate != NULL && + refnameRangeTableEntry(pstate->p_rtable, refname) != NULL) + elog(WARN, "Table name %s specified more than once", refname); + + rte->relname = pstrdup(relname); + rte->refname = pstrdup(refname); + + relation = heap_openr(relname); + if (relation == NULL) + { + elog(WARN, "%s: %s", + relname, aclcheck_error_strings[ACLCHECK_NO_CLASS]); + } + + /* + * Flags - zero or more from inheritance,union,version or + * recursive (transitive closure) [we don't support them all -- ay + * 9/94 ] + */ + rte->inh = inh; + + /* RelOID */ + rte->relid = RelationGetRelationId(relation); + + rte->inFromCl = inFromCl; + + /* + * close the relation we're done with it for now. + */ + if (pstate != NULL) + pstate->p_rtable = lappend(pstate->p_rtable, rte); + + heap_close(relation); + + return rte; +} + +/* + * expandAll - + * makes a list of attributes + * assumes reldesc caching works + */ +List * +expandAll(ParseState *pstate, char *relname, char *refname, int *this_resno) +{ + Relation rdesc; + List *te_tail = NIL, + *te_head = NIL; + Var *varnode; + int varattno, + maxattrs; + Oid type_id; + int type_len; + RangeTblEntry *rte; + + rte = refnameRangeTableEntry(pstate->p_rtable, refname); + if (rte == NULL) + rte = addRangeTableEntry(pstate, relname, refname, FALSE, FALSE); + + rdesc = heap_open(rte->relid); + + if (rdesc == NULL) + { + elog(WARN, "Unable to expand all -- heap_open failed on %s", + rte->refname); + return NIL; + } + maxattrs = RelationGetNumberOfAttributes(rdesc); + + for (varattno = 0; varattno <= maxattrs - 1; varattno++) + { + char *attrname; + char *resname = NULL; + TargetEntry *te = makeNode(TargetEntry); + + attrname = pstrdup((rdesc->rd_att->attrs[varattno]->attname).data); + varnode = (Var *) make_var(pstate, refname, attrname, &type_id); + type_len = (int) typeLen(typeidType(type_id)); + + handleTargetColname(pstate, &resname, refname, attrname); + if (resname != NULL) + attrname = resname; + + /* + * Even if the elements making up a set are complex, the set + * itself is not. + */ + + te->resdom = makeResdom((AttrNumber) (*this_resno)++, + type_id, + (Size) type_len, + attrname, + (Index) 0, + (Oid) 0, + 0); + te->expr = (Node *) varnode; + if (te_head == NIL) + te_head = te_tail = lcons(te, NIL); + else + te_tail = lappend(te_tail, te); + } + + heap_close(rdesc); + return (te_head); +} + +/* given relation and att name, return id of variable */ +int +attnameAttNum(Relation rd, char *a) +{ + int i; + + for (i = 0; i < rd->rd_rel->relnatts; i++) + if (!namestrcmp(&(rd->rd_att->attrs[i]->attname), a)) + return (i + 1); + + for (i = 0; i < SPECIALS; i++) + if (!strcmp(special_attr[i].field, a)) + return (special_attr[i].code); + + /* on failure */ + elog(WARN, "Relation %s does not have attribute %s", + RelationGetRelationName(rd), a); + return 0; /* lint */ +} + +/* Given range variable, return whether attribute of this name + * is a set. + * NOTE the ASSUMPTION here that no system attributes are, or ever + * will be, sets. + */ +bool +attnameIsSet(Relation rd, char *name) +{ + int i; + + /* First check if this is a system attribute */ + for (i = 0; i < SPECIALS; i++) + { + if (!strcmp(special_attr[i].field, name)) + { + return (false); /* no sys attr is a set */ + } + } + return (get_attisset(rd->rd_id, name)); +} + +/*------------- + * given an attribute number and a relation, return its relation name + */ +char * +attnumAttName(Relation rd, int attrno) +{ + char *name; + int i; + + if (attrno < 0) + { + for (i = 0; i < SPECIALS; i++) + { + if (special_attr[i].code == attrno) + { + name = special_attr[i].field; + return (name); + } + } + elog(WARN, "Illegal attr no %d for relation %s", + attrno, RelationGetRelationName(rd)); + } + else if (attrno >= 1 && attrno <= RelationGetNumberOfAttributes(rd)) + { + name = (rd->rd_att->attrs[attrno - 1]->attname).data; + return (name); + } + else + { + elog(WARN, "Illegal attr no %d for relation %s", + attrno, RelationGetRelationName(rd)); + } + + /* + * Shouldn't get here, but we want lint to be happy... + */ + + return (NULL); +} + +int +attnumAttNelems(Relation rd, int attid) +{ + return (rd->rd_att->attrs[attid - 1]->attnelems); +} + +Oid +attnameTypeId(Oid relid, char *attrname) +{ + int attid; + Oid vartype; + Relation rd; + + rd = heap_open(relid); + if (!RelationIsValid(rd)) + { + rd = heap_openr(typeidTypeName(relid)); + if (!RelationIsValid(rd)) + elog(WARN, "cannot compute type of att %s for relid %d", + attrname, relid); + } + + attid = attnameAttNum(rd, attrname); /* could elog(WARN) and never return */ + + vartype = attnumTypeId(rd, attid); + + /* + * close relation we're done with it now + */ + heap_close(rd); + + return (vartype); +} + +/* given attribute id, return type of that attribute */ +/* XXX Special case for pseudo-attributes is a hack */ +Oid +attnumTypeId(Relation rd, int attid) +{ + + if (attid < 0) + return (typeTypeId(typenameType(attnum_type[-attid - 1]))); + + /* + * -1 because varattno (where attid comes from) returns one more than + * index + */ + return (rd->rd_att->attrs[attid - 1]->atttypid); +} + +/* + * handleTargetColname - + * use column names from insert + */ +void +handleTargetColname(ParseState *pstate, char **resname, + char *refname, char *colname) +{ + if (pstate->p_is_insert) + { + if (pstate->p_insert_columns != NIL) + { + Ident *id = lfirst(pstate->p_insert_columns); + + *resname = id->name; + pstate->p_insert_columns = lnext(pstate->p_insert_columns); + } + else + elog(WARN, "insert: more expressions than target columns"); + } + if (pstate->p_is_insert || pstate->p_is_update) + checkTargetTypes(pstate, *resname, refname, colname); +} + +/* + * checkTargetTypes - + * checks value and target column types + */ +void +checkTargetTypes(ParseState *pstate, char *target_colname, + char *refname, char *colname) +{ + Oid attrtype_id, + attrtype_target; + int resdomno_id, + resdomno_target; + Relation rd; + RangeTblEntry *rte; + + if (target_colname == NULL || colname == NULL) + return; + + if (refname != NULL) + rte = refnameRangeTableEntry(pstate->p_rtable, refname); + else + { + rte = colnameRangeTableEntry(pstate, colname); + if (rte == (RangeTblEntry *) NULL) + elog(WARN, "attribute %s not found", colname); + refname = rte->refname; + } + +/* + if (pstate->p_is_insert && rte == pstate->p_target_rangetblentry) + elog(WARN, "%s not available in this context", colname); +*/ + rd = heap_open(rte->relid); + + resdomno_id = attnameAttNum(rd, colname); + attrtype_id = attnumTypeId(rd, resdomno_id); + + resdomno_target = attnameAttNum(pstate->p_target_relation, target_colname); + attrtype_target = attnumTypeId(pstate->p_target_relation, resdomno_target); + + if (attrtype_id != attrtype_target) + elog(WARN, "Type of %s does not match target column %s", + colname, target_colname); + + if ((attrtype_id == BPCHAROID || attrtype_id == VARCHAROID) && + rd->rd_att->attrs[resdomno_id - 1]->attlen != + pstate->p_target_relation->rd_att->attrs[resdomno_target - 1]->attlen) + elog(WARN, "Length of %s does not match length of target column %s", + colname, target_colname); + + heap_close(rd); +} diff --git a/src/backend/parser/parse_target.c b/src/backend/parser/parse_target.c new file mode 100644 index 0000000000..f29aa49d34 --- /dev/null +++ b/src/backend/parser/parse_target.c @@ -0,0 +1,679 @@ +/*------------------------------------------------------------------------- + * + * parse_target.c + * handle target lists + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_target.c,v 1.1 1997/11/25 22:05:47 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include +#include +#include "postgres.h" +#include "catalog/pg_type.h" +#include "nodes/makefuncs.h" +#include "nodes/primnodes.h" +#include "parser/parse_expr.h" +#include "parser/parse_relation.h" +#include "parser/parse_target.h" +#include "parser/parse_node.h" +#include "utils/builtins.h" + +#ifdef 0 +#include "nodes/nodes.h" +#include "nodes/params.h" +#include "nodes/parsenodes.h" +#include "nodes/relation.h" +#include "parse.h" /* for AND, OR, etc. */ +#include "catalog/pg_aggregate.h" +#include "catalog/pg_proc.h" +#include "utils/elog.h" +#include "utils/palloc.h" +#include "utils/mcxt.h" +#include "utils/syscache.h" +#include "utils/acl.h" +#include "nodes/nodeFuncs.h" +#include "commands/sequence.h" + +#include "optimizer/clauses.h" +#include "access/heapam.h" + +#include "miscadmin.h" + +#include "port-protos.h" /* strdup() */ +#endif + +/* + * transformTargetList - + * turns a list of ResTarget's into a list of TargetEntry's + */ +List * +transformTargetList(ParseState *pstate, List *targetlist) +{ + List *p_target = NIL; + List *tail_p_target = NIL; + + while (targetlist != NIL) + { + ResTarget *res = (ResTarget *) lfirst(targetlist); + TargetEntry *tent = makeNode(TargetEntry); + + switch (nodeTag(res->val)) + { + case T_Ident: + { + Node *expr; + Oid type_id; + int type_len; + char *identname; + char *resname; + + identname = ((Ident *) res->val)->name; + handleTargetColname(pstate, &res->name, NULL, identname); + + /* + * here we want to look for column names only, not relation + * names (even though they can be stored in Ident nodes, too) + */ + expr = transformIdent(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); + type_id = exprType(expr); + type_len = typeLen(typeidType(type_id)); + resname = (res->name) ? res->name : identname; + tent->resdom = makeResdom((AttrNumber) pstate->p_last_resno++, + (Oid) type_id, + (Size) type_len, + resname, + (Index) 0, + (Oid) 0, + 0); + + tent->expr = expr; + break; + } + case T_ParamNo: + case T_FuncCall: + case T_A_Const: + case T_A_Expr: + { + Node *expr = transformExpr(pstate, (Node *) res->val, EXPR_COLUMN_FIRST); + + handleTargetColname(pstate, &res->name, NULL, NULL); + /* note indirection has not been transformed */ + if (pstate->p_is_insert && res->indirection != NIL) + { + /* this is an array assignment */ + char *val; + char *str, + *save_str; + List *elt; + int i = 0, + ndims; + int lindx[MAXDIM], + uindx[MAXDIM]; + int resdomno; + Relation rd; + Value *constval; + + if (exprType(expr) != UNKNOWNOID || + !IsA(expr, Const)) + elog(WARN, "yyparse: string constant expected"); + + val = (char *) textout((struct varlena *) + ((Const *) expr)->constvalue); + str = save_str = (char *) palloc(strlen(val) + MAXDIM * 25 + 2); + foreach(elt, res->indirection) + { + A_Indices *aind = (A_Indices *) lfirst(elt); + + aind->uidx = transformExpr(pstate, aind->uidx, EXPR_COLUMN_FIRST); + if (!IsA(aind->uidx, Const)) + elog(WARN, + "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(WARN, + "Array Index for Append should be a constant"); + lindx[i] = ((Const *) aind->lidx)->constvalue; + } + else + { + lindx[i] = 1; + } + if (lindx[i] > uindx[i]) + elog(WARN, "yyparse: lower index cannot be greater than upper index"); + sprintf(str, "[%d:%d]", lindx[i], uindx[i]); + str += strlen(str); + i++; + } + sprintf(str, "=%s", val); + rd = pstate->p_target_relation; + Assert(rd != NULL); + resdomno = attnameAttNum(rd, res->name); + ndims = attnumAttNelems(rd, resdomno); + if (i != ndims) + elog(WARN, "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), + NULL); + pfree(save_str); + } + else + { + char *colname = res->name; + + /* this is not an array assignment */ + if (colname == NULL) + { + + /* + * if you're wondering why this is here, look + * at the yacc grammar for why a name can be + * missing. -ay + */ + colname = figureColname(expr, res->val); + } + if (res->indirection) + { + List *ilist = res->indirection; + + while (ilist != NIL) + { + A_Indices *ind = lfirst(ilist); + + ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); + ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); + ilist = lnext(ilist); + } + } + res->name = colname; + tent = make_targetlist_expr(pstate, res->name, expr, + res->indirection); + } + break; + } + case T_Attr: + { + Oid type_id; + int type_len; + Attr *att = (Attr *) res->val; + Node *result; + char *attrname; + char *resname; + Resdom *resnode; + List *attrs = att->attrs; + + /* + * Target item is a single '*', expand all tables (eg. + * SELECT * FROM emp) + */ + if (att->relname != NULL && !strcmp(att->relname, "*")) + { + if (tail_p_target == NIL) + p_target = tail_p_target = expandAllTables(pstate); + else + lnext(tail_p_target) = expandAllTables(pstate); + + while (lnext(tail_p_target) != NIL) + /* make sure we point to the last target entry */ + tail_p_target = lnext(tail_p_target); + + /* + * skip rest of while loop + */ + targetlist = lnext(targetlist); + continue; + } + + /* + * Target item is relation.*, expand the table (eg. + * SELECT emp.*, dname FROM emp, dept) + */ + attrname = strVal(lfirst(att->attrs)); + if (att->attrs != NIL && !strcmp(attrname, "*")) + { + + /* + * tail_p_target is the target list we're building + * in the while loop. Make sure we fix it after + * appending more nodes. + */ + if (tail_p_target == NIL) + p_target = tail_p_target = expandAll(pstate, att->relname, + att->relname, &pstate->p_last_resno); + else + lnext(tail_p_target) = + expandAll(pstate, att->relname, att->relname, + &pstate->p_last_resno); + while (lnext(tail_p_target) != NIL) + /* make sure we point to the last target entry */ + tail_p_target = lnext(tail_p_target); + + /* + * skip the rest of the while loop + */ + targetlist = lnext(targetlist); + continue; + } + + + /* + * Target item is fully specified: ie. + * relation.attribute + */ + result = handleNestedDots(pstate, att, &pstate->p_last_resno); + handleTargetColname(pstate, &res->name, att->relname, attrname); + if (att->indirection != NIL) + { + List *ilist = att->indirection; + + while (ilist != NIL) + { + A_Indices *ind = lfirst(ilist); + + ind->lidx = transformExpr(pstate, ind->lidx, EXPR_COLUMN_FIRST); + ind->uidx = transformExpr(pstate, ind->uidx, EXPR_COLUMN_FIRST); + ilist = lnext(ilist); + } + result = (Node *) make_array_ref(result, att->indirection); + } + type_id = exprType(result); + type_len = typeLen(typeidType(type_id)); + /* move to last entry */ + while (lnext(attrs) != NIL) + attrs = lnext(attrs); + resname = (res->name) ? res->name : strVal(lfirst(attrs)); + resnode = makeResdom((AttrNumber) pstate->p_last_resno++, + (Oid) type_id, + (Size) type_len, + resname, + (Index) 0, + (Oid) 0, + 0); + tent->resdom = resnode; + tent->expr = result; + break; + } + default: + /* internal error */ + elog(WARN, + "internal error: do not know how to transform targetlist"); + break; + } + + if (p_target == NIL) + { + p_target = tail_p_target = lcons(tent, NIL); + } + else + { + lnext(tail_p_target) = lcons(tent, NIL); + tail_p_target = lnext(tail_p_target); + } + targetlist = lnext(targetlist); + } + + return p_target; +} + + +/* + * make_targetlist_expr - + * make a TargetEntry from an expression + * + * arrayRef is a list of transformed A_Indices + */ +TargetEntry * +make_targetlist_expr(ParseState *pstate, + char *colname, + Node *expr, + List *arrayRef) +{ + Oid type_id, + attrtype; + int type_len, + attrlen; + int resdomno; + Relation rd; + bool attrisset; + TargetEntry *tent; + Resdom *resnode; + + if (expr == NULL) + elog(WARN, "make_targetlist_expr: invalid use of NULL expression"); + + type_id = exprType(expr); + if (type_id == InvalidOid) + { + type_len = 0; + } + else + type_len = typeLen(typeidType(type_id)); + + /* I have no idea what the following does! */ + /* It appears to process target columns that will be receiving results */ + if (pstate->p_is_insert || pstate->p_is_update) + { + + /* + * append or replace query -- append, replace work only on one + * relation, so multiple occurence of same resdomno is bogus + */ + rd = pstate->p_target_relation; + Assert(rd != NULL); + resdomno = attnameAttNum(rd, colname); + attrisset = attnameIsSet(rd, colname); + attrtype = attnumTypeId(rd, resdomno); + if ((arrayRef != NIL) && (lfirst(arrayRef) == NIL)) + attrtype = GetArrayElementType(attrtype); + if (attrtype == BPCHAROID || attrtype == VARCHAROID) + { + attrlen = rd->rd_att->attrs[resdomno - 1]->attlen; + } + else + { + attrlen = typeLen(typeidType(attrtype)); + } +#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(typeidRetinfunc(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) + { + 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(WARN, "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)) + { + /* updating a single item */ + Oid typelem = typeidTypElem(attrtype); + + expr = (Node *) parser_typecast2(expr, + type_id, + typeidType(typelem), + attrlen); + } + else + expr = (Node *) parser_typecast2(expr, + type_id, + typeidType(attrtype), + attrlen); + } + else + { + /* currently, we can't handle casting of expressions */ + elog(WARN, "parser: attribute '%s' is of type '%s' but expression is of type '%s'", + colname, + typeidTypeName(attrtype), + typeidTypeName(type_id)); + } + } + + if (arrayRef != NIL) + { + Expr *target_expr; + Attr *att = makeNode(Attr); + List *ar = arrayRef; + List *upperIndexpr = NIL; + List *lowerIndexpr = NIL; + + att->relname = pstrdup(RelationGetRelationName(rd)->data); + att->attrs = lcons(makeString(colname), NIL); + target_expr = (Expr *) handleNestedDots(pstate, att, + &pstate->p_last_resno); + while (ar != NIL) + { + A_Indices *ind = lfirst(ar); + + if (lowerIndexpr || (!upperIndexpr && ind->lidx)) + { + + /* + * XXX assume all lowerIndexpr is non-null in this + * case + */ + lowerIndexpr = lappend(lowerIndexpr, ind->lidx); + } + upperIndexpr = lappend(upperIndexpr, ind->uidx); + ar = lnext(ar); + } + + expr = (Node *) make_array_set(target_expr, + upperIndexpr, + lowerIndexpr, + (Expr *) expr); + attrtype = attnumTypeId(rd, resdomno); + attrlen = typeLen(typeidType(attrtype)); + } + } + else + { + resdomno = pstate->p_last_resno++; + attrtype = type_id; + attrlen = type_len; + } + tent = makeNode(TargetEntry); + + resnode = makeResdom((AttrNumber) resdomno, + (Oid) attrtype, + (Size) attrlen, + colname, + (Index) 0, + (Oid) 0, + 0); + + tent->resdom = resnode; + tent->expr = expr; + + return tent; +} + +/* + * makeTargetNames - + * generate a list of column names if not supplied or + * test supplied column names to make sure they are in target table + * (used exclusively for inserts) + */ +List * +makeTargetNames(ParseState *pstate, List *cols) +{ + List *tl = NULL; + + /* Generate ResTarget if not supplied */ + + if (cols == NIL) + { + int numcol; + int i; + AttributeTupleForm *attr = pstate->p_target_relation->rd_att->attrs; + + numcol = pstate->p_target_relation->rd_rel->relnatts; + for (i = 0; i < numcol; i++) + { + Ident *id = makeNode(Ident); + + id->name = palloc(NAMEDATALEN); + StrNCpy(id->name, attr[i]->attname.data, NAMEDATALEN); + id->indirection = NIL; + id->isRel = false; + if (tl == NIL) + cols = tl = lcons(id, NIL); + else + { + lnext(tl) = lcons(id, NIL); + tl = lnext(tl); + } + } + } + else + { + foreach(tl, cols) + { + List *nxt; + char *name = ((Ident *) lfirst(tl))->name; + + /* elog on failure */ + attnameAttNum(pstate->p_target_relation, name); + foreach(nxt, lnext(tl)) + if (!strcmp(name, ((Ident *) lfirst(nxt))->name)) + elog (WARN, "Attribute '%s' should be specified only once", name); + } + } + + return cols; +} + +/* + * expandAllTables - + * turns '*' (in the target list) into a list of attributes + * (of all relations in the range table) + */ +List * +expandAllTables(ParseState *pstate) +{ + List *target = NIL; + List *legit_rtable = NIL; + List *rt, + *rtable; + + rtable = pstate->p_rtable; + if (pstate->p_is_rule) + { + + /* + * skip first two entries, "*new*" and "*current*" + */ + rtable = lnext(lnext(pstate->p_rtable)); + } + + /* this should not happen */ + if (rtable == NULL) + elog(WARN, "cannot expand: null p_rtable"); + + /* + * go through the range table and make a list of range table entries + * which we will expand. + */ + foreach(rt, rtable) + { + RangeTblEntry *rte = lfirst(rt); + + /* + * we only expand those specify in the from clause. (This will + * also prevent us from using the wrong table in inserts: eg. + * tenk2 in "insert into tenk2 select * from tenk1;") + */ + if (!rte->inFromCl) + continue; + legit_rtable = lappend(legit_rtable, rte); + } + + foreach(rt, legit_rtable) + { + RangeTblEntry *rte = lfirst(rt); + List *temp = target; + + if (temp == NIL) + target = expandAll(pstate, rte->relname, rte->refname, + &pstate->p_last_resno); + else + { + while (temp != NIL && lnext(temp) != NIL) + temp = lnext(temp); + lnext(temp) = expandAll(pstate, rte->relname, rte->refname, + &pstate->p_last_resno); + } + } + return target; +} + +/* + * figureColname - + * if the name of the resulting column is not specified in the target + * list, we have to guess. + * + */ +char * +figureColname(Node *expr, Node *resval) +{ + switch (nodeTag(expr)) + { + case T_Aggreg: + return (char *) /* XXX */ + ((Aggreg *) expr)->aggname; + case T_Expr: + if (((Expr *) expr)->opType == FUNC_EXPR) + { + if (nodeTag(resval) == T_FuncCall) + return ((FuncCall *) resval)->funcname; + } + break; + default: + break; + } + + return "?column?"; +} diff --git a/src/backend/parser/parse_type.c b/src/backend/parser/parse_type.c new file mode 100644 index 0000000000..67632cdab6 --- /dev/null +++ b/src/backend/parser/parse_type.c @@ -0,0 +1,319 @@ +/*------------------------------------------------------------------------- + * + * parse_type.h + * handle type operations for parser + * + * Copyright (c) 1994, Regents of the University of California + * + * + * IDENTIFICATION + * $Header: /cvsroot/pgsql/src/backend/parser/parse_type.c,v 1.1 1997/11/25 22:05:51 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#include +#include "postgres.h" +#include "fmgr.h" + +#include +#include +#include +#include "utils/syscache.h" + +#ifdef 0 +#include "lib/dllist.h" +#include "utils/datum.h" + +#include "utils/builtins.h" +#include "utils/elog.h" +#include "utils/palloc.h" + +#include "nodes/pg_list.h" +#include "nodes/parsenodes.h" +#include "catalog/catname.h" + +#include "catalog/pg_inherits.h" +#include "catalog/pg_operator.h" +#include "catalog/pg_proc.h" +#include "catalog/indexing.h" +#include "catalog/catname.h" + +#include "access/skey.h" +#include "access/relscan.h" +#include "access/tupdesc.h" +#include "access/htup.h" +#include "access/heapam.h" +#include "access/genam.h" +#include "access/itup.h" +#include "access/tupmacs.h" + +#include "storage/buf.h" +#include "storage/bufmgr.h" +#include "utils/lsyscache.h" +#include "storage/lmgr.h" + +#include "port-protos.h" /* strdup() */ +#endif + +/* check to see if a type id is valid, + * returns true if it is. By using this call before calling + * typeidType or typeidTypeName, more meaningful error messages + * can be produced because the caller typically has more context of + * what's going on - jolly + */ +bool +typeidIsValid(Oid id) +{ + return (SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(id), + 0, 0, 0) != NULL); +} + +/* return a type name, given a typeid */ +char * +typeidTypeName(Oid id) +{ + HeapTuple tup; + TypeTupleForm typetuple; + + if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id), + 0, 0, 0))) + { + elog(WARN, "type id lookup of %ud failed", id); + return (NULL); + } + typetuple = (TypeTupleForm) GETSTRUCT(tup); + return (typetuple->typname).data; +} + +/* return a Type structure, given an typid */ +Type +typeidType(Oid id) +{ + HeapTuple tup; + + if (!(tup = SearchSysCacheTuple(TYPOID, ObjectIdGetDatum(id), + 0, 0, 0))) + { + elog(WARN, "type id lookup of %ud failed", id); + return (NULL); + } + return ((Type) tup); +} + +/* return a Type structure, given type name */ +Type +typenameType(char *s) +{ + HeapTuple tup; + + if (s == NULL) + { + elog(WARN, "type(): Null type"); + } + + if (!(tup = SearchSysCacheTuple(TYPNAME, PointerGetDatum(s), 0, 0, 0))) + { + elog(WARN, "type name lookup of %s failed", s); + } + return ((Type) tup); +} + +/* given type, return the type OID */ +Oid +typeTypeId(Type tp) +{ + if (tp == NULL) + elog(WARN, "typeTypeId() called with NULL type struct"); + return (tp->t_oid); +} + +/* given type (as type struct), return the length of type */ +int16 +typeLen(Type t) +{ + TypeTupleForm typ; + + typ = (TypeTupleForm) GETSTRUCT(t); + return (typ->typlen); +} + +/* given type (as type struct), return the value of its 'byval' attribute.*/ +bool +typeByVal(Type t) +{ + TypeTupleForm typ; + + typ = (TypeTupleForm) GETSTRUCT(t); + return (typ->typbyval); +} + +/* given type (as type struct), return the name of type */ +char * +typeTypeName(Type t) +{ + TypeTupleForm typ; + + typ = (TypeTupleForm) GETSTRUCT(t); + return (typ->typname).data; +} + +/* given a type, return its typetype ('c' for 'c'atalog types) */ +char +typeTypeFlag(Type t) +{ + TypeTupleForm typ; + + typ = (TypeTupleForm) GETSTRUCT(t); + return (typ->typtype); +} + +/* Given a type structure and a string, returns the internal form of + that string */ +char * +stringTypeString(Type tp, char *string, int typlen) +{ + Oid op; + Oid typelem; + + op = ((TypeTupleForm) GETSTRUCT(tp))->typinput; + typelem = ((TypeTupleForm) GETSTRUCT(tp))->typelem; /* XXX - used for array_in */ + /* typlen is for bpcharin() and varcharin() */ + return ((char *) fmgr(op, string, typelem, typlen)); +} + +/* Given a type id, returns the out-conversion function of the type */ +Oid +typeidRetoutfunc(Oid type_id) +{ + HeapTuple typeTuple; + TypeTupleForm type; + Oid outfunc; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type_id), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(WARN, "typeidRetoutfunc: Invalid type - oid = %u", type_id); + + type = (TypeTupleForm) GETSTRUCT(typeTuple); + outfunc = type->typoutput; + return (outfunc); +} + +Oid +typeidTypeRelid(Oid type_id) +{ + HeapTuple typeTuple; + TypeTupleForm type; + Oid infunc; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type_id), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(WARN, "typeidTypeRelid: Invalid type - oid = %u", type_id); + + type = (TypeTupleForm) GETSTRUCT(typeTuple); + infunc = type->typrelid; + return (infunc); +} + +Oid +typeTypeRelid(Type typ) +{ + TypeTupleForm typtup; + + typtup = (TypeTupleForm) GETSTRUCT(typ); + + return (typtup->typrelid); +} + +Oid +typeidTypElem(Oid type_id) +{ + HeapTuple typeTuple; + TypeTupleForm type; + + if (!(typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type_id), + 0, 0, 0))) + { + elog(WARN, "type id lookup of %u failed", type_id); + } + type = (TypeTupleForm) GETSTRUCT(typeTuple); + + return (type->typelem); +} + +/* Given the attribute type of an array return the arrtribute type of + an element of the array */ + +Oid +GetArrayElementType(Oid typearray) +{ + HeapTuple type_tuple; + TypeTupleForm type_struct_array; + + type_tuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(typearray), + 0, 0, 0); + + if (!HeapTupleIsValid(type_tuple)) + elog(WARN, "GetArrayElementType: Cache lookup failed for type %d", + typearray); + + /* get the array type struct from the type tuple */ + type_struct_array = (TypeTupleForm) GETSTRUCT(type_tuple); + + if (type_struct_array->typelem == InvalidOid) + { + elog(WARN, "GetArrayElementType: type %s is not an array", + (Name) &(type_struct_array->typname.data[0])); + } + + return (type_struct_array->typelem); +} + +/* Given a type id, returns the in-conversion function of the type */ +Oid +typeidRetinfunc(Oid type_id) +{ + HeapTuple typeTuple; + TypeTupleForm type; + Oid infunc; + + typeTuple = SearchSysCacheTuple(TYPOID, + ObjectIdGetDatum(type_id), + 0, 0, 0); + if (!HeapTupleIsValid(typeTuple)) + elog(WARN, "typeidRetinfunc: Invalid type - oid = %u", type_id); + + type = (TypeTupleForm) GETSTRUCT(typeTuple); + infunc = type->typinput; + return (infunc); +} + + +#ifdef NOT_USED +char +FindDelimiter(char *typename) +{ + char delim; + HeapTuple typeTuple; + TypeTupleForm type; + + + if (!(typeTuple = SearchSysCacheTuple(TYPNAME, + PointerGetDatum(typename), + 0, 0, 0))) + { + elog(WARN, "type name lookup of %s failed", typename); + } + type = (TypeTupleForm) GETSTRUCT(typeTuple); + + delim = type->typdelim; + return (delim); +} + +#endif diff --git a/src/backend/parser/parser.c b/src/backend/parser/parser.c index d658c30896..ea309c3c28 100644 --- a/src/backend/parser/parser.c +++ b/src/backend/parser/parser.c @@ -6,7 +6,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.28 1997/11/20 23:22:24 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/parser.c,v 1.29 1997/11/25 22:05:52 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -14,9 +14,20 @@ #include #include "postgres.h" +#include "nodes/pg_list.h" +#include "parser/parser.h" +#include "parser/analyze.h" +#include "parser/parse_node.h" + +void init_io(); /* from scan.l */ +void parser_init(Oid *typev, int nargs); /* from gram.y */ +int yyparse(); /* from gram.c */ + +#ifdef 0 +#include "parser/parse.h" #include "parser/gramparse.h" -#include "parser/parse_query.h" #include "utils/palloc.h" +#endif char *parseString; /* the char* which holds the string to be * parsed */ @@ -103,10 +114,10 @@ static void define_sets(Node *clause) { Oid setoid; - Type t = type("oid"); - Oid typeoid = typeid(t); - Size oidsize = tlen(t); - bool oidbyval = tbyval(t); + Type t = typeidType(OIDOID); + Oid typeoid = typeTypeId(t); + Size oidsize = typeLen(t); + bool oidbyval = typeByVal(t); if (clause == NULL) { @@ -125,11 +136,11 @@ define_sets(Node *clause) return; } setoid = SetDefine(((Const *) clause)->constvalue, - get_id_typname(((Const *) clause)->consttype)); + typeidTypeName(((Const *) clause)->consttype)); set_constvalue((Const) clause, setoid); set_consttype((Const) clause, typeoid); set_constlen((Const) clause, oidsize); - set_constbyval((Const) clause, oidbyval); + set_constypeByVal((Const) clause, oidbyval); } else if (IsA(clause, Iter)) { @@ -173,6 +184,5 @@ define_sets(Node *clause) define_sets(get_rightop(clause)); } } - #endif diff --git a/src/backend/parser/scansup.c b/src/backend/parser/scansup.c index dcc66145a5..e50757940a 100644 --- a/src/backend/parser/scansup.c +++ b/src/backend/parser/scansup.c @@ -8,7 +8,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.7 1997/09/08 02:25:22 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/parser/scansup.c,v 1.8 1997/11/25 22:05:55 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -17,7 +17,6 @@ #include #include -#include "c.h" #include "postgres.h" #include "miscadmin.h" #include "utils/elog.h" diff --git a/src/backend/rewrite/rewriteDefine.c b/src/backend/rewrite/rewriteDefine.c index 37d1e1c155..c6f471915c 100644 --- a/src/backend/rewrite/rewriteDefine.c +++ b/src/backend/rewrite/rewriteDefine.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.7 1997/10/25 05:37:07 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/rewrite/rewriteDefine.c,v 1.8 1997/11/25 22:06:04 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -23,7 +23,8 @@ #include "utils/lsyscache.h" /* for get_typlen */ #include "nodes/pg_list.h" /* for Lisp support */ #include "nodes/parsenodes.h" -#include "parser/catalog_utils.h" +#include "parser/parse_relation.h" + #include "rewrite/locks.h" #include "rewrite/rewriteDefine.h" #include "rewrite/rewriteRemove.h" @@ -107,7 +108,7 @@ InsertRule(char *rulname, if (evslot == NULL) evslot_index = -1; else - evslot_index = varattno(eventrel, (char *) evslot); + evslot_index = attnameAttNum(eventrel, (char *) evslot); heap_close(eventrel); if (evinstead) @@ -221,8 +222,8 @@ DefineQueryRewrite(RuleStmt *stmt) } else { - event_attno = varattno(event_relation, eslot_string); - event_attype = att_typeid(event_relation, event_attno); + event_attno = attnameAttNum(event_relation, eslot_string); + event_attype = attnumTypeId(event_relation, event_attno); } heap_close(event_relation); diff --git a/src/backend/tcop/aclchk.c b/src/backend/tcop/aclchk.c index 0b8a49f188..0021213365 100644 --- a/src/backend/tcop/aclchk.c +++ b/src/backend/tcop/aclchk.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/Attic/aclchk.c,v 1.19 1997/11/24 05:08:47 momjian Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/Attic/aclchk.c,v 1.20 1997/11/25 22:06:08 momjian Exp $ * * NOTES * See acl.h. @@ -31,10 +31,12 @@ #include "catalog/pg_operator.h" #include "catalog/pg_aggregate.h" #include "catalog/pg_proc.h" +#include "catalog/pg_type.h" #include "catalog/pg_user.h" +#include "parser/parse_agg.h" +#include "parser/parse_func.h" #include "utils/syscache.h" #include "utils/tqual.h" -#include "parser/catalog_utils.h" #include "fmgr.h" static int32 aclcheck(Acl *acl, AclId id, AclIdType idtype, AclMode mode); diff --git a/src/backend/tcop/postgres.c b/src/backend/tcop/postgres.c index 7659d90275..d0d134e331 100644 --- a/src/backend/tcop/postgres.c +++ b/src/backend/tcop/postgres.c @@ -7,7 +7,7 @@ * * * IDENTIFICATION - * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.54 1997/11/10 15:24:55 thomas Exp $ + * $Header: /cvsroot/pgsql/src/backend/tcop/postgres.c,v 1.55 1997/11/25 22:06:14 momjian Exp $ * * NOTES * this is the "main" module of the postgres backend and @@ -45,11 +45,10 @@ #include "lib/dllist.h" -#include "parser/catalog_utils.h" -#include "parser/parse_query.h" /* for MakeTimeRange() */ #include "commands/async.h" #include "tcop/tcopprot.h" /* where declarations for this file go */ #include "optimizer/planner.h" +#include "parser/parser.h" #include "tcop/tcopprot.h" #include "tcop/tcopdebug.h" @@ -1341,7 +1340,7 @@ PostgresMain(int argc, char *argv[]) if (IsUnderPostmaster == false) { puts("\nPOSTGRES backend interactive interface"); - puts("$Revision: 1.54 $ $Date: 1997/11/10 15:24:55 $"); + puts("$Revision: 1.55 $ $Date: 1997/11/25 22:06:14 $"); } /* ---------------- diff --git a/src/include/executor/spi.h b/src/include/executor/spi.h index 4a38e207e8..7a02f6aaab 100644 --- a/src/include/executor/spi.h +++ b/src/include/executor/spi.h @@ -15,7 +15,6 @@ #include "nodes/execnodes.h" #include "nodes/plannodes.h" #include "catalog/pg_proc.h" -#include "parser/parse_query.h" #include "tcop/pquery.h" #include "tcop/tcopprot.h" #include "tcop/utility.h" diff --git a/src/include/nodes/nodeFuncs.h b/src/include/nodes/nodeFuncs.h index de5ad32ab5..696f5f7fa6 100644 --- a/src/include/nodes/nodeFuncs.h +++ b/src/include/nodes/nodeFuncs.h @@ -6,13 +6,16 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: nodeFuncs.h,v 1.5 1997/09/08 21:52:45 momjian Exp $ + * $Id: nodeFuncs.h,v 1.6 1997/11/25 22:06:30 momjian Exp $ * *------------------------------------------------------------------------- */ #ifndef NODEFUNCS_H #define NODEFUNCS_H +#include +#include + extern bool single_node(Node *node); extern bool var_is_outer(Var *var); extern bool var_is_rel(Var *var); diff --git a/src/include/optimizer/planner.h b/src/include/optimizer/planner.h index da90b768c6..59bd8738d0 100644 --- a/src/include/optimizer/planner.h +++ b/src/include/optimizer/planner.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: planner.h,v 1.5 1997/09/08 21:53:29 momjian Exp $ + * $Id: planner.h,v 1.6 1997/11/25 22:06:37 momjian Exp $ * *------------------------------------------------------------------------- */ @@ -16,6 +16,8 @@ /* */ +#include + extern Plan *planner(Query *parse); extern void pg_checkretval(Oid rettype, QueryTreeList *querytree_list); diff --git a/src/include/parser/analyze.h b/src/include/parser/analyze.h new file mode 100644 index 0000000000..a85e2074bb --- /dev/null +++ b/src/include/parser/analyze.h @@ -0,0 +1,19 @@ +/*------------------------------------------------------------------------- + * + * analyze.h + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: analyze.h,v 1.1 1997/11/25 22:06:47 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef ANALYZE_H +#define ANALYZE_H + +#include + +QueryTreeList *parse_analyze(List *pl); + +#endif /* ANALYZE_H */ diff --git a/src/include/parser/catalog_utils.h b/src/include/parser/catalog_utils.h deleted file mode 100644 index 707ca3a1c6..0000000000 --- a/src/include/parser/catalog_utils.h +++ /dev/null @@ -1,54 +0,0 @@ -/*------------------------------------------------------------------------- - * - * catalog_utils.h-- - * - * - * - * Copyright (c) 1994, Regents of the University of California - * - * $Id: catalog_utils.h,v 1.13 1997/09/08 21:53:35 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef CATALOG_UTILS_H -#define CATALOG_UTILS_H - -#include -#include - -typedef HeapTuple Type; -typedef HeapTuple Operator; - -extern Type get_id_type(Oid id); -extern char *get_id_typname(Oid id); -extern Type type(char *); -extern Oid att_typeid(Relation rd, int attid); -extern int att_attnelems(Relation rd, int attid); -extern Oid typeid(Type tp); -extern int16 tlen(Type t); -extern bool tbyval(Type t); -extern char *tname(Type t); -extern int tbyvalue(Type t); -extern Oid oprid(Operator op); -extern Operator oper(char *op, Oid arg1, Oid arg2, bool noWarnings); -extern Operator right_oper(char *op, Oid arg); -extern Operator left_oper(char *op, Oid arg); -extern int varattno(Relation rd, char *a); -extern bool varisset(Relation rd, char *name); -extern int nf_varattno(Relation rd, char *a); -extern char *getAttrName(Relation rd, int attrno); -extern char *instr2(Type tp, char *string, int typlen); -extern Oid GetArrayElementType(Oid typearray); -extern Oid funcid_get_rettype(Oid funcid); -extern bool -func_get_detail(char *funcname, int nargs, Oid *oid_array, - Oid *funcid, Oid *rettype, bool *retset, Oid **true_typeids); -extern Oid typeid_get_retinfunc(Oid type_id); -extern Oid typeid_get_retoutfunc(Oid type_id); -extern Oid typeid_get_relid(Oid type_id); -extern Oid get_typrelid(Type typ); -extern Oid get_typelem(Oid type_id); -extern void func_error(char *caller, char *funcname, int nargs, Oid *argtypes); -extern void agg_error(char *caller, char *aggname, Oid basetypeID); - -#endif /* CATALOG_UTILS_H */ diff --git a/src/include/parser/parse_agg.h b/src/include/parser/parse_agg.h new file mode 100644 index 0000000000..21ef36248f --- /dev/null +++ b/src/include/parser/parse_agg.h @@ -0,0 +1,38 @@ +/*------------------------------------------------------------------------- + * + * parse_agg.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_agg.h,v 1.1 1997/11/25 22:06:53 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_AGG_H +#define PARSE_AGG_H + +#include +#include +#include +#include + +void AddAggToParseState(ParseState *pstate, Aggreg *aggreg); + +void finalizeAggregates(ParseState *pstate, Query *qry); + +bool contain_agg_clause(Node *clause); + +bool exprIsAggOrGroupCol(Node *expr, List *groupClause); + +bool tleIsAggOrGroupCol(TargetEntry *tle, List *groupClause); + +void parseCheckAggregates(ParseState *pstate, Query *qry); + +Aggreg *ParseAgg(char *aggname, Oid basetype, Node *target); + +void agg_error(char *caller, char *aggname, Oid basetypeID); + +#endif /* PARSE_AGG_H */ + diff --git a/src/include/parser/parse_clause.h b/src/include/parser/parse_clause.h new file mode 100644 index 0000000000..2c0a5278f6 --- /dev/null +++ b/src/include/parser/parse_clause.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * parse_clause.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_clause.h,v 1.1 1997/11/25 22:06:54 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_CLAUSE_H +#define PARSE_CLAUSE_H + +#include +#include +#include +#include +#include + +void parseFromClause(ParseState *pstate, List *frmList); + +void makeRangeTable(ParseState *pstate, char *relname, List *frmList); + +Node *transformWhereClause(ParseState *pstate, Node *a_expr); + +TargetEntry *find_targetlist_entry(ParseState *pstate, + SortGroupBy *sortgroupby, List *tlist); + +List *transformGroupClause(ParseState *pstate, List *grouplist, + List *targetlist); + +List *transformSortClause(ParseState *pstate, + List *orderlist, List *targetlist, + char *uniqueFlag); + +#endif /* PARSE_CLAUSE_H */ + diff --git a/src/include/parser/parse_expr.h b/src/include/parser/parse_expr.h new file mode 100644 index 0000000000..e7c4a04b01 --- /dev/null +++ b/src/include/parser/parse_expr.h @@ -0,0 +1,34 @@ +/*------------------------------------------------------------------------- + * + * parse_exer.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_expr.h,v 1.1 1997/11/25 22:06:55 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_EXPR_H +#define PARSE_EXPR_H + +#include +#include +#include +#include + +Node *transformExpr(ParseState *pstate, Node *expr, int precedence); + +Node *transformIdent(ParseState *pstate, Node *expr, int precedence); + +Oid exprType(Node *expr); + +Node *handleNestedDots(ParseState *pstate, Attr *attr, int *curr_resno); + +Node *parser_typecast(Value *expr, TypeName *typename, int typlen); + +Node *parser_typecast2(Node *expr, Oid exprType, Type tp, int typlen); + +#endif /* PARSE_EXPR_H */ + diff --git a/src/include/parser/parse_func.h b/src/include/parser/parse_func.h new file mode 100644 index 0000000000..de8fc66d84 --- /dev/null +++ b/src/include/parser/parse_func.h @@ -0,0 +1,97 @@ +/*------------------------------------------------------------------------- + * + * catalog_utils.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_func.h,v 1.1 1997/11/25 22:06:56 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSER_FUNC_H +#define PARSER_FUNC_H + +#include +#include +#include +#include +#include +#include + +/* + * This structure is used to explore the inheritance hierarchy above + * nodes in the type tree in order to disambiguate among polymorphic + * functions. + */ +typedef struct _InhPaths +{ + int nsupers; /* number of superclasses */ + Oid self; /* this class */ + Oid *supervec; /* vector of superclasses */ +} InhPaths; + +/* + * This structure holds a list of possible functions or operators that + * agree with the known name and argument types of the function/operator. + */ +typedef struct _CandidateList +{ + Oid *args; + struct _CandidateList *next; +} *CandidateList; + +Node *ParseFunc(ParseState *pstate, char *funcname, List *fargs, + int *curr_resno); + +Oid funcid_get_rettype(Oid funcid); + +CandidateList func_get_candidates(char *funcname, int nargs); + +bool can_coerce(int nargs, Oid *input_typeids, Oid *func_typeids); + +int match_argtypes(int nargs, + Oid *input_typeids, + CandidateList function_typeids, + CandidateList *candidates); + +Oid * func_select_candidate(int nargs, + Oid *input_typeids, + CandidateList candidates); + +bool func_get_detail(char *funcname, + int nargs, + Oid *oid_array, + Oid *funcid, /* return value */ + Oid *rettype, /* return value */ + bool *retset, /* return value */ + Oid **true_typeids); + +Oid ** argtype_inherit(int nargs, Oid *oid_array); + +int findsupers(Oid relid, Oid **supervec); + +Oid **genxprod(InhPaths *arginh, int nargs); + +void make_arguments(int nargs, + List *fargs, + Oid *input_typeids, + Oid *function_typeids); + +List *setup_tlist(char *attname, Oid relid); + +List *setup_base_tlist(Oid typeid); + +Node *ParseComplexProjection(ParseState *pstate, + char *funcname, + Node *first_arg, + bool *attisset); + +void func_error(char *caller, char *funcname, int nargs, Oid *argtypes); + + + + +#endif /* PARSE_FUNC_H */ + diff --git a/src/include/parser/parse_node.h b/src/include/parser/parse_node.h new file mode 100644 index 0000000000..bac05cb150 --- /dev/null +++ b/src/include/parser/parse_node.h @@ -0,0 +1,67 @@ +/*------------------------------------------------------------------------- + * + * parse_node.h + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_node.h,v 1.1 1997/11/25 22:06:57 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_NODE_H +#define PARSE_NODE_H + +#include +#include +#include +#include +#include +#include + +typedef struct QueryTreeList +{ + int len; /* number of queries */ + Query **qtrees; +} QueryTreeList; + +/* state information used during parse analysis */ +typedef struct ParseState +{ + int p_last_resno; + List *p_rtable; + int p_numAgg; + List *p_aggs; + bool p_is_insert; + List *p_insert_columns; + bool p_is_update; + bool p_is_rule; + bool p_in_where_clause; + Relation p_target_relation; + RangeTblEntry *p_target_rangetblentry; +} ParseState; + +ParseState *make_parsestate(void); + +Node *make_operand(char *opname, + Node *tree, + Oid orig_typeId, + Oid true_typeId); + +void disallow_setop(char *op, Type optype, Node *operand); + +Expr *make_op(char *opname, Node *ltree, Node *rtree); + +Var *make_var(ParseState *pstate, char *refname, char *attrname, Oid *type_id); + +ArrayRef *make_array_ref(Node *expr, + List *indirection); + +ArrayRef *make_array_set(Expr *target_expr, + List *upperIndexpr, + List *lowerIndexpr, + Expr *expr); + +Const *make_const(Value *value); + +#endif /* PARSE_NODE_H */ diff --git a/src/include/parser/parse_oper.h b/src/include/parser/parse_oper.h new file mode 100644 index 0000000000..c013af628c --- /dev/null +++ b/src/include/parser/parse_oper.h @@ -0,0 +1,50 @@ +/*------------------------------------------------------------------------- + * + * catalog_utils.h-- + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_oper.h,v 1.1 1997/11/25 22:06:59 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_OPER_H +#define PARSE_OPER_H + +#include +#include + +typedef HeapTuple Operator; + +Oid any_ordering_op(int restype); + +Oid oprid(Operator op); + +int binary_oper_get_candidates(char *opname, + Oid leftTypeId, + Oid rightTypeId, + CandidateList *candidates); + +bool equivalentOpersAfterPromotion(CandidateList candidates); + +CandidateList binary_oper_select_candidate(Oid arg1, + Oid arg2, + CandidateList candidates); + +Operator oper(char *op, Oid arg1, Oid arg2, bool noWarnings); + +int +unary_oper_get_candidates(char *op, + Oid typeId, + CandidateList *candidates, + char rightleft); + +Operator right_oper(char *op, Oid arg); + +Operator left_oper(char *op, Oid arg); + +void op_error(char *op, Oid arg1, Oid arg2); + +#endif /* PARSE_OPER_H */ diff --git a/src/include/parser/parse_query.h b/src/include/parser/parse_query.h deleted file mode 100644 index 0a7d534b85..0000000000 --- a/src/include/parser/parse_query.h +++ /dev/null @@ -1,70 +0,0 @@ - /*------------------------------------------------------------------------- - * - * parse_query.h-- - * prototypes for parse_query.c. - * - * - * Copyright (c) 1994, Regents of the University of California - * - * $Id: parse_query.h,v 1.14 1997/11/20 23:23:53 momjian Exp $ - * - *------------------------------------------------------------------------- - */ -#ifndef PARSE_QUERY_H -#define PARSE_QUERY_H - -#include -#include -#include - -typedef struct QueryTreeList -{ - int len; /* number of queries */ - Query **qtrees; -} QueryTreeList; - -extern RangeTblEntry *refnameRangeTableEntry(List *rtable, char *refname); -extern RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname); -extern int refnameRangeTablePosn(List *rtable, char *refname); -extern RangeTblEntry * -addRangeTableEntry(ParseState *pstate, - char *relname, char *refname, - bool inh, bool inFromCl); -extern List * -expandAll(ParseState *pstate, char *relname, char *refname, - int *this_resno); -extern Expr *make_op(char *opname, Node *ltree, Node *rtree); - -extern Oid find_atttype(Oid relid, char *attrname); -extern Var * -make_var(ParseState *pstate, - char *relname, char *attrname, Oid *type_id); -extern ArrayRef *make_array_ref(Node *array, List *indirection); -extern ArrayRef * -make_array_set(Expr *target_expr, List *upperIndexpr, - List *lowerIndexpr, Expr *expr); -extern Const *make_const(Value *value); - -extern void param_type_init(Oid *typev, int nargs); -extern Oid param_type(int t); - -extern QueryTreeList *parser(char *str, Oid *typev, int nargs); - -extern void handleTargetColname(ParseState *pstate, char **resname, - char *refname, char *colname); - -/* - * analyze.c - */ - -Oid exprType(Node *expr); -QueryTreeList *parse_analyze(List *querytree_list); - -/* define in parse_query.c, used in gram.y */ -extern Oid *param_type_info; -extern int pfunc_num_args; - -/* useful macros */ -#define ISCOMPLEX(type) (typeid_get_relid(type) ? true : false) - -#endif /* PARSE_QUERY_H */ diff --git a/src/include/parser/parse_relation.h b/src/include/parser/parse_relation.h new file mode 100644 index 0000000000..ed470822ff --- /dev/null +++ b/src/include/parser/parse_relation.h @@ -0,0 +1,56 @@ + /*------------------------------------------------------------------------- + * + * parse_query.h-- + * prototypes for parse_query.c. + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_relation.h,v 1.1 1997/11/25 22:07:02 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_QUERY_H +#define PARSE_RANGE_H + +#include +#include +#include +#include +#include +#include + +RangeTblEntry *refnameRangeTableEntry(List *rtable, char *refname); + +int refnameRangeTablePosn(List *rtable, char *refname); + +RangeTblEntry *colnameRangeTableEntry(ParseState *pstate, char *colname); + +RangeTblEntry *addRangeTableEntry(ParseState *pstate, + char *relname, + char *refname, + bool inh, + bool inFromCl); + +List *expandAll(ParseState *pstate, char *relname, char *refname, + int *this_resno); + +int attnameAttNum(Relation rd, char *a); + +bool attnameIsSet(Relation rd, char *name); + +char *attnumAttName(Relation rd, int attrno); + +int attnumAttNelems(Relation rd, int attid); + +Oid attnameTypeId(Oid relid, char *attrname); + +Oid attnumTypeId(Relation rd, int attid); + +void handleTargetColname(ParseState *pstate, char **resname, + char *refname, char *colname); + +void checkTargetTypes(ParseState *pstate, char *target_colname, + char *refname, char *colname); + +#endif /* PARSE_RANGE_H */ diff --git a/src/include/parser/parse_state.h b/src/include/parser/parse_state.h deleted file mode 100644 index abda19b18c..0000000000 --- a/src/include/parser/parse_state.h +++ /dev/null @@ -1,34 +0,0 @@ -/*------------------------------------------------------------------------- - * - * parse_state.h-- - * - * Copyright (c) 1994, Regents of the University of California - * - * $Id: parse_state.h,v 1.8 1997/09/08 21:53:40 momjian Exp $ - * - *------------------------------------------------------------------------- - */ - -#ifndef PARSE_STATE_H -#define PARSE_STATE_H - -#include -#include - -/* state information used during parse analysis */ -typedef struct ParseState -{ - int p_last_resno; - List *p_rtable; - int p_numAgg; - List *p_aggs; - bool p_is_insert; - List *p_insert_columns; - bool p_is_update; - bool p_is_rule; - Relation p_target_relation; - RangeTblEntry *p_target_rangetblentry; -} ParseState; - - -#endif /* PARSE_QUERY_H */ diff --git a/src/include/parser/parse_target.h b/src/include/parser/parse_target.h new file mode 100644 index 0000000000..c7faa6b3db --- /dev/null +++ b/src/include/parser/parse_target.h @@ -0,0 +1,39 @@ +/*------------------------------------------------------------------------- + * + * parse_target.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_target.h,v 1.1 1997/11/25 22:07:06 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_TARGET_H +#define PARSE_TARGET_H + +#include +#include +#include +#include +#include + +#define EXPR_COLUMN_FIRST 1 +#define EXPR_RELATION_FIRST 2 + +List *transformTargetList(ParseState *pstate, List *targetlist); + +TargetEntry *make_targetlist_expr(ParseState *pstate, + char *colname, + Node *expr, + List *arrayRef); + +List *expandAllTables(ParseState *pstate); + +char *figureColname(Node *expr, Node *resval); + +List *makeTargetNames(ParseState *pstate, List *cols); + +#endif /* PARSE_TARGET_H */ + diff --git a/src/include/parser/parse_type.h b/src/include/parser/parse_type.h new file mode 100644 index 0000000000..63c38ab98d --- /dev/null +++ b/src/include/parser/parse_type.h @@ -0,0 +1,37 @@ +/*------------------------------------------------------------------------- + * + * parse_type.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parse_type.h,v 1.1 1997/11/25 22:07:07 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSE_TYPE_H +#define PARSE_TYPE_H + +#include "access/htup.h" + +typedef HeapTuple Type; + +bool typeidIsValid(Oid id); +Type typeidType(Oid id); +Type typenameType(char *s); +char *typeidTypeName(Oid id); +Oid typeTypeId(Type tp); +int16 typeLen(Type t); +bool typeByVal(Type t); +char *typeTypeName(Type t); +char typeTypeFlag(Type t); +char *stringTypeString(Type tp, char *string, int typlen); +Oid typeidRetoutfunc(Oid type_id); +Oid typeidTypeRelid(Oid type_id); +Oid typeTypeRelid(Type typ); +Oid typeidTypElem(Oid type_id); +Oid GetArrayElementType(Oid typearray); +Oid typeidRetinfunc(Oid type_id); + +#endif /* PARSE_TYPE_H */ diff --git a/src/include/parser/parser.h b/src/include/parser/parser.h new file mode 100644 index 0000000000..4362ebb480 --- /dev/null +++ b/src/include/parser/parser.h @@ -0,0 +1,21 @@ +/*------------------------------------------------------------------------- + * + * parser.h + * + * + * + * Copyright (c) 1994, Regents of the University of California + * + * $Id: parser.h,v 1.1 1997/11/25 22:07:08 momjian Exp $ + * + *------------------------------------------------------------------------- + */ +#ifndef PARSER_H +#define PARSER_H + +#include + +QueryTreeList *parser(char *str, Oid *typev, int nargs); + +#endif /* PARSER_H */ + diff --git a/src/include/tcop/tcopprot.h b/src/include/tcop/tcopprot.h index 6142f24e96..eeb21b91be 100644 --- a/src/include/tcop/tcopprot.h +++ b/src/include/tcop/tcopprot.h @@ -6,7 +6,7 @@ * * Copyright (c) 1994, Regents of the University of California * - * $Id: tcopprot.h,v 1.7 1997/09/08 21:54:42 momjian Exp $ + * $Id: tcopprot.h,v 1.8 1997/11/25 22:07:10 momjian Exp $ * * OLD COMMENTS * This file was created so that other c files could get the two @@ -19,7 +19,7 @@ #define TCOPPROT_H #include -#include +#include #ifndef BOOTSTRAP_INCLUDE extern List * diff --git a/src/test/regress/checkresults b/src/test/regress/checkresults index 1536dc2dba..39783cf468 100755 --- a/src/test/regress/checkresults +++ b/src/test/regress/checkresults @@ -1,6 +1,8 @@ #!/bin/sh # check regression tests -# usage: checkresults < results.out +# usage: checkresults [ regress.out ] + +[ "$#" -eq 0 ] && set regress.out for file in `cat "$@" | grep 'failed$' | cut -d " " -f 1` do