6995-6129c.patch
new/src/com/goldencode/p2j/persist/Persistence.java 2023-02-08 14:13:43 +0000 | ||
---|---|---|
584 | 584 |
** OM 20221103 New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache. |
585 | 585 |
** CA 20221109 Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded |
586 | 586 |
** as 'partial/incomplete'. |
587 |
** RAA 20230208 Removed the checks for no-undo as they are no longer necessary. |
|
587 | 588 |
*/ |
588 | 589 | |
589 | 590 |
/* |
... | ... | |
2871 | 2872 |
|
2872 | 2873 |
Query query = getQuery(local, fql, 0, 0, values, false); |
2873 | 2874 |
count = query.executeUpdate(session); |
2874 |
if (noUndo) |
|
2875 |
{ |
|
2876 |
session.noUndoBulkUpdate(query); |
|
2877 |
} |
|
2878 | 2875 |
|
2879 | 2876 |
if (LOG.isLoggable(Level.FINER)) |
2880 | 2877 |
{ |
... | ... | |
3477 | 3474 |
|
3478 | 3475 |
rc = query.executeUpdate(session); |
3479 | 3476 |
|
3480 |
if (noUndo) |
|
3481 |
{ |
|
3482 |
session.noUndoBulkUpdate(query); |
|
3483 |
} |
|
3484 |
|
|
3485 | 3477 |
if (LOG.isLoggable(Level.FINER)) |
3486 | 3478 |
{ |
3487 | 3479 |
LOG.log(Level.FINER, "[" + sql + "] rc = " + rc); |
new/src/com/goldencode/p2j/persist/TempTableHelper.java 2023-02-08 14:30:56 +0000 | ||
---|---|---|
54 | 54 |
** OM 20221027 H2 dialect requires that the index names to be truly unique. |
55 | 55 |
** SVL 20230110 P2JIndex.components() provides direct access to the components array in order to |
56 | 56 |
** improve performance. |
57 |
** RAA 20230208 Added "no-undo" as a temp table postfix in constructor. |
|
57 | 58 |
*/ |
58 | 59 | |
59 | 60 |
/* |
... | ... | |
391 | 392 |
String primaryIndexName = null; |
392 | 393 |
String primarySqlIndexName = null; |
393 | 394 |
|
394 |
String createTemporaryTablePostfix = dialect.getCreateTemporaryTablePostfix(); |
|
395 |
String createTemporaryTablePostfix = dialect.getCreateTemporaryTablePostfix(false); |
|
396 |
String createTemporaryNoUndoTablePostfix = dialect.getCreateTemporaryTablePostfix(true); |
|
395 | 397 |
String delimiter = dialect.getDelimiter(); |
396 | 398 |
String suffix = createTemporaryTablePostfix + delimiter; |
397 | 399 |
while (databaseIndexes.hasNext()) |
... | ... | |
622 | 624 |
{ |
623 | 625 |
table.append(tab).append("primary key (").append(Session.PK).append(")\n"); |
624 | 626 |
} |
625 |
table.append(")").append(createTemporaryTablePostfix).append(delimiter); |
|
627 |
|
|
628 |
table.append(")").append(createTemporaryTablePostfix); |
|
629 |
if (dmo.noUndo) |
|
630 |
{ |
|
631 |
table.append(createTemporaryNoUndoTablePostfix); |
|
632 |
} |
|
633 |
|
|
634 |
table.append(delimiter); |
|
626 | 635 |
|
627 | 636 |
String tableDDL = table.toString(); |
628 | 637 |
ddlCreateTables.add(tableDDL); |
new/src/com/goldencode/p2j/persist/dialect/Dialect.java 2023-02-08 14:31:55 +0000 | ||
---|---|---|
95 | 95 |
** SVL 20230110 P2JIndex.components() provides direct access to the components array in order to |
96 | 96 |
** improve performance. |
97 | 97 |
** ECF 20230127 Added resolveBooleanConstant method. |
98 |
** RAA 20230208 Modified function for creating a temp table postfix. Depending on the parameter, |
|
99 |
** it can resume its initial purpose or it can return the keyword specific to |
|
100 |
** no-undo temp-tables. |
|
98 | 101 |
*/ |
99 | 102 | |
100 | 103 |
/* |
... | ... | |
1312 | 1315 |
/** |
1313 | 1316 |
* Return the ending of the DDL statement that creates a temporary table. |
1314 | 1317 |
* |
1318 |
* @param noUndo |
|
1319 |
* If {@code true}, this function will return the keyword specific to no-undo temp-tables. |
|
1320 |
* |
|
1315 | 1321 |
* @return A string representing the ending of the DDL statement that creates a temporary |
1316 | 1322 |
* table, if any, or empty string otherwise. |
1317 | 1323 |
*/ |
1318 |
public abstract String getCreateTemporaryTablePostfix(); |
|
1324 |
public abstract String getCreateTemporaryTablePostfix(boolean noUndo);
|
|
1319 | 1325 |
|
1320 | 1326 |
/** |
1321 | 1327 |
* Returns the beginning of the DDL statement for creating an index. |
new/src/com/goldencode/p2j/persist/dialect/MariaDbLenientDialect.java 2023-02-08 14:24:51 +0000 | ||
---|---|---|
14 | 14 |
** BS 20230126 Added support for SAVE CACHE. |
15 | 15 |
** ECF 20230127 Data type adjustment for SAVE CACHE. |
16 | 16 |
** OM 20230131 Code cleanup. |
17 |
** RAA 20230208 Added parameter for getCreateTemporaryTablePostfix. |
|
17 | 18 |
*/ |
18 | 19 | |
19 | 20 |
/* |
... | ... | |
1471 | 1472 |
/** |
1472 | 1473 |
* Return the ending of the DDL statement that creates a temporary table. |
1473 | 1474 |
* |
1475 |
* @param noUndo |
|
1476 |
* This parameter is not used at the moment. The intention was to generate a keyword for |
|
1477 |
* no-undo tables. Only {@link P2JH2Dialect} benefits from this parameter. |
|
1478 |
* |
|
1474 | 1479 |
* @return A string representing the ending of the DDL statement that creates a temporary table, if any, |
1475 | 1480 |
* or empty string otherwise. |
1476 | 1481 |
* |
1477 | 1482 |
* @see <a href="https://mariadb.com/kb/en/create-table/#transactional">CREATE TABLE TRANSACTIONAL</a> |
1478 | 1483 |
*/ |
1479 | 1484 |
@Override |
1480 |
public String getCreateTemporaryTablePostfix() |
|
1485 |
public String getCreateTemporaryTablePostfix(boolean noUndo)
|
|
1481 | 1486 |
{ |
1482 | 1487 |
return " transactional"; |
1483 | 1488 |
} |
new/src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java 2023-02-08 14:26:29 +0000 | ||
---|---|---|
106 | 106 |
** OM 20221026 Excluded table name from index name by default. It will be added as needed by |
107 | 107 |
** dialects. |
108 | 108 |
** RAA 20230109 Overrided methods getSequenceSetValString() and getSequenceCurrValString(). |
109 |
** RAA 20230208 Overrided method for returning the postfix of a no-undo temp-table. |
|
109 | 110 |
*/ |
110 | 111 | |
111 | 112 |
/* |
... | ... | |
1321 | 1322 |
/** |
1322 | 1323 |
* Return the ending of the DDL statement that creates a temporary table. |
1323 | 1324 |
* |
1325 |
* @param noUndo |
|
1326 |
* Parameter that indicates what keyword should be returned. The choices are "transactional" |
|
1327 |
* and "noundo". |
|
1328 |
* |
|
1324 | 1329 |
* @return A string representing the ending of the DDL statement that creates a temporary |
1325 | 1330 |
* table. |
1326 | 1331 |
*/ |
1327 | 1332 |
@Override |
1328 |
public String getCreateTemporaryTablePostfix() |
|
1333 |
public String getCreateTemporaryTablePostfix(boolean noUndo)
|
|
1329 | 1334 |
{ |
1330 |
return " transactional"; |
|
1335 |
if (!noUndo) |
|
1336 |
{ |
|
1337 |
return " transactional"; |
|
1338 |
} |
|
1339 |
|
|
1340 |
return " noundo"; |
|
1331 | 1341 |
} |
1332 | 1342 |
|
1333 | 1343 |
/** |
new/src/com/goldencode/p2j/persist/dialect/P2JPostgreSQLDialect.java 2023-02-08 14:27:50 +0000 | ||
---|---|---|
123 | 123 |
** OM 20221026 Excluded table name from index name by default. It will be added as needed by |
124 | 124 |
** dialects. |
125 | 125 |
** RAA 20230109 Overrided method getSequenceSetValString(). |
126 |
** RAA 20230208 Added parameter for getCreateTemporaryTablePostfix. |
|
126 | 127 |
*/ |
127 | 128 | |
128 | 129 |
/* |
... | ... | |
1679 | 1680 |
/** |
1680 | 1681 |
* Return the ending of the DDL statement that creates a temporary table. |
1681 | 1682 |
* |
1683 |
* @param noUndo |
|
1684 |
* This parameter is not used at the moment. The intention was to generate a keyword for |
|
1685 |
* no-undo tables. Only {@link P2JH2Dialect} benefits from this parameter. |
|
1686 |
* |
|
1682 | 1687 |
* @return A string representing the ending of the DDL statement that creates a temporary |
1683 | 1688 |
* table. |
1684 | 1689 |
*/ |
1685 | 1690 |
@Override |
1686 |
public String getCreateTemporaryTablePostfix() |
|
1691 |
public String getCreateTemporaryTablePostfix(boolean noUndo)
|
|
1687 | 1692 |
{ |
1688 | 1693 |
return " on commit drop"; |
1689 | 1694 |
} |
new/src/com/goldencode/p2j/persist/dialect/P2JSQLServer2008Dialect.java 2023-02-08 14:28:55 +0000 | ||
---|---|---|
43 | 43 |
** RAA 20230109 Overrided method getSequenceSetValString(). |
44 | 44 |
** SVL 20230110 P2JIndex.components() provides direct access to the components array in order to improve |
45 | 45 |
** performance. |
46 |
** RAA 20230208 Added parameter for getCreateTemporaryTablePostfix. |
|
46 | 47 |
*/ |
47 | 48 | |
48 | 49 |
/* |
... | ... | |
1163 | 1164 |
/** |
1164 | 1165 |
* Return the ending of the DDL statement that creates a temporary table. |
1165 | 1166 |
* |
1167 |
* @param noUndo |
|
1168 |
* This parameter is not used at the moment. The intention was to generate a keyword for |
|
1169 |
* no-undo tables. Only {@link P2JH2Dialect} benefits from this parameter. |
|
1170 |
* |
|
1166 | 1171 |
* @return An empty string. This dialect does not support this feature. |
1167 | 1172 |
*/ |
1168 | 1173 |
@Override |
1169 |
public String getCreateTemporaryTablePostfix() |
|
1174 |
public String getCreateTemporaryTablePostfix(boolean noUndo)
|
|
1170 | 1175 |
{ |
1171 | 1176 |
return ""; |
1172 | 1177 |
} |
new/src/com/goldencode/p2j/persist/orm/SQLRedo.java 1970-01-01 00:00:00 +0000 | ||
---|---|---|
1 |
/* |
|
2 |
** Module : SQLRedo.java |
|
3 |
** Abstract : Redoable SQL operation for NO-UNDO temp-table use. |
|
4 |
** |
|
5 |
** Copyright (c) 2020, Golden Code Development Corporation. |
|
6 |
** |
|
7 |
** -#- -I- --Date-- ---------------------------------Description--------------------------------- |
|
8 |
** 001 ECF 20200413 First revision. A redoable SQL operation for use with NO-UNDO temp-tables. |
|
9 |
*/ |
|
10 | ||
11 |
/* |
|
12 |
** This program is free software: you can redistribute it and/or modify |
|
13 |
** it under the terms of the GNU Affero General Public License as |
|
14 |
** published by the Free Software Foundation, either version 3 of the |
|
15 |
** License, or (at your option) any later version. |
|
16 |
** |
|
17 |
** This program is distributed in the hope that it will be useful, |
|
18 |
** but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
19 |
** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
20 |
** GNU Affero General Public License for more details. |
|
21 |
** |
|
22 |
** You may find a copy of the GNU Affero GPL version 3 at the following |
|
23 |
** location: https://www.gnu.org/licenses/agpl-3.0.en.html |
|
24 |
** |
|
25 |
** Additional terms under GNU Affero GPL version 3 section 7: |
|
26 |
** |
|
27 |
** Under Section 7 of the GNU Affero GPL version 3, the following additional |
|
28 |
** terms apply to the works covered under the License. These additional terms |
|
29 |
** are non-permissive additional terms allowed under Section 7 of the GNU |
|
30 |
** Affero GPL version 3 and may not be removed by you. |
|
31 |
** |
|
32 |
** 0. Attribution Requirement. |
|
33 |
** |
|
34 |
** You must preserve all legal notices or author attributions in the covered |
|
35 |
** work or Appropriate Legal Notices displayed by works containing the covered |
|
36 |
** work. You may not remove from the covered work any author or developer |
|
37 |
** credit already included within the covered work. |
|
38 |
** |
|
39 |
** 1. No License To Use Trademarks. |
|
40 |
** |
|
41 |
** This license does not grant any license or rights to use the trademarks |
|
42 |
** Golden Code, FWD, any Golden Code or FWD logo, or any other trademarks |
|
43 |
** of Golden Code Development Corporation. You are not authorized to use the |
|
44 |
** name Golden Code, FWD, or the names of any author or contributor, for |
|
45 |
** publicity purposes without written authorization. |
|
46 |
** |
|
47 |
** 2. No Misrepresentation of Affiliation. |
|
48 |
** |
|
49 |
** You may not represent yourself as Golden Code Development Corporation or FWD. |
|
50 |
** |
|
51 |
** You may not represent yourself for publicity purposes as associated with |
|
52 |
** Golden Code Development Corporation, FWD, or any author or contributor to |
|
53 |
** the covered work, without written authorization. |
|
54 |
** |
|
55 |
** 3. No Misrepresentation of Source or Origin. |
|
56 |
** |
|
57 |
** You may not represent the covered work as solely your work. All modified |
|
58 |
** versions of the covered work must be marked in a reasonable way to make it |
|
59 |
** clear that the modified work is not originating from Golden Code Development |
|
60 |
** Corporation or FWD. All modified versions must contain the notices of |
|
61 |
** attribution required in this license. |
|
62 |
*/ |
|
63 | ||
64 |
package com.goldencode.p2j.persist.orm; |
|
65 | ||
66 |
import com.goldencode.p2j.persist.*; |
|
67 | ||
68 |
/** |
|
69 |
* This class re-applies a single database insert, update, or [bulk] delete action, in order to |
|
70 |
* redo such an action that was undone by a database rollback (savepoint or full transaction). |
|
71 |
* It is needed to implement the NO-UNDO feature of 4GL temp-tables. |
|
72 |
* <p> |
|
73 |
* The top-level class is abstract. To use this class, instantiate one of the specialized, |
|
74 |
* concrete, inner subclasses. |
|
75 |
*/ |
|
76 |
abstract class SQLRedo |
|
77 |
{ |
|
78 |
/** Lambda which performs the redo action and possibly throws a persistence exception */ |
|
79 |
private final ThrowingAction action; |
|
80 |
|
|
81 |
/** Flag indicating that the original action was rolled back and redo is required */ |
|
82 |
private boolean active = false; |
|
83 |
|
|
84 |
/** |
|
85 |
* Constructor. |
|
86 |
* |
|
87 |
* @param action |
|
88 |
* Lambda which performs the redo action and possibly throws a persistence exception. |
|
89 |
*/ |
|
90 |
protected SQLRedo(ThrowingAction action) |
|
91 |
{ |
|
92 |
this.action = action; |
|
93 |
} |
|
94 |
|
|
95 |
/** |
|
96 |
* Mark this redo action as active, meaning the original action which this object was undone |
|
97 |
* by a full or sub-transaction rollback, and needs to be re-applied. If an instance of this |
|
98 |
* class is not marked active, it is a no-op. |
|
99 |
*/ |
|
100 |
void setActive() |
|
101 |
{ |
|
102 |
active = true; |
|
103 |
} |
|
104 |
|
|
105 |
/** |
|
106 |
* Perform this object's redo action, if it was marked active. |
|
107 |
* |
|
108 |
* @throws PersistenceException |
|
109 |
* if there is an error executing the action. |
|
110 |
*/ |
|
111 |
void perform() |
|
112 |
throws PersistenceException |
|
113 |
{ |
|
114 |
if (active) |
|
115 |
{ |
|
116 |
action.run(); |
|
117 |
} |
|
118 |
} |
|
119 |
|
|
120 |
/** |
|
121 |
* A no-arg functional interface which throws PersistenceException, to avoid having to handle |
|
122 |
* this exception in every defined lambda expression. |
|
123 |
*/ |
|
124 |
@FunctionalInterface |
|
125 |
protected interface ThrowingAction |
|
126 |
{ |
|
127 |
/** |
|
128 |
* Run a SQL operation. |
|
129 |
* |
|
130 |
* @throws PersistenceException |
|
131 |
* if a persistence error occurs. |
|
132 |
*/ |
|
133 |
public void run() |
|
134 |
throws PersistenceException; |
|
135 |
} |
|
136 |
|
|
137 |
/** |
|
138 |
* An insert action for a single record. |
|
139 |
*/ |
|
140 |
static class Insert |
|
141 |
extends SQLRedo |
|
142 |
{ |
|
143 |
Insert(Persister persister, BaseRecord dmo) |
|
144 |
{ |
|
145 |
super(() -> persister.insert(dmo)); |
|
146 |
} |
|
147 |
} |
|
148 |
|
|
149 |
/** |
|
150 |
* An update action for a single record. |
|
151 |
*/ |
|
152 |
static class Update |
|
153 |
extends SQLRedo |
|
154 |
{ |
|
155 |
Update(Persister persister, BaseRecord dmo) |
|
156 |
{ |
|
157 |
super(() -> persister.update(dmo)); |
|
158 |
} |
|
159 |
} |
|
160 |
|
|
161 |
/** |
|
162 |
* A delete action for a single record. |
|
163 |
*/ |
|
164 |
static class Delete |
|
165 |
extends SQLRedo |
|
166 |
{ |
|
167 |
Delete(Persister persister, Class<? extends BaseRecord> dmoClass, Long id) |
|
168 |
{ |
|
169 |
super(() -> persister.delete(dmoClass, id)); |
|
170 |
} |
|
171 |
} |
|
172 |
|
|
173 |
/** |
|
174 |
* A bulk action (delete or update) which affects 0 or more records. |
|
175 |
*/ |
|
176 |
static class BulkUpdate |
|
177 |
extends SQLRedo |
|
178 |
{ |
|
179 |
BulkUpdate(Session session, SQLQuery query) |
|
180 |
{ |
|
181 |
super(() -> query.executeUpdate(session)); |
|
182 |
} |
|
183 |
} |
|
184 |
} |
new/src/com/goldencode/p2j/persist/orm/SavepointManager.java 2023-02-08 14:29:25 +0000 | ||
---|---|---|
26 | 26 |
** Added JMX for profiling NO-UNDO operation tracing. |
27 | 27 |
** CA 20220630 Cleanup the 'redoable' list once the full transaction ends. |
28 | 28 |
** CA 20230116 Avoid creating a collection until is needed. |
29 |
** RAA 20230208 Removed all code that was linked to redo operations as it is no longer needed. |
|
29 | 30 |
*/ |
30 | 31 | |
31 | 32 |
/* |
... | ... | |
150 | 151 |
/** Stack of block states which coincide with transaction block scopes */ |
151 | 152 |
private final Deque<Block> blocks = new ArrayDeque<>(); |
152 | 153 |
|
153 |
/** The list of redoable insert, update, and delete SQL operations for NO-UNDO temp-tables */ |
|
154 |
private final List<SQLRedo> redoable = new ArrayList<>(64); |
|
155 |
|
|
156 | 154 |
/** Database session used for savepoint processing */ |
157 | 155 |
private Session session = null; |
158 | 156 |
|
... | ... | |
331 | 329 |
|
332 | 330 |
/** |
333 | 331 |
* Roll back the active block, which entails rolling back any savepoint, rolling up tracked |
334 |
* resources, and marking any tracked undoable records as stale and any redoable operations |
|
335 |
* as active. |
|
332 |
* resources, and marking any tracked undoable records as stale. |
|
336 | 333 |
* |
337 | 334 |
* @param transaction |
338 | 335 |
* {@code true} if the current block is a full transaction; {@code false} if it is |
... | ... | |
461 | 458 |
} |
462 | 459 |
|
463 | 460 |
/** |
464 |
* Create a redoable SQL operation for the insertion of a NO-UNDO record into the database. If |
|
465 |
* the original operation is rolled back at the database, this operation must be re-applied |
|
466 |
* after the full or sub-transaction which rolled it back completes. |
|
467 |
* |
|
468 |
* @param dmo |
|
469 |
* DMO which was updated. |
|
470 |
*/ |
|
471 |
void noUndoInsert(BaseRecord dmo) |
|
472 |
{ |
|
473 |
if (session != null && activeBlock != null) |
|
474 |
{ |
|
475 |
NO_UNDO_TRACE.timer(() -> { |
|
476 |
SQLRedo.Insert redo = new SQLRedo.Insert(session.getPersister(), dmo.deepCopy()); |
|
477 |
redoable.add(redo); |
|
478 |
}); |
|
479 |
} |
|
480 |
} |
|
481 |
|
|
482 |
/** |
|
483 |
* Create a redoable SQL operation for the update of a NO-UNDO record in the database. If |
|
484 |
* the original operation is rolled back at the database, this operation must be re-applied |
|
485 |
* after the full or sub-transaction which rolled it back completes. |
|
486 |
* |
|
487 |
* @param dmo |
|
488 |
* DMO which was updated. |
|
489 |
*/ |
|
490 |
void noUndoUpdate(BaseRecord dmo) |
|
491 |
{ |
|
492 |
if (session != null && activeBlock != null && dmo.checkState(CHANGED)) |
|
493 |
{ |
|
494 |
NO_UNDO_TRACE.timer(() -> { |
|
495 |
SQLRedo.Update redo = new SQLRedo.Update(session.getPersister(), dmo.deepCopy()); |
|
496 |
redoable.add(redo); |
|
497 |
}); |
|
498 |
} |
|
499 |
} |
|
500 |
|
|
501 |
/** |
|
502 |
* Create a redoable SQL operation for the deletion of a NO-UNDO record from the database. If |
|
503 |
* the original operation is rolled back at the database, this operation must be re-applied |
|
504 |
* after the full or sub-transaction which rolled it back completes. |
|
505 |
* |
|
506 |
* @param dmoClass |
|
507 |
* Implementation class of the DMO which was deleted. |
|
508 |
* @param id |
|
509 |
* Primary key of the record which was deleted. |
|
510 |
*/ |
|
511 |
void noUndoDelete(Class<? extends BaseRecord> dmoClass, Long id) |
|
512 |
{ |
|
513 |
if (session != null && activeBlock != null) |
|
514 |
{ |
|
515 |
NO_UNDO_TRACE.timer(() -> { |
|
516 |
SQLRedo.Delete redo = new SQLRedo.Delete(session.getPersister(), dmoClass, id); |
|
517 |
redoable.add(redo); |
|
518 |
}); |
|
519 |
} |
|
520 |
} |
|
521 |
|
|
522 |
/** |
|
523 |
* Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in |
|
524 |
* the database. The operation may be an update or a delete. If the original operation is |
|
525 |
* rolled back at the database, this operation must be re-applied after the full or |
|
526 |
* sub-transaction which rolled it back completes. |
|
527 |
* |
|
528 |
* @param query |
|
529 |
* SQL query which performed the original bulk operation. |
|
530 |
*/ |
|
531 |
void noUndoBulkUpdate(SQLQuery query) |
|
532 |
{ |
|
533 |
if (session != null && activeBlock != null) |
|
534 |
{ |
|
535 |
NO_UNDO_TRACE.timer(() -> { |
|
536 |
SQLRedo.BulkUpdate redo = new SQLRedo.BulkUpdate(session, query); |
|
537 |
redoable.add(redo); |
|
538 |
}); |
|
539 |
} |
|
540 |
} |
|
541 |
|
|
542 |
/** |
|
543 | 461 |
* Set a savepoint in the active block and in every previous block which has not yet had one set, |
544 | 462 |
* except for the outermost (which represents the full transaction block). |
545 | 463 |
* <p> |
... | ... | |
622 | 540 |
private void prepareBlock() |
623 | 541 |
{ |
624 | 542 |
parentBlock = blocks.peek(); |
625 |
activeBlock = new Block(blocks.size(), redoable);
|
|
543 |
activeBlock = new Block(blocks.size()); |
|
626 | 544 |
blocks.push(activeBlock); |
627 | 545 |
|
628 | 546 |
if (debug) |
... | ... | |
643 | 561 |
Block lastActiveBlock = activeBlock; |
644 | 562 |
Block popped = blocks.pop(); |
645 | 563 |
|
646 |
// reapply undone changes for any NO-UNDO temporary tables |
|
647 |
// let the noUndoRolledBack flag check here to avoid profiling disabled redo |
|
648 |
if (session != null && lastActiveBlock != null && lastActiveBlock.noUndoRolledBack) |
|
649 |
{ |
|
650 |
REDO.timer(() -> lastActiveBlock.maybeReapplyNoUndo(session)); |
|
651 |
} |
|
652 |
|
|
653 | 564 |
// reactivate the next block in the stack (if any) |
654 | 565 |
activeBlock = blocks.isEmpty() ? null : blocks.pop(); |
655 | 566 |
parentBlock = blocks.isEmpty() ? null : blocks.peek(); |
... | ... | |
667 | 578 |
{ |
668 | 579 |
// full transaction is finished, deactivate savepoint manager (i.e., remove session) |
669 | 580 |
this.session = null; |
670 |
if (redoable != null) |
|
671 |
{ |
|
672 |
redoable.clear(); |
|
673 |
} |
|
674 | 581 |
} |
675 | 582 |
} |
676 | 583 |
|
... | ... | |
735 | 642 |
/** Records which have been changed in the scope of this block */ |
736 | 643 |
private Set<BaseRecord> changed = null; |
737 | 644 |
|
738 |
/** List of SQL redo helpers for NO-UNDO records, shared among all blocks */ |
|
739 |
private final List<SQLRedo> redoable; |
|
740 |
|
|
741 |
/** Starting index for this block in full transaction redoable list */ |
|
742 |
private final int redoHead; |
|
743 |
|
|
744 | 645 |
/** Database savepoint for a sub-transaction block ({@code null} for full transaction */ |
745 | 646 |
private Savepoint savepoint = null; |
746 | 647 |
|
747 |
/** Have we rolled back NO-UNDO changes in this block which will need to be re-applied? */ |
|
748 |
private boolean noUndoRolledBack = false; |
|
749 |
|
|
750 | 648 |
/** |
751 | 649 |
* Constructor. |
752 | 650 |
*/ |
753 |
Block(int txLevel, List<SQLRedo> redoable)
|
|
651 |
Block(int txLevel) |
|
754 | 652 |
{ |
755 | 653 |
this.txLevel = txLevel; |
756 |
this.redoable = redoable; |
|
757 |
this.redoHead = redoable.size(); |
|
758 | 654 |
} |
759 | 655 |
|
760 | 656 |
/** |
... | ... | |
808 | 704 |
} |
809 | 705 |
|
810 | 706 |
/** |
811 |
* Roll back changes to undoable records and activate redoable actions for NO-UNDO records.
|
|
707 |
* Roll back changes to undoable records. |
|
812 | 708 |
*/ |
813 | 709 |
void rollback() |
814 | 710 |
throws PersistenceException |
815 | 711 |
{ |
816 | 712 |
if (txLevel > 0 && savepoint == null) |
817 | 713 |
{ |
818 |
// no DMOs were updated within this block, so nothing to undo nor mark for deferred redo
|
|
714 |
// no DMOs were updated within this block, so nothing to undo |
|
819 | 715 |
return; |
820 | 716 |
} |
821 | 717 |
|
... | ... | |
826 | 722 |
dmo.rollback(txLevel); |
827 | 723 |
} |
828 | 724 |
} |
829 |
|
|
830 |
int redoSize = redoable.size(); |
|
831 |
if (redoSize > redoHead) |
|
832 |
{ |
|
833 |
noUndoRolledBack = true; |
|
834 |
|
|
835 |
for (int i = redoHead; i < redoSize; i++) |
|
836 |
{ |
|
837 |
redoable.get(i).setActive(); |
|
838 |
} |
|
839 |
} |
|
840 | 725 |
} |
841 | 726 |
|
842 | 727 |
/** Notify the FastFind cache that the changed tables need to be invalidated. */ |
... | ... | |
868 | 753 |
} |
869 | 754 |
|
870 | 755 |
/** |
871 |
* Indicate whether this block is tracking any NO-UNDO records. |
|
872 |
* |
|
873 |
* @return {@code true} if there are any NO-UNDO records associated with this block, else |
|
874 |
* {@code false}. |
|
875 |
*/ |
|
876 |
boolean isTrackingNoUndo() |
|
877 |
{ |
|
878 |
return redoable.size() > redoHead; |
|
879 |
} |
|
880 |
|
|
881 |
/** |
|
882 |
* Re-apply any changes to no-undo records which were rolled back in the most recent |
|
883 |
* savepoint or full transaction. |
|
884 |
* |
|
885 |
* @param session |
|
886 |
* Database session helper object. |
|
887 |
*/ |
|
888 |
void maybeReapplyNoUndo(Session session) |
|
889 |
{ |
|
890 |
if (!noUndoRolledBack) |
|
891 |
{ |
|
892 |
// nothing to do |
|
893 |
return; |
|
894 |
} |
|
895 |
|
|
896 |
boolean fullTx = false; |
|
897 |
|
|
898 |
try |
|
899 |
{ |
|
900 |
// if the active block was a full transaction block, we need to start a separate, |
|
901 |
// full, transaction to re-roll forward any NO-UNDO changes that were rolled back |
|
902 |
// at the database; for sub-transactions, we will still be in the context of the |
|
903 |
// existing transaction |
|
904 |
if (txLevel == 0) |
|
905 |
{ |
|
906 |
fullTx = session.beginTransaction(null); |
|
907 |
} |
|
908 |
|
|
909 |
int redoSize = redoable.size(); |
|
910 |
for (int i = redoHead; i < redoSize; i++) |
|
911 |
{ |
|
912 |
SQLRedo redo = redoable.get(i); |
|
913 |
redo.perform(); |
|
914 |
} |
|
915 |
|
|
916 |
if (fullTx) |
|
917 |
{ |
|
918 |
session.commit(); |
|
919 |
} |
|
920 |
} |
|
921 |
catch (PersistenceException exc) |
|
922 |
{ |
|
923 |
if (fullTx) |
|
924 |
{ |
|
925 |
if (log.isLoggable(Level.SEVERE)) |
|
926 |
{ |
|
927 |
log.log(Level.SEVERE, "Error reapplying rolled back NO-UNDO changes", exc); |
|
928 |
} |
|
929 |
|
|
930 |
try |
|
931 |
{ |
|
932 |
session.rollback(); |
|
933 |
} |
|
934 |
catch (PersistenceException exc1) |
|
935 |
{ |
|
936 |
if (log.isLoggable(Level.SEVERE)) |
|
937 |
{ |
|
938 |
log.log(Level.SEVERE, "Error rolling back NO-UNDO reapply transaction", exc1); |
|
939 |
} |
|
940 |
} |
|
941 |
} |
|
942 |
|
|
943 |
// TODO: proper error handling |
|
944 |
throw new StopConditionException(exc); |
|
945 |
} |
|
946 |
} |
|
947 |
|
|
948 |
/** |
|
949 | 756 |
* Get a debug text representation of this object. |
950 | 757 |
* |
951 | 758 |
* @return Text representation of state. |
new/src/com/goldencode/p2j/persist/orm/Session.java 2023-02-08 14:29:58 +0000 | ||
---|---|---|
38 | 38 |
** the database, and not just returned. |
39 | 39 |
** CA 20230104 If a cached record is incomplete, preserve the dirty properties when loading the full |
40 | 40 |
** record. |
41 |
** RAA 20230208 Removed everything that had to do with redo operations as it is no longer needed. |
|
41 | 42 |
*/ |
42 | 43 | |
43 | 44 |
/* |
... | ... | |
743 | 744 |
boolean wasNew = dmo.checkState(NEW); |
744 | 745 |
boolean wasDirty = wasNew || dmo.isDirty(); |
745 | 746 |
|
746 |
if (wasDirty && savepointManager != null) |
|
747 |
{ |
|
748 |
// undoable events must be logged with the DMO before the insert/update takes place, because |
|
749 |
// the logging might trigger a database savepoint to be lazily set, and this savepoint must |
|
750 |
// include the insert/update |
|
751 |
|
|
752 |
if (dmo.checkState(NOUNDO)) |
|
753 |
{ |
|
754 |
if (wasNew) |
|
755 |
{ |
|
756 |
savepointManager.noUndoInsert(dmo); |
|
757 |
} |
|
758 |
else |
|
759 |
{ |
|
760 |
savepointManager.noUndoUpdate(dmo); |
|
761 |
} |
|
762 |
} |
|
763 |
} |
|
764 |
|
|
765 | 747 |
// decide what to do with the record |
766 | 748 |
if (wasNew) |
767 | 749 |
{ |
... | ... | |
873 | 855 |
|
874 | 856 |
// attempt to delete the record if it is not newly created (i.e., as of yet unflushed) |
875 | 857 |
if (!dmo.checkState(NEW)) |
876 |
{ |
|
877 |
if (savepointManager != null && dmo.checkState(NOUNDO)) |
|
878 |
{ |
|
879 |
savepointManager.noUndoDelete(dmo.getClass(), dmo.primaryKey()); |
|
880 |
} |
|
881 |
|
|
858 |
{ |
|
882 | 859 |
success = persister.delete(dmo); |
883 | 860 |
} |
884 | 861 |
|
... | ... | |
957 | 934 |
@SuppressWarnings("unchecked") |
958 | 935 |
Class<? extends Record> recordClass = (Class<? extends Record>) dmoClass; |
959 | 936 |
|
960 |
if (savepointManager != null && cached != null && cached.checkState(NOUNDO)) |
|
961 |
{ |
|
962 |
savepointManager.noUndoDelete(recordClass, id); |
|
963 |
} |
|
964 |
|
|
965 | 937 |
success = persister.delete(recordClass, id); |
966 | 938 |
} |
967 | 939 |
|
... | ... | |
1518 | 1490 |
} |
1519 | 1491 |
|
1520 | 1492 |
/** |
1521 |
* Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in |
|
1522 |
* the database. The operation may be an update or a delete. If the original operation is |
|
1523 |
* rolled back at the database, this operation must be re-applied after the full or |
|
1524 |
* sub-transaction which rolled it back completes. |
|
1525 |
* <p> |
|
1526 |
* This method delegates to the session's savepoint manager, if any, whose responsibility it |
|
1527 |
* is to track and apply the corresponding redoable SQL operation at the appropriate moment. |
|
1528 |
* |
|
1529 |
* @param query |
|
1530 |
* FQL query which performed the original bulk operation. |
|
1531 |
*/ |
|
1532 |
public void noUndoBulkUpdate(Query query) |
|
1533 |
{ |
|
1534 |
noUndoBulkUpdate(query.getSqlQuery()); |
|
1535 |
} |
|
1536 |
|
|
1537 |
/** |
|
1538 |
* Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in |
|
1539 |
* the database. The operation may be an update or a delete. If the original operation is |
|
1540 |
* rolled back at the database, this operation must be re-applied after the full or |
|
1541 |
* sub-transaction which rolled it back completes. |
|
1542 |
* <p> |
|
1543 |
* This method delegates to the session's savepoint manager, if any, whose responsibility it |
|
1544 |
* is to track and apply the corresponding redoable SQL operation at the appropriate moment. |
|
1545 |
* |
|
1546 |
* @param query |
|
1547 |
* SQL query which performed the original bulk operation. |
|
1548 |
*/ |
|
1549 |
public void noUndoBulkUpdate(SQLQuery query) |
|
1550 |
{ |
|
1551 |
if (savepointManager != null) |
|
1552 |
{ |
|
1553 |
savepointManager.noUndoBulkUpdate(query); |
|
1554 |
} |
|
1555 |
} |
|
1556 |
|
|
1557 |
/** |
|
1558 | 1493 |
* Get the zero-based transaction block nesting level, where 0 indicates the current block is a full |
1559 | 1494 |
* transaction block, 1 indicates the current block is the first level of nested subtransaction, and so |
1560 | 1495 |
* on. Note that this includes only blocks with transaction properties; no-transaction blocks which may |