Commit 0772f1e5 authored by Tom Lane's avatar Tom Lane

Change plpgsql from using textual substitution to insert variable references

into SQL expressions, to using the newly added parser callback hooks.

This allows us to do the substitutions in a more semantically-aware way:
a variable reference will only be recognized where it can validly go,
ie, a place where a column value or parameter would be legal, instead of
the former behavior that would replace any textual match including
table names and column aliases (leading to syntax errors later on).
A release-note-worthy fine point is that plpgsql variable names that match
fully-reserved words will now need to be quoted.

This commit preserves the former behavior that variable references take
precedence over any possible match to a column name.  The infrastructure
is in place to support the reverse precedence or throwing an error on
ambiguity, but those behaviors aren't accessible yet.

Most of the code changes here are associated with making the namespace
data structure persist so that it can be consulted at runtime, instead
of throwing it away at the end of initial function parsing.

The plpgsql scanner is still doing name lookups, but that behavior is
now irrelevant for SQL expressions.  A future commit will deal with
removing unnecessary lookups.
parent 593f4b85
......@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.130 2009/11/05 16:58:36 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/gram.y,v 1.131 2009/11/06 18:37:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -520,7 +520,9 @@ decl_aliasitem : any_identifier
plpgsql_ns_setlocal(false);
nsi = plpgsql_ns_lookup(name, NULL, NULL, NULL);
nsi = plpgsql_ns_lookup(plpgsql_ns_top(),
name, NULL, NULL,
NULL);
if (nsi == NULL)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
......@@ -550,7 +552,7 @@ decl_varname : T_WORD
{
/*
* Since the scanner is only searching the topmost
* namestack entry, getting T_SCALAR etc can only
* namespace level, getting T_SCALAR etc can only
* happen if the name is already declared in this
* block.
*/
......@@ -1046,12 +1048,6 @@ for_control :
(errcode(ERRCODE_SYNTAX_ERROR),
errmsg("cursor FOR loop must have only one target variable")));
/* create loop's private RECORD variable */
plpgsql_convert_ident($2.name, &varname, 1);
new->rec = plpgsql_build_record(varname,
$2.lineno,
true);
/* can't use an unbound cursor this way */
if (cursor->cursor_explicit_expr == NULL)
ereport(ERROR,
......@@ -1063,6 +1059,12 @@ for_control :
K_LOOP,
"LOOP");
/* create loop's private RECORD variable */
plpgsql_convert_ident($2.name, &varname, 1);
new->rec = plpgsql_build_record(varname,
$2.lineno,
true);
$$ = (PLpgSQL_stmt *) new;
}
else
......@@ -1157,9 +1159,10 @@ for_control :
else
{
/*
* No "..", so it must be a query loop. We've prefixed an
* extra SELECT to the query text, so we need to remove that
* before performing syntax checking.
* No "..", so it must be a query loop. We've
* prefixed an extra SELECT to the query text,
* so we need to remove that before performing
* syntax checking.
*/
char *tmp_query;
PLpgSQL_stmt_fors *new;
......@@ -1700,7 +1703,9 @@ exception_sect :
/*
* We use a mid-rule action to add these
* special variables to the namespace before
* parsing the WHEN clauses themselves.
* parsing the WHEN clauses themselves. The
* scope of the names extends to the end of the
* current block.
*/
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
......@@ -1937,8 +1942,6 @@ read_sql_construct(int until,
int lno;
StringInfoData ds;
int parenlevel = 0;
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_expr *expr;
lno = plpgsql_scanner_lineno();
......@@ -1986,31 +1989,7 @@ read_sql_construct(int until,
if (plpgsql_SpaceScanned)
appendStringInfoChar(&ds, ' ');
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
appendStringInfoString(&ds, yytext);
break;
}
}
if (endtoken)
......@@ -2020,7 +1999,8 @@ read_sql_construct(int until,
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->paramnos = paramnos;
expr->paramnos = NULL;
expr->ns = plpgsql_ns_top();
pfree(ds.data);
if (valid_sql)
......@@ -2100,8 +2080,6 @@ static PLpgSQL_stmt *
make_execsql_stmt(const char *sqlstart, int lineno)
{
StringInfoData ds;
Bitmapset *paramnos = NULL;
char buf[32];
PLpgSQL_stmt_execsql *execsql;
PLpgSQL_expr *expr;
PLpgSQL_row *row = NULL;
......@@ -2147,38 +2125,15 @@ make_execsql_stmt(const char *sqlstart, int lineno)
if (plpgsql_SpaceScanned)
appendStringInfoChar(&ds, ' ');
switch (tok)
{
case T_SCALAR:
snprintf(buf, sizeof(buf), " $%d ", yylval.scalar->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.scalar->dno);
break;
case T_ROW:
snprintf(buf, sizeof(buf), " $%d ", yylval.row->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.row->dno);
break;
case T_RECORD:
snprintf(buf, sizeof(buf), " $%d ", yylval.rec->dno + 1);
appendStringInfoString(&ds, buf);
paramnos = bms_add_member(paramnos, yylval.rec->dno);
break;
default:
appendStringInfoString(&ds, yytext);
break;
}
}
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
expr->paramnos = paramnos;
expr->paramnos = NULL;
expr->ns = plpgsql_ns_top();
pfree(ds.data);
check_sql_expr(expr->query);
......@@ -2804,7 +2759,7 @@ check_label(const char *yytxt)
char *label_name;
plpgsql_convert_ident(yytxt, &label_name, 1);
if (plpgsql_ns_lookup_label(label_name) == NULL)
if (plpgsql_ns_lookup_label(plpgsql_ns_top(), label_name) == NULL)
yyerror("label does not exist");
return label_name;
}
......@@ -3005,20 +2960,23 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
*/
if (t_expr)
{
ListCell *l;
char varname[32];
PLpgSQL_var *t_var;
int t_varno;
ListCell *l;
/* use a name unlikely to collide with any user names */
snprintf(varname, sizeof(varname), "__Case__Variable_%d__",
plpgsql_nDatums);
/*
* We don't yet know the result datatype of t_expr. Build the
* variable as if it were INT4; we'll fix this at runtime if needed.
*/
t_var = (PLpgSQL_var *)
plpgsql_build_variable("*case*", lineno,
plpgsql_build_variable(varname, lineno,
plpgsql_build_datatype(INT4OID, -1),
false);
t_varno = t_var->dno;
new->t_varno = t_varno;
true);
new->t_varno = t_var->dno;
foreach(l, case_when_list)
{
......@@ -3026,21 +2984,19 @@ make_case(int lineno, PLpgSQL_expr *t_expr,
PLpgSQL_expr *expr = cwt->expr;
StringInfoData ds;
/* Must add the CASE variable as an extra param to expression */
expr->paramnos = bms_add_member(expr->paramnos, t_varno);
/* copy expression query without SELECT keyword (expr->query + 7) */
Assert(strncmp(expr->query, "SELECT ", 7) == 0);
/* And do the string hacking */
initStringInfo(&ds);
appendStringInfo(&ds, "SELECT $%d IN (%s)",
t_varno + 1,
expr->query + 7);
appendStringInfo(&ds, "SELECT \"%s\" IN (%s)",
varname, expr->query + 7);
pfree(expr->query);
expr->query = pstrdup(ds.data);
/* Adjust expr's namespace to include the case variable */
expr->ns = plpgsql_ns_top();
pfree(ds.data);
}
......
This diff is collapsed.
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.249 2009/11/04 22:26:07 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.250 2009/11/06 18:37:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -26,7 +26,6 @@
#include "lib/stringinfo.h"
#include "miscadmin.h"
#include "nodes/nodeFuncs.h"
#include "parser/parse_node.h"
#include "parser/scansup.h"
#include "storage/proc.h"
#include "tcop/tcopprot.h"
......@@ -158,8 +157,6 @@ static void exec_eval_datum(PLpgSQL_execstate *estate,
Oid *typeid,
Datum *value,
bool *isnull);
static Oid exec_get_datum_type(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum);
static int exec_eval_integer(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr,
bool *isNull);
......@@ -176,8 +173,6 @@ static int exec_for_query(PLpgSQL_execstate *estate, PLpgSQL_stmt_forq *stmt,
Portal portal, bool prefetch_ok);
static ParamListInfo setup_param_list(PLpgSQL_execstate *estate,
PLpgSQL_expr *expr);
static void plpgsql_parser_setup(ParseState *pstate, PLpgSQL_expr *expr);
static Node *plpgsql_param_ref(ParseState *pstate, ParamRef *pref);
static void plpgsql_param_fetch(ParamListInfo params, int paramid);
static void exec_move_row(PLpgSQL_execstate *estate,
PLpgSQL_rec *rec,
......@@ -3992,7 +3987,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
* a tupdesc but no row value for a record variable. (This currently can
* happen only for a trigger's NEW/OLD records.)
*/
static Oid
Oid
exec_get_datum_type(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum)
{
......@@ -4068,6 +4063,36 @@ exec_get_datum_type(PLpgSQL_execstate *estate,
return typeid;
}
/*
* exec_get_rec_fieldtype Get datatype of a PLpgSQL record field
*
* Also returns the field number to *fieldno.
*/
Oid
exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname,
int *fieldno)
{
Oid typeid;
int fno;
if (rec->tupdesc == NULL)
ereport(ERROR,
(errcode(ERRCODE_OBJECT_NOT_IN_PREREQUISITE_STATE),
errmsg("record \"%s\" is not assigned yet",
rec->refname),
errdetail("The tuple structure of a not-yet-assigned record is indeterminate.")));
fno = SPI_fnumber(rec->tupdesc, fieldname);
if (fno == SPI_ERROR_NOATTRIBUTE)
ereport(ERROR,
(errcode(ERRCODE_UNDEFINED_COLUMN),
errmsg("record \"%s\" has no field \"%s\"",
rec->refname, fieldname)));
typeid = SPI_gettypeid(rec->tupdesc, fno);
*fieldno = fno;
return typeid;
}
/* ----------
* exec_eval_integer Evaluate an expression, coerce result to int4
*
......@@ -4590,51 +4615,6 @@ setup_param_list(PLpgSQL_execstate *estate, PLpgSQL_expr *expr)
return paramLI;
}
/*
* plpgsql_parser_setup set up parser hooks for dynamic parameters
*/
static void
plpgsql_parser_setup(ParseState *pstate, PLpgSQL_expr *expr)
{
pstate->p_ref_hook_state = (void *) expr;
pstate->p_paramref_hook = plpgsql_param_ref;
/* no need to use p_coerce_param_hook */
}
/*
* plpgsql_param_ref parser callback for ParamRefs ($n symbols)
*/
static Node *
plpgsql_param_ref(ParseState *pstate, ParamRef *pref)
{
int paramno = pref->number;
PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
PLpgSQL_execstate *estate;
Param *param;
/* Let's just check parameter number is in range */
if (!bms_is_member(paramno-1, expr->paramnos))
return NULL;
/*
* We use the function's current estate to resolve parameter data types.
* This is really pretty bogus because there is no provision for updating
* plans when those types change ...
*/
estate = expr->func->cur_estate;
Assert(paramno <= estate->ndatums);
param = makeNode(Param);
param->paramkind = PARAM_EXTERN;
param->paramid = paramno;
param->paramtype = exec_get_datum_type(estate,
estate->datums[paramno-1]);
param->paramtypmod = -1;
param->location = pref->location;
return (Node *) param;
}
/*
* plpgsql_param_fetch paramFetch callback for dynamic parameter fetch
*/
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.83 2009/11/05 16:58:36 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.84 2009/11/06 18:37:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -21,21 +21,31 @@
/* ----------
* Local variables for the namestack handling
* Local variables for namespace handling
*
* The namespace structure actually forms a tree, of which only one linear
* list or "chain" (from the youngest item to the root) is accessible from
* any one plpgsql statement. During initial parsing of a function, ns_top
* points to the youngest item accessible from the block currently being
* parsed. We store the entire tree, however, since at runtime we will need
* to access the chain that's relevant to any one statement.
*
* Block boundaries in the namespace chain are marked by PLPGSQL_NSTYPE_LABEL
* items.
* ----------
*/
static PLpgSQL_ns *ns_current = NULL;
static PLpgSQL_nsitem *ns_top = NULL;
static bool ns_localmode = false;
/* ----------
* plpgsql_ns_init Initialize the namestack
* plpgsql_ns_init Initialize namespace processing for a new function
* ----------
*/
void
plpgsql_ns_init(void)
{
ns_current = NULL;
ns_top = NULL;
ns_localmode = false;
}
......@@ -49,7 +59,9 @@ plpgsql_ns_init(void)
* examining a name being declared in a DECLARE section. For that case
* we only want to know if there is a conflicting name earlier in the
* same DECLARE section. So the grammar must temporarily set local mode
* before scanning decl_varnames.
* before scanning decl_varnames. This should eventually go away in favor
* of a localmode argument to plpgsql_ns_lookup, or perhaps some less
* indirect method of dealing with duplicate namespace entries.
* ----------
*/
bool
......@@ -64,83 +76,67 @@ plpgsql_ns_setlocal(bool flag)
/* ----------
* plpgsql_ns_push Enter a new namestack level
* plpgsql_ns_push Create a new namespace level
* ----------
*/
void
plpgsql_ns_push(const char *label)
{
PLpgSQL_ns *new;
if (label == NULL)
label = "";
new = palloc0(sizeof(PLpgSQL_ns));
new->upper = ns_current;
ns_current = new;
plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
}
/* ----------
* plpgsql_ns_pop Return to the previous level
* plpgsql_ns_pop Pop entries back to (and including) the last label
* ----------
*/
void
plpgsql_ns_pop(void)
{
int i;
PLpgSQL_ns *old;
Assert(ns_top != NULL);
while (ns_top->itemtype != PLPGSQL_NSTYPE_LABEL)
ns_top = ns_top->prev;
ns_top = ns_top->prev;
}
old = ns_current;
ns_current = old->upper;
for (i = 0; i < old->items_used; i++)
pfree(old->items[i]);
pfree(old->items);
pfree(old);
/* ----------
* plpgsql_ns_top Fetch the current namespace chain end
* ----------
*/
PLpgSQL_nsitem *
plpgsql_ns_top(void)
{
return ns_top;
}
/* ----------
* plpgsql_ns_additem Add an item to the current
* namestack level
* plpgsql_ns_additem Add an item to the current namespace chain
* ----------
*/
void
plpgsql_ns_additem(int itemtype, int itemno, const char *name)
{
PLpgSQL_ns *ns = ns_current;
PLpgSQL_nsitem *nse;
Assert(name != NULL);
if (ns->items_used == ns->items_alloc)
{
if (ns->items_alloc == 0)
{
ns->items_alloc = 32;
ns->items = palloc(sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
}
else
{
ns->items_alloc *= 2;
ns->items = repalloc(ns->items,
sizeof(PLpgSQL_nsitem *) * ns->items_alloc);
}
}
/* first item added must be a label */
Assert(ns_top != NULL || itemtype == PLPGSQL_NSTYPE_LABEL);
nse = palloc(sizeof(PLpgSQL_nsitem) + strlen(name));
nse->itemtype = itemtype;
nse->itemno = itemno;
nse->prev = ns_top;
strcpy(nse->name, name);
ns->items[ns->items_used++] = nse;
ns_top = nse;
}
/* ----------
* plpgsql_ns_lookup Lookup an identifier in the namestack
* plpgsql_ns_lookup Lookup an identifier in the given namespace chain
*
* Note that this only searches for variables, not labels.
*
......@@ -158,20 +154,20 @@ plpgsql_ns_additem(int itemtype, int itemno, const char *name)
* ----------
*/
PLpgSQL_nsitem *
plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3,
plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur,
const char *name1, const char *name2, const char *name3,
int *names_used)
{
PLpgSQL_ns *ns;
int i;
/* Scan each level of the namestack */
for (ns = ns_current; ns != NULL; ns = ns->upper)
{
/* Check for unqualified match to variable name */
for (i = 1; i < ns->items_used; i++)
/* Outer loop iterates once per block level in the namespace chain */
while (ns_cur != NULL)
{
PLpgSQL_nsitem *nsitem = ns->items[i];
PLpgSQL_nsitem *nsitem;
/* Check this level for unqualified match to variable name */
for (nsitem = ns_cur;
nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
nsitem = nsitem->prev)
{
if (strcmp(nsitem->name, name1) == 0)
{
if (name2 == NULL ||
......@@ -184,14 +180,14 @@ plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3,
}
}
/* Check for qualified match to variable name */
/* Check this level for qualified match to variable name */
if (name2 != NULL &&
strcmp(ns->items[0]->name, name1) == 0)
strcmp(nsitem->name, name1) == 0)
{
for (i = 1; i < ns->items_used; i++)
for (nsitem = ns_cur;
nsitem->itemtype != PLPGSQL_NSTYPE_LABEL;
nsitem = nsitem->prev)
{
PLpgSQL_nsitem *nsitem = ns->items[i];
if (strcmp(nsitem->name, name2) == 0)
{
if (name3 == NULL ||
......@@ -207,6 +203,8 @@ plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3,
if (ns_localmode)
break; /* do not look into upper levels */
ns_cur = nsitem->prev;
}
/* This is just to suppress possibly-uninitialized-variable warnings */
......@@ -217,18 +215,18 @@ plpgsql_ns_lookup(const char *name1, const char *name2, const char *name3,
/* ----------
* plpgsql_ns_lookup_label Lookup a label in the namestack
* plpgsql_ns_lookup_label Lookup a label in the given namespace chain
* ----------
*/
PLpgSQL_nsitem *
plpgsql_ns_lookup_label(const char *name)
plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur, const char *name)
{
PLpgSQL_ns *ns;
for (ns = ns_current; ns != NULL; ns = ns->upper)
while (ns_cur != NULL)
{
if (strcmp(ns->items[0]->name, name) == 0)
return ns->items[0];
if (ns_cur->itemtype == PLPGSQL_NSTYPE_LABEL &&
strcmp(ns_cur->name, name) == 0)
return ns_cur;
ns_cur = ns_cur->prev;
}
return NULL; /* label not found */
......
......@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.119 2009/11/05 16:58:36 tgl Exp $
* $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.120 2009/11/06 18:37:54 tgl Exp $
*
*-------------------------------------------------------------------------
*/
......@@ -37,7 +37,7 @@
#define _(x) dgettext(TEXTDOMAIN, x)
/* ----------
* Compiler's namestack item types
* Compiler's namespace item types
* ----------
*/
enum
......@@ -140,6 +140,17 @@ enum
PLPGSQL_RAISEOPTION_HINT
};
/* --------
* Behavioral modes for plpgsql variable resolution
* --------
*/
typedef enum
{
PLPGSQL_RESOLVE_BEFORE, /* prefer plpgsql var to table column */
PLPGSQL_RESOLVE_AFTER, /* prefer table column to plpgsql var */
PLPGSQL_RESOLVE_ERROR /* throw error if ambiguous */
} PLpgSQL_resolve_option;
/**********************************************************************
* Node and structure definitions
......@@ -193,6 +204,9 @@ typedef struct PLpgSQL_expr
/* function containing this expr (not set until we first parse query) */
struct PLpgSQL_function *func;
/* namespace chain visible to this expr */
struct PLpgSQL_nsitem *ns;
/* fields for "simple expression" fast-path execution: */
Expr *expr_simple_expr; /* NULL means not a simple expr */
int expr_simple_generation; /* plancache generation we checked */
......@@ -283,24 +297,15 @@ typedef struct
} PLpgSQL_arrayelem;
typedef struct
{ /* Item in the compilers namestack */
typedef struct PLpgSQL_nsitem
{ /* Item in the compilers namespace tree */
int itemtype;
int itemno;
struct PLpgSQL_nsitem *prev;
char name[1]; /* actually, as long as needed */
} PLpgSQL_nsitem;
/* XXX: consider adapting this to use List */
typedef struct PLpgSQL_ns
{ /* Compiler namestack level */
int items_alloc;
int items_used;
PLpgSQL_nsitem **items;
struct PLpgSQL_ns *upper;
} PLpgSQL_ns;
typedef struct
{ /* Generic execution node */
int cmd_type;
......@@ -663,6 +668,8 @@ typedef struct PLpgSQL_function
int tg_nargs_varno;
int tg_argv_varno;
PLpgSQL_resolve_option resolve_option;
int ndatums;
PLpgSQL_datum **datums;
PLpgSQL_stmt_block *action;
......@@ -795,6 +802,8 @@ extern PLpgSQL_plugin **plugin_ptr;
extern PLpgSQL_function *plpgsql_compile(FunctionCallInfo fcinfo,
bool forValidator);
extern PLpgSQL_function *plpgsql_compile_inline(char *proc_source);
extern void plpgsql_parser_setup(struct ParseState *pstate,
PLpgSQL_expr *expr);
extern int plpgsql_parse_word(const char *word);
extern int plpgsql_parse_dblword(const char *word);
extern int plpgsql_parse_tripword(const char *word);
......@@ -838,19 +847,26 @@ extern HeapTuple plpgsql_exec_trigger(PLpgSQL_function *func,
extern void plpgsql_xact_cb(XactEvent event, void *arg);
extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg);
extern Oid exec_get_datum_type(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum);
extern Oid exec_get_rec_fieldtype(PLpgSQL_rec *rec, const char *fieldname,
int *fieldno);
/* ----------
* Functions for namestack handling in pl_funcs.c
* Functions for namespace handling in pl_funcs.c
* ----------
*/
extern void plpgsql_ns_init(void);
extern bool plpgsql_ns_setlocal(bool flag);
extern void plpgsql_ns_push(const char *label);
extern void plpgsql_ns_pop(void);
extern PLpgSQL_nsitem *plpgsql_ns_top(void);
extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name1, const char *name2,
extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_nsitem *ns_cur,
const char *name1, const char *name2,
const char *name3, int *names_used);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(const char *name);
extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_nsitem *ns_cur,
const char *name);
/* ----------
* Other functions in pl_funcs.c
......
......@@ -899,7 +899,7 @@ begin
declare
rec record;
begin
select into rec * from PLine where slotname = outer.rec.backlink;
select into rec * from PLine where slotname = "outer".rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';
......@@ -3938,3 +3938,43 @@ LINE 1: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY room...
^
QUERY: SELECT rtrim(roomno) AS roomno, foo FROM Room ORDER BY roomno
CONTEXT: PL/pgSQL function "inline_code_block" line 3 at FOR over SELECT rows
-- Check variable scoping -- a var is not available in its own or prior
-- default expressions.
create function scope_test() returns int as $$
declare x int := 42;
begin
declare y int := x + 1;
x int := x + 2;
begin
return x * 100 + y;
end;
end;
$$ language plpgsql;
select scope_test();
scope_test
------------
4443
(1 row)
drop function scope_test();
-- Check handling of conflicts between plpgsql vars and table columns.
create function conflict_test() returns setof int8_tbl as $$
declare r record;
q1 bigint := 42;
begin
for r in select q1,q2 from int8_tbl loop
return next r;
end loop;
end;
$$ language plpgsql;
select * from conflict_test();
q1 | q2
----+-------------------
42 | 456
42 | 4567890123456789
42 | 123
42 | 4567890123456789
42 | -4567890123456789
(5 rows)
drop function conflict_test();
......@@ -1026,7 +1026,7 @@ begin
declare
rec record;
begin
select into rec * from PLine where slotname = outer.rec.backlink;
select into rec * from PLine where slotname = "outer".rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';
......@@ -3135,3 +3135,37 @@ BEGIN
RAISE NOTICE '%, %', r.roomno, r.comment;
END LOOP;
END$$;
-- Check variable scoping -- a var is not available in its own or prior
-- default expressions.
create function scope_test() returns int as $$
declare x int := 42;
begin
declare y int := x + 1;
x int := x + 2;
begin
return x * 100 + y;
end;
end;
$$ language plpgsql;
select scope_test();
drop function scope_test();
-- Check handling of conflicts between plpgsql vars and table columns.
create function conflict_test() returns setof int8_tbl as $$
declare r record;
q1 bigint := 42;
begin
for r in select q1,q2 from int8_tbl loop
return next r;
end loop;
end;
$$ language plpgsql;
select * from conflict_test();
drop function conflict_test();
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment