diff --git a/expected/pg_variables.out b/expected/pg_variables.out index ba0b64a..180ebd1 100644 --- a/expected/pg_variables.out +++ b/expected/pg_variables.out @@ -563,11 +563,13 @@ ERROR: variable "j1" requires "jsonb" value SELECT pgv_insert('vars3', 'r1', tab) FROM tab; ERROR: there is a record in the variable "r1" with same key SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2')); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record structure have 3 attributes, but variable "r1" structure have 2. SELECT pgv_insert('vars3', 'r1', row(1, 1)); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record attribute type for attribute number 2 differs from variable "r1" structure. +HINT: You may need explicit type casts. SELECT pgv_insert('vars3', 'r1', row('str1', 'str1')); -ERROR: new record structure differs from variable "r1" structure +ERROR: new record attribute type for attribute number 1 differs from variable "r1" structure. +HINT: You may need explicit type casts. SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail ERROR: searching for elements in multidimensional arrays is not supported -- Test variables caching @@ -929,3 +931,60 @@ SELECT * FROM pgv_list() order by package, name; ---------+------+------------------ (0 rows) +-- Check insert of record with various amount of fields +CREATE TEMP TABLE foo(id int, t text); +INSERT INTO foo VALUES (0, 'str00'); +SELECT pgv_insert('vars', 'r1', row(1, 'str1'::text, 'str2'::text)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r1', foo) FROM foo; +ERROR: new record structure have 2 attributes, but variable "r1" structure have 3. +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', foo) FROM foo; +ERROR: new record attribute type for attribute number 2 differs from variable "r2" structure. +HINT: You may need explicit type casts. +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (1,str1) +(1 row) + +SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r3', foo) FROM foo; + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r3'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + diff --git a/expected/pg_variables_trans.out b/expected/pg_variables_trans.out index 2389efa..2f46e04 100644 --- a/expected/pg_variables_trans.out +++ b/expected/pg_variables_trans.out @@ -3773,3 +3773,59 @@ SELECT pgv_free(); (1 row) +--- +--- Test case for issue #32 [PGPRO-4456] +--- +CREATE TEMP TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'); +SELECT pgv_insert('vars', 'r1', row(1, 'str1', 'str2')); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'a', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r1', tab) FROM tab; +ERROR: new record structure have 2 attributes, but variable "r1" structure have 3. +SELECT pgv_select('vars', 'r1'); + pgv_select +--------------- + (1,str1,str2) +(1 row) + +SELECT pgv_insert('vars', 'r2', row(1, 'str1'::varchar)); + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'b', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_insert('vars', 'r2', tab) FROM tab; + pgv_insert +------------ + +(1 row) + +SELECT pgv_select('vars', 'r2'); + pgv_select +------------ + (1,str1) + (0,str00) +(2 rows) + +SELECT pgv_free(); + pgv_free +---------- + +(1 row) + diff --git a/pg_variables.c b/pg_variables.c index 4bd6bdd..b75701a 100644 --- a/pg_variables.c +++ b/pg_variables.c @@ -121,8 +121,6 @@ static MemoryContext ModuleContext = NULL; static Package *LastPackage = NULL; /* Recent variable */ static Variable *LastVariable = NULL; -/* Recent row type id */ -static Oid LastTypeId = InvalidOid; /* Saved hook values for recall */ static ExecutorEnd_hook_type prev_ExecutorEnd = NULL; @@ -696,35 +694,25 @@ variable_insert(PG_FUNCTION_ARGS) tupTypmod = HeapTupleHeaderGetTypMod(rec); record = &(GetActualValue(variable).record); - if (!record->tupdesc) + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + + if (!record->tupdesc || variable->is_deleted) { /* * This is the first record for the var_name. Initialize record. */ - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); init_record(record, tupdesc, variable); + variable->is_deleted = false; } - else if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || - LastTypeId != tupType) + else { /* * We need to check attributes of the new row if this is a transient * record type or if last record has different id. */ - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - if (variable->is_deleted) - { - init_record(record, tupdesc, variable); - variable->is_deleted = false; - } - else - { - check_attributes(variable, tupdesc); - } + check_attributes(variable, tupdesc); } - LastTypeId = tupType; - insert_record(variable, rec); /* Release resources */ @@ -749,6 +737,7 @@ variable_update(PG_FUNCTION_ARGS) bool res; Oid tupType; int32 tupTypmod; + TupleDesc tupdesc = NULL; /* Checks */ CHECK_ARGS_FOR_NULL(); @@ -801,17 +790,9 @@ variable_update(PG_FUNCTION_ARGS) tupType = HeapTupleHeaderGetTypeId(rec); tupTypmod = HeapTupleHeaderGetTypMod(rec); - if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) || - LastTypeId != tupType) - { - TupleDesc tupdesc = NULL; - - tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); - check_attributes(variable, tupdesc); - ReleaseTupleDesc(tupdesc); - } - - LastTypeId = tupType; + tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod); + check_attributes(variable, tupdesc); + ReleaseTupleDesc(tupdesc); res = update_record(variable, rec); @@ -1337,7 +1318,6 @@ resetVariablesCache(void) /* Remove package and variable from cache */ LastPackage = NULL; LastVariable = NULL; - LastTypeId = InvalidOid; } /* diff --git a/pg_variables_record.c b/pg_variables_record.c index b97ffca..3d7ca18 100644 --- a/pg_variables_record.c +++ b/pg_variables_record.c @@ -188,8 +188,9 @@ check_attributes(Variable *variable, TupleDesc tupdesc) if (record->tupdesc->natts != tupdesc->natts) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("new record structure differs from variable \"%s\" " - "structure", GetName(variable)))); + errmsg("new record structure have %d attributes, but variable " + "\"%s\" structure have %d.", + tupdesc->natts, GetName(variable), record->tupdesc->natts))); /* Second, check columns type. */ for (i = 0; i < tupdesc->natts; i++) @@ -202,8 +203,10 @@ check_attributes(Variable *variable, TupleDesc tupdesc) || (attr1->atttypmod != attr2->atttypmod)) ereport(ERROR, (errcode(ERRCODE_INVALID_PARAMETER_VALUE), - errmsg("new record structure differs from variable \"%s\" " - "structure", GetName(variable)))); + errmsg("new record attribute type for attribute number %d " + "differs from variable \"%s\" structure.", + i + 1, GetName(variable)), + errhint("You may need explicit type casts."))); } } diff --git a/sql/pg_variables.sql b/sql/pg_variables.sql index a9fdbbd..bad7976 100644 --- a/sql/pg_variables.sql +++ b/sql/pg_variables.sql @@ -259,3 +259,19 @@ SELECT pgv_free(); SELECT pgv_exists('vars'); SELECT * FROM pgv_list() order by package, name; +-- Check insert of record with various amount of fields +CREATE TEMP TABLE foo(id int, t text); +INSERT INTO foo VALUES (0, 'str00'); + +SELECT pgv_insert('vars', 'r1', row(1, 'str1'::text, 'str2'::text)); +SELECT pgv_select('vars', 'r1'); +SELECT pgv_insert('vars', 'r1', foo) FROM foo; +SELECT pgv_select('vars', 'r1'); + +SELECT pgv_insert('vars', 'r2', row(1, 'str1')); +SELECT pgv_insert('vars', 'r2', foo) FROM foo; +SELECT pgv_select('vars', 'r2'); + +SELECT pgv_insert('vars', 'r3', row(1, 'str1'::text)); +SELECT pgv_insert('vars', 'r3', foo) FROM foo; +SELECT pgv_select('vars', 'r3'); diff --git a/sql/pg_variables_trans.sql b/sql/pg_variables_trans.sql index 0d67ae1..72d9559 100644 --- a/sql/pg_variables_trans.sql +++ b/sql/pg_variables_trans.sql @@ -1145,3 +1145,21 @@ COMMIT; DROP VIEW pgv_stats_view; SELECT pgv_free(); + +--- +--- Test case for issue #32 [PGPRO-4456] +--- +CREATE TEMP TABLE tab (id int, t varchar); +INSERT INTO tab VALUES (0, 'str00'); + +SELECT pgv_insert('vars', 'r1', row(1, 'str1', 'str2')); +SELECT pgv_insert('vars', 'a', tab) FROM tab; +SELECT pgv_insert('vars', 'r1', tab) FROM tab; +SELECT pgv_select('vars', 'r1'); + +SELECT pgv_insert('vars', 'r2', row(1, 'str1'::varchar)); +SELECT pgv_insert('vars', 'b', tab) FROM tab; +SELECT pgv_insert('vars', 'r2', tab) FROM tab; +SELECT pgv_select('vars', 'r2'); + +SELECT pgv_free();