Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1d1bfe2

Browse files
committedNov 28, 2019
Merge branch 'master' into stable
2 parents 89c120c + b01b0f9 commit 1d1bfe2

9 files changed

+1036
-444
lines changed
 

‎.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ lib*.pc
3838
/pgsql.sln.cache
3939
/Debug/
4040
/Release/
41+
/log/
4142
/tmp_install/
4243
Dockerfile
4344
pg_variables--1.1.sql

‎README.md

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,10 @@ SELECT pgv_get('vars', 'trans_int', NULL::int);
3838
101
3939
```
4040

41+
You can aggregate variables into packages. This is done to be able to have
42+
variables with different names or to quickly remove the whole batch of
43+
variables. If the package becomes empty, it is automatically deleted.
44+
4145
## License
4246

4347
This module available under the [license](LICENSE) similar to
@@ -56,7 +60,7 @@ Typical installation procedure may look like this:
5660
## Module functions
5761

5862
The functions provided by the **pg_variables** module are shown in the tables
59-
below. The module supports the following scalar and record types.
63+
below.
6064

6165
To use **pgv_get()** function required package and variable must exists. It is
6266
necessary to set variable with **pgv_set()** function to use **pgv_get()**
@@ -98,6 +102,28 @@ Function | Returns
98102
`pgv_set(package text, name text, value anyarray, is_transactional bool default false)` | `void`
99103
`pgv_get(package text, name text, var_type anyarray, strict bool default true)` | `anyarray`
100104

105+
`pgv_set` arguments:
106+
- `package` - name of the package, it will be created if it doesn't exist.
107+
- `name` - name of the variable, it will be created if it doesn't exist.
108+
`pgv_set` fails if the variable already exists and its transactionality doesn't
109+
match `is_transactional` argument.
110+
- `value` - new value for the variable. `pgv_set` fails if the variable already
111+
exists and its type doesn't match new value's type.
112+
- `is_transactional` - transactionality of the newly created variable, by
113+
default it is false.
114+
115+
`pgv_get` arguments:
116+
- `package` - name of the existing package. If the package doesn't exist result
117+
depends on `strict` argument: if it is false then `pgv_get` returns NULL
118+
otherwise it fails.
119+
- `name` - name of the the existing variable. If the variable doesn't exist
120+
result depends on `strict` argument: if it is false then `pgv_get` returns NULL
121+
otherwise it fails.
122+
- `var_type` - type of the existing variable. It is necessary to pass it to get
123+
correct return type.
124+
- `strict` - pass false if `pgv_get` shouldn't raise an error if a variable or a
125+
package didn't created before, by default it is true.
126+
101127
## **Deprecated** scalar variables functions
102128

103129
### Integer variables

‎expected/pg_variables.out

Lines changed: 178 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,19 @@
11
CREATE EXTENSION pg_variables;
2+
-- Test packages - sanity checks
3+
SELECT pgv_free();
4+
pgv_free
5+
----------
6+
7+
(1 row)
8+
9+
SELECT pgv_exists(NULL); -- fail
10+
ERROR: package name can not be NULL
11+
SELECT pgv_remove(NULL); -- fail
12+
ERROR: package name can not be NULL
13+
SELECT pgv_remove('vars'); -- fail
14+
ERROR: unrecognized package "vars"
15+
SELECT pgv_exists('vars111111111111111111111111111111111111111111111111111111111111'); -- fail
16+
ERROR: name "vars111111111111111111111111111111111111111111111111111111111111" is too long
217
-- Integer variables
318
SELECT pgv_get_int('vars', 'int1');
419
ERROR: unrecognized package "vars"
@@ -553,20 +568,44 @@ SELECT pgv_insert('vars3', 'r1', row(1, 1));
553568
ERROR: new record structure differs from variable "r1" structure
554569
SELECT pgv_insert('vars3', 'r1', row('str1', 'str1'));
555570
ERROR: new record structure differs from variable "r1" structure
556-
SELECT pgv_select('vars3', 'r1') LIMIT 2;
557-
pgv_select
571+
SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail
572+
ERROR: searching for elements in multidimensional arrays is not supported
573+
-- Test variables caching
574+
SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2'));
575+
pgv_insert
558576
------------
559-
(,strNULL)
560-
(1,str11)
561-
(2 rows)
577+
578+
(1 row)
562579

563-
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
564-
pgv_select
580+
SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar));
581+
pgv_update
565582
------------
566-
(2,)
567-
(0,str00)
568-
(2 rows)
583+
f
584+
(1 row)
569585

586+
SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail
587+
ERROR: unrecognized package "vars4"
588+
select pgv_delete('vars3', 'r2', NULL::int);
589+
pgv_delete
590+
------------
591+
f
592+
(1 row)
593+
594+
select pgv_delete('vars4', 'r2', NULL::int); -- fail
595+
ERROR: unrecognized package "vars4"
596+
-- Test NULL values
597+
SELECT pgv_insert('vars3', 'r2', NULL); -- fail
598+
ERROR: record argument can not be NULL
599+
SELECT pgv_update('vars3', 'r2', NULL); -- fail
600+
ERROR: record argument can not be NULL
601+
select pgv_delete('vars3', 'r2', NULL::int);
602+
pgv_delete
603+
------------
604+
f
605+
(1 row)
606+
607+
SELECT pgv_select('vars3', 'r1', NULL::int[]); -- fail
608+
ERROR: array argument can not be NULL
570609
SELECT pgv_select('vars3', 'r1');
571610
pgv_select
572611
------------
@@ -582,6 +621,8 @@ SELECT pgv_select('vars3', 'r1', 1);
582621
(1,str11)
583622
(1 row)
584623

624+
SELECT pgv_select('vars3', 'r1', 1::float); -- fail
625+
ERROR: requested value type differs from variable "r1" key type
585626
SELECT pgv_select('vars3', 'r1', 0);
586627
pgv_select
587628
------------
@@ -612,6 +653,12 @@ SELECT pgv_update('vars3', 'r1', tab) FROM tab;
612653
t
613654
(4 rows)
614655

656+
SELECT pgv_update('vars3', 'r1', row(4, 'str44'::varchar));
657+
pgv_update
658+
------------
659+
f
660+
(1 row)
661+
615662
SELECT pgv_select('vars3', 'r1');
616663
pgv_select
617664
------------
@@ -657,6 +704,119 @@ SELECT pgv_exists('vars3', 'r1');
657704

658705
SELECT pgv_select('vars2', 'j1');
659706
ERROR: variable "j1" requires "jsonb" value
707+
-- PGPRO-2601 - Test pgv_select() on TupleDesc of dropped table
708+
DROP TABLE tab;
709+
SELECT pgv_select('vars3', 'r1');
710+
pgv_select
711+
------------
712+
(,strNULL)
713+
(2,)
714+
(0,str00)
715+
(3 rows)
716+
717+
-- Tests for SRF's sequential scan of an internal hash table
718+
DO
719+
$$BEGIN
720+
PERFORM pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
721+
PERFORM pgv_select('vars3', 'r3');
722+
END$$;
723+
ERROR: unrecognized variable "r3"
724+
CONTEXT: SQL statement "SELECT pgv_select('vars3', 'r3')"
725+
PL/pgSQL function inline_code_block line 3 at PERFORM
726+
-- Check that the hash table was cleaned up after rollback
727+
SET client_min_messages to 'ERROR';
728+
SELECT pgv_select('vars3', 'r1', 1);
729+
pgv_select
730+
------------
731+
732+
(1 row)
733+
734+
SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning
735+
pgv_select
736+
------------
737+
(,strNULL)
738+
(2,)
739+
(2 rows)
740+
741+
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
742+
pgv_select
743+
------------
744+
(0,str00)
745+
(1 row)
746+
747+
-- PGPRO-2601 - Test a cursor with the hash table
748+
BEGIN;
749+
DECLARE r1_cur CURSOR FOR SELECT pgv_select('vars3', 'r1');
750+
FETCH 1 in r1_cur;
751+
pgv_select
752+
------------
753+
(,strNULL)
754+
(1 row)
755+
756+
SELECT pgv_select('vars3', 'r1');
757+
pgv_select
758+
------------
759+
(,strNULL)
760+
(2,)
761+
(0,str00)
762+
(3 rows)
763+
764+
FETCH 1 in r1_cur;
765+
pgv_select
766+
------------
767+
(2,)
768+
(1 row)
769+
770+
CLOSE r1_cur;
771+
COMMIT; -- warning
772+
RESET client_min_messages;
773+
-- Clean memory after unsuccessful creation of a variable
774+
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail
775+
ERROR: could not identify a hash function for type unknown
776+
SELECT package FROM pgv_stats() WHERE package = 'vars4';
777+
package
778+
---------
779+
(0 rows)
780+
781+
-- Remove package if it is empty
782+
SELECT pgv_insert('vars4', 'r2', row(1, 'str1', 'str2'));
783+
pgv_insert
784+
------------
785+
786+
(1 row)
787+
788+
SELECT pgv_remove('vars4', 'r2');
789+
pgv_remove
790+
------------
791+
792+
(1 row)
793+
794+
SELECT package FROM pgv_stats() WHERE package = 'vars4';
795+
package
796+
---------
797+
(0 rows)
798+
799+
-- Record variables as scalar
800+
SELECT pgv_set('vars5', 'r1', row(1, 'str11'));
801+
pgv_set
802+
---------
803+
804+
(1 row)
805+
806+
SELECT pgv_get('vars5', 'r1', NULL::record);
807+
pgv_get
808+
-----------
809+
(1,str11)
810+
(1 row)
811+
812+
SELECT pgv_set('vars5', 'r1', row(1, 'str11'), true); -- fail
813+
ERROR: variable "r1" already created as NOT TRANSACTIONAL
814+
SELECT pgv_insert('vars5', 'r1', row(1, 'str11')); -- fail
815+
ERROR: "r1" isn't a record variable
816+
SELECT pgv_select('vars5', 'r1'); -- fail
817+
ERROR: "r1" isn't a record variable
818+
SELECT pgv_get('vars3', 'r1', NULL::record); -- fail
819+
ERROR: "r1" isn't a scalar variable
660820
-- Manipulate variables
661821
SELECT * FROM pgv_list() order by package, name;
662822
package | name | is_transactional
@@ -683,15 +843,18 @@ SELECT * FROM pgv_list() order by package, name;
683843
vars2 | j1 | f
684844
vars2 | j2 | f
685845
vars3 | r1 | f
686-
(22 rows)
846+
vars3 | r2 | f
847+
vars5 | r1 | f
848+
(24 rows)
687849

688850
SELECT package FROM pgv_stats() order by package;
689851
package
690852
---------
691853
vars
692854
vars2
693855
vars3
694-
(3 rows)
856+
vars5
857+
(4 rows)
695858

696859
SELECT pgv_remove('vars', 'int3');
697860
ERROR: unrecognized variable "int3"
@@ -745,7 +908,9 @@ SELECT * FROM pgv_list() order by package, name;
745908
vars | tstz2 | f
746909
vars | tstzNULL | f
747910
vars3 | r1 | f
748-
(19 rows)
911+
vars3 | r2 | f
912+
vars5 | r1 | f
913+
(21 rows)
749914

750915
SELECT pgv_free();
751916
pgv_free

‎expected/pg_variables_trans.out

Lines changed: 175 additions & 29 deletions
Large diffs are not rendered by default.

‎pg_variables.c

Lines changed: 398 additions & 358 deletions
Large diffs are not rendered by default.

‎pg_variables.h

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,7 +63,8 @@ typedef struct TransState
6363
/* List node that stores one of the package's states */
6464
typedef struct PackState
6565
{
66-
TransState state;
66+
TransState state;
67+
unsigned long trans_var_num; /* Number of valid transactional variables */
6768
} PackState;
6869

6970
/* List node that stores one of the variable's states */
@@ -101,6 +102,11 @@ typedef struct Variable
101102
TransObject transObject;
102103
Package *package;
103104
Oid typid;
105+
/*
106+
* We need an additional flag to determine variable's type since we can
107+
* store record type DATUM within scalar variable
108+
*/
109+
bool is_record;
104110

105111
/*
106112
* The flag determines the further behavior of the variable. Can be
@@ -122,7 +128,7 @@ typedef struct HashRecordKey
122128
typedef struct HashRecordEntry
123129
{
124130
HashRecordKey key;
125-
HeapTuple tuple;
131+
Datum tuple;
126132
} HashRecordEntry;
127133

128134
/* Element of list with objects created, changed or removed within transaction */
@@ -155,6 +161,8 @@ extern void check_record_key(Variable *variable, Oid typid);
155161
extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader);
156162
extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader);
157163
extern bool delete_record(Variable *variable, Datum value, bool is_null);
164+
extern void insert_record_copy(RecordVar *dest_record, Datum src_tuple,
165+
Variable *variable);
158166
extern void removeObject(TransObject *object, TransObjectType type);
159167

160168
#define GetActualState(object) \
@@ -163,12 +171,11 @@ extern void removeObject(TransObject *object, TransObjectType type);
163171
#define GetActualValue(variable) \
164172
(((VarState *) GetActualState(variable))->value)
165173

174+
#define GetPackState(package) \
175+
(((PackState *) GetActualState(package)))
176+
166177
#define GetName(object) \
167178
(AssertVariableIsOfTypeMacro(object->transObject, TransObject), \
168179
object->transObject.name)
169180

170-
#define GetStateStorage(object) \
171-
(AssertVariableIsOfTypeMacro(object->transObject, TransObject), \
172-
&(object->transObject.states))
173-
174181
#endif /* __PG_VARIABLES_H__ */

‎pg_variables_record.c

Lines changed: 126 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,10 @@
88
*-------------------------------------------------------------------------
99
*/
1010
#include "postgres.h"
11+
#include "funcapi.h"
1112

1213
#include "access/htup_details.h"
14+
#include "access/tuptoaster.h"
1315
#include "catalog/pg_collation.h"
1416
#include "catalog/pg_type.h"
1517
#include "utils/builtins.h"
@@ -134,7 +136,13 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable)
134136
#endif
135137

136138
oldcxt = MemoryContextSwitchTo(record->hctx);
137-
record->tupdesc = CreateTupleDescCopyConstr(tupdesc);
139+
record->tupdesc = CreateTupleDescCopy(tupdesc);
140+
#if PG_VERSION_NUM < 120000
141+
record->tupdesc->tdhasoid = false;
142+
#endif
143+
record->tupdesc->tdtypeid = RECORDOID;
144+
record->tupdesc->tdtypmod = -1;
145+
record->tupdesc = BlessTupleDesc(record->tupdesc);
138146

139147
/* Initialize hash table. */
140148
ctl.keysize = sizeof(HashRecordKey);
@@ -206,15 +214,78 @@ check_record_key(Variable *variable, Oid typid)
206214
"key type", GetName(variable))));
207215
}
208216

217+
static Datum
218+
copy_record_tuple(RecordVar *record, HeapTupleHeader tupleHeader)
219+
{
220+
TupleDesc tupdesc;
221+
HeapTupleHeader result;
222+
int tuple_len;
223+
224+
tupdesc = record->tupdesc;
225+
226+
/*
227+
* If the tuple contains any external TOAST pointers, we have to inline
228+
* those fields to meet the conventions for composite-type Datums.
229+
*/
230+
if (HeapTupleHeaderHasExternal(tupleHeader))
231+
return toast_flatten_tuple_to_datum(tupleHeader,
232+
HeapTupleHeaderGetDatumLength(tupleHeader),
233+
tupdesc);
234+
235+
/*
236+
* Fast path for easy case: just make a palloc'd copy and insert the
237+
* correct composite-Datum header fields (since those may not be set if
238+
* the given tuple came from disk, rather than from heap_form_tuple).
239+
*/
240+
tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader);
241+
result = (HeapTupleHeader) palloc(tuple_len);
242+
memcpy((char *) result, (char *) tupleHeader, tuple_len);
243+
244+
HeapTupleHeaderSetDatumLength(result, tuple_len);
245+
HeapTupleHeaderSetTypeId(result, tupdesc->tdtypeid);
246+
HeapTupleHeaderSetTypMod(result, tupdesc->tdtypmod);
247+
248+
return PointerGetDatum(result);
249+
}
250+
251+
static Datum
252+
get_record_key(Datum tuple, TupleDesc tupdesc, bool *isnull)
253+
{
254+
HeapTupleHeader th = (HeapTupleHeader) DatumGetPointer(tuple);
255+
bool hasnulls = th->t_infomask & HEAP_HASNULL;
256+
bits8 *bp = th->t_bits; /* ptr to null bitmap in tuple */
257+
char *tp; /* ptr to tuple data */
258+
long off; /* offset in tuple data */
259+
int keyatt = 0;
260+
Form_pg_attribute attr = GetTupleDescAttr(tupdesc, keyatt);
261+
262+
if (hasnulls && att_isnull(keyatt, bp))
263+
{
264+
*isnull = true;
265+
return (Datum) NULL;
266+
}
267+
268+
tp = (char *) th + th->t_hoff;
269+
off = 0;
270+
if (attr->attlen == -1)
271+
off = att_align_pointer(off, attr->attalign, -1, tp + off);
272+
else
273+
{
274+
/* not varlena, so safe to use att_align_nominal */
275+
off = att_align_nominal(off, attr->attalign);
276+
}
277+
278+
*isnull = false;
279+
return fetchatt(attr, tp + off);
280+
}
281+
209282
/*
210283
* Insert a new record. New record key should be unique in the variable.
211284
*/
212285
void
213286
insert_record(Variable *variable, HeapTupleHeader tupleHeader)
214287
{
215-
TupleDesc tupdesc;
216-
HeapTuple tuple;
217-
int tuple_len;
288+
Datum tuple;
218289
Datum value;
219290
bool isnull;
220291
RecordVar *record;
@@ -229,20 +300,10 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
229300

230301
oldcxt = MemoryContextSwitchTo(record->hctx);
231302

232-
tupdesc = record->tupdesc;
233-
234-
/* Build a HeapTuple control structure */
235-
tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader);
236-
237-
tuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple_len);
238-
tuple->t_len = tuple_len;
239-
ItemPointerSetInvalid(&(tuple->t_self));
240-
tuple->t_tableOid = InvalidOid;
241-
tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
242-
memcpy((char *) tuple->t_data, (char *) tupleHeader, tuple_len);
303+
tuple = copy_record_tuple(record, tupleHeader);
243304

244305
/* Inserting a new record */
245-
value = fastgetattr(tuple, 1, tupdesc, &isnull);
306+
value = get_record_key(tuple, record->tupdesc, &isnull);
246307
/* First, check if there is a record with same key */
247308
k.value = value;
248309
k.is_null = isnull;
@@ -253,7 +314,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
253314
HASH_ENTER, &found);
254315
if (found)
255316
{
256-
heap_freetuple(tuple);
317+
pfree(DatumGetPointer(tuple));
257318
MemoryContextSwitchTo(oldcxt);
258319
ereport(ERROR,
259320
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
@@ -272,9 +333,7 @@ insert_record(Variable *variable, HeapTupleHeader tupleHeader)
272333
bool
273334
update_record(Variable *variable, HeapTupleHeader tupleHeader)
274335
{
275-
TupleDesc tupdesc;
276-
HeapTuple tuple;
277-
int tuple_len;
336+
Datum tuple;
278337
Datum value;
279338
bool isnull;
280339
RecordVar *record;
@@ -289,20 +348,10 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader)
289348

290349
oldcxt = MemoryContextSwitchTo(record->hctx);
291350

292-
tupdesc = record->tupdesc;
293-
294-
/* Build a HeapTuple control structure */
295-
tuple_len = HeapTupleHeaderGetDatumLength(tupleHeader);
296-
297-
tuple = (HeapTuple) palloc(HEAPTUPLESIZE + tuple_len);
298-
tuple->t_len = tuple_len;
299-
ItemPointerSetInvalid(&(tuple->t_self));
300-
tuple->t_tableOid = InvalidOid;
301-
tuple->t_data = (HeapTupleHeader) ((char *) tuple + HEAPTUPLESIZE);
302-
memcpy((char *) tuple->t_data, (char *) tupleHeader, tuple_len);
351+
tuple = copy_record_tuple(record, tupleHeader);
303352

304353
/* Update a record */
305-
value = fastgetattr(tuple, 1, tupdesc, &isnull);
354+
value = get_record_key(tuple, record->tupdesc, &isnull);
306355
k.value = value;
307356
k.is_null = isnull;
308357
k.hash_proc = &record->hash_proc;
@@ -312,13 +361,13 @@ update_record(Variable *variable, HeapTupleHeader tupleHeader)
312361
HASH_FIND, &found);
313362
if (!found)
314363
{
315-
heap_freetuple(tuple);
364+
pfree(DatumGetPointer(tuple));
316365
MemoryContextSwitchTo(oldcxt);
317366
return false;
318367
}
319368

320369
/* Release old tuple */
321-
heap_freetuple(item->tuple);
370+
pfree(DatumGetPointer(item->tuple));
322371
item->tuple = tuple;
323372

324373
MemoryContextSwitchTo(oldcxt);
@@ -346,7 +395,49 @@ delete_record(Variable *variable, Datum value, bool is_null)
346395
item = (HashRecordEntry *) hash_search(record->rhash, &k,
347396
HASH_REMOVE, &found);
348397
if (found)
349-
heap_freetuple(item->tuple);
398+
pfree(DatumGetPointer(item->tuple));
350399

351400
return found;
352401
}
402+
403+
/*
404+
* Copy record using src_tuple.
405+
*/
406+
void
407+
insert_record_copy(RecordVar *dest_record, Datum src_tuple, Variable *variable)
408+
{
409+
Datum tuple;
410+
Datum value;
411+
bool isnull;
412+
HashRecordKey k;
413+
HashRecordEntry *item;
414+
bool found;
415+
MemoryContext oldcxt;
416+
417+
oldcxt = MemoryContextSwitchTo(dest_record->hctx);
418+
419+
/* Inserting a new record into dest_record */
420+
tuple = copy_record_tuple(dest_record,
421+
(HeapTupleHeader) DatumGetPointer(src_tuple));
422+
value = get_record_key(tuple, dest_record->tupdesc, &isnull);
423+
424+
k.value = value;
425+
k.is_null = isnull;
426+
k.hash_proc = &dest_record->hash_proc;
427+
k.cmp_proc = &dest_record->cmp_proc;
428+
429+
item = (HashRecordEntry *) hash_search(dest_record->rhash, &k,
430+
HASH_ENTER, &found);
431+
if (found)
432+
{
433+
pfree(DatumGetPointer(tuple));
434+
MemoryContextSwitchTo(oldcxt);
435+
ereport(ERROR,
436+
(errcode(ERRCODE_INVALID_PARAMETER_VALUE),
437+
errmsg("there is a record in the variable \"%s\" with same "
438+
"key", GetName(variable))));
439+
}
440+
item->tuple = tuple;
441+
442+
MemoryContextSwitchTo(oldcxt);
443+
}

‎sql/pg_variables.sql

Lines changed: 67 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
CREATE EXTENSION pg_variables;
22

3+
-- Test packages - sanity checks
4+
SELECT pgv_free();
5+
SELECT pgv_exists(NULL); -- fail
6+
SELECT pgv_remove(NULL); -- fail
7+
SELECT pgv_remove('vars'); -- fail
8+
SELECT pgv_exists('vars111111111111111111111111111111111111111111111111111111111111'); -- fail
9+
310
-- Integer variables
411
SELECT pgv_get_int('vars', 'int1');
512
SELECT pgv_get_int('vars', 'int1', false);
@@ -152,18 +159,31 @@ SELECT pgv_insert('vars3', 'r1', tab) FROM tab;
152159
SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2'));
153160
SELECT pgv_insert('vars3', 'r1', row(1, 1));
154161
SELECT pgv_insert('vars3', 'r1', row('str1', 'str1'));
162+
SELECT pgv_select('vars3', 'r1', ARRAY[[1,2]]); -- fail
155163

156-
SELECT pgv_select('vars3', 'r1') LIMIT 2;
157-
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
164+
-- Test variables caching
165+
SELECT pgv_insert('vars3', 'r2', row(1, 'str1', 'str2'));
166+
SELECT pgv_update('vars3', 'r1', row(3, 'str22'::varchar));
167+
SELECT pgv_update('vars4', 'r1', row(3, 'str22'::varchar)); -- fail
168+
select pgv_delete('vars3', 'r2', NULL::int);
169+
select pgv_delete('vars4', 'r2', NULL::int); -- fail
170+
171+
-- Test NULL values
172+
SELECT pgv_insert('vars3', 'r2', NULL); -- fail
173+
SELECT pgv_update('vars3', 'r2', NULL); -- fail
174+
select pgv_delete('vars3', 'r2', NULL::int);
175+
SELECT pgv_select('vars3', 'r1', NULL::int[]); -- fail
158176

159177
SELECT pgv_select('vars3', 'r1');
160178
SELECT pgv_select('vars3', 'r1', 1);
179+
SELECT pgv_select('vars3', 'r1', 1::float); -- fail
161180
SELECT pgv_select('vars3', 'r1', 0);
162181
SELECT pgv_select('vars3', 'r1', NULL::int);
163182
SELECT pgv_select('vars3', 'r1', ARRAY[1, 0, NULL]);
164183

165184
UPDATE tab SET t = 'str33' WHERE id = 1;
166185
SELECT pgv_update('vars3', 'r1', tab) FROM tab;
186+
SELECT pgv_update('vars3', 'r1', row(4, 'str44'::varchar));
167187
SELECT pgv_select('vars3', 'r1');
168188

169189
SELECT pgv_delete('vars3', 'r1', 1);
@@ -175,6 +195,51 @@ SELECT pgv_exists('vars3', 'r3');
175195
SELECT pgv_exists('vars3', 'r1');
176196
SELECT pgv_select('vars2', 'j1');
177197

198+
-- PGPRO-2601 - Test pgv_select() on TupleDesc of dropped table
199+
DROP TABLE tab;
200+
SELECT pgv_select('vars3', 'r1');
201+
202+
-- Tests for SRF's sequential scan of an internal hash table
203+
DO
204+
$$BEGIN
205+
PERFORM pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
206+
PERFORM pgv_select('vars3', 'r3');
207+
END$$;
208+
-- Check that the hash table was cleaned up after rollback
209+
SET client_min_messages to 'ERROR';
210+
SELECT pgv_select('vars3', 'r1', 1);
211+
SELECT pgv_select('vars3', 'r1') LIMIT 2; -- warning
212+
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
213+
214+
-- PGPRO-2601 - Test a cursor with the hash table
215+
BEGIN;
216+
DECLARE r1_cur CURSOR FOR SELECT pgv_select('vars3', 'r1');
217+
FETCH 1 in r1_cur;
218+
SELECT pgv_select('vars3', 'r1');
219+
FETCH 1 in r1_cur;
220+
CLOSE r1_cur;
221+
COMMIT; -- warning
222+
RESET client_min_messages;
223+
224+
-- Clean memory after unsuccessful creation of a variable
225+
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1')); -- fail
226+
SELECT package FROM pgv_stats() WHERE package = 'vars4';
227+
228+
-- Remove package if it is empty
229+
SELECT pgv_insert('vars4', 'r2', row(1, 'str1', 'str2'));
230+
SELECT pgv_remove('vars4', 'r2');
231+
SELECT package FROM pgv_stats() WHERE package = 'vars4';
232+
233+
-- Record variables as scalar
234+
SELECT pgv_set('vars5', 'r1', row(1, 'str11'));
235+
SELECT pgv_get('vars5', 'r1', NULL::record);
236+
SELECT pgv_set('vars5', 'r1', row(1, 'str11'), true); -- fail
237+
238+
SELECT pgv_insert('vars5', 'r1', row(1, 'str11')); -- fail
239+
SELECT pgv_select('vars5', 'r1'); -- fail
240+
241+
SELECT pgv_get('vars3', 'r1', NULL::record); -- fail
242+
178243
-- Manipulate variables
179244
SELECT * FROM pgv_list() order by package, name;
180245
SELECT package FROM pgv_stats() order by package;

‎sql/pg_variables_trans.sql

Lines changed: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ SELECT pgv_get_jsonb('vars2', 'j1');
8080
SELECT pgv_get_jsonb('vars2', 'j2');
8181
COMMIT;
8282

83+
CREATE TABLE tab (id int, t varchar);
84+
INSERT INTO tab VALUES (0, 'str00'), (1, 'str33'), (2, NULL), (NULL, 'strNULL');
8385

8486
BEGIN;
8587
SELECT pgv_insert('vars3', 'r1', tab, true) FROM tab;
@@ -482,4 +484,53 @@ RELEASE comm;
482484
SELECT pgv_get('vars', 'any1',NULL::text);
483485
COMMIT;
484486

487+
-- Tests for PGPRO-2440
488+
SELECT pgv_insert('vars3', 'r3', row(1 :: integer, NULL::varchar), true);
489+
BEGIN;
490+
SELECT pgv_insert('vars3', 'r3', row(2 :: integer, NULL::varchar), true);
491+
SAVEPOINT comm;
492+
SELECT pgv_insert('vars3', 'r3', row(3 :: integer, NULL::varchar), true);
493+
COMMIT;
494+
SELECT pgv_delete('vars3', 'r3', 3);
495+
496+
BEGIN;
497+
SELECT pgv_set('vars1', 't1', ''::text);
498+
SELECT pgv_set('vars2', 't2', ''::text, true);
499+
SAVEPOINT sp1;
500+
SAVEPOINT sp2;
501+
SELECT pgv_free();
502+
ERROR;
503+
COMMIT;
504+
505+
BEGIN;
506+
SELECT pgv_set('vars', 'any1', 'some value'::text, true);
507+
SELECT pgv_free();
508+
SAVEPOINT sp_to_rollback;
509+
SELECT pgv_set('vars', 'any1', 'some value'::text, true);
510+
ROLLBACK TO sp_to_rollback;
511+
COMMIT;
512+
SELECT package FROM pgv_stats() ORDER BY package;
513+
514+
-- Package should exist after rollback if it contains regular variable
515+
BEGIN;
516+
SELECT pgv_set('vars', 'any1', 'some value'::text);
517+
ROLLBACK;
518+
SELECT package FROM pgv_stats() ORDER BY package;
519+
520+
-- Package should not exist if it becomes empty in rolled back transaction
521+
BEGIN;
522+
SAVEPOINT comm2;
523+
SELECT pgv_remove('vars');
524+
ROLLBACK TO comm2;
525+
SELECT pgv_exists('vars');
526+
SELECT package FROM pgv_stats() ORDER BY package;
527+
COMMIT;
528+
SELECT package FROM pgv_stats() ORDER BY package;
529+
530+
SELECT pgv_set('vars', 'any1', 'some value'::text);
531+
BEGIN;
532+
SELECT pgv_remove('vars');
533+
ROLLBACK;
534+
SELECT package FROM pgv_stats() ORDER BY package;
535+
485536
SELECT pgv_free();

0 commit comments

Comments
 (0)
Please sign in to comment.