REGRESS_OPTS = --dbname=$(PL_TESTDB)
-REGRESS = plpgsql_call plpgsql_control plpgsql_record plpgsql_transaction
+REGRESS = plpgsql_call plpgsql_control plpgsql_record \
+ plpgsql_transaction plpgsql_varprops
all: all-lib
--- /dev/null
+--
+-- Tests for PL/pgSQL variable properties: CONSTANT, NOT NULL, initializers
+--
+create type var_record as (f1 int4, f2 int4);
+create domain int_nn as int not null;
+create domain var_record_nn as var_record not null;
+create domain var_record_colnn as var_record check((value).f2 is not null);
+-- CONSTANT
+do $$
+declare x constant int := 42;
+begin
+ raise notice 'x = %', x;
+end$$;
+NOTICE: x = 42
+do $$
+declare x constant int;
+begin
+ x := 42; -- fail
+end$$;
+ERROR: variable "x" is declared CONSTANT
+LINE 4: x := 42; -- fail
+ ^
+do $$
+declare x constant int; y int;
+begin
+ for x, y in select 1, 2 loop -- fail
+ end loop;
+end$$;
+ERROR: variable "x" is declared CONSTANT
+LINE 4: for x, y in select 1, 2 loop -- fail
+ ^
+do $$
+declare x constant int[];
+begin
+ x[1] := 42; -- fail
+end$$;
+ERROR: variable "x" is declared CONSTANT
+LINE 4: x[1] := 42; -- fail
+ ^
+do $$
+declare x constant int[]; y int;
+begin
+ for x[1], y in select 1, 2 loop -- fail (currently, unsupported syntax)
+ end loop;
+end$$;
+ERROR: syntax error at or near "["
+LINE 4: for x[1], y in select 1, 2 loop -- fail (currently, unsup...
+ ^
+do $$
+declare x constant var_record;
+begin
+ x.f1 := 42; -- fail
+end$$;
+ERROR: variable "x" is declared CONSTANT
+LINE 4: x.f1 := 42; -- fail
+ ^
+do $$
+declare x constant var_record; y int;
+begin
+ for x.f1, y in select 1, 2 loop -- fail
+ end loop;
+end$$;
+ERROR: variable "x" is declared CONSTANT
+LINE 4: for x.f1, y in select 1, 2 loop -- fail
+ ^
+-- initializer expressions
+do $$
+declare x int := sin(0);
+begin
+ raise notice 'x = %', x;
+end$$;
+NOTICE: x = 0
+do $$
+declare x int := 1/0; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: division by zero
+CONTEXT: SQL statement "SELECT 1/0"
+PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x bigint[] := array[1,3,5];
+begin
+ raise notice 'x = %', x;
+end$$;
+NOTICE: x = {1,3,5}
+do $$
+declare x record := row(1,2,3);
+begin
+ raise notice 'x = %', x;
+end$$;
+NOTICE: x = (1,2,3)
+do $$
+declare x var_record := row(1,2);
+begin
+ raise notice 'x = %', x;
+end$$;
+NOTICE: x = (1,2)
+-- NOT NULL
+do $$
+declare x int not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: variable "x" must have a default value, since it's declared NOT NULL
+LINE 2: declare x int not null; -- fail
+ ^
+do $$
+declare x int not null := 42;
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+NOTICE: x = 42
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
+do $$
+declare x int not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x record not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: variable "x" must have a default value, since it's declared NOT NULL
+LINE 2: declare x record not null; -- fail
+ ^
+do $$
+declare x record not null := row(42);
+begin
+ raise notice 'x = %', x;
+ x := row(null); -- ok
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+NOTICE: x = (42)
+NOTICE: x = ()
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 7 at assignment
+do $$
+declare x record not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: variable "x" must have a default value, since it's declared NOT NULL
+LINE 2: declare x var_record not null; -- fail
+ ^
+do $$
+declare x var_record not null := row(41,42);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- ok
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+NOTICE: x = (41,42)
+NOTICE: x = (,)
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 7 at assignment
+do $$
+declare x var_record not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: null value cannot be assigned to variable "x" declared NOT NULL
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+-- Check that variables are reinitialized on block re-entry.
+\set VERBOSITY terse \\ -- needed for output stability
+do $$
+begin
+ for i in 1..3 loop
+ declare
+ x int;
+ y int := i;
+ r record;
+ c var_record;
+ begin
+ if i = 1 then
+ x := 42;
+ r := row(i, i+1);
+ c := row(i, i+1);
+ end if;
+ raise notice 'x = %', x;
+ raise notice 'y = %', y;
+ raise notice 'r = %', r;
+ raise notice 'c = %', c;
+ end;
+ end loop;
+end$$;
+NOTICE: x = 42
+NOTICE: y = 1
+NOTICE: r = (1,2)
+NOTICE: c = (1,2)
+NOTICE: x = <NULL>
+NOTICE: y = 2
+NOTICE: r = <NULL>
+NOTICE: c = <NULL>
+NOTICE: x = <NULL>
+NOTICE: y = 3
+NOTICE: r = <NULL>
+NOTICE: c = <NULL>
+\set VERBOSITY default
+-- Check enforcement of domain constraints during initialization
+do $$
+declare x int_nn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: domain int_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x int_nn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: domain int_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x int_nn := 42;
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+NOTICE: x = 42
+ERROR: domain int_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
+do $$
+declare x var_record_nn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: domain var_record_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record_nn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: domain var_record_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record_nn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- ok
+ x := null; -- fail
+end$$;
+NOTICE: x = (1,2)
+ERROR: domain var_record_nn does not allow null values
+CONTEXT: PL/pgSQL function inline_code_block line 6 at assignment
+do $$
+declare x var_record_colnn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record_colnn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record_colnn := row(1,null); -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
+CONTEXT: PL/pgSQL function inline_code_block line 3 during statement block local variable initialization
+do $$
+declare x var_record_colnn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+NOTICE: x = (1,2)
+ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
+do $$
+declare x var_record_colnn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- fail
+end$$;
+NOTICE: x = (1,2)
+ERROR: value for domain var_record_colnn violates check constraint "var_record_colnn_check"
+CONTEXT: PL/pgSQL function inline_code_block line 5 at assignment
errhint("The arguments of the trigger can be accessed through TG_NARGS and TG_ARGV instead.")));
/* Add the record for referencing NEW ROW */
- rec = plpgsql_build_record("new", 0, RECORDOID, true);
+ rec = plpgsql_build_record("new", 0, NULL, RECORDOID, true);
function->new_varno = rec->dno;
/* Add the record for referencing OLD ROW */
- rec = plpgsql_build_record("old", 0, RECORDOID, true);
+ rec = plpgsql_build_record("old", 0, NULL, RECORDOID, true);
function->old_varno = rec->dno;
/* Add the variable tg_name */
var->refname = pstrdup(refname);
var->lineno = lineno;
var->datatype = dtype;
- /* other fields might be filled by caller */
+ /* other fields are left as 0, might be changed by caller */
/* preset to NULL */
var->value = 0;
/* Composite type -- build a record variable */
PLpgSQL_rec *rec;
- rec = plpgsql_build_record(refname, lineno, dtype->typoid,
+ rec = plpgsql_build_record(refname, lineno,
+ dtype, dtype->typoid,
add2namespace);
result = (PLpgSQL_variable *) rec;
break;
* Build empty named record variable, and optionally add it to namespace
*/
PLpgSQL_rec *
-plpgsql_build_record(const char *refname, int lineno, Oid rectypeid,
+plpgsql_build_record(const char *refname, int lineno,
+ PLpgSQL_type *dtype, Oid rectypeid,
bool add2namespace)
{
PLpgSQL_rec *rec;
rec->dtype = PLPGSQL_DTYPE_REC;
rec->refname = pstrdup(refname);
rec->lineno = lineno;
+ /* other fields are left as 0, might be changed by caller */
+ rec->datatype = dtype;
rec->rectypeid = rectypeid;
rec->firstfield = -1;
rec->erh = NULL;
int32 typmod;
Oid typcoll;
+ /* Member vars of a row should never be const */
+ Assert(!var->isconst);
+
switch (var->dtype)
{
case PLPGSQL_DTYPE_VAR:
}
else
{
- /* If arg is null, treat it as an empty row */
+ /* If arg is null, set variable to null */
exec_move_row(&estate, (PLpgSQL_variable *) rec,
NULL, NULL);
}
{
/*
* If needed, give the datatype a chance to reject
- * NULLs, by assigning a NULL to the variable. We
+ * NULLs, by assigning a NULL to the variable. We
* claim the value is of type UNKNOWN, not the var's
- * datatype, else coercion will be skipped. (Do this
- * before the notnull check to be consistent with
- * exec_assign_value.)
+ * datatype, else coercion will be skipped.
*/
if (var->datatype->typtype == TYPTYPE_DOMAIN)
exec_assign_value(estate,
UNKNOWNOID,
-1);
- if (var->notnull)
- ereport(ERROR,
- (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
- errmsg("variable \"%s\" declared NOT NULL cannot default to NULL",
- var->refname)));
+ /* parser should have rejected NOT NULL */
+ Assert(!var->notnull);
}
else
{
{
PLpgSQL_rec *rec = (PLpgSQL_rec *) datum;
- if (rec->erh)
- DeleteExpandedObject(ExpandedRecordGetDatum(rec->erh));
- rec->erh = NULL;
+ /*
+ * Deletion of any existing object will be handled during
+ * the assignments below, and in some cases it's more
+ * efficient for us not to get rid of it beforehand.
+ */
+ if (rec->default_val == NULL)
+ {
+ /*
+ * If needed, give the datatype a chance to reject
+ * NULLs, by assigning a NULL to the variable.
+ */
+ exec_move_row(estate, (PLpgSQL_variable *) rec,
+ NULL, NULL);
+
+ /* parser should have rejected NOT NULL */
+ Assert(!rec->notnull);
+ }
+ else
+ {
+ exec_assign_expr(estate, (PLpgSQL_datum *) rec,
+ rec->default_val);
+ }
}
break;
if (isNull)
{
- /* If source is null, just assign nulls to the record */
+ if (rec->notnull)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("null value cannot be assigned to variable \"%s\" declared NOT NULL",
+ rec->refname)));
+
+ /* Set variable to a simple NULL */
exec_move_row(estate, (PLpgSQL_variable *) rec,
NULL, NULL);
}
*/
if (tupdesc == NULL)
{
- if (rec->erh)
- DeleteExpandedObject(ExpandedRecordGetDatum(rec->erh));
- rec->erh = NULL;
+ if (rec->datatype &&
+ rec->datatype->typtype == TYPTYPE_DOMAIN)
+ {
+ /*
+ * If it's a composite domain, NULL might not be a legal
+ * value, so we instead need to make an empty expanded record
+ * and ensure that domain type checking gets done. If there
+ * is already an expanded record, piggyback on its lookups.
+ */
+ newerh = make_expanded_record_for_rec(estate, rec,
+ NULL, rec->erh);
+ expanded_record_set_tuple(newerh, NULL, false);
+ assign_record_var(estate, rec, newerh);
+ }
+ else
+ {
+ /* Just clear it to NULL */
+ if (rec->erh)
+ DeleteExpandedObject(ExpandedRecordGetDatum(rec->erh));
+ rec->erh = NULL;
+ }
return;
}
case PLPGSQL_DTYPE_ROW:
break;
case PLPGSQL_DTYPE_REC:
+ {
+ PLpgSQL_rec *rec = (PLpgSQL_rec *) d;
+
+ free_expr(rec->default_val);
+ }
break;
case PLPGSQL_DTYPE_RECFIELD:
break;
printf("REC %-16s typoid %u\n",
((PLpgSQL_rec *) d)->refname,
((PLpgSQL_rec *) d)->rectypeid);
+ if (((PLpgSQL_rec *) d)->isconst)
+ printf(" CONSTANT\n");
+ if (((PLpgSQL_rec *) d)->notnull)
+ printf(" NOT NULL\n");
+ if (((PLpgSQL_rec *) d)->default_val != NULL)
+ {
+ printf(" DEFAULT ");
+ dump_expr(((PLpgSQL_rec *) d)->default_val);
+ printf("\n");
+ }
break;
case PLPGSQL_DTYPE_RECFIELD:
printf("RECFIELD %-16s of REC %d\n",
var = plpgsql_build_variable($1.name, $1.lineno,
$3, true);
- if ($2)
- {
- if (var->dtype == PLPGSQL_DTYPE_VAR)
- ((PLpgSQL_var *) var)->isconst = $2;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("record variable cannot be CONSTANT"),
- parser_errposition(@2)));
- }
- if ($5)
- {
- if (var->dtype == PLPGSQL_DTYPE_VAR)
- ((PLpgSQL_var *) var)->notnull = $5;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("record variable cannot be NOT NULL"),
- parser_errposition(@4)));
+ var->isconst = $2;
+ var->notnull = $5;
+ var->default_val = $6;
- }
- if ($6 != NULL)
- {
- if (var->dtype == PLPGSQL_DTYPE_VAR)
- ((PLpgSQL_var *) var)->default_val = $6;
- else
- ereport(ERROR,
- (errcode(ERRCODE_FEATURE_NOT_SUPPORTED),
- errmsg("default value for record variable is not supported"),
- parser_errposition(@5)));
- }
+ /*
+ * The combination of NOT NULL without an initializer
+ * can't work, so let's reject it at compile time.
+ */
+ if (var->notnull && var->default_val == NULL)
+ ereport(ERROR,
+ (errcode(ERRCODE_NULL_VALUE_NOT_ALLOWED),
+ errmsg("variable \"%s\" must have a default value, since it's declared NOT NULL",
+ var->refname),
+ parser_errposition(@5)));
}
| decl_varname K_ALIAS K_FOR decl_aliasitem ';'
{
foreach (l, $2)
{
PLpgSQL_variable *arg = (PLpgSQL_variable *) lfirst(l);
+ Assert(!arg->isconst);
new->fieldnames[i] = arg->refname;
new->varnos[i] = arg->dno;
i++;
new->var = (PLpgSQL_variable *)
plpgsql_build_record($1.name,
$1.lineno,
+ NULL,
RECORDOID,
true);
-1,
plpgsql_curr_compile->fn_input_collation),
true);
- ((PLpgSQL_var *) var)->isconst = true;
+ var->isconst = true;
new->sqlstate_varno = var->dno;
var = plpgsql_build_variable("sqlerrm", lineno,
-1,
plpgsql_curr_compile->fn_input_collation),
true);
- ((PLpgSQL_var *) var)->isconst = true;
+ var->isconst = true;
new->sqlerrm_varno = var->dno;
$<exception_block>$ = new;
{
case PLPGSQL_DTYPE_VAR:
case PLPGSQL_DTYPE_PROMISE:
- if (((PLpgSQL_var *) datum)->isconst)
+ case PLPGSQL_DTYPE_REC:
+ if (((PLpgSQL_variable *) datum)->isconst)
ereport(ERROR,
(errcode(ERRCODE_ERROR_IN_ASSIGNMENT),
- errmsg("\"%s\" is declared CONSTANT",
- ((PLpgSQL_var *) datum)->refname),
+ errmsg("variable \"%s\" is declared CONSTANT",
+ ((PLpgSQL_variable *) datum)->refname),
parser_errposition(location)));
break;
case PLPGSQL_DTYPE_ROW:
- /* always assignable? Shouldn't we check member vars? */
- break;
- case PLPGSQL_DTYPE_REC:
- /* always assignable? What about NEW/OLD? */
+ /* always assignable; member vars were checked at compile time */
break;
case PLPGSQL_DTYPE_RECFIELD:
- /* always assignable? */
+ /* assignable if parent record is */
+ check_assignable(plpgsql_Datums[((PLpgSQL_recfield *) datum)->recparentno],
+ location);
break;
case PLPGSQL_DTYPE_ARRAYELEM:
- /* always assignable? */
+ /* assignable if parent array is */
+ check_assignable(plpgsql_Datums[((PLpgSQL_arrayelem *) datum)->arrayparentno],
+ location);
break;
default:
elog(ERROR, "unrecognized dtype: %d", datum->dtype);
*/
plpgsql_push_back_token(tok);
- row = palloc(sizeof(PLpgSQL_row));
+ row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = pstrdup("*internal*");
row->lineno = plpgsql_location_to_lineno(initial_location);
row->rowtupdesc = NULL;
row->nfields = nfields;
check_assignable(initial_datum, location);
- row = palloc(sizeof(PLpgSQL_row));
+ row = palloc0(sizeof(PLpgSQL_row));
row->dtype = PLPGSQL_DTYPE_ROW;
- row->refname = pstrdup("*internal*");
row->lineno = lineno;
row->rowtupdesc = NULL;
row->nfields = 1;
int dno;
char *refname;
int lineno;
+ bool isconst;
+ bool notnull;
+ PLpgSQL_expr *default_val;
} PLpgSQL_variable;
/*
int dno;
char *refname;
int lineno;
- /* end of PLpgSQL_variable fields */
-
bool isconst;
bool notnull;
- PLpgSQL_type *datatype;
PLpgSQL_expr *default_val;
+ /* end of PLpgSQL_variable fields */
+
+ PLpgSQL_type *datatype;
/*
* Variables declared as CURSOR FOR <query> are mostly like ordinary
*
* Note that there's no way to name the row as such from PL/pgSQL code,
* so many functions don't need to support these.
+ *
+ * refname, isconst, notnull, and default_val are unsupported (and hence
+ * always zero/null) for a row. The member variables of a row should have
+ * been checked to be writable at compile time, so isconst is correctly set
+ * to false. notnull and default_val aren't applicable.
*/
typedef struct PLpgSQL_row
{
int dno;
char *refname;
int lineno;
+ bool isconst;
+ bool notnull;
+ PLpgSQL_expr *default_val;
/* end of PLpgSQL_variable fields */
/*
int dno;
char *refname;
int lineno;
+ bool isconst;
+ bool notnull;
+ PLpgSQL_expr *default_val;
/* end of PLpgSQL_variable fields */
+ PLpgSQL_type *datatype; /* can be NULL, if rectypeid is RECORDOID */
Oid rectypeid; /* declared type of variable */
/* RECFIELDs for this record are chained together for easy access */
int firstfield; /* dno of first RECFIELD, or -1 if none */
+
+ /* Fields below here can change at runtime */
+
/* We always store record variables as "expanded" records */
ExpandedRecordHeader *erh;
} PLpgSQL_rec;
PLpgSQL_type *dtype,
bool add2namespace);
extern PLpgSQL_rec *plpgsql_build_record(const char *refname, int lineno,
- Oid rectypeid, bool add2namespace);
+ PLpgSQL_type *dtype, Oid rectypeid,
+ bool add2namespace);
extern PLpgSQL_recfield *plpgsql_build_recfield(PLpgSQL_rec *rec,
const char *fldname);
extern int plpgsql_recognize_err_condition(const char *condname,
--- /dev/null
+--
+-- Tests for PL/pgSQL variable properties: CONSTANT, NOT NULL, initializers
+--
+
+create type var_record as (f1 int4, f2 int4);
+create domain int_nn as int not null;
+create domain var_record_nn as var_record not null;
+create domain var_record_colnn as var_record check((value).f2 is not null);
+
+-- CONSTANT
+
+do $$
+declare x constant int := 42;
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x constant int;
+begin
+ x := 42; -- fail
+end$$;
+
+do $$
+declare x constant int; y int;
+begin
+ for x, y in select 1, 2 loop -- fail
+ end loop;
+end$$;
+
+do $$
+declare x constant int[];
+begin
+ x[1] := 42; -- fail
+end$$;
+
+do $$
+declare x constant int[]; y int;
+begin
+ for x[1], y in select 1, 2 loop -- fail (currently, unsupported syntax)
+ end loop;
+end$$;
+
+do $$
+declare x constant var_record;
+begin
+ x.f1 := 42; -- fail
+end$$;
+
+do $$
+declare x constant var_record; y int;
+begin
+ for x.f1, y in select 1, 2 loop -- fail
+ end loop;
+end$$;
+
+-- initializer expressions
+
+do $$
+declare x int := sin(0);
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x int := 1/0; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x bigint[] := array[1,3,5];
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x record := row(1,2,3);
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record := row(1,2);
+begin
+ raise notice 'x = %', x;
+end$$;
+
+-- NOT NULL
+
+do $$
+declare x int not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x int not null := 42;
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+
+do $$
+declare x int not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x record not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x record not null := row(42);
+begin
+ raise notice 'x = %', x;
+ x := row(null); -- ok
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+
+do $$
+declare x record not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record not null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record not null := row(41,42);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- ok
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+
+do $$
+declare x var_record not null := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+-- Check that variables are reinitialized on block re-entry.
+
+\set VERBOSITY terse \\ -- needed for output stability
+do $$
+begin
+ for i in 1..3 loop
+ declare
+ x int;
+ y int := i;
+ r record;
+ c var_record;
+ begin
+ if i = 1 then
+ x := 42;
+ r := row(i, i+1);
+ c := row(i, i+1);
+ end if;
+ raise notice 'x = %', x;
+ raise notice 'y = %', y;
+ raise notice 'r = %', r;
+ raise notice 'c = %', c;
+ end;
+ end loop;
+end$$;
+\set VERBOSITY default
+
+-- Check enforcement of domain constraints during initialization
+
+do $$
+declare x int_nn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x int_nn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x int_nn := 42;
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+
+do $$
+declare x var_record_nn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record_nn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record_nn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- ok
+ x := null; -- fail
+end$$;
+
+do $$
+declare x var_record_colnn; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record_colnn := null; -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record_colnn := row(1,null); -- fail
+begin
+ raise notice 'x = %', x;
+end$$;
+
+do $$
+declare x var_record_colnn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := null; -- fail
+end$$;
+
+do $$
+declare x var_record_colnn := row(1,2);
+begin
+ raise notice 'x = %', x;
+ x := row(null,null); -- fail
+end$$;
(1 row)
drop function scope_test();
--- Check that variables are reinitialized on block re-entry.
-\set VERBOSITY terse \\ -- needed for output stability
-do $$
-begin
- for i in 1..3 loop
- declare
- x int;
- y int := i;
- r record;
- c int8_tbl;
- begin
- if i = 1 then
- x := 42;
- r := row(i, i+1);
- c := row(i, i+1);
- end if;
- raise notice 'x = %', x;
- raise notice 'y = %', y;
- raise notice 'r = %', r;
- raise notice 'c = %', c;
- end;
- end loop;
-end$$;
-NOTICE: x = 42
-NOTICE: y = 1
-NOTICE: r = (1,2)
-NOTICE: c = (1,2)
-NOTICE: x = <NULL>
-NOTICE: y = 2
-NOTICE: r = <NULL>
-NOTICE: c = <NULL>
-NOTICE: x = <NULL>
-NOTICE: y = 3
-NOTICE: r = <NULL>
-NOTICE: c = <NULL>
-\set VERBOSITY default
-- Check handling of conflicts between plpgsql vars and table columns.
set plpgsql.variable_conflict = error;
create function conflict_test() returns setof int8_tbl as $$
drop function scope_test();
--- Check that variables are reinitialized on block re-entry.
-
-\set VERBOSITY terse \\ -- needed for output stability
-do $$
-begin
- for i in 1..3 loop
- declare
- x int;
- y int := i;
- r record;
- c int8_tbl;
- begin
- if i = 1 then
- x := 42;
- r := row(i, i+1);
- c := row(i, i+1);
- end if;
- raise notice 'x = %', x;
- raise notice 'y = %', y;
- raise notice 'r = %', r;
- raise notice 'c = %', c;
- end;
- end loop;
-end$$;
-\set VERBOSITY default
-
-- Check handling of conflicts between plpgsql vars and table columns.
set plpgsql.variable_conflict = error;