aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorBruce Momjian <bruce@momjian.us>2006-05-30 12:03:13 +0000
committerBruce Momjian <bruce@momjian.us>2006-05-30 12:03:13 +0000
commit38c7700f56cc44377a1b46b4ff41ee57ae21e428 (patch)
treede22aa6f47791a2c6e4aaae31f373d2baec4f028 /src
parent88ba64d3963e2dff92426103ab7891a32032900e (diff)
downloadpostgresql-38c7700f56cc44377a1b46b4ff41ee57ae21e428.tar.gz
postgresql-38c7700f56cc44377a1b46b4ff41ee57ae21e428.zip
Add dynamic record inspection to PL/PgSQL, useful for generic triggers:
tval2 := r.(cname); or columns := r.(*); Titus von Boxberg
Diffstat (limited to 'src')
-rw-r--r--src/pl/plpgsql/src/pl_comp.c134
-rw-r--r--src/pl/plpgsql/src/pl_exec.c211
-rw-r--r--src/pl/plpgsql/src/pl_funcs.c12
-rw-r--r--src/pl/plpgsql/src/plpgsql.h24
-rw-r--r--src/pl/plpgsql/src/scan.l8
-rw-r--r--src/test/regress/expected/plpgsql.out38
-rw-r--r--src/test/regress/sql/plpgsql.sql32
7 files changed, 422 insertions, 37 deletions
diff --git a/src/pl/plpgsql/src/pl_comp.c b/src/pl/plpgsql/src/pl_comp.c
index a9279ceb994..db7bc6bb106 100644
--- a/src/pl/plpgsql/src/pl_comp.c
+++ b/src/pl/plpgsql/src/pl_comp.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.104 2006/05/30 11:58:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_comp.c,v 1.105 2006/05/30 12:03:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -884,7 +884,8 @@ plpgsql_parse_dblword(char *word)
new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = pstrdup(cp[1]);
+ new->fieldindex.fieldname = pstrdup(cp[1]);
+ new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -990,7 +991,8 @@ plpgsql_parse_tripword(char *word)
new = palloc(sizeof(PLpgSQL_recfield));
new->dtype = PLPGSQL_DTYPE_RECFIELD;
- new->fieldname = pstrdup(cp[2]);
+ new->fieldindex.fieldname = pstrdup(cp[2]);
+ new->fieldindex_flag = RECFIELD_USE_FIELDNAME;
new->recparentno = ns->itemno;
plpgsql_adddatum((PLpgSQL_datum *) new);
@@ -1438,6 +1440,132 @@ plpgsql_parse_dblwordrowtype(char *word)
return T_DTYPE;
}
+/* ----------
+ * plpgsql_parse_recindex
+ * lookup associative index into record
+ * ----------
+ */
+int
+plpgsql_parse_recindex(char *word)
+{
+ PLpgSQL_nsitem *ns1, *ns2;
+ char *cp[2];
+ int ret = T_ERROR;
+ char *fieldvar;
+ int fl;
+
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(word, cp, 2);
+ Assert(cp[1] != NULL);
+
+ /* cleanup the "(identifier)" string to "identifier" */
+ fieldvar = cp[1];
+ Assert(*fieldvar == '(');
+ ++fieldvar; /* get rid of ( */
+
+ fl = strlen(fieldvar);
+ Assert(fieldvar[fl-1] == ')');
+ fieldvar[fl-1] = 0; /* get rid of ) */
+
+ /*
+ * Lookup the first word
+ */
+ ns1 = plpgsql_ns_lookup(cp[0], NULL);
+ if ( ns1 == NULL )
+ {
+ pfree(cp[0]);
+ pfree(cp[1]);
+ return T_ERROR;
+ }
+
+ ns2 = plpgsql_ns_lookup(fieldvar, NULL);
+ pfree(cp[0]);
+ pfree(cp[1]);
+ if ( ns2 == NULL ) /* name lookup failed */
+ return T_ERROR;
+
+ switch (ns1->itemtype)
+ {
+ case PLPGSQL_NSTYPE_REC:
+ {
+ /*
+ * First word is a record name, so second word must be an
+ * variable holding the field name in this record.
+ */
+ if ( ns2->itemtype == PLPGSQL_NSTYPE_VAR ) {
+ PLpgSQL_recfield *new;
+
+ new = palloc(sizeof(PLpgSQL_recfield));
+ new->dtype = PLPGSQL_DTYPE_RECFIELD;
+ new->fieldindex.indexvar_no = ns2->itemno;
+ new->fieldindex_flag = RECFIELD_USE_INDEX_VAR;
+ new->recparentno = ns1->itemno;
+
+ plpgsql_adddatum((PLpgSQL_datum *) new);
+
+ plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+ ret = T_SCALAR;
+ }
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+
+/* ----------
+ * plpgsql_parse_recfieldnames
+ * create fieldnames of a record
+ * ----------
+ */
+int
+plpgsql_parse_recfieldnames(char *word)
+{
+ PLpgSQL_nsitem *ns1;
+ char *cp[2];
+ int ret = T_ERROR;
+
+ /* Do case conversion and word separation */
+ plpgsql_convert_ident(word, cp, 2);
+
+ /*
+ * Lookup the first word
+ */
+ ns1 = plpgsql_ns_lookup(cp[0], NULL);
+ if ( ns1 == NULL )
+ {
+ pfree(cp[0]);
+ pfree(cp[1]);
+ return T_ERROR;
+ }
+
+ pfree(cp[0]);
+ pfree(cp[1]);
+
+ switch (ns1->itemtype)
+ {
+ case PLPGSQL_NSTYPE_REC:
+ {
+ PLpgSQL_recfieldproperties *new;
+
+ new = palloc(sizeof(PLpgSQL_recfieldproperties));
+ new->dtype = PLPGSQL_DTYPE_RECFIELDNAMES;
+ new->recparentno = ns1->itemno;
+ new->save_fieldnames = NULL;
+ plpgsql_adddatum((PLpgSQL_datum *) new);
+ plpgsql_yylval.scalar = (PLpgSQL_datum *) new;
+ ret = T_SCALAR; /* ??? */
+ break;
+ }
+ default:
+ break;
+ }
+ return ret;
+}
+
+
/*
* plpgsql_build_variable - build a datum-array entry of a given
* datatype
diff --git a/src/pl/plpgsql/src/pl_exec.c b/src/pl/plpgsql/src/pl_exec.c
index afeb49f857f..07f1211f4e5 100644
--- a/src/pl/plpgsql/src/pl_exec.c
+++ b/src/pl/plpgsql/src/pl_exec.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.167 2006/05/30 11:58:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_exec.c,v 1.168 2006/05/30 12:03:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -741,7 +741,7 @@ copy_plpgsql_datum(PLpgSQL_datum *datum)
case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
case PLPGSQL_DTYPE_TRIGARG:
-
+ case PLPGSQL_DTYPE_RECFIELDNAMES:
/*
* These datum records are read-only at runtime, so no need to
* copy them
@@ -851,6 +851,7 @@ exec_stmt_block(PLpgSQL_execstate *estate, PLpgSQL_stmt_block *block)
case PLPGSQL_DTYPE_RECFIELD:
case PLPGSQL_DTYPE_ARRAYELEM:
+ case PLPGSQL_DTYPE_RECFIELDNAMES:
break;
default:
@@ -2179,6 +2180,8 @@ plpgsql_estate_setup(PLpgSQL_execstate *estate,
static void
exec_eval_cleanup(PLpgSQL_execstate *estate)
{
+ int i;
+ ArrayType *a;
/* Clear result of a full SPI_execute */
if (estate->eval_tuptable != NULL)
SPI_freetuptable(estate->eval_tuptable);
@@ -2187,6 +2190,14 @@ exec_eval_cleanup(PLpgSQL_execstate *estate)
/* Clear result of exec_eval_simple_expr (but keep the econtext) */
if (estate->eval_econtext != NULL)
ResetExprContext(estate->eval_econtext);
+ for ( i = 0; i < estate->ndatums; ++i ) {
+ if ( estate->datums[i]->dtype == PLPGSQL_DTYPE_RECFIELDNAMES ) {
+ a = ((PLpgSQL_recfieldproperties *)(estate->datums[i]))->save_fieldnames;
+ if ( a )
+ pfree(a);
+ ((PLpgSQL_recfieldproperties *)(estate->datums[i]))->save_fieldnames = NULL;
+ }
+ }
}
@@ -3156,7 +3167,7 @@ exec_assign_value(PLpgSQL_execstate *estate,
*/
PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) target;
PLpgSQL_rec *rec;
- int fno;
+ int fno = 0;
HeapTuple newtup;
int natts;
int i;
@@ -3185,12 +3196,35 @@ exec_assign_value(PLpgSQL_execstate *estate,
* Get the number of the records field to change and the
* number of attributes in the tuple.
*/
- fno = SPI_fnumber(rec->tupdesc, recfield->fieldname);
- if (fno == SPI_ERROR_NOATTRIBUTE)
+ if ( recfield->fieldindex_flag == RECFIELD_USE_FIELDNAME ) {
+ fno = SPI_fnumber(rec->tupdesc, recfield->fieldindex.fieldname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\" has no field \"%s\"",
+ rec->refname, recfield->fieldindex.fieldname)));
+ }
+ else if ( recfield->fieldindex_flag == RECFIELD_USE_INDEX_VAR ) {
+ PLpgSQL_var * idxvar = (PLpgSQL_var *) (estate->datums[recfield->fieldindex.indexvar_no]);
+ char * fname = convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
+ if ( fname == NULL )
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\": cannot evaluate variable to record index string",
+ rec->refname)));
+ fno = SPI_fnumber(rec->tupdesc, fname);
+ pfree(fname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\" has no field \"%s\"",
+ rec->refname, fname)));
+ }
+ else
ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("record \"%s\" has no field \"%s\"",
- rec->refname, recfield->fieldname)));
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\": internal error",
+ rec->refname)));
fno--;
natts = rec->tupdesc->natts;
@@ -3510,7 +3544,7 @@ exec_eval_datum(PLpgSQL_execstate *estate,
{
PLpgSQL_recfield *recfield = (PLpgSQL_recfield *) datum;
PLpgSQL_rec *rec;
- int fno;
+ int fno = 0;
rec = (PLpgSQL_rec *) (estate->datums[recfield->recparentno]);
if (!HeapTupleIsValid(rec->tup))
@@ -3519,22 +3553,125 @@ exec_eval_datum(PLpgSQL_execstate *estate,
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, recfield->fieldname);
- if (fno == SPI_ERROR_NOATTRIBUTE)
- ereport(ERROR,
- (errcode(ERRCODE_UNDEFINED_COLUMN),
- errmsg("record \"%s\" has no field \"%s\"",
- rec->refname, recfield->fieldname)));
- *typeid = SPI_gettypeid(rec->tupdesc, fno);
- *value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
- if (expectedtypeid != InvalidOid && expectedtypeid != *typeid)
- ereport(ERROR,
- (errcode(ERRCODE_DATATYPE_MISMATCH),
- errmsg("type of \"%s.%s\" does not match that when preparing the plan",
- rec->refname, recfield->fieldname)));
- break;
- }
-
+ if ( recfield->fieldindex_flag == RECFIELD_USE_FIELDNAME ) {
+ fno = SPI_fnumber(rec->tupdesc, recfield->fieldindex.fieldname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\" has no field \"%s\"",
+ rec->refname, recfield->fieldindex.fieldname)));
+ }
+ else if ( recfield->fieldindex_flag == RECFIELD_USE_INDEX_VAR ) {
+ PLpgSQL_var * idxvar = (PLpgSQL_var *) (estate->datums[recfield->fieldindex.indexvar_no]);
+ char * fname = convert_value_to_string(idxvar->value, idxvar->datatype->typoid);
+ if ( fname == NULL )
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\": cannot evaluate variable to record index string",
+ rec->refname)));
+ fno = SPI_fnumber(rec->tupdesc, fname);
+ pfree(fname);
+ if (fno == SPI_ERROR_NOATTRIBUTE)
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\" has no field \"%s\"",
+ rec->refname, fname)));
+ }
+ else
+ ereport(ERROR,
+ (errcode(ERRCODE_UNDEFINED_COLUMN),
+ errmsg("record \"%s\": internal error",
+ rec->refname)));
+
+ /* Do not allow typeids to become "narrowed" by InvalidOids
+ causing specialized typeids from the tuple restricting the destination */
+ if ( expectedtypeid != InvalidOid && expectedtypeid != SPI_gettypeid(rec->tupdesc, fno) ) {
+ Datum cval = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+ cval = exec_simple_cast_value(cval,
+ SPI_gettypeid(rec->tupdesc, fno),
+ expectedtypeid,
+ -1,
+ *isnull);
+
+ *value = cval;
+ *typeid = expectedtypeid;
+ /* ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of \"%s\" does not match that when preparing the plan",
+ rec->refname)));
+ */
+ }
+ else { /* expected typeid matches */
+ *value = SPI_getbinval(rec->tup, rec->tupdesc, fno, isnull);
+ *typeid = SPI_gettypeid(rec->tupdesc, fno);
+ }
+ break;
+ }
+
+ case PLPGSQL_DTYPE_RECFIELDNAMES:
+ /* Construct array datum from record field names */
+ {
+ Oid arraytypeid,
+ arrayelemtypeid = TEXTOID;
+ int16 arraytyplen,
+ elemtyplen;
+ bool elemtypbyval;
+ char elemtypalign;
+ ArrayType *arrayval;
+ PLpgSQL_recfieldproperties * recfp = (PLpgSQL_recfieldproperties *) datum;
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) (estate->datums[recfp->recparentno]);
+ int fc, tfc = 0;
+ Datum *arrayelems;
+ char *fieldname;
+
+ if (!HeapTupleIsValid(rec->tup))
+ 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.")));
+ arrayelems = palloc(sizeof(Datum) * rec->tupdesc->natts);
+ arraytypeid = get_array_type(arrayelemtypeid);
+ arraytyplen = get_typlen(arraytypeid);
+ get_typlenbyvalalign(arrayelemtypeid,
+ &elemtyplen,
+ &elemtypbyval,
+ &elemtypalign);
+
+ if ( expectedtypeid != InvalidOid && expectedtypeid != arraytypeid )
+ ereport(ERROR,
+ (errcode(ERRCODE_DATATYPE_MISMATCH),
+ errmsg("type of \"%s\" does not match array type when preparing the plan",
+ rec->refname)));
+ for ( fc = 0; fc < rec->tupdesc->natts; ++fc ) {
+ fieldname = SPI_fname(rec->tupdesc, fc+1);
+ if ( fieldname ) {
+ arrayelems[fc] = DirectFunctionCall1(textin, CStringGetDatum(fieldname));
+ pfree(fieldname);
+ ++tfc;
+ }
+ }
+ arrayval = construct_array(arrayelems, tfc,
+ arrayelemtypeid,
+ elemtyplen,
+ elemtypbyval,
+ elemtypalign);
+
+
+ /* construct_array copies data; free temp elem array */
+ for ( fc = 0; fc < tfc; ++fc )
+ pfree(DatumGetPointer(arrayelems[fc]));
+ pfree(arrayelems);
+ *value = PointerGetDatum(arrayval);
+ *typeid = arraytypeid;
+ *isnull = false;
+ /* need to save the pointer because otherwise it does not get freed */
+ if ( recfp->save_fieldnames )
+ pfree(recfp->save_fieldnames);
+ recfp->save_fieldnames = arrayval;
+ break;
+ }
+
case PLPGSQL_DTYPE_TRIGARG:
{
PLpgSQL_trigarg *trigarg = (PLpgSQL_trigarg *) datum;
@@ -3632,7 +3769,29 @@ exec_eval_expr(PLpgSQL_execstate *estate,
*/
if (expr->plan == NULL)
exec_prepare_plan(estate, expr);
-
+ else {
+ /*
+ * check for any subexpressions with varying type in the expression
+ * currently (July 05), this is a record field of a record indexed by a variable
+ */
+ int i;
+ PLpgSQL_datum *d;
+ PLpgSQL_recfield *rf;
+ for ( i = 0; i < expr->nparams; ++i ) {
+ d = estate->datums[expr->params[i]];
+ if ( d->dtype == PLPGSQL_DTYPE_RECFIELD ) {
+ rf = (PLpgSQL_recfield *)d;
+ if ( rf->fieldindex_flag == RECFIELD_USE_INDEX_VAR )
+ break;
+ }
+ }
+ if ( i < expr->nparams ) { /* expr may change it's type */
+ /* now discard the plan and get new one */
+ SPI_freeplan(expr->plan);
+ expr->plan = NULL;
+ exec_prepare_plan(estate, expr);
+ }
+ }
/*
* If this is a simple expression, bypass SPI and use the executor
* directly
diff --git a/src/pl/plpgsql/src/pl_funcs.c b/src/pl/plpgsql/src/pl_funcs.c
index 30818842a59..c2905055bdf 100644
--- a/src/pl/plpgsql/src/pl_funcs.c
+++ b/src/pl/plpgsql/src/pl_funcs.c
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.50 2006/05/30 11:58:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/pl_funcs.c,v 1.51 2006/05/30 12:03:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -1044,9 +1044,13 @@ plpgsql_dumptree(PLpgSQL_function *func)
printf("REC %s\n", ((PLpgSQL_rec *) d)->refname);
break;
case PLPGSQL_DTYPE_RECFIELD:
- printf("RECFIELD %-16s of REC %d\n",
- ((PLpgSQL_recfield *) d)->fieldname,
- ((PLpgSQL_recfield *) d)->recparentno);
+ if ( ((PLpgSQL_recfield *) d)->fieldindex_flag == RECFIELD_USE_FIELDNAME )
+ printf("RECFIELD %-16s of REC %d\n",
+ ((PLpgSQL_recfield *) d)->fieldindex.fieldname,
+ ((PLpgSQL_recfield *) d)->recparentno);
+ else
+ printf("RECFIELD Variable of REC %d\n",
+ ((PLpgSQL_recfield *) d)->recparentno);
break;
case PLPGSQL_DTYPE_ARRAYELEM:
printf("ARRAYELEM of VAR %d subscript ",
diff --git a/src/pl/plpgsql/src/plpgsql.h b/src/pl/plpgsql/src/plpgsql.h
index 2c1f7fc8889..4a46597f095 100644
--- a/src/pl/plpgsql/src/plpgsql.h
+++ b/src/pl/plpgsql/src/plpgsql.h
@@ -8,7 +8,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.72 2006/05/30 11:58:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/plpgsql.h,v 1.73 2006/05/30 12:03:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -52,7 +52,8 @@ enum
PLPGSQL_DTYPE_RECFIELD,
PLPGSQL_DTYPE_ARRAYELEM,
PLPGSQL_DTYPE_EXPR,
- PLPGSQL_DTYPE_TRIGARG
+ PLPGSQL_DTYPE_TRIGARG,
+ PLPGSQL_DTYPE_RECFIELDNAMES
};
/* ----------
@@ -251,10 +252,25 @@ typedef struct
{ /* Field in record */
int dtype;
int rfno;
- char *fieldname;
+ union {
+ char *fieldname;
+ int indexvar_no; /* dno of variable holding index string */
+ } fieldindex;
+ enum {
+ RECFIELD_USE_FIELDNAME,
+ RECFIELD_USE_INDEX_VAR,
+ } fieldindex_flag;
int recparentno; /* dno of parent record */
} PLpgSQL_recfield;
+typedef struct
+{ /* Field in record */
+ int dtype;
+ int rfno;
+ int recparentno; /* dno of parent record */
+ ArrayType * save_fieldnames;
+} PLpgSQL_recfieldproperties;
+
typedef struct
{ /* Element of array variable */
@@ -661,6 +677,8 @@ extern int plpgsql_parse_dblwordtype(char *word);
extern int plpgsql_parse_tripwordtype(char *word);
extern int plpgsql_parse_wordrowtype(char *word);
extern int plpgsql_parse_dblwordrowtype(char *word);
+extern int plpgsql_parse_recfieldnames(char *word);
+extern int plpgsql_parse_recindex(char *word);
extern PLpgSQL_type *plpgsql_parse_datatype(const char *string);
extern PLpgSQL_type *plpgsql_build_datatype(Oid typeOid, int32 typmod);
extern PLpgSQL_variable *plpgsql_build_variable(const char *refname, int lineno,
diff --git a/src/pl/plpgsql/src/scan.l b/src/pl/plpgsql/src/scan.l
index aee2c60f74b..5202058f4c6 100644
--- a/src/pl/plpgsql/src/scan.l
+++ b/src/pl/plpgsql/src/scan.l
@@ -9,7 +9,7 @@
*
*
* IDENTIFICATION
- * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.47 2006/05/30 11:58:05 momjian Exp $
+ * $PostgreSQL: pgsql/src/pl/plpgsql/src/scan.l,v 1.48 2006/05/30 12:03:13 momjian Exp $
*
*-------------------------------------------------------------------------
*/
@@ -222,6 +222,12 @@ dump { return O_DUMP; }
{param}{space}*\.{space}*{identifier}{space}*%ROWTYPE {
plpgsql_error_lineno = plpgsql_scanner_lineno();
return plpgsql_parse_dblwordrowtype(yytext); }
+{identifier}{space}*\.\(\*\) {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ return plpgsql_parse_recfieldnames(yytext); }
+{identifier}{space}*\.\({identifier}\) {
+ plpgsql_error_lineno = plpgsql_scanner_lineno();
+ return plpgsql_parse_recindex(yytext); }
{digit}+ { return T_NUMBER; }
diff --git a/src/test/regress/expected/plpgsql.out b/src/test/regress/expected/plpgsql.out
index 6e6597dadb2..e20d76c0aeb 100644
--- a/src/test/regress/expected/plpgsql.out
+++ b/src/test/regress/expected/plpgsql.out
@@ -2725,6 +2725,44 @@ end;
$$ language plpgsql;
ERROR: end label "outer_label" specified for unlabelled block
CONTEXT: compile of PL/pgSQL function "end_label4" near line 5
+-- check introspective records
+create table ritest (i INT4, t TEXT);
+insert into ritest (i, t) VALUES (1, 'sometext');
+create function test_record() returns void as $$
+declare
+ cname text;
+ tval text;
+ ival int4;
+ tval2 text;
+ ival2 int4;
+ columns text[];
+ r RECORD;
+begin
+ SELECT INTO r * FROM ritest WHERE i = 1;
+ ival := r.i;
+ tval := r.t;
+ RAISE NOTICE 'ival=%, tval=%', ival, tval;
+ cname := 'i';
+ ival2 := r.(cname);
+ cname :='t';
+ tval2 := r.(cname);
+ RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
+ columns := r.(*);
+ RAISE NOTICE 'fieldnames=%', columns;
+ RETURN;
+end;
+$$ language plpgsql;
+select test_record();
+NOTICE: ival=1, tval=sometext
+NOTICE: ival2=1, tval2=sometext
+NOTICE: fieldnames={i,t}
+ test_record
+-------------
+
+ (1 row)
+
+drop table ritest;
+drop function test_record();
-- using list of scalars in fori and fore stmts
create function for_vect() returns void as $proc$
<<lbl>>declare a integer; b varchar; c varchar; r record;
diff --git a/src/test/regress/sql/plpgsql.sql b/src/test/regress/sql/plpgsql.sql
index 19e145be65f..05460629860 100644
--- a/src/test/regress/sql/plpgsql.sql
+++ b/src/test/regress/sql/plpgsql.sql
@@ -2281,6 +2281,38 @@ begin
end;
$$ language plpgsql;
+-- check introspective records
+create table ritest (i INT4, t TEXT);
+insert into ritest (i, t) VALUES (1, 'sometext');
+create function test_record() returns void as $$
+declare
+ cname text;
+ tval text;
+ ival int4;
+ tval2 text;
+ ival2 int4;
+ columns text[];
+ r RECORD;
+begin
+ SELECT INTO r * FROM ritest WHERE i = 1;
+ ival := r.i;
+ tval := r.t;
+ RAISE NOTICE 'ival=%, tval=%', ival, tval;
+ cname := 'i';
+ ival2 := r.(cname);
+ cname :='t';
+ tval2 := r.(cname);
+ RAISE NOTICE 'ival2=%, tval2=%', ival2, tval2;
+ columns := r.(*);
+ RAISE NOTICE 'fieldnames=%', columns;
+ RETURN;
+end;
+$$ language plpgsql;
+select test_record();
+drop table ritest;
+drop function test_record();
+
+
-- using list of scalars in fori and fore stmts
create function for_vect() returns void as $proc$
<<lbl>>declare a integer; b varchar; c varchar; r record;