plpgsql-uses-hooks-1.patch
text/x-patch
Filename: plpgsql-uses-hooks-1.patch
Type: text/x-patch
Part: 0
Patch
Format: context
| File | + | − |
|---|---|---|
| src/pl/plpgsql/src/gram.y | 30 | 74 |
| src/pl/plpgsql/src/pl_comp.c | 320 | 30 |
| src/pl/plpgsql/src/pl_exec.c | 31 | 51 |
| src/pl/plpgsql/src/pl_funcs.c | 33 | 21 |
| src/pl/plpgsql/src/plpgsql.h | 19 | 7 |
| src/test/regress/expected/plpgsql.out | 1 | 1 |
| src/test/regress/sql/plpgsql.sql | 1 | 1 |
Index: src/pl/plpgsql/src/gram.y
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/gram.y,v
retrieving revision 1.130
diff -c -r1.130 gram.y
*** src/pl/plpgsql/src/gram.y 5 Nov 2009 16:58:36 -0000 1.130
--- src/pl/plpgsql/src/gram.y 6 Nov 2009 14:31:53 -0000
***************
*** 520,526 ****
plpgsql_ns_setlocal(false);
! nsi = plpgsql_ns_lookup(name, NULL, NULL, NULL);
if (nsi == NULL)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
--- 520,528 ----
plpgsql_ns_setlocal(false);
! nsi = plpgsql_ns_lookup(plpgsql_ns_current(),
! name, NULL, NULL,
! NULL);
if (nsi == NULL)
{
plpgsql_error_lineno = plpgsql_scanner_lineno();
***************
*** 550,556 ****
{
/*
* Since the scanner is only searching the topmost
! * namestack entry, getting T_SCALAR etc can only
* happen if the name is already declared in this
* block.
*/
--- 552,558 ----
{
/*
* Since the scanner is only searching the topmost
! * namespace entry, getting T_SCALAR etc can only
* happen if the name is already declared in this
* block.
*/
***************
*** 1698,1710 ****
| K_EXCEPTION lno
{
/*
! * We use a mid-rule action to add these
! * special variables to the namespace before
! * parsing the WHEN clauses themselves.
*/
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
var = plpgsql_build_variable("sqlstate", $2,
plpgsql_build_datatype(TEXTOID, -1),
true);
--- 1700,1715 ----
| K_EXCEPTION lno
{
/*
! * We use a mid-rule action to add a local namespace
! * containing these special variables before parsing
! * the WHEN clauses themselves. The namespace ensures
! * the vars are only visible within the WHEN clauses.
*/
PLpgSQL_exception_block *new = palloc(sizeof(PLpgSQL_exception_block));
PLpgSQL_variable *var;
+ plpgsql_ns_push(NULL);
+
var = plpgsql_build_variable("sqlstate", $2,
plpgsql_build_datatype(TEXTOID, -1),
true);
***************
*** 1722,1728 ****
--- 1727,1735 ----
proc_exceptions
{
PLpgSQL_exception_block *new = $<exception_block>3;
+
new->exc_list = $4;
+ plpgsql_ns_pop();
$$ = new;
}
***************
*** 1937,1944 ****
int lno;
StringInfoData ds;
int parenlevel = 0;
- Bitmapset *paramnos = NULL;
- char buf[32];
PLpgSQL_expr *expr;
lno = plpgsql_scanner_lineno();
--- 1944,1949 ----
***************
*** 1986,2016 ****
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)
--- 1991,1997 ----
if (plpgsql_SpaceScanned)
appendStringInfoChar(&ds, ' ');
! appendStringInfoString(&ds, yytext);
}
if (endtoken)
***************
*** 2020,2026 ****
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->paramnos = paramnos;
pfree(ds.data);
if (valid_sql)
--- 2001,2008 ----
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->paramnos = NULL;
! expr->ns = plpgsql_ns_current();
pfree(ds.data);
if (valid_sql)
***************
*** 2100,2107 ****
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;
--- 2082,2087 ----
***************
*** 2147,2184 ****
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;
pfree(ds.data);
check_sql_expr(expr->query);
--- 2127,2141 ----
if (plpgsql_SpaceScanned)
appendStringInfoChar(&ds, ' ');
! appendStringInfoString(&ds, yytext);
}
expr = palloc0(sizeof(PLpgSQL_expr));
expr->dtype = PLPGSQL_DTYPE_EXPR;
expr->query = pstrdup(ds.data);
expr->plan = NULL;
! expr->paramnos = NULL;
! expr->ns = plpgsql_ns_current();
pfree(ds.data);
check_sql_expr(expr->query);
***************
*** 2804,2810 ****
char *label_name;
plpgsql_convert_ident(yytxt, &label_name, 1);
! if (plpgsql_ns_lookup_label(label_name) == NULL)
yyerror("label does not exist");
return label_name;
}
--- 2761,2767 ----
char *label_name;
plpgsql_convert_ident(yytxt, &label_name, 1);
! if (plpgsql_ns_lookup_label(plpgsql_ns_current(), label_name) == NULL)
yyerror("label does not exist");
return label_name;
}
***************
*** 3005,3024 ****
*/
if (t_expr)
{
! ListCell *l;
PLpgSQL_var *t_var;
! int t_varno;
/*
* 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_datatype(INT4OID, -1),
! false);
! t_varno = t_var->dno;
! new->t_varno = t_varno;
foreach(l, case_when_list)
{
--- 2962,2984 ----
*/
if (t_expr)
{
! char varname[32];
PLpgSQL_var *t_var;
! 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(varname, lineno,
plpgsql_build_datatype(INT4OID, -1),
! true);
! new->t_varno = t_var->dno;
foreach(l, case_when_list)
{
***************
*** 3026,3043 ****
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);
pfree(expr->query);
expr->query = pstrdup(ds.data);
--- 2986,2999 ----
PLpgSQL_expr *expr = cwt->expr;
StringInfoData ds;
/* 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 \"%s\" IN (%s)",
! varname, expr->query + 7);
pfree(expr->query);
expr->query = pstrdup(ds.data);
Index: src/pl/plpgsql/src/pl_comp.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_comp.c,v
retrieving revision 1.140
diff -c -r1.140 pl_comp.c
*** src/pl/plpgsql/src/pl_comp.c 4 Nov 2009 22:26:07 -0000 1.140
--- src/pl/plpgsql/src/pl_comp.c 6 Nov 2009 14:31:53 -0000
***************
*** 96,101 ****
--- 96,104 ----
PLpgSQL_func_hashkey *hashkey,
bool forValidator);
static void add_dummy_return(PLpgSQL_function *function);
+ static Node *plpgsql_pre_column_ref(ParseState *pstate, ColumnRef *cref);
+ static Node *plpgsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var);
+ static Node *plpgsql_param_ref(ParseState *pstate, ParamRef *pref);
static PLpgSQL_row *build_row_from_class(Oid classOid);
static PLpgSQL_row *build_row_from_vars(PLpgSQL_variable **vars, int numvars);
static PLpgSQL_type *build_datatype(HeapTuple typeTup, int32 typmod);
***************
*** 307,327 ****
error_context_stack = &plerrcontext;
/*
- * Initialize the compiler, particularly the namespace stack. The
- * outermost namespace contains function parameters and other special
- * variables (such as FOUND), and is named after the function itself.
- */
- plpgsql_ns_init();
- plpgsql_ns_push(NameStr(procStruct->proname));
- plpgsql_DumpExecTree = false;
-
- datums_alloc = 128;
- plpgsql_nDatums = 0;
- /* This is short-lived, so needn't allocate in function's cxt */
- plpgsql_Datums = palloc(sizeof(PLpgSQL_datum *) * datums_alloc);
- datums_last = 0;
-
- /*
* Do extra syntax checks when validating the function definition. We skip
* this when actually compiling functions for execution, for performance
* reasons.
--- 310,315 ----
***************
*** 345,352 ****
plpgsql_curr_compile = function;
/*
! * All the rest of the compile-time storage (e.g. parse tree) is kept in
! * its own memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(TopMemoryContext,
"PL/PgSQL function context",
--- 333,340 ----
plpgsql_curr_compile = function;
/*
! * All the permanent output of compilation (e.g. parse tree) is kept in
! * a per-function memory context, so it can be reclaimed easily.
*/
func_cxt = AllocSetContextCreate(TopMemoryContext,
"PL/PgSQL function context",
***************
*** 363,368 ****
--- 351,372 ----
function->fn_cxt = func_cxt;
function->out_param_varno = -1; /* set up for no OUT param */
+ /*
+ * Initialize the compiler, particularly the namespace stack. The
+ * outermost namespace contains function parameters and other special
+ * variables (such as FOUND), and is named after the function itself.
+ */
+ plpgsql_ns_init();
+ plpgsql_ns_push(NameStr(procStruct->proname));
+ plpgsql_DumpExecTree = false;
+
+ datums_alloc = 128;
+ plpgsql_nDatums = 0;
+ /* This is short-lived, so needn't allocate in function's cxt */
+ plpgsql_Datums = MemoryContextAlloc(compile_tmp_cxt,
+ sizeof(PLpgSQL_datum *) * datums_alloc);
+ datums_last = 0;
+
switch (is_trigger)
{
case false:
***************
*** 920,925 ****
--- 924,1205 ----
}
+ /*
+ * plpgsql_parser_setup set up parser hooks for dynamic parameters
+ *
+ * Note: although this routine, and the hook functions it prepares for,
+ * are logically part of plpgsql parsing, they actually run during function
+ * execution, when we are ready to evaluate a SQL query or expression
+ * that has not previously been parsed and planned.
+ */
+ void
+ plpgsql_parser_setup(struct ParseState *pstate, PLpgSQL_expr *expr)
+ {
+ pstate->p_pre_columnref_hook = plpgsql_pre_column_ref;
+ pstate->p_post_columnref_hook = plpgsql_post_column_ref;
+ pstate->p_paramref_hook = plpgsql_param_ref;
+ /* no need to use p_coerce_param_hook */
+ pstate->p_ref_hook_state = (void *) expr;
+ }
+
+ /*
+ * Helper for columnref parsing: build a Param referencing a plpgsql datum,
+ * and make sure that that datum is listed in the expression's paramnos.
+ */
+ static Node *
+ make_datum_param(PLpgSQL_expr *expr, int dno, int location)
+ {
+ PLpgSQL_execstate *estate;
+ Param *param;
+ MemoryContext oldcontext;
+
+ /* see comment in plpgsql_pre_column_ref */
+ estate = expr->func->cur_estate;
+
+ Assert(dno >= 0 && dno < estate->ndatums);
+
+ /*
+ * Bitmapset must be allocated in function's permanent memory context
+ */
+ oldcontext = MemoryContextSwitchTo(expr->func->fn_cxt);
+ expr->paramnos = bms_add_member(expr->paramnos, dno);
+ MemoryContextSwitchTo(oldcontext);
+
+ param = makeNode(Param);
+ param->paramkind = PARAM_EXTERN;
+ param->paramid = dno + 1;
+ param->paramtype = exec_get_datum_type(estate, estate->datums[dno]);
+ param->paramtypmod = -1;
+ param->location = location;
+
+ return (Node *) param;
+ }
+
+ /*
+ * plpgsql_pre_column_ref parser callback before parsing a ColumnRef
+ */
+ static Node *
+ plpgsql_pre_column_ref(ParseState *pstate, ColumnRef *cref)
+ {
+ PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
+ PLpgSQL_execstate *estate;
+ PLpgSQL_nsitem *nse;
+ const char *name1;
+ const char *name2 = NULL;
+ const char *name3 = NULL;
+ const char *colname = NULL;
+ int nnames;
+ int nnames_scalar = 0;
+ int nnames_wholerow = 0;
+ int nnames_field = 0;
+
+ /*
+ * 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;
+
+ /*----------
+ * The allowed syntaxes are:
+ *
+ * A Scalar variable reference, or whole-row record reference.
+ * A.B Qualified scalar or whole-row reference, or field reference.
+ * A.B.C Qualified record field reference.
+ * A.* Whole-row record reference.
+ * A.B.* Qualified whole-row record reference.
+ *----------
+ */
+ switch (list_length(cref->fields))
+ {
+ case 1:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+
+ Assert(IsA(field1, String));
+ name1 = strVal(field1);
+ nnames_scalar = 1;
+ nnames_wholerow = 1;
+ break;
+ }
+ case 2:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+ Node *field2 = (Node *) lsecond(cref->fields);
+
+ Assert(IsA(field1, String));
+ name1 = strVal(field1);
+
+ /* Whole-row reference? */
+ if (IsA(field2, A_Star))
+ {
+ /* Set name2 to prevent matches to scalar variables */
+ name2 = "*";
+ nnames_wholerow = 1;
+ break;
+ }
+
+ Assert(IsA(field2, String));
+ name2 = strVal(field2);
+ colname = name2;
+ nnames_scalar = 2;
+ nnames_wholerow = 2;
+ nnames_field = 1;
+ break;
+ }
+ case 3:
+ {
+ Node *field1 = (Node *) linitial(cref->fields);
+ Node *field2 = (Node *) lsecond(cref->fields);
+ Node *field3 = (Node *) lthird(cref->fields);
+
+ Assert(IsA(field1, String));
+ name1 = strVal(field1);
+ Assert(IsA(field2, String));
+ name2 = strVal(field2);
+
+ /* Whole-row reference? */
+ if (IsA(field3, A_Star))
+ {
+ /* Set name3 to prevent matches to scalar variables */
+ name3 = "*";
+ nnames_wholerow = 2;
+ break;
+ }
+
+ Assert(IsA(field3, String));
+ name3 = strVal(field3);
+ colname = name3;
+ nnames_field = 2;
+ break;
+ }
+ default:
+ /* too many names, ignore */
+ return NULL;
+ }
+
+ nse = plpgsql_ns_lookup(expr->ns,
+ name1, name2, name3,
+ &nnames);
+
+ if (nse == NULL)
+ return NULL; /* name not known to plpgsql */
+
+ switch (nse->itemtype)
+ {
+ case PLPGSQL_NSTYPE_VAR:
+ if (nnames == nnames_scalar)
+ return make_datum_param(expr, nse->itemno, cref->location);
+ break;
+ case PLPGSQL_NSTYPE_REC:
+ if (nnames == nnames_wholerow)
+ return make_datum_param(expr, nse->itemno, cref->location);
+ if (nnames == nnames_field)
+ {
+ /* colname must be a field in this record */
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) estate->datums[nse->itemno];
+ FieldSelect *fselect;
+ Oid fldtype;
+ int fldno;
+ int i;
+
+ /* search for a datum referencing this field */
+ for (i = 0; i < estate->ndatums; i++)
+ {
+ PLpgSQL_recfield *fld = (PLpgSQL_recfield *) estate->datums[i];
+
+ if (fld->dtype == PLPGSQL_DTYPE_RECFIELD &&
+ fld->recparentno == nse->itemno &&
+ strcmp(fld->fieldname, colname) == 0)
+ {
+ return make_datum_param(expr, i, cref->location);
+ }
+ }
+
+ /*
+ * We can't readily add a recfield datum at runtime, so
+ * instead build a whole-row Param and a FieldSelect node.
+ * This is a bit less efficient, so we prefer the recfield
+ * way when possible.
+ */
+ fldtype = exec_get_rec_fieldtype(rec, colname,
+ &fldno);
+ fselect = makeNode(FieldSelect);
+ fselect->arg = (Expr *) make_datum_param(expr, nse->itemno,
+ cref->location);
+ fselect->fieldnum = fldno;
+ fselect->resulttype = fldtype;
+ fselect->resulttypmod = -1;
+ return (Node *) fselect;
+ }
+ break;
+ case PLPGSQL_NSTYPE_ROW:
+ if (nnames == nnames_wholerow)
+ return make_datum_param(expr, nse->itemno, cref->location);
+ if (nnames == nnames_field)
+ {
+ /* colname must be a field in this row */
+ PLpgSQL_row *row = (PLpgSQL_row *) estate->datums[nse->itemno];
+ int i;
+
+ for (i = 0; i < row->nfields; i++)
+ {
+ if (row->fieldnames[i] &&
+ strcmp(row->fieldnames[i], colname) == 0)
+ {
+ return make_datum_param(expr, row->varnos[i],
+ cref->location);
+ }
+ }
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("row \"%s\" has no field \"%s\"",
+ row->refname, colname)));
+ }
+ break;
+ default:
+ elog(ERROR, "unrecognized plpgsql itemtype");
+ }
+
+ /* Name format doesn't match the plpgsql variable type */
+ return NULL;
+ }
+
+ /*
+ * plpgsql_post_column_ref parser callback after parsing a ColumnRef
+ */
+ static Node *
+ plpgsql_post_column_ref(ParseState *pstate, ColumnRef *cref, Node *var)
+ {
+ /* Not used yet */
+ return NULL;
+ }
+
+ /*
+ * plpgsql_param_ref parser callback for ParamRefs ($n symbols)
+ */
+ static Node *
+ plpgsql_param_ref(ParseState *pstate, ParamRef *pref)
+ {
+ PLpgSQL_expr *expr = (PLpgSQL_expr *) pstate->p_ref_hook_state;
+ char pname[32];
+ PLpgSQL_nsitem *nse;
+
+ snprintf(pname, sizeof(pname), "$%d", pref->number);
+
+ nse = plpgsql_ns_lookup(expr->ns,
+ pname, NULL, NULL,
+ NULL);
+
+ if (nse == NULL)
+ return NULL; /* name not known to plpgsql */
+
+ Assert(nse->itemtype == PLPGSQL_NSTYPE_VAR);
+
+ return make_datum_param(expr, nse->itemno, pref->location);
+ }
+
+
/* ----------
* plpgsql_parse_word The scanner calls this to postparse
* any single word not found by a
***************
*** 936,944 ****
plpgsql_convert_ident(word, cp, 1);
/*
! * Do a lookup on the compiler's namestack
*/
! nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL);
pfree(cp[0]);
if (nse != NULL)
--- 1216,1226 ----
plpgsql_convert_ident(word, cp, 1);
/*
! * Do a lookup in the current namespace stack
*/
! nse = plpgsql_ns_lookup(plpgsql_ns_current(),
! cp[0], NULL, NULL,
! NULL);
pfree(cp[0]);
if (nse != NULL)
***************
*** 986,994 ****
plpgsql_convert_ident(word, cp, 2);
/*
! * Do a lookup on the compiler's namestack
*/
! ns = plpgsql_ns_lookup(cp[0], cp[1], NULL, &nnames);
if (ns == NULL)
{
pfree(cp[0]);
--- 1268,1278 ----
plpgsql_convert_ident(word, cp, 2);
/*
! * Do a lookup in the current namespace stack
*/
! ns = plpgsql_ns_lookup(plpgsql_ns_current(),
! cp[0], cp[1], NULL,
! &nnames);
if (ns == NULL)
{
pfree(cp[0]);
***************
*** 1098,1107 ****
plpgsql_convert_ident(word, cp, 3);
/*
! * Do a lookup on the compiler's namestack. Must find a qualified
* reference.
*/
! ns = plpgsql_ns_lookup(cp[0], cp[1], cp[2], &nnames);
if (ns == NULL || nnames != 2)
{
pfree(cp[0]);
--- 1382,1393 ----
plpgsql_convert_ident(word, cp, 3);
/*
! * Do a lookup in the current namespace stack. Must find a qualified
* reference.
*/
! ns = plpgsql_ns_lookup(plpgsql_ns_current(),
! cp[0], cp[1], cp[2],
! &nnames);
if (ns == NULL || nnames != 2)
{
pfree(cp[0]);
***************
*** 1201,1210 ****
pfree(cp[1]);
/*
! * Do a lookup on the compiler's namestack. Ensure we scan all levels.
*/
old_nsstate = plpgsql_ns_setlocal(false);
! nse = plpgsql_ns_lookup(cp[0], NULL, NULL, NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL)
--- 1487,1498 ----
pfree(cp[1]);
/*
! * Do a lookup in the current namespace stack. Ensure we scan all levels.
*/
old_nsstate = plpgsql_ns_setlocal(false);
! nse = plpgsql_ns_lookup(plpgsql_ns_current(),
! cp[0], NULL, NULL,
! NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL)
***************
*** 1224,1231 ****
}
/*
! * Word wasn't found on the namestack. Try to find a data type with that
! * name, but ignore shell types and complex types.
*/
typeTup = LookupTypeName(NULL, makeTypeName(cp[0]), NULL);
if (typeTup)
--- 1512,1519 ----
}
/*
! * Word wasn't found in the namespace stack. Try to find a data type
! * with that name, but ignore shell types and complex types.
*/
typeTup = LookupTypeName(NULL, makeTypeName(cp[0]), NULL);
if (typeTup)
***************
*** 1289,1300 ****
pfree(cp[2]);
/*
! * Do a lookup on the compiler's namestack. Ensure we scan all levels. We
! * don't need to check number of names matched, because we will only
* consider scalar variables.
*/
old_nsstate = plpgsql_ns_setlocal(false);
! nse = plpgsql_ns_lookup(cp[0], cp[1], NULL, NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
--- 1577,1590 ----
pfree(cp[2]);
/*
! * Do a lookup in the current namespace stack. Ensure we scan all levels.
! * We don't need to check number of names matched, because we will only
* consider scalar variables.
*/
old_nsstate = plpgsql_ns_setlocal(false);
! nse = plpgsql_ns_lookup(plpgsql_ns_current(),
! cp[0], cp[1], NULL,
! NULL);
plpgsql_ns_setlocal(old_nsstate);
if (nse != NULL && nse->itemtype == PLPGSQL_NSTYPE_VAR)
Index: src/pl/plpgsql/src/pl_exec.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_exec.c,v
retrieving revision 1.249
diff -c -r1.249 pl_exec.c
*** src/pl/plpgsql/src/pl_exec.c 4 Nov 2009 22:26:07 -0000 1.249
--- src/pl/plpgsql/src/pl_exec.c 6 Nov 2009 14:31:53 -0000
***************
*** 26,32 ****
#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"
--- 26,31 ----
***************
*** 158,165 ****
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);
--- 157,162 ----
***************
*** 176,183 ****
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,
--- 173,178 ----
***************
*** 3992,3998 ****
* a tupdesc but no row value for a record variable. (This currently can
* happen only for a trigger's NEW/OLD records.)
*/
! static Oid
exec_get_datum_type(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum)
{
--- 3987,3993 ----
* a tupdesc but no row value for a record variable. (This currently can
* happen only for a trigger's NEW/OLD records.)
*/
! Oid
exec_get_datum_type(PLpgSQL_execstate *estate,
PLpgSQL_datum *datum)
{
***************
*** 4068,4073 ****
--- 4063,4098 ----
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
*
***************
*** 4591,4641 ****
}
/*
- * 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
*/
static void
--- 4616,4621 ----
Index: src/pl/plpgsql/src/pl_funcs.c
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/pl_funcs.c,v
retrieving revision 1.83
diff -c -r1.83 pl_funcs.c
*** src/pl/plpgsql/src/pl_funcs.c 5 Nov 2009 16:58:36 -0000 1.83
--- src/pl/plpgsql/src/pl_funcs.c 6 Nov 2009 14:31:53 -0000
***************
*** 21,27 ****
/* ----------
! * Local variables for the namestack handling
* ----------
*/
static PLpgSQL_ns *ns_current = NULL;
--- 21,34 ----
/* ----------
! * Local variables for namespace handling
! *
! * The namespace structure actually forms a tree, of which only one chain
! * is accessible from any one plpgsql statement. During initial parsing
! * of a function, ns_current points to the chain 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.
* ----------
*/
static PLpgSQL_ns *ns_current = NULL;
***************
*** 29,35 ****
/* ----------
! * plpgsql_ns_init Initialize the namestack
* ----------
*/
void
--- 36,42 ----
/* ----------
! * plpgsql_ns_init Initialize namespace processing for a new function
* ----------
*/
void
***************
*** 49,55 ****
* 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.
* ----------
*/
bool
--- 56,64 ----
* 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. 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,70 ****
/* ----------
! * plpgsql_ns_push Enter a new namestack level
* ----------
*/
void
--- 73,79 ----
/* ----------
! * plpgsql_ns_push Create a new namespace level
* ----------
*/
void
***************
*** 79,84 ****
--- 88,94 ----
new->upper = ns_current;
ns_current = new;
+ /* The label will always be item zero in the namespace */
plpgsql_ns_additem(PLPGSQL_NSTYPE_LABEL, 0, label);
}
***************
*** 90,111 ****
void
plpgsql_ns_pop(void)
{
! int i;
! PLpgSQL_ns *old;
- 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_additem Add an item to the current
! * namestack level
* ----------
*/
void
--- 100,122 ----
void
plpgsql_ns_pop(void)
{
! ns_current = ns_current->upper;
! }
! /* ----------
! * plpgsql_ns_current Fetch the current namespace chain
! * ----------
! */
! PLpgSQL_ns *
! plpgsql_ns_current(void)
! {
! return ns_current;
}
/* ----------
! * plpgsql_ns_additem Add an item to the current namespace level
* ----------
*/
void
***************
*** 140,146 ****
/* ----------
! * plpgsql_ns_lookup Lookup an identifier in the namestack
*
* Note that this only searches for variables, not labels.
*
--- 151,157 ----
/* ----------
! * plpgsql_ns_lookup Lookup an identifier in given namespace chain
*
* Note that this only searches for variables, not labels.
*
***************
*** 158,171 ****
* ----------
*/
PLpgSQL_nsitem *
! plpgsql_ns_lookup(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++)
--- 169,183 ----
* ----------
*/
PLpgSQL_nsitem *
! plpgsql_ns_lookup(PLpgSQL_ns *ns_cur,
! const char *name1, const char *name2, const char *name3,
int *names_used)
{
PLpgSQL_ns *ns;
int i;
! /* Scan each level of the namespace chain */
! for (ns = ns_cur; ns != NULL; ns = ns->upper)
{
/* Check for unqualified match to variable name */
for (i = 1; i < ns->items_used; i++)
***************
*** 217,231 ****
/* ----------
! * plpgsql_ns_lookup_label Lookup a label in the namestack
* ----------
*/
PLpgSQL_nsitem *
! plpgsql_ns_lookup_label(const char *name)
{
PLpgSQL_ns *ns;
! for (ns = ns_current; ns != NULL; ns = ns->upper)
{
if (strcmp(ns->items[0]->name, name) == 0)
return ns->items[0];
--- 229,243 ----
/* ----------
! * plpgsql_ns_lookup_label Lookup a label in the given namespace chain
* ----------
*/
PLpgSQL_nsitem *
! plpgsql_ns_lookup_label(PLpgSQL_ns *ns_cur, const char *name)
{
PLpgSQL_ns *ns;
! for (ns = ns_cur; ns != NULL; ns = ns->upper)
{
if (strcmp(ns->items[0]->name, name) == 0)
return ns->items[0];
Index: src/pl/plpgsql/src/plpgsql.h
===================================================================
RCS file: /cvsroot/pgsql/src/pl/plpgsql/src/plpgsql.h,v
retrieving revision 1.119
diff -c -r1.119 plpgsql.h
*** src/pl/plpgsql/src/plpgsql.h 5 Nov 2009 16:58:36 -0000 1.119
--- src/pl/plpgsql/src/plpgsql.h 6 Nov 2009 14:31:53 -0000
***************
*** 37,43 ****
#define _(x) dgettext(TEXTDOMAIN, x)
/* ----------
! * Compiler's namestack item types
* ----------
*/
enum
--- 37,43 ----
#define _(x) dgettext(TEXTDOMAIN, x)
/* ----------
! * Compiler's namespace item types
* ----------
*/
enum
***************
*** 193,198 ****
--- 193,201 ----
/* function containing this expr (not set until we first parse query) */
struct PLpgSQL_function *func;
+ /* namespace chain visible to this expr */
+ struct PLpgSQL_ns *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 */
***************
*** 284,290 ****
typedef struct
! { /* Item in the compilers namestack */
int itemtype;
int itemno;
char name[1]; /* actually, as long as needed */
--- 287,293 ----
typedef struct
! { /* Item in the compilers namespace tree */
int itemtype;
int itemno;
char name[1]; /* actually, as long as needed */
***************
*** 293,299 ****
/* XXX: consider adapting this to use List */
typedef struct PLpgSQL_ns
! { /* Compiler namestack level */
int items_alloc;
int items_used;
PLpgSQL_nsitem **items;
--- 296,302 ----
/* XXX: consider adapting this to use List */
typedef struct PLpgSQL_ns
! { /* Compiler namespace level */
int items_alloc;
int items_used;
PLpgSQL_nsitem **items;
***************
*** 795,800 ****
--- 798,805 ----
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,856 ****
extern void plpgsql_xact_cb(XactEvent event, void *arg);
extern void plpgsql_subxact_cb(SubXactEvent event, SubTransactionId mySubid,
SubTransactionId parentSubid, void *arg);
/* ----------
! * Functions for namestack 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 void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
! extern PLpgSQL_nsitem *plpgsql_ns_lookup(const char *name1, const char *name2,
! const char *name3, int *names_used);
! extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(const char *name);
/* ----------
* Other functions in pl_funcs.c
--- 843,868 ----
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 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_ns *plpgsql_ns_current(void);
extern void plpgsql_ns_additem(int itemtype, int itemno, const char *name);
! extern PLpgSQL_nsitem *plpgsql_ns_lookup(PLpgSQL_ns *ns_cur,
! const char *name1, const char *name2,
! const char *name3, int *names_used);
! extern PLpgSQL_nsitem *plpgsql_ns_lookup_label(PLpgSQL_ns *ns_cur,
! const char *name);
/* ----------
* Other functions in pl_funcs.c
Index: src/test/regress/expected/plpgsql.out
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/expected/plpgsql.out,v
retrieving revision 1.77
diff -c -r1.77 plpgsql.out
*** src/test/regress/expected/plpgsql.out 5 Nov 2009 16:58:36 -0000 1.77
--- src/test/regress/expected/plpgsql.out 6 Nov 2009 14:31:53 -0000
***************
*** 899,905 ****
declare
rec record;
begin
! select into rec * from PLine where slotname = outer.rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';
--- 899,905 ----
declare
rec record;
begin
! select into rec * from PLine where slotname = "outer".rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';
Index: src/test/regress/sql/plpgsql.sql
===================================================================
RCS file: /cvsroot/pgsql/src/test/regress/sql/plpgsql.sql,v
retrieving revision 1.65
diff -c -r1.65 plpgsql.sql
*** src/test/regress/sql/plpgsql.sql 5 Nov 2009 16:58:36 -0000 1.65
--- src/test/regress/sql/plpgsql.sql 6 Nov 2009 14:31:54 -0000
***************
*** 1026,1032 ****
declare
rec record;
begin
! select into rec * from PLine where slotname = outer.rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';
--- 1026,1032 ----
declare
rec record;
begin
! select into rec * from PLine where slotname = "outer".rec.backlink;
retval := ''Phone line '' || trim(rec.phonenumber);
if rec.comment != '''' then
retval := retval || '' ('';