@@ -76,7 +76,8 @@ static HashVariableEntry *createVariableInternal(HashPackageEntry *package,
7676 text * name , Oid typid ,
7777 bool is_transactional );
7878static void createSavepoint (HashPackageEntry * package , HashVariableEntry * variable );
79- static bool isVarChangedInTrans (HashVariableEntry * variable );
79+ static bool isVarChangedInCurrentTrans (HashVariableEntry * variable );
80+ static bool isVarChangedInUpperTrans (HashVariableEntry * variable );
8081static void addToChangedVars (HashPackageEntry * package , HashVariableEntry * variable );
8182
8283#define CHECK_ARGS_FOR_NULL () \
@@ -100,12 +101,11 @@ static HashPackageEntry *LastPackage = NULL;
100101static HashVariableEntry * LastVariable = NULL ;
101102
102103/*
103- * List of variables, changed in top level transaction. Used to limit
104+ * Stack of lists of variables, changed in each transaction level . Used to limit
104105 * number of proceeded variables on start of transaction.
105106 */
106- static dlist_head * changedVars = NULL ;
107- static MemoryContext changedVarsContext = NULL ;
108107static dlist_head * changedVarsStack = NULL ;
108+ static MemoryContext changedVarsContext = NULL ;
109109#define get_actual_changed_vars_list () \
110110 ((dlist_head_element(ChangedVarsStackNode, node, changedVarsStack))-> \
111111 changedVarsList)
@@ -621,7 +621,7 @@ variable_insert(PG_FUNCTION_ARGS)
621621 errmsg ("variable \"%s\" already created as %sTRANSACTIONAL" ,
622622 key , LastVariable -> is_transactional ? "" : "NOT " )));
623623 }
624- if (!isVarChangedInTrans (variable ) && variable -> is_transactional )
624+ if (!isVarChangedInCurrentTrans (variable ) && variable -> is_transactional )
625625 {
626626 createSavepoint (package , variable );
627627 addToChangedVars (package , variable );
@@ -707,7 +707,7 @@ variable_update(PG_FUNCTION_ARGS)
707707 else
708708 variable = LastVariable ;
709709
710- if (variable -> is_transactional && !isVarChangedInTrans (variable ))
710+ if (variable -> is_transactional && !isVarChangedInCurrentTrans (variable ))
711711 {
712712 createSavepoint (package , variable );
713713 addToChangedVars (package , variable );
@@ -785,7 +785,7 @@ variable_delete(PG_FUNCTION_ARGS)
785785 else
786786 variable = LastVariable ;
787787
788- if (variable -> is_transactional && !isVarChangedInTrans (variable ))
788+ if (variable -> is_transactional && !isVarChangedInCurrentTrans (variable ))
789789 {
790790 createSavepoint (package , variable );
791791 addToChangedVars (package , variable );
@@ -1220,7 +1220,7 @@ remove_packages(PG_FUNCTION_ARGS)
12201220
12211221 packagesHash = NULL ;
12221222 ModuleContext = NULL ;
1223- changedVars = NULL ;
1223+ changedVarsStack = NULL ;
12241224
12251225 PG_RETURN_VOID ();
12261226}
@@ -1663,7 +1663,7 @@ createVariableInternal(HashPackageEntry *package, text *name, Oid typid,
16631663 * For each transaction level there should be own savepoint.
16641664 * New value should be stored in a last state.
16651665 */
1666- if (variable -> is_transactional && !isVarChangedInTrans (variable ))
1666+ if (variable -> is_transactional && !isVarChangedInCurrentTrans (variable ))
16671667 {
16681668 createSavepoint (package , variable );
16691669 }
@@ -1769,6 +1769,11 @@ releaseSavepoint(HashVariableEntry *variable)
17691769 dlist_delete (nodeToDelete );
17701770 pfree (historyEntryToDelete );
17711771 }
1772+ /*
1773+ * If variable was changed in subtransaction, so it is considered it
1774+ * was changed in parent transaction.
1775+ */
1776+ (get_actual_value (variable )-> level )-- ;
17721777}
17731778
17741779/*
@@ -1792,22 +1797,32 @@ rollbackSavepoint(HashPackageEntry *package, HashVariableEntry *variable)
17921797 * Check if variable was changed in current transaction level
17931798 */
17941799static bool
1795- isVarChangedInTrans (HashVariableEntry * variable )
1800+ isVarChangedInCurrentTrans (HashVariableEntry * variable )
17961801{
1797- dlist_iter iter ;
1798- dlist_head * changedVars ;
1802+ ValueHistoryEntry * var_state ;
17991803
18001804 if (!changedVarsStack )
18011805 return false;
18021806
1803- changedVars = get_actual_changed_vars_list ();
1804- dlist_foreach (iter , changedVars )
1805- {
1806- ChangedVarsNode * cvn ;
1807+ var_state = get_actual_value (variable );
1808+ return (var_state -> level == GetCurrentTransactionNestLevel ());
1809+ }
18071810
1808- cvn = dlist_container (ChangedVarsNode , node , iter .cur );
1809- if (cvn -> variable == variable )
1810- return true;
1811+ /*
1812+ * Check if variable was changed in parent transaction level
1813+ */
1814+ static bool
1815+ isVarChangedInUpperTrans (HashVariableEntry * variable )
1816+ {
1817+ ValueHistoryEntry * var_state ,
1818+ * var_prev_state ;
1819+
1820+ var_state = get_actual_value (variable );
1821+
1822+ if (dlist_has_next (& variable -> data , & var_state -> node ))
1823+ {
1824+ var_prev_state = get_history_entry (var_state -> node .next );
1825+ return (var_prev_state -> level == (GetCurrentTransactionNestLevel () - 1 ));
18111826 }
18121827 return false;
18131828}
@@ -1890,12 +1905,12 @@ popChangedVarsStack()
18901905{
18911906 if (changedVarsStack )
18921907 {
1893- ChangedVarsStackNode * cvse ;
1908+ ChangedVarsStackNode * cvsn ;
18941909
18951910 Assert (!dlist_is_empty (changedVarsStack ));
1896- cvse = dlist_container (ChangedVarsStackNode , node ,
1911+ cvsn = dlist_container (ChangedVarsStackNode , node ,
18971912 dlist_pop_head_node (changedVarsStack ));
1898- MemoryContextDelete (cvse -> ctx );
1913+ MemoryContextDelete (cvsn -> ctx );
18991914 if (dlist_is_empty (changedVarsStack ))
19001915 {
19011916 MemoryContextDelete (changedVarsContext );
@@ -1905,6 +1920,20 @@ popChangedVarsStack()
19051920 }
19061921}
19071922
1923+ /*
1924+ * Initialize an instance of ChangedVarsNode datatype
1925+ */
1926+ static inline ChangedVarsNode *
1927+ initChangedVarsNode (MemoryContext ctx , HashPackageEntry * package , HashVariableEntry * variable )
1928+ {
1929+ ChangedVarsNode * cvn ;
1930+
1931+ cvn = MemoryContextAllocZero (ctx , sizeof (ChangedVarsNode ));
1932+ cvn -> package = package ;
1933+ cvn -> variable = variable ;
1934+ return cvn ;
1935+ }
1936+
19081937/*
19091938 * Add a variable to list of changed vars in current transaction level
19101939 */
@@ -1925,20 +1954,19 @@ addToChangedVars(HashPackageEntry *package, HashVariableEntry *variable)
19251954
19261955 Assert (changedVarsStack && changedVarsContext );
19271956
1928- if (!isVarChangedInTrans (variable ))
1957+ if (!isVarChangedInCurrentTrans (variable ))
19291958 {
19301959 ChangedVarsNode * cvn ;
19311960
19321961 cvsn = dlist_head_element (ChangedVarsStackNode , node , changedVarsStack );
1933- cvn = MemoryContextAllocZero (cvsn -> ctx , sizeof (ChangedVarsNode ));
1934- cvn -> package = package ;
1935- cvn -> variable = variable ;
1962+ cvn = initChangedVarsNode (cvsn -> ctx , package , variable );
19361963 dlist_push_head (cvsn -> changedVarsList , & cvn -> node );
1964+ get_actual_value (cvn -> variable )-> level = GetCurrentTransactionNestLevel ();
19371965 }
19381966}
19391967
19401968/*
1941- * If variable was chenged in some subtransaction, it is considered that it was
1969+ * If variable was changed in some subtransaction, it is considered that it was
19421970 * changed in parent transaction. So it is important to add this variable to
19431971 * list of changes of parent transaction. But if var was already changed in
19441972 * upper level, it has savepoint there, so we need to release it.
@@ -1957,13 +1985,25 @@ levelUpOrRelease()
19571985 Assert (!dlist_is_empty (changedVarsStack ));
19581986 dlist_foreach (iter , bottom_list -> changedVarsList )
19591987 {
1960- ChangedVarsNode * cvn ;
1988+ ChangedVarsNode * cvn_old ;
19611989
1962- cvn = dlist_container (ChangedVarsNode , node , iter .cur );
1963- if (isVarChangedInTrans ( cvn -> variable ))
1964- releaseSavepoint (cvn -> variable );
1990+ cvn_old = dlist_container (ChangedVarsNode , node , iter .cur );
1991+ if (isVarChangedInUpperTrans ( cvn_old -> variable ))
1992+ releaseSavepoint (cvn_old -> variable );
19651993 else
1966- addToChangedVars (cvn -> package , cvn -> variable );
1994+ {
1995+ ChangedVarsNode * cvn_new ;
1996+ ChangedVarsStackNode * cvsn ;
1997+
1998+ /*
1999+ * Impossible to push in upper list existing node because
2000+ * it was created in another context
2001+ */
2002+ cvsn = dlist_head_element (ChangedVarsStackNode , node , changedVarsStack );
2003+ cvn_new = initChangedVarsNode (cvsn -> ctx , cvn_old -> package , cvn_old -> variable );
2004+ dlist_push_head (cvsn -> changedVarsList , & cvn_new -> node );
2005+ (get_actual_value (cvn_new -> variable )-> level )-- ;
2006+ }
19672007 }
19682008 MemoryContextDelete (bottom_list -> ctx );
19692009 }
0 commit comments