提交 fcaad7e2 编写于 作者: T Tom Lane

Standardize on the assumption that the arguments of a RowExpr correspond

to the physical layout of the rowtype, ie, there are dummy arguments
corresponding to any dropped columns in the rowtype.  We formerly had a
couple of places that did it this way and several others that did not.
Fixes Gaetano Mendola's "cache lookup failed for type 0" bug of 5-Aug.
上级 388ffad0
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.165 2004/08/02 01:30:41 tgl Exp $ * $PostgreSQL: pgsql/src/backend/executor/execQual.c,v 1.166 2004/08/17 18:47:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -44,6 +44,7 @@ ...@@ -44,6 +44,7 @@
#include "executor/nodeSubplan.h" #include "executor/nodeSubplan.h"
#include "funcapi.h" #include "funcapi.h"
#include "miscadmin.h" #include "miscadmin.h"
#include "nodes/makefuncs.h"
#include "optimizer/planmain.h" #include "optimizer/planmain.h"
#include "parser/parse_expr.h" #include "parser/parse_expr.h"
#include "utils/acl.h" #include "utils/acl.h"
...@@ -2096,7 +2097,7 @@ ExecEvalRow(RowExprState *rstate, ...@@ -2096,7 +2097,7 @@ ExecEvalRow(RowExprState *rstate,
HeapTuple tuple; HeapTuple tuple;
Datum *values; Datum *values;
char *nulls; char *nulls;
int nargs; int natts;
ListCell *arg; ListCell *arg;
int i; int i;
...@@ -2106,9 +2107,12 @@ ExecEvalRow(RowExprState *rstate, ...@@ -2106,9 +2107,12 @@ ExecEvalRow(RowExprState *rstate,
*isDone = ExprSingleResult; *isDone = ExprSingleResult;
/* Allocate workspace */ /* Allocate workspace */
nargs = list_length(rstate->args); natts = rstate->tupdesc->natts;
values = (Datum *) palloc(nargs * sizeof(Datum)); values = (Datum *) palloc0(natts * sizeof(Datum));
nulls = (char *) palloc(nargs * sizeof(char)); nulls = (char *) palloc(natts * sizeof(char));
/* preset to nulls in case rowtype has some later-added columns */
memset(nulls, 'n', natts * sizeof(char));
/* Evaluate field values */ /* Evaluate field values */
i = 0; i = 0;
...@@ -2979,19 +2983,12 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -2979,19 +2983,12 @@ ExecInitExpr(Expr *node, PlanState *parent)
{ {
RowExpr *rowexpr = (RowExpr *) node; RowExpr *rowexpr = (RowExpr *) node;
RowExprState *rstate = makeNode(RowExprState); RowExprState *rstate = makeNode(RowExprState);
Form_pg_attribute *attrs;
List *outlist = NIL; List *outlist = NIL;
ListCell *l; ListCell *l;
int i;
rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow; rstate->xprstate.evalfunc = (ExprStateEvalFunc) ExecEvalRow;
foreach(l, rowexpr->args)
{
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
}
rstate->args = outlist;
/* Build tupdesc to describe result tuples */ /* Build tupdesc to describe result tuples */
if (rowexpr->row_typeid == RECORDOID) if (rowexpr->row_typeid == RECORDOID)
{ {
...@@ -3003,7 +3000,46 @@ ExecInitExpr(Expr *node, PlanState *parent) ...@@ -3003,7 +3000,46 @@ ExecInitExpr(Expr *node, PlanState *parent)
{ {
/* it's been cast to a named type, use that */ /* it's been cast to a named type, use that */
rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1); rstate->tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
rstate->tupdesc = CreateTupleDescCopy(rstate->tupdesc);
} }
/* Set up evaluation, skipping any deleted columns */
Assert(list_length(rowexpr->args) <= rstate->tupdesc->natts);
attrs = rstate->tupdesc->attrs;
i = 0;
foreach(l, rowexpr->args)
{
Expr *e = (Expr *) lfirst(l);
ExprState *estate;
if (!attrs[i]->attisdropped)
{
/*
* Guard against ALTER COLUMN TYPE on rowtype
* since the RowExpr was created. XXX should we
* check typmod too? Not sure we can be sure it'll
* be the same.
*/
if (exprType((Node *) e) != attrs[i]->atttypid)
ereport(ERROR,
(errcode(ERRCODE_DATATYPE_MISMATCH),
errmsg("ROW() column has type %s instead of type %s",
format_type_be(exprType((Node *) e)),
format_type_be(attrs[i]->atttypid))));
}
else
{
/*
* Ignore original expression and insert a NULL.
* We don't really care what type of NULL it is,
* so always make an int4 NULL.
*/
e = (Expr *) makeNullConst(INT4OID);
}
estate = ExecInitExpr(e, parent);
outlist = lappend(outlist, estate);
i++;
}
rstate->args = outlist;
state = (ExprState *) rstate; state = (ExprState *) rstate;
} }
break; break;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.177 2004/08/02 01:30:43 tgl Exp $ * $PostgreSQL: pgsql/src/backend/optimizer/util/clauses.c,v 1.178 2004/08/17 18:47:08 tgl Exp $
* *
* HISTORY * HISTORY
* AUTHOR DATE MAJOR EVENT * AUTHOR DATE MAJOR EVENT
...@@ -40,6 +40,7 @@ ...@@ -40,6 +40,7 @@
#include "utils/lsyscache.h" #include "utils/lsyscache.h"
#include "utils/memutils.h" #include "utils/memutils.h"
#include "utils/syscache.h" #include "utils/syscache.h"
#include "utils/typcache.h"
typedef struct typedef struct
...@@ -1054,6 +1055,33 @@ set_coercionform_dontcare_walker(Node *node, void *context) ...@@ -1054,6 +1055,33 @@ set_coercionform_dontcare_walker(Node *node, void *context)
context); context);
} }
/*
* Helper for eval_const_expressions: check that datatype of an attribute
* is still what it was when the expression was parsed. This is needed to
* guard against improper simplification after ALTER COLUMN TYPE. (XXX we
* may well need to make similar checks elsewhere?)
*/
static bool
rowtype_field_matches(Oid rowtypeid, int fieldnum,
Oid expectedtype, int32 expectedtypmod)
{
TupleDesc tupdesc;
Form_pg_attribute attr;
/* No issue for RECORD, since there is no way to ALTER such a type */
if (rowtypeid == RECORDOID)
return true;
tupdesc = lookup_rowtype_tupdesc(rowtypeid, -1);
if (fieldnum <= 0 || fieldnum > tupdesc->natts)
return false;
attr = tupdesc->attrs[fieldnum - 1];
if (attr->attisdropped ||
attr->atttypid != expectedtype ||
attr->atttypmod != expectedtypmod)
return false;
return true;
}
/*-------------------- /*--------------------
* eval_const_expressions * eval_const_expressions
...@@ -1630,6 +1658,10 @@ eval_const_expressions_mutator(Node *node, ...@@ -1630,6 +1658,10 @@ eval_const_expressions_mutator(Node *node,
* parser, because ParseComplexProjection short-circuits it. But * parser, because ParseComplexProjection short-circuits it. But
* it can arise while simplifying functions.) Also, we can * it can arise while simplifying functions.) Also, we can
* optimize field selection from a RowExpr construct. * optimize field selection from a RowExpr construct.
*
* We must however check that the declared type of the field is
* still the same as when the FieldSelect was created --- this
* can change if someone did ALTER COLUMN TYPE on the rowtype.
*/ */
FieldSelect *fselect = (FieldSelect *) node; FieldSelect *fselect = (FieldSelect *) node;
FieldSelect *newfselect; FieldSelect *newfselect;
...@@ -1640,11 +1672,15 @@ eval_const_expressions_mutator(Node *node, ...@@ -1640,11 +1672,15 @@ eval_const_expressions_mutator(Node *node,
if (arg && IsA(arg, Var) && if (arg && IsA(arg, Var) &&
((Var *) arg)->varattno == InvalidAttrNumber) ((Var *) arg)->varattno == InvalidAttrNumber)
{ {
return (Node *) makeVar(((Var *) arg)->varno, if (rowtype_field_matches(((Var *) arg)->vartype,
fselect->fieldnum, fselect->fieldnum,
fselect->resulttype, fselect->resulttype,
fselect->resulttypmod, fselect->resulttypmod))
((Var *) arg)->varlevelsup); return (Node *) makeVar(((Var *) arg)->varno,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod,
((Var *) arg)->varlevelsup);
} }
if (arg && IsA(arg, RowExpr)) if (arg && IsA(arg, RowExpr))
{ {
...@@ -1652,7 +1688,18 @@ eval_const_expressions_mutator(Node *node, ...@@ -1652,7 +1688,18 @@ eval_const_expressions_mutator(Node *node,
if (fselect->fieldnum > 0 && if (fselect->fieldnum > 0 &&
fselect->fieldnum <= list_length(rowexpr->args)) fselect->fieldnum <= list_length(rowexpr->args))
return (Node *) list_nth(rowexpr->args, fselect->fieldnum - 1); {
Node *fld = (Node *) list_nth(rowexpr->args,
fselect->fieldnum - 1);
if (rowtype_field_matches(rowexpr->row_typeid,
fselect->fieldnum,
fselect->resulttype,
fselect->resulttypmod) &&
fselect->resulttype == exprType(fld) &&
fselect->resulttypmod == exprTypmod(fld))
return fld;
}
} }
newfselect = makeNode(FieldSelect); newfselect = makeNode(FieldSelect);
newfselect->arg = (Expr *) arg; newfselect->arg = (Expr *) arg;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.119 2004/06/16 01:26:44 tgl Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_coerce.c,v 2.120 2004/08/17 18:47:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -648,10 +648,15 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ...@@ -648,10 +648,15 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
List *args = NIL; List *args = NIL;
List *newargs; List *newargs;
int i; int i;
int ucolno;
ListCell *arg; ListCell *arg;
if (node && IsA(node, RowExpr)) if (node && IsA(node, RowExpr))
{ {
/*
* Since the RowExpr must be of type RECORD, we needn't worry
* about it containing any dropped columns.
*/
args = ((RowExpr *) node)->args; args = ((RowExpr *) node)->args;
} }
else if (node && IsA(node, Var) && else if (node && IsA(node, Var) &&
...@@ -670,6 +675,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ...@@ -670,6 +675,8 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
Oid vartype; Oid vartype;
int32 vartypmod; int32 vartypmod;
if (get_rte_attribute_is_dropped(rte, nf))
continue;
get_rte_attribute_type(rte, nf, &vartype, &vartypmod); get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
args = lappend(args, args = lappend(args,
makeVar(((Var *) node)->varno, makeVar(((Var *) node)->varno,
...@@ -687,19 +694,34 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ...@@ -687,19 +694,34 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
format_type_be(targetTypeId)))); format_type_be(targetTypeId))));
tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1); tupdesc = lookup_rowtype_tupdesc(targetTypeId, -1);
if (list_length(args) != tupdesc->natts)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has wrong number of columns.")));
newargs = NIL; newargs = NIL;
i = 0; ucolno = 1;
foreach(arg, args) arg = list_head(args);
for (i = 0; i < tupdesc->natts; i++)
{ {
Node *expr = (Node *) lfirst(arg); Node *expr;
Oid exprtype = exprType(expr); Oid exprtype;
/* Fill in NULLs for dropped columns in rowtype */
if (tupdesc->attrs[i]->attisdropped)
{
/*
* can't use atttypid here, but it doesn't really matter
* what type the Const claims to be.
*/
newargs = lappend(newargs, makeNullConst(INT4OID));
continue;
}
if (arg == NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has too few columns.")));
expr = (Node *) lfirst(arg);
exprtype = exprType(expr);
expr = coerce_to_target_type(pstate, expr = coerce_to_target_type(pstate,
expr, exprtype, expr, exprtype,
...@@ -716,10 +738,18 @@ coerce_record_to_complex(ParseState *pstate, Node *node, ...@@ -716,10 +738,18 @@ coerce_record_to_complex(ParseState *pstate, Node *node,
errdetail("Cannot cast type %s to %s in column %d.", errdetail("Cannot cast type %s to %s in column %d.",
format_type_be(exprtype), format_type_be(exprtype),
format_type_be(tupdesc->attrs[i]->atttypid), format_type_be(tupdesc->attrs[i]->atttypid),
i + 1))); ucolno)));
newargs = lappend(newargs, expr); newargs = lappend(newargs, expr);
i++; ucolno++;
arg = lnext(arg);
} }
if (arg != NULL)
ereport(ERROR,
(errcode(ERRCODE_CANNOT_COERCE),
errmsg("cannot cast type %s to %s",
format_type_be(RECORDOID),
format_type_be(targetTypeId)),
errdetail("Input has too many columns.")));
rowexpr = makeNode(RowExpr); rowexpr = makeNode(RowExpr);
rowexpr->args = newargs; rowexpr->args = newargs;
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.96 2004/05/30 23:40:35 neilc Exp $ * $PostgreSQL: pgsql/src/backend/parser/parse_relation.c,v 1.97 2004/08/17 18:47:08 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -42,8 +42,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode, ...@@ -42,8 +42,6 @@ static Node *scanNameSpaceForRelid(ParseState *pstate, Node *nsnode,
static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode, static void scanNameSpaceForConflict(ParseState *pstate, Node *nsnode,
RangeTblEntry *rte1, const char *aliasname1); RangeTblEntry *rte1, const char *aliasname1);
static bool isForUpdate(ParseState *pstate, char *refname); static bool isForUpdate(ParseState *pstate, char *refname);
static bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
AttrNumber attnum);
static int specialAttNum(const char *attname); static int specialAttNum(const char *attname);
static void warnAutoRange(ParseState *pstate, RangeVar *relation); static void warnAutoRange(ParseState *pstate, RangeVar *relation);
...@@ -1699,7 +1697,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, ...@@ -1699,7 +1697,7 @@ get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
* get_rte_attribute_is_dropped * get_rte_attribute_is_dropped
* Check whether attempted attribute ref is to a dropped column * Check whether attempted attribute ref is to a dropped column
*/ */
static bool bool
get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum) get_rte_attribute_is_dropped(RangeTblEntry *rte, AttrNumber attnum)
{ {
bool result; bool result;
......
...@@ -7,12 +7,13 @@ ...@@ -7,12 +7,13 @@
* *
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.84 2004/05/30 23:40:35 neilc Exp $ * $PostgreSQL: pgsql/src/backend/rewrite/rewriteManip.c,v 1.85 2004/08/17 18:47:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
#include "postgres.h" #include "postgres.h"
#include "catalog/pg_type.h"
#include "nodes/makefuncs.h" #include "nodes/makefuncs.h"
#include "optimizer/clauses.h" #include "optimizer/clauses.h"
#include "optimizer/tlist.h" #include "optimizer/tlist.h"
...@@ -938,18 +939,30 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context) ...@@ -938,18 +939,30 @@ ResolveNew_mutator(Node *node, ResolveNew_context *context)
for (nf = 1; nf <= nfields; nf++) for (nf = 1; nf <= nfields; nf++)
{ {
Oid vartype; if (get_rte_attribute_is_dropped(rte, nf))
int32 vartypmod; {
Var *newvar; /*
* can't determine att type here, but it doesn't
get_rte_attribute_type(rte, nf, &vartype, &vartypmod); * really matter what type the Const claims to be.
newvar = makeVar(this_varno, */
nf, fields = lappend(fields,
vartype, makeNullConst(INT4OID));
vartypmod, }
this_varlevelsup); else
fields = lappend(fields, {
resolve_one_var(newvar, context)); Oid vartype;
int32 vartypmod;
Var *newvar;
get_rte_attribute_type(rte, nf, &vartype, &vartypmod);
newvar = makeVar(this_varno,
nf,
vartype,
vartypmod,
this_varlevelsup);
fields = lappend(fields,
resolve_one_var(newvar, context));
}
} }
rowexpr = makeNode(RowExpr); rowexpr = makeNode(RowExpr);
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* back to source text * back to source text
* *
* IDENTIFICATION * IDENTIFICATION
* $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.176 2004/08/02 04:27:15 tgl Exp $ * $PostgreSQL: pgsql/src/backend/utils/adt/ruleutils.c,v 1.177 2004/08/17 18:47:09 tgl Exp $
* *
* This software is copyrighted by Jan Wieck - Hamburg. * This software is copyrighted by Jan Wieck - Hamburg.
* *
...@@ -3283,22 +3283,54 @@ get_rule_expr(Node *node, deparse_context *context, ...@@ -3283,22 +3283,54 @@ get_rule_expr(Node *node, deparse_context *context,
case T_RowExpr: case T_RowExpr:
{ {
RowExpr *rowexpr = (RowExpr *) node; RowExpr *rowexpr = (RowExpr *) node;
TupleDesc tupdesc = NULL;
ListCell *arg; ListCell *arg;
int i;
char *sep; char *sep;
/* /*
* SQL99 allows "ROW" to be omitted when list_length(args) > 1, * If it's a named type and not RECORD, we may have to skip
* but for simplicity we always print it. * dropped columns and/or claim there are NULLs for added
* columns.
*/
if (rowexpr->row_typeid != RECORDOID)
{
tupdesc = lookup_rowtype_tupdesc(rowexpr->row_typeid, -1);
Assert(list_length(rowexpr->args) <= tupdesc->natts);
}
/*
* SQL99 allows "ROW" to be omitted when there is more than
* one column, but for simplicity we always print it.
*/ */
appendStringInfo(buf, "ROW("); appendStringInfo(buf, "ROW(");
sep = ""; sep = "";
i = 0;
foreach(arg, rowexpr->args) foreach(arg, rowexpr->args)
{ {
Node *e = (Node *) lfirst(arg); Node *e = (Node *) lfirst(arg);
appendStringInfo(buf, sep); if (tupdesc == NULL ||
get_rule_expr(e, context, true); !tupdesc->attrs[i]->attisdropped)
sep = ", "; {
appendStringInfo(buf, sep);
get_rule_expr(e, context, true);
sep = ", ";
}
i++;
}
if (tupdesc != NULL)
{
while (i < tupdesc->natts)
{
if (!tupdesc->attrs[i]->attisdropped)
{
appendStringInfo(buf, sep);
appendStringInfo(buf, "NULL");
sep = ", ";
}
i++;
}
} }
appendStringInfo(buf, ")"); appendStringInfo(buf, ")");
if (rowexpr->row_format == COERCE_EXPLICIT_CAST) if (rowexpr->row_format == COERCE_EXPLICIT_CAST)
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.100 2004/06/09 19:08:18 tgl Exp $ * $PostgreSQL: pgsql/src/include/nodes/primnodes.h,v 1.101 2004/08/17 18:47:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -97,13 +97,16 @@ typedef struct Resdom ...@@ -97,13 +97,16 @@ typedef struct Resdom
* Alias - * Alias -
* specifies an alias for a range variable; the alias might also * specifies an alias for a range variable; the alias might also
* specify renaming of columns within the table. * specify renaming of columns within the table.
*
* Note: colnames is a list of Value nodes (always strings). In an RTE's
* eref Alias, the colnames list includes dropped columns, so that the
* colname list position matches the physical attribute number.
*/ */
typedef struct Alias typedef struct Alias
{ {
NodeTag type; NodeTag type;
char *aliasname; /* aliased rel name (never qualified) */ char *aliasname; /* aliased rel name (never qualified) */
List *colnames; /* optional list of column aliases */ List *colnames; /* optional list of column aliases */
/* Note: colnames is a list of Value nodes (always strings) */
} Alias; } Alias;
typedef enum InhOption typedef enum InhOption
...@@ -663,6 +666,16 @@ typedef struct ArrayExpr ...@@ -663,6 +666,16 @@ typedef struct ArrayExpr
/* /*
* RowExpr - a ROW() expression * RowExpr - a ROW() expression
*
* Note: the list of fields must have a one-for-one correspondence with
* physical fields of the associated rowtype, although it is okay for it
* to be shorter than the rowtype. That is, the N'th list element must
* match up with the N'th physical field. When the N'th physical field
* is a dropped column (attisdropped) then the N'th list element can just
* be a NULL constant. (This case can only occur for named composite types,
* not RECORD types, since those are built from the RowExpr itself rather
* than vice versa.) It is important not to assume that length(args) is
* the same as the number of columns logically present in the rowtype.
*/ */
typedef struct RowExpr typedef struct RowExpr
{ {
......
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group * Portions Copyright (c) 1996-2003, PostgreSQL Global Development Group
* Portions Copyright (c) 1994, Regents of the University of California * Portions Copyright (c) 1994, Regents of the University of California
* *
* $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.24 2004/05/26 04:41:46 neilc Exp $ * $PostgreSQL: pgsql/src/include/parser/parsetree.h,v 1.25 2004/08/17 18:47:09 tgl Exp $
* *
*------------------------------------------------------------------------- *-------------------------------------------------------------------------
*/ */
...@@ -55,6 +55,13 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum); ...@@ -55,6 +55,13 @@ extern char *get_rte_attribute_name(RangeTblEntry *rte, AttrNumber attnum);
extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum, extern void get_rte_attribute_type(RangeTblEntry *rte, AttrNumber attnum,
Oid *vartype, int32 *vartypmod); Oid *vartype, int32 *vartypmod);
/*
* Check whether an attribute of an RTE has been dropped (note that
* get_rte_attribute_type will fail on such an attr)
*/
extern bool get_rte_attribute_is_dropped(RangeTblEntry *rte,
AttrNumber attnum);
/* ---------------- /* ----------------
* target list operations * target list operations
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册