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 649bf07

Browse files
committedJan 28, 2019
Merge branch 'master' into stable
2 parents 13afa3f + dd9f391 commit 649bf07

7 files changed

+137
-26
lines changed
 

‎expected/pg_variables.out

+14
Original file line numberDiff line numberDiff line change
@@ -553,6 +553,20 @@ SELECT pgv_insert('vars3', 'r1', row(1, 1));
553553
ERROR: new record structure differs from variable "r1" structure
554554
SELECT pgv_insert('vars3', 'r1', row('str1', 'str1'));
555555
ERROR: new record structure differs from variable "r1" structure
556+
SELECT pgv_select('vars3', 'r1') LIMIT 2;
557+
pgv_select
558+
------------
559+
(,strNULL)
560+
(1,str11)
561+
(2 rows)
562+
563+
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
564+
pgv_select
565+
------------
566+
(2,)
567+
(0,str00)
568+
(2 rows)
569+
556570
SELECT pgv_select('vars3', 'r1');
557571
pgv_select
558572
------------

‎expected/pg_variables_trans.out

+5
Original file line numberDiff line numberDiff line change
@@ -1854,6 +1854,11 @@ SELECT pgv_insert('package', 'errs',row(1), true);
18541854

18551855
(1 row)
18561856

1857+
-- Variable should not exists in case when error occurs during creation
1858+
SELECT pgv_insert('vars4', 'r1', row('str1', 'str1'));
1859+
ERROR: could not identify a hash function for type unknown
1860+
SELECT pgv_select('vars4', 'r1', 0);
1861+
ERROR: unrecognized variable "r1"
18571862
SELECT pgv_free();
18581863
pgv_free
18591864
----------

‎pg_variables.c

+98-26
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ static Variable *createVariableInternal(Package *package,
5757
text *name, Oid typid,
5858
bool is_transactional);
5959
static void removePackageInternal(Package *package);
60+
static void resetVariablesCache(bool with_package);
6061

6162
/* Functions to work with transactional objects */
6263
static void createSavepoint(TransObject *object, TransObjectType type);
@@ -67,7 +68,6 @@ static void copyValue(VarState *src, VarState *dest, Variable *destVar);
6768
static void freeValue(VarState *varstate, Oid typid);
6869
static void removeState(TransObject *object, TransObjectType type,
6970
TransState *stateToDelete);
70-
static void removeObject(TransObject *object, TransObjectType type);
7171
static bool isObjectChangedInCurrentTrans(TransObject *object);
7272
static bool isObjectChangedInUpperTrans(TransObject *object);
7373

@@ -80,6 +80,9 @@ static void makePackHTAB(Package *package, bool is_trans);
8080
static inline ChangedObject *makeChangedObject(TransObject *object,
8181
MemoryContext ctx);
8282

83+
/* Hook functions */
84+
static void variable_ExecutorEnd(QueryDesc *queryDesc);
85+
8386
#define CHECK_ARGS_FOR_NULL() \
8487
do { \
8588
if (fcinfo->argnull[0]) \
@@ -99,7 +102,19 @@ static MemoryContext ModuleContext = NULL;
99102
static Package *LastPackage = NULL;
100103
/* Recent variable */
101104
static Variable *LastVariable = NULL;
105+
/* Recent row type id */
106+
static Oid LastTypeId = InvalidOid;
107+
108+
/*
109+
* Cache sequentially search through hash table status. It is necessary for
110+
* clean up if hash_seq_term() wasn't called or if we didn't scan the whole
111+
* table. In this case we need to manually call hash_seq_term() within
112+
* variable_ExecutorEnd().
113+
*/
114+
static HASH_SEQ_STATUS *LastHSeqStatus = NULL;
102115

116+
/* Saved hook values for recall */
117+
static ExecutorEnd_hook_type prev_ExecutorEnd = NULL;
103118

104119
/* This stack contains lists of changed variables and packages per each subxact level */
105120
static dlist_head *changesStack = NULL;
@@ -291,7 +306,7 @@ variable_insert(PG_FUNCTION_ARGS)
291306

292307
Oid tupType;
293308
int32 tupTypmod;
294-
TupleDesc tupdesc;
309+
TupleDesc tupdesc = NULL;
295310
RecordVar *record;
296311

297312
/* Checks */
@@ -360,23 +375,34 @@ variable_insert(PG_FUNCTION_ARGS)
360375
/* Insert a record */
361376
tupType = HeapTupleHeaderGetTypeId(rec);
362377
tupTypmod = HeapTupleHeaderGetTypMod(rec);
363-
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
364378

365379
record = &(GetActualValue(variable).record);
366380
if (!record->tupdesc)
367381
{
368382
/*
369383
* This is the first record for the var_name. Initialize record.
370384
*/
385+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
371386
init_record(record, tupdesc, variable);
372387
}
373-
else
388+
else if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) ||
389+
LastTypeId != tupType)
390+
{
391+
/*
392+
* We need to check attributes of the new row if this is a transient
393+
* record type or if last record has different id.
394+
*/
395+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
374396
check_attributes(variable, tupdesc);
397+
}
398+
399+
LastTypeId = tupType;
375400

376401
insert_record(variable, rec);
377402

378403
/* Release resources */
379-
ReleaseTupleDesc(tupdesc);
404+
if (tupdesc)
405+
ReleaseTupleDesc(tupdesc);
380406

381407
PG_FREE_IF_COPY(package_name, 0);
382408
PG_FREE_IF_COPY(var_name, 1);
@@ -396,7 +422,6 @@ variable_update(PG_FUNCTION_ARGS)
396422
bool res;
397423
Oid tupType;
398424
int32 tupTypmod;
399-
TupleDesc tupdesc;
400425

401426
/* Checks */
402427
CHECK_ARGS_FOR_NULL();
@@ -447,14 +472,22 @@ variable_update(PG_FUNCTION_ARGS)
447472
/* Update a record */
448473
tupType = HeapTupleHeaderGetTypeId(rec);
449474
tupTypmod = HeapTupleHeaderGetTypMod(rec);
450-
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
451475

452-
check_attributes(variable, tupdesc);
476+
if (LastTypeId == RECORDOID || !OidIsValid(LastTypeId) ||
477+
LastTypeId != tupType)
478+
{
479+
TupleDesc tupdesc = NULL;
480+
481+
tupdesc = lookup_rowtype_tupdesc(tupType, tupTypmod);
482+
check_attributes(variable, tupdesc);
483+
ReleaseTupleDesc(tupdesc);
484+
}
485+
486+
LastTypeId = tupType;
487+
453488
res = update_record(variable, rec);
454489

455490
/* Release resources */
456-
ReleaseTupleDesc(tupdesc);
457-
458491
PG_FREE_IF_COPY(package_name, 0);
459492
PG_FREE_IF_COPY(var_name, 1);
460493

@@ -575,6 +608,8 @@ variable_select(PG_FUNCTION_ARGS)
575608
MemoryContextSwitchTo(oldcontext);
576609
PG_FREE_IF_COPY(package_name, 0);
577610
PG_FREE_IF_COPY(var_name, 1);
611+
612+
LastHSeqStatus = rstat;
578613
}
579614

580615
funcctx = SRF_PERCALL_SETUP();
@@ -592,6 +627,7 @@ variable_select(PG_FUNCTION_ARGS)
592627
}
593628
else
594629
{
630+
LastHSeqStatus = NULL;
595631
pfree(rstat);
596632
SRF_RETURN_DONE(funcctx);
597633
}
@@ -867,8 +903,7 @@ remove_variable(PG_FUNCTION_ARGS)
867903
GetActualState(variable)->is_valid = false;
868904
}
869905

870-
/* Remove variable from cache */
871-
LastVariable = NULL;
906+
resetVariablesCache(false);
872907

873908
PG_FREE_IF_COPY(package_name, 0);
874909
PG_FREE_IF_COPY(var_name, 1);
@@ -904,9 +939,7 @@ remove_package(PG_FUNCTION_ARGS)
904939
errmsg("unrecognized package \"%s\"", key)));
905940
}
906941

907-
/* Remove package and variable from cache */
908-
LastPackage = NULL;
909-
LastVariable = NULL;
942+
resetVariablesCache(true);
910943

911944
PG_FREE_IF_COPY(package_name, 0);
912945
PG_RETURN_VOID();
@@ -934,6 +967,20 @@ removePackageInternal(Package *package)
934967
GetActualState(package)->is_valid = false;
935968
}
936969

970+
/*
971+
* Reset cache variables to their default values. It is necessary to do in case
972+
* of some changes: removing, rollbacking, etc.
973+
*/
974+
static void
975+
resetVariablesCache(bool with_package)
976+
{
977+
/* Remove package and variable from cache */
978+
if (with_package)
979+
LastPackage = NULL;
980+
LastVariable = NULL;
981+
LastTypeId = InvalidOid;
982+
}
983+
937984
/*
938985
* Remove all packages and variables.
939986
* Memory context will be released after committing.
@@ -955,9 +1002,7 @@ remove_packages(PG_FUNCTION_ARGS)
9551002
removePackageInternal(package);
9561003
}
9571004

958-
/* Remove package and variable from cache */
959-
LastPackage = NULL;
960-
LastVariable = NULL;
1005+
resetVariablesCache(true);
9611006

9621007
PG_RETURN_VOID();
9631008
}
@@ -1157,6 +1202,8 @@ get_packages_stats(PG_FUNCTION_ARGS)
11571202
hash_seq_init(pstat, packagesHash);
11581203

11591204
funcctx->user_fctx = pstat;
1205+
1206+
LastHSeqStatus = pstat;
11601207
}
11611208
else
11621209
funcctx->user_fctx = NULL;
@@ -1202,6 +1249,7 @@ get_packages_stats(PG_FUNCTION_ARGS)
12021249
}
12031250
else
12041251
{
1252+
LastHSeqStatus = NULL;
12051253
pfree(pstat);
12061254
SRF_RETURN_DONE(funcctx);
12071255
}
@@ -1577,13 +1625,14 @@ copyValue(VarState *src, VarState *dest, Variable *destVar)
15771625
static void
15781626
freeValue(VarState *varstate, Oid typid)
15791627
{
1580-
if (typid == RECORDOID)
1628+
if (typid == RECORDOID && varstate->value.record.hctx)
15811629
{
15821630
/* All records will be freed */
15831631
MemoryContextDelete(varstate->value.record.hctx);
15841632
}
15851633
else if (varstate->value.scalar.typbyval == false &&
1586-
varstate->value.scalar.is_null == false)
1634+
varstate->value.scalar.is_null == false &&
1635+
varstate->value.scalar.value)
15871636
{
15881637
pfree(DatumGetPointer(varstate->value.scalar.value));
15891638
}
@@ -1602,7 +1651,8 @@ removeState(TransObject *object, TransObjectType type, TransState *stateToDelete
16021651
pfree(stateToDelete);
16031652
}
16041653

1605-
static void
1654+
/* Remove package or variable (either transactional or regular) */
1655+
void
16061656
removeObject(TransObject *object, TransObjectType type)
16071657
{
16081658
bool found;
@@ -1623,7 +1673,9 @@ removeObject(TransObject *object, TransObjectType type)
16231673
hash = packagesHash;
16241674
}
16251675
else
1626-
hash = ((Variable *) object)->package->varHashTransact;
1676+
hash = ((Variable *) object)->is_transactional ?
1677+
((Variable *) object)->package->varHashTransact :
1678+
((Variable *) object)->package->varHashRegular;
16271679

16281680
/* Remove all object's states */
16291681
while (!dlist_is_empty(&object->states))
@@ -1632,8 +1684,7 @@ removeObject(TransObject *object, TransObjectType type)
16321684
/* Remove object from hash table */
16331685
hash_search(hash, object->name, HASH_REMOVE, &found);
16341686

1635-
LastPackage = NULL;
1636-
LastVariable = NULL;
1687+
resetVariablesCache(true);
16371688
}
16381689

16391690
/*
@@ -2004,8 +2055,7 @@ processChanges(Action action)
20042055
MemoryContextDelete(ModuleContext);
20052056
packagesHash = NULL;
20062057
ModuleContext = NULL;
2007-
LastPackage = NULL;
2008-
LastVariable = NULL;
2058+
resetVariablesCache(true);
20092059
changesStack = NULL;
20102060
changesStackContext = NULL;
20112061
}
@@ -2065,6 +2115,23 @@ pgvTransCallback(XactEvent event, void *arg)
20652115
}
20662116
}
20672117

2118+
/*
2119+
* ExecutorEnd hook: clean up hash table sequential scan status
2120+
*/
2121+
static void
2122+
variable_ExecutorEnd(QueryDesc *queryDesc)
2123+
{
2124+
if (LastHSeqStatus)
2125+
{
2126+
hash_seq_term(LastHSeqStatus);
2127+
LastHSeqStatus = NULL;
2128+
}
2129+
if (prev_ExecutorEnd)
2130+
prev_ExecutorEnd(queryDesc);
2131+
else
2132+
standard_ExecutorEnd(queryDesc);
2133+
}
2134+
20682135
/*
20692136
* Register callback function when module starts
20702137
*/
@@ -2073,6 +2140,10 @@ _PG_init(void)
20732140
{
20742141
RegisterXactCallback(pgvTransCallback, NULL);
20752142
RegisterSubXactCallback(pgvSubTransCallback, NULL);
2143+
2144+
/* Install hooks. */
2145+
prev_ExecutorEnd = ExecutorEnd_hook;
2146+
ExecutorEnd_hook = variable_ExecutorEnd;
20762147
}
20772148

20782149
/*
@@ -2083,4 +2154,5 @@ _PG_fini(void)
20832154
{
20842155
UnregisterXactCallback(pgvTransCallback, NULL);
20852156
UnregisterSubXactCallback(pgvSubTransCallback, NULL);
2157+
ExecutorEnd_hook = prev_ExecutorEnd;
20862158
}

‎pg_variables.h

+1
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,7 @@ extern void check_record_key(Variable *variable, Oid typid);
155155
extern void insert_record(Variable *variable, HeapTupleHeader tupleHeader);
156156
extern bool update_record(Variable *variable, HeapTupleHeader tupleHeader);
157157
extern bool delete_record(Variable *variable, Datum value, bool is_null);
158+
extern void removeObject(TransObject *object, TransObjectType type);
158159

159160
#define GetActualState(object) \
160161
(dlist_head_element(TransState, node, &((TransObject *) object)->states))

‎pg_variables_record.c

+12
Original file line numberDiff line numberDiff line change
@@ -81,17 +81,29 @@ init_record(RecordVar *record, TupleDesc tupdesc, Variable *variable)
8181
TYPECACHE_HASH_PROC_FINFO |
8282
TYPECACHE_CMP_PROC_FINFO);
8383

84+
/*
85+
* In case something went wrong, you need to roll back the changes before
86+
* completing the transaction, because the variable may be regular
87+
* and not present in list of changed vars.
88+
*/
8489
if (!OidIsValid(typentry->hash_proc_finfo.fn_oid))
90+
{
91+
/* At this point variable is just created, so we simply remove it. */
92+
removeObject(&variable->transObject, TRANS_VARIABLE);
8593
ereport(ERROR,
8694
(errcode(ERRCODE_UNDEFINED_FUNCTION),
8795
errmsg("could not identify a hash function for type %s",
8896
format_type_be(keyid))));
97+
}
8998

9099
if (!OidIsValid(typentry->cmp_proc_finfo.fn_oid))
100+
{
101+
removeObject(&variable->transObject, TRANS_VARIABLE);
91102
ereport(ERROR,
92103
(errcode(ERRCODE_UNDEFINED_FUNCTION),
93104
errmsg("could not identify a matching function for type %s",
94105
format_type_be(keyid))));
106+
}
95107

96108
/* Initialize the record */
97109

‎sql/pg_variables.sql

+3
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ SELECT pgv_insert('vars3', 'r1', row(1, 'str1', 'str2'));
153153
SELECT pgv_insert('vars3', 'r1', row(1, 1));
154154
SELECT pgv_insert('vars3', 'r1', row('str1', 'str1'));
155155

156+
SELECT pgv_select('vars3', 'r1') LIMIT 2;
157+
SELECT pgv_select('vars3', 'r1') LIMIT 2 OFFSET 2;
158+
156159
SELECT pgv_select('vars3', 'r1');
157160
SELECT pgv_select('vars3', 'r1', 1);
158161
SELECT pgv_select('vars3', 'r1', 0);

0 commit comments

Comments
 (0)