mirror of https://github.com/postgres/postgres
parent
3aff4011c7
commit
4a5b781d71
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@ -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 <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#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)); |
||||
} |
||||
} |
||||
|
@ -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 <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#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; |
||||
} |
@ -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 <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
|
||||
#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 = "<unnamed>"; |
||||
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); |
||||
}
|
File diff suppressed because it is too large
Load Diff
@ -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 <string.h> |
||||
#include "postgres.h" |
||||
#include <fmgr.h> |
||||
|
||||
#include <access/heapam.h> |
||||
#include <access/relscan.h> |
||||
#include <catalog/catname.h> |
||||
#include <catalog/pg_operator.h> |
||||
#include <catalog/pg_proc.h> |
||||
#include <catalog/pg_type.h> |
||||
#include <parser/parse_oper.h> |
||||
#include <parser/parse_type.h> |
||||
#include <storage/bufmgr.h> |
||||
#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/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)); |
||||
} |
||||
|
@ -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 <ctype.h> |
||||
#include <string.h> |
||||
|
||||
#include "postgres.h" |
||||
#include "access/heapam.h" |
||||
#include <access/htup.h> |
||||
#include <catalog/pg_type.h> |
||||
#include "nodes/makefuncs.h" |
||||
#include <parser/parse_relation.h> |
||||
#include <utils/acl.h> |
||||
#include "utils/builtins.h" |
||||
#include <utils/lsyscache.h> |
||||
|
||||
#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); |
||||
} |
@ -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 <stdio.h> |
||||
#include <stdlib.h> |
||||
#include <string.h> |
||||
#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?"; |
||||
} |
@ -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 <string.h> |
||||
#include "postgres.h" |
||||
#include "fmgr.h" |
||||
|
||||
#include <catalog/pg_type.h> |
||||
#include <parser/parse_target.h> |
||||
#include <parser/parse_type.h> |
||||
#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 |
@ -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 <parser/parse_node.h> |
||||
|
||||
QueryTreeList *parse_analyze(List *pl); |
||||
|
||||
#endif /* ANALYZE_H */ |
@ -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 <catalog/pg_type.h> |
||||
#include <access/htup.h> |
||||
|
||||
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 */ |
@ -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 <nodes/nodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
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 */ |
||||
|
@ -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 <nodes/pg_list.h> |
||||
#include <nodes/nodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
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 */ |
||||
|
@ -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 <nodes/nodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
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 */ |
||||
|
@ -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 <nodes/nodes.h> |
||||
#include <nodes/pg_list.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_func.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
/*
|
||||
* 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 */ |
||||
|
@ -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 <nodes/nodes.h> |
||||
#include <nodes/pg_list.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <parser/parse_type.h> |
||||
#include <utils/rel.h> |
||||
|
||||
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 */ |
@ -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 <parser/parse_func.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
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 */ |
@ -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 <parser/catalog_utils.h> |
||||
#include <parser/parse_state.h> |
||||
#include <nodes/parsenodes.h> |
||||
|
||||
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 */ |
@ -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 <nodes/nodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/pg_list.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_node.h> |
||||
#include <utils/rel.h> |
||||
|
||||
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 */ |
@ -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 <nodes/parsenodes.h> |
||||
#include <utils/rel.h> |
||||
|
||||
/* 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 */ |
@ -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 <nodes/pg_list.h> |
||||
#include <nodes/nodes.h> |
||||
#include <nodes/parsenodes.h> |
||||
#include <nodes/primnodes.h> |
||||
#include <parser/parse_node.h> |
||||
|
||||
#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 */ |
||||
|
@ -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 */ |
@ -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 <parser/parse_node.h> |
||||
|
||||
QueryTreeList *parser(char *str, Oid *typev, int nargs); |
||||
|
||||
#endif /* PARSER_H */ |
||||
|
Loading…
Reference in new issue