Project

General

Profile

6995-6129b.patch

Radu Apetrii, 01/26/2023 08:16 AM

Download (25.2 KB)

View differences:

new/src/com/goldencode/p2j/persist/Persistence.java 2023-01-26 11:46:46 +0000
579 579
**     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
580 580
**     CA  20221109          Implemented runtime for EXCEPT/FIELDS options - only NO-LOCK records are loaded 
581 581
**                           as 'partial/incomplete'.
582
**     RAA 20230126          Removed the checks for no-undo as they are no longer necessary. 
582 583
*/
583 584

  
584 585
/*
......
2862 2863
         
2863 2864
         Query query = getQuery(local, fql, 0, 0, values, false);
2864 2865
         count = query.executeUpdate(session);
2865
         if (noUndo)
2866
         {
2867
            session.noUndoBulkUpdate(query);
2868
         }
2869 2866
         
2870 2867
         if (LOG.isLoggable(Level.FINER))
2871 2868
         {
......
3474 3471
         
3475 3472
         rc = query.executeUpdate(session);
3476 3473
         
3477
         if (noUndo)
3478
         {
3479
            session.noUndoBulkUpdate(query);
3480
         }
3481
         
3482 3474
         if (LOG.isLoggable(Level.FINER))
3483 3475
         {
3484 3476
            LOG.log(Level.FINER, "[" + sql + "] rc = " + rc);
new/src/com/goldencode/p2j/persist/TempTableHelper.java 2023-01-26 11:49:21 +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 20230126          Added "no-undo" as a temp table postfix in constructor.
57 58
*/
58 59

  
59 60
/*
......
392 393
      String primarySqlIndexName = null;
393 394
      
394 395
      String createTemporaryTablePostfix = dialect.getCreateTemporaryTablePostfix();
396
      String createTemporaryNoUndoTablePostfix = dialect.getCreateTemporaryTableNoUndoPostfix();
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-01-26 11:53:44 +0000
91 91
**     RAA 20230109          Added method getSequenceSetValString().
92 92
**     SVL 20230110          P2JIndex.components() provides direct access to the components array in order to
93 93
**                           improve performance.
94
**     RAA 20230126          Added method for creating a temp table postfix responsible with the "no-undo"
95
**                           keyword. By default it is null, representing the string of an undoable table. 
94 96
*/
95 97

  
96 98
/*
......
1297 1299
   public abstract String getCreateTemporaryTablePostfix();
1298 1300
   
1299 1301
   /**
1302
    * Return the ending of the DDL statement that creates a temporary no-undo table.
1303
    *
1304
    * @return  A string representing the ending of the DDL statement that creates a temporary
1305
    *          no-undo table, or empty string if the table is undoable.
1306
    */
1307
   public String getCreateTemporaryTableNoUndoPostfix()
1308
   {
1309
      return null;
1310
   }
1311
   
1312
   /**
1300 1313
    * Returns the beginning of the DDL statement for creating an index.
1301 1314
    * 
1302 1315
    * @param   unique
new/src/com/goldencode/p2j/persist/dialect/P2JH2Dialect.java 2023-01-26 11:56:09 +0000
105 105
**     OM  20221026          Excluded table name from index name by default. It will be added as needed by
106 106
**                           dialects.
107 107
**     RAA 20230109          Overrided methods getSequenceSetValString() and getSequenceCurrValString().
108
**     RAA 20230126          Overrided method for returning the postfix of a no-undo temp-table.
108 109
*/
109 110

  
110 111
/*
......
1329 1330
   }
1330 1331
   
1331 1332
   /**
1333
    * Return the ending of the DDL statement that creates a temporary no-undo table.
1334
    *
1335
    * @return  A string representing the ending of the DDL statement that creates a temporary
1336
    *          no-undo table.
1337
    */
1338
   @Override
1339
   public String getCreateTemporaryTableNoUndoPostfix()
1340
   {
1341
      return " noundo";
1342
   }
1343
   
1344
   /**
1332 1345
    * Obtain a DDL statement which drops a SQL index.
1333 1346
    *
1334 1347
    * @param   ifExists
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-01-26 12:00:49 +0000
23 23
**                  with a Savepoint at a later time when the invoker requires.
24 24
**     CA  20220630 Cleanup the 'redoable' list once the full transaction ends.
25 25
**     CA  20230116 Avoid creating a collection until is needed.
26
**     RAA 20230126 Removed all code that was linked to redo operations as it is no longer needed.
26 27
*/
27 28

  
28 29
/*
......
138 139
   /** Stack of block states which coincide with transaction block scopes */
139 140
   private final Deque<Block> blocks = new ArrayDeque<>();
140 141
   
141
   /** The list of redoable insert, update, and delete SQL operations for NO-UNDO temp-tables */
142
   private final List<SQLRedo> redoable = new ArrayList<>(64);
143
   
144 142
   /** Database session used for savepoint processing */
145 143
   private Session session = null;
146 144
   
......
319 317
   
320 318
   /**
321 319
    * Roll back the active block, which entails rolling back any savepoint, rolling up tracked
322
    * resources, and marking any tracked undoable records as stale and any redoable operations
323
    * as active.
320
    * resources, and marking any tracked undoable records as stale.
324 321
    * 
325 322
    * @param   transaction
326 323
    *          {@code true} if the current block is a full transaction; {@code false} if it is
......
469 466
   }
470 467
   
471 468
   /**
472
    * Create a redoable SQL operation for the insertion of a NO-UNDO record into the database. If
473
    * the original operation is rolled back at the database, this operation must be re-applied
474
    * after the full or sub-transaction which rolled it back completes.
475
    * 
476
    * @param   dmo
477
    *          DMO which was updated.
478
    */
479
   void noUndoInsert(BaseRecord dmo)
480
   {
481
      if (session != null && activeBlock != null)
482
      {
483
         SQLRedo.Insert redo = new SQLRedo.Insert(session.getPersister(), dmo.deepCopy());
484
         redoable.add(redo);
485
      }
486
   }
487
   
488
   /**
489
    * Create a redoable SQL operation for the update of a NO-UNDO record in the database. If
490
    * the original operation is rolled back at the database, this operation must be re-applied
491
    * after the full or sub-transaction which rolled it back completes.
492
    * 
493
    * @param   dmo
494
    *          DMO which was updated.
495
    */
496
   void noUndoUpdate(BaseRecord dmo)
497
   {
498
      if (session != null && activeBlock != null && dmo.checkState(CHANGED))
499
      {
500
         SQLRedo.Update redo = new SQLRedo.Update(session.getPersister(), dmo.deepCopy());
501
         redoable.add(redo);
502
      }
503
   }
504
   
505
   /**
506
    * Create a redoable SQL operation for the deletion of a NO-UNDO record from the database. If
507
    * the original operation is rolled back at the database, this operation must be re-applied
508
    * after the full or sub-transaction which rolled it back completes.
509
    * 
510
    * @param   dmoClass
511
    *          Implementation class of the DMO which was deleted.
512
    * @param   id
513
    *          Primary key of the record which was deleted.
514
    */
515
   void noUndoDelete(Class<? extends BaseRecord> dmoClass, Long id)
516
   {
517
      if (session != null && activeBlock != null)
518
      {
519
         SQLRedo.Delete redo = new SQLRedo.Delete(session.getPersister(), dmoClass, id);
520
         redoable.add(redo);
521
      }
522
   }
523
   
524
   /**
525
    * Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in
526
    * the database. The operation may be an update or a delete. If the original operation is
527
    * rolled back at the database, this operation must be re-applied after the full or
528
    * sub-transaction which rolled it back completes.
529
    * 
530
    * @param   query
531
    *          SQL query which performed the original bulk operation.
532
    */
533
   void noUndoBulkUpdate(SQLQuery query)
534
   {
535
      if (session != null && activeBlock != null)
536
      {
537
         SQLRedo.BulkUpdate redo = new SQLRedo.BulkUpdate(session, query);
538
         redoable.add(redo);
539
      }
540
   }
541
   
542
   /**
543 469
    * Set a savepoint in the active block and in every previous block which has not yet had one set,
544 470
    * except for the outermost (which represents the full transaction block).
545 471
    * <p>
......
622 548
   private void prepareBlock()
623 549
   {
624 550
      parentBlock = blocks.peek();
625
      activeBlock = new Block(blocks.size(), redoable);
551
      activeBlock = new Block(blocks.size());
626 552
      blocks.push(activeBlock);
627 553
      
628 554
      if (debug)
......
643 569
      Block lastActiveBlock = activeBlock;
644 570
      Block popped = blocks.pop();
645 571
      
646
      // reapply undone changes for any NO-UNDO temporary tables
647
      if (session != null && lastActiveBlock != null)
648
      {
649
         lastActiveBlock.maybeReapplyNoUndo(session);
650
      }
651
      
652 572
      // reactivate the next block in the stack (if any)
653 573
      activeBlock = blocks.isEmpty() ? null : blocks.pop();
654 574
      parentBlock = blocks.isEmpty() ? null : blocks.peek();
......
666 586
      {
667 587
         // full transaction is finished, deactivate savepoint manager (i.e., remove session)
668 588
         this.session = null;
669
         if (redoable != null)
670
         {
671
            redoable.clear();
672
         }
673 589
      }
674 590
   }
675 591
   
......
739 655
      /** The current element being processed or the last one which was processed. */
740 656
      private BaseRecord changedLastIteration = null;
741 657
      
742
      /** List of SQL redo helpers for NO-UNDO records, shared among all blocks */
743
      private final List<SQLRedo> redoable;
744
      
745
      /** Starting index for this block in full transaction redoable list */
746
      private final int redoHead;
747
      
748 658
      /** Database savepoint for a sub-transaction block ({@code null} for full transaction */
749 659
      private Savepoint savepoint = null;
750 660
      
751
      /** Have we rolled back NO-UNDO changes in this block which will need to be re-applied? */
752
      private boolean noUndoRolledBack = false;
753
      
754 661
      /**
755 662
       * Constructor.
756 663
       */
757
      Block(int txLevel, List<SQLRedo> redoable)
664
      Block(int txLevel)
758 665
      {
759 666
         this.txLevel = txLevel;
760
         this.redoable = redoable;
761
         this.redoHead = redoable.size();
762 667
      }
763 668
      
764 669
      /**
......
832 737
      }
833 738
      
834 739
      /**
835
       * Roll back changes to undoable records and activate redoable actions for NO-UNDO records.
740
       * Roll back changes to undoable records.
836 741
       */
837 742
      void rollback()
838 743
      throws PersistenceException
839 744
      {
840 745
         if (txLevel > 0 && savepoint == null)
841 746
         {
842
            // no DMOs were updated within this block, so nothing to undo nor mark for deferred redo
747
            // no DMOs were updated within this block, so nothing to undo
843 748
            return;
844 749
         }
845 750
         
......
860 765
               changedIsIterated = false;
861 766
            }
862 767
         }
863
         
864
         int redoSize = redoable.size();
865
         if (redoSize > redoHead)
866
         {
867
            noUndoRolledBack = true;
868
            
869
            for (int i = redoHead; i < redoSize; i++)
870
            {
871
               redoable.get(i).setActive();
872
            }
873
         }
874 768
      }
875 769
      
876 770
      /** Notify the FastFind cache that the changed tables need to be invalidated. */
......
912 806
      }
913 807
      
914 808
      /**
915
       * Indicate whether this block is tracking any NO-UNDO records.
916
       * 
917
       * @return  {@code true} if there are any NO-UNDO records associated with this block, else
918
       *          {@code false}.
919
       */
920
      boolean isTrackingNoUndo()
921
      {
922
         return redoable.size() > redoHead;
923
      }
924
      
925
      /**
926
       * Re-apply any changes to no-undo records which were rolled back in the most recent
927
       * savepoint or full transaction.
928
       * 
929
       * @param   session
930
       *          Database session helper object.
931
       */
932
      void maybeReapplyNoUndo(Session session)
933
      {
934
         if (!noUndoRolledBack)
935
         {
936
            // nothing to do
937
            return;
938
         }
939
         
940
         boolean fullTx = false;
941
         
942
         try
943
         {
944
            // if the active block was a full transaction block, we need to start a separate,
945
            // full, transaction to re-roll forward any NO-UNDO changes that were rolled back
946
            // at the database; for sub-transactions, we will still be in the context of the
947
            // existing transaction
948
            if (txLevel == 0)
949
            {
950
               fullTx = session.beginTransaction(null);
951
            }
952
            
953
            int redoSize = redoable.size();
954
            for (int i = redoHead; i < redoSize; i++)
955
            {
956
               SQLRedo redo = redoable.get(i);
957
               redo.perform();
958
            }
959
            
960
            if (fullTx)
961
            {
962
               session.commit();
963
            }
964
         }
965
         catch (PersistenceException exc)
966
         {
967
            if (fullTx)
968
            {
969
               if (log.isLoggable(Level.SEVERE))
970
               {
971
                  log.log(Level.SEVERE, "Error reapplying rolled back NO-UNDO changes", exc);
972
               }
973
               
974
               try
975
               {
976
                  session.rollback();
977
               }
978
               catch (PersistenceException exc1)
979
               {
980
                  if (log.isLoggable(Level.SEVERE))
981
                  {
982
                     log.log(Level.SEVERE, "Error rolling back NO-UNDO reapply transaction", exc1);
983
                  }
984
               }
985
            }
986
            
987
            // TODO: proper error handling
988
            throw new StopConditionException(exc);
989
         }
990
      }
991
      
992
      /**
993 809
       * Get a debug text representation of this object.
994 810
       * 
995 811
       * @return  Text representation of state.
new/src/com/goldencode/p2j/persist/orm/Session.java 2023-01-26 12:04:15 +0000
32 32
**                  the database, and not just returned.
33 33
**     CA  20230104 If a cached record is incomplete, preserve the dirty properties when loading the full
34 34
**                  record.
35
**     RAA 20230126 Removed everything that had to do with redo operations as it is no longer needed.
35 36
*/
36 37

  
37 38
/*
......
737 738
      boolean wasNew = dmo.checkState(NEW);
738 739
      boolean wasDirty = wasNew || dmo.isDirty();
739 740
      
740
      if (wasDirty && savepointManager != null)
741
      {
742
         // undoable events must be logged with the DMO before the insert/update takes place, because
743
         // the logging might trigger a database savepoint to be lazily set, and this savepoint must
744
         // include the insert/update
745
         
746
         if (dmo.checkState(NOUNDO))
747
         {
748
            if (wasNew)
749
            {
750
               savepointManager.noUndoInsert(dmo);
751
            }
752
            else
753
            {
754
               savepointManager.noUndoUpdate(dmo);
755
            }
756
         }
757
      }
758
      
759 741
      // decide what to do with the record
760 742
      if (wasNew)
761 743
      {
......
867 849
      
868 850
      // attempt to delete the record if it is not newly created (i.e., as of yet unflushed)
869 851
      if (!dmo.checkState(NEW))
870
      {
871
         if (savepointManager != null && dmo.checkState(NOUNDO))
872
         {
873
            savepointManager.noUndoDelete(dmo.getClass(), dmo.primaryKey());
874
         }
875
         
852
      {  
876 853
         success = persister.delete(dmo);
877 854
      }
878 855
      
......
951 928
         @SuppressWarnings("unchecked")
952 929
         Class<? extends Record> recordClass = (Class<? extends Record>) dmoClass;
953 930
         
954
         if (savepointManager != null && cached != null && cached.checkState(NOUNDO))
955
         {
956
            savepointManager.noUndoDelete(recordClass, id);
957
         }
958
         
959 931
         success = persister.delete(recordClass, id);
960 932
      }
961 933
      
......
1512 1484
   }
1513 1485
   
1514 1486
   /**
1515
    * Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in
1516
    * the database. The operation may be an update or a delete. If the original operation is
1517
    * rolled back at the database, this operation must be re-applied after the full or
1518
    * sub-transaction which rolled it back completes.
1519
    * <p>
1520
    * This method delegates to the session's savepoint manager, if any, whose responsibility it
1521
    * is to track and apply the corresponding redoable SQL operation at the appropriate moment.
1522
    * 
1523
    * @param   query
1524
    *          FQL query which performed the original bulk operation.
1525
    */
1526
   public void noUndoBulkUpdate(Query query)
1527
   {
1528
      noUndoBulkUpdate(query.getSqlQuery());
1529
   }
1530
   
1531
   /**
1532
    * Create a redoable SQL operation for the bulk update of zero or more NO-UNDO records in
1533
    * the database. The operation may be an update or a delete. If the original operation is
1534
    * rolled back at the database, this operation must be re-applied after the full or
1535
    * sub-transaction which rolled it back completes.
1536
    * <p>
1537
    * This method delegates to the session's savepoint manager, if any, whose responsibility it
1538
    * is to track and apply the corresponding redoable SQL operation at the appropriate moment.
1539
    * 
1540
    * @param   query
1541
    *          SQL query which performed the original bulk operation.
1542
    */
1543
   public void noUndoBulkUpdate(SQLQuery query)
1544
   {
1545
      if (savepointManager != null)
1546
      {
1547
         savepointManager.noUndoBulkUpdate(query);
1548
      }
1549
   }
1550
   
1551
   /**
1552 1487
    * Get the zero-based transaction block nesting level, where 0 indicates the current block is a full
1553 1488
    * transaction block, 1 indicates the current block is the first level of nested subtransaction, and so
1554 1489
    * on. Note that this includes only blocks with transaction properties; no-transaction blocks which may