Project

General

Profile

6129b-6816.patch

Radu Apetrii, 01/05/2023 06:25 AM

Download (24.5 KB)

View differences:

new/src/com/goldencode/cache/Cache.java 2023-01-05 10:49:48 +0000
11 11
**                  ExpiryCache implementation class. Added for-each loop iteration methods keys(), values(),
12 12
**                  and entries(), plus a size() method.
13 13
**     VVT 20210917 Javadoc fixed. 
14
**     RAA 20221215 Added computeIfAbsent.
14 15
*/
15 16
/*
16 17
** This program is free software: you can redistribute it and/or modify
......
67 68

  
68 69
package com.goldencode.cache;
69 70

  
71
import java.util.function.*;
72

  
70 73
/**
71 74
 * A simple interface for a cache, with a map-like API.
72 75
 * 
......
116 119
   public V putIfAbsent(K key, V value);
117 120
   
118 121
   /**
122
    * Function which does the following things:
123
    * 1. First, it checks if the key is present in the cache.
124
    * If the key is present, and a non-null value is related to the key, then it returns that value.
125
    * If not (the key is not present or is present, but has a null value related), it moves on to step 2.
126
    * 
127
    * 2. It attempts to compute the value using the given {@code mappingFunction}.
128
    * It also enters the calculated value into the map unless the calculated value is null. 
129
    * 
130
    * @param   key
131
    *          A hashable key.
132
    * @param   mappingFunction
133
    *          Function that takes one argument and produces a result.
134
    *          That result, unless it is null, is then put in the cache.
135
    *          
136
    * @return  The value at the given key, either directly retrieved or calculated.
137
    */
138
   public V computeIfAbsent(K key,
139
            Function<? super K, ? extends V> mappingFunction);
140
   
141
   /**
119 142
    * Remove the entry associated with the given key from the cache.
120 143
    * 
121 144
    * @param   key
new/src/com/goldencode/cache/ExpiryCache.java 2023-01-05 10:49:48 +0000
18 18
**                  cache grows and subsequent attempts will be made to shrink it back down. Refactored the
19 19
**                  implementation and added methods to give some for-each loop iteration capability.
20 20
**      OM 20210831 Replaced anonymous classes with equivalent lambdas.
21
*      RAA 20221215 Added computeIfAbsent.
21 22
*/
22 23

  
23 24
/*
......
312 313
   }
313 314
   
314 315
   /**
316
    * Function which does the following things:
317
    * 1. First, it checks if the key is present in the cache.
318
    * If the key is present, and a non-null value is related to the key, then it returns that value.
319
    * If not (the key is not present or is present, but has a null value related), it moves on to step 2.
320
    * 
321
    * 2. It attempts to compute the value using the given {@code mappingFunction}.
322
    * It also enters the calculated value into the map unless the calculated value is null.
323
    * When the cache is full, before adding another element, it pushes out the least desirable element(s).
324
    * 
325
    * @param   key
326
    *          A hashable key.
327
    * @param   mappingFunction
328
    *          Function that takes one argument and produces a result.
329
    *          That result, unless it is null, is then put in the cache.
330
    *          
331
    * @return  The value at the given key, either directly retrieved or calculated.
332
    */
333
   public V computeIfAbsent(K key, Function<? super K, ? extends V> mappingFunction)
334
   {
335
      
336
      Objects.requireNonNull(mappingFunction);
337
      V v;
338
      if ((v = get(key)) == null) 
339
      {
340
         V newValue;
341
         if ((newValue = mappingFunction.apply(key)) != null) 
342
         {
343
            put(key, newValue);
344
            return newValue;
345
         }
346
      }
347

  
348
      return v;
349
    }
350
   
351
   /**
315 352
    * Remove the entry associated with the given key from the cache.
316 353
    * 
317 354
    * @param   key
new/src/com/goldencode/p2j/persist/AdaptiveComponent.java 2023-01-05 11:05:42 +0000
21 21
**     CA  20221130 Refactored the WHERE clause translation (when the bound and definition buffers are not the
22 22
**                  same), to be aware of the external buffers (i.e. added for CAN-FIND sub-select clauses).  
23 23
**                  The translate will be performed before the query is being executed.
24
**     RAA 20230105 Changed how a SortCriterion is generated. It now uses SortCriterionFactory
25
**                  instead of just calling the constructor.
24 26
*/
25 27

  
26 28
/*
......
430 432
      String alias = getBuffer().getDMOAlias();
431 433
      for (SortCriterion criterion : sortCriteria)
432 434
      {
433
         ret.add(criterion.getAlias().equals(alias) ? criterion : new SortCriterion(criterion, alias));
435
         ret.add(criterion.getAlias().equals(alias) ? criterion :
436
                 SortCriterionFactory.getInstance(criterion, alias));
434 437
      }
435 438
      
436 439
      return ret;
new/src/com/goldencode/p2j/persist/PreselectQuery.java 2023-01-05 11:07:12 +0000
596 596
**                           are not the same), to be aware of the external buffers (i.e. added for CAN-FIND
597 597
**                           sub-select clauses).  The translate will be performed before the query is being
598 598
**                           executed.
599
**     RAA 20230105          Changed how a SortCriterion is generated. It now uses SortCriterionFactory
600
**                           instead of just calling the constructor.
599 601
*/
600 602

  
601 603
/*
......
4944 4946
         }
4945 4947
         
4946 4948
         // create the SortCriterion instance and add it to the master list and to the appropriate sub-list
4947
         SortCriterion crit = new SortCriterion(buffer, part);
4949
         SortCriterion crit = SortCriterionFactory.getInstance(buffer, part);
4948 4950
         
4949 4951
         sortCriteria.add(crit);
4950 4952
         subSortCrit.add(crit);
......
5009 5011
         
5010 5012
         try
5011 5013
         {
5012
            workList.add(new SortCriterion(buffer, sb.toString()));
5014
            workList.add(SortCriterionFactory.getInstance(buffer, sb.toString()));
5013 5015
         }
5014 5016
         catch (PersistenceException exc)
5015 5017
         {
new/src/com/goldencode/p2j/persist/PresortQuery.java 2023-01-05 10:49:48 +0000
92 92
**     OM  20201007          Optimized SortCriterion by using DmoMeta data instead of map lookups.
93 93
**     CA  20210310          A dynamic predicate must set the default lock to NONE, instead of SHARE.
94 94
**     OM  20220701          Dropped overriding methods which were duplicating code from super class.
95
**     RAA 20221215          Changed how a SortCriterion is generated. It now uses SortCriterionFactory
96
**                           instead of just calling the constructor.
95 97
*/
96 98

  
97 99
/*
......
961 963
      while (iter.hasNext())
962 964
      {
963 965
         RecordBuffer buffer = iter.next().getBuffer();
964
         SortCriterion crit = new SortCriterion(
966
         SortCriterion crit = SortCriterionFactory.getInstance(
965 967
               buffer, buffer.getDMOAlias() + "." + DatabaseManager.PRIMARY_KEY + " asc");
966 968
         sortCriteria.add(crit);
967 969
      }
new/src/com/goldencode/p2j/persist/SortCriterion.java 2023-01-05 11:09:26 +0000
98 98
**     AL2 20220706          Added isCompatible to filter out the sort clauses not belonging to the buffer.
99 99
**     OM  20220727          FieldId and PropertyId are different for denormalized extent fields.
100 100
**     OM  20221103          New class names for FQLPreprocessor, FQLExpression, FQLBundle, and FQLCache.
101
**     RAA 20221213          Added a cache for storing the result of toSortExpression() method.
102
**     RAA 20221215          Changed how a SortCriterion is generated. It now uses SortCriterionFactory
103
**                           instead of just calling the constructor.
101 104
*/
102 105

  
103 106
/*
......
225 228
   /** Lock for cache */
226 229
   private static final Object cacheLock = new Object();
227 230
   
231
   /** Cache for storing the result of {@code toSortExpression} method. */
232
   private final Vector<String> toStringCache; 
233
   
228 234
   /** <code>true</code> if sort is ascending, else it is descending */
229 235
   private final boolean ascending;
230 236
   
......
253 259
   private final String originalName;
254 260
   
255 261
   /** Sort property name, usually qualified */
256
   private String name;
262
   private final String name;
257 263
   
258 264
   /** Subscript value, if any */
259
   private int subscript = -1;
265
   private final int subscript;
260 266
   
261 267
   /** SQL dialect which used this sort criterion; used for rtrim calls on character properties */
262
   private Dialect dialect = null;
268
   private final Dialect dialect;
263 269
   
264 270
   /**
265 271
    * Constructor which parses original sort component text, deriving the property name, sort
......
347 353
      // parse the test and extract alias, property and optional extent
348 354
      int spacePos = text.indexOf(' ');
349 355
      String rawName = ((spacePos != -1) ? text.substring(0, spacePos) : text).trim();
350
      name = rawName;
356
      String actualName = rawName;
351 357
      originalName = internStrings ? rawName.intern() : rawName;
352 358
      int bracketPos = rawName.indexOf("[");
353 359
      int endPos = -1;
360
      int actualSubscript = -1;
354 361
      if (bracketPos >= 0)
355 362
      {
356 363
         endPos = rawName.indexOf("]");
......
360 367
         }
361 368
         
362 369
         // extract base name
363
         name = rawName.substring(0, bracketPos);
370
         actualName = rawName.substring(0, bracketPos);
364 371
      }
365 372
      
366
      int dotPos = name.lastIndexOf(".");
373
      int dotPos = actualName.lastIndexOf(".");
367 374
      if (dotPos < 0)
368 375
      {
369 376
         throw new PersistenceException("Sort property may not be unqualified:  " + rawName);
370 377
      }
371 378
      
372
      String propName = name.substring(dotPos + 1);
373
      String alias = name.substring(0, dotPos);
379
      String propName = actualName.substring(dotPos + 1);
380
      String alias = actualName.substring(0, dotPos);
374 381
      DmoMeta dmoMeta = DmoMetadataManager.getDmoInfo(dmoClass);
375 382
      Property propMeta = dmoMeta.getFieldInfo(propName);
376 383
      this.propertyName = propMeta.name; // already interned, anyway
......
385 392
            String sub = rawName.substring(bracketPos + 1, endPos);
386 393
            try
387 394
            {
388
               subscript = Integer.parseInt(sub);
395
               actualSubscript = Integer.parseInt(sub);
389 396
            }
390 397
            catch (NumberFormatException exc)
391 398
            {
392 399
               throw new PersistenceException("Unsupported subscript:  " + sub);
393 400
            }
394 401
            
395
            name = alias + "." + propertyName;
402
            actualName = alias + "." + propertyName;
396 403
         }
397 404
      }
398 405
      
399 406
      if (internStrings)
400 407
      {
401
         name = name.intern();
408
         actualName = actualName.intern();
402 409
      }
403 410
      
404 411
      // determine sort direction
......
448 455
         ignoreCase = isCharacter && !caseSens;
449 456
         computedColumnPrefix = ccPrefix;
450 457
      }
458
      
459
      name = actualName;
460
      subscript = actualSubscript;
461
      toStringCache = new Vector<>(16);
462
      toStringCache.setSize(16);
451 463
   }
452 464
   
453 465
   /**
......
475 487
      this.originalName = other.originalName;
476 488
      this.name = alias + "." + propertyName + 
477 489
                  (subscript < 0 ? "" : "[" + subscript + "]") + (ascending ? " asc" : " desc");
490
      toStringCache = new Vector<>(16);
491
      toStringCache.setSize(16);
478 492
   }
479 493
   
480 494
   /**
......
565 579
               continue;
566 580
            }
567 581
            
568
            next = new SortCriterion(dialect, sortComp.trim(), dmoInfo);
582
            next = SortCriterionFactory.getInstance(dialect, sortComp.trim(), dmoInfo);
569 583
            sortCriteria.add(next);
570 584
            sortNames.add(next.getUnqualifiedName());
571 585
         }
......
582 596
      if (makeUnique && !sortNames.contains(DatabaseManager.PRIMARY_KEY))
583 597
      {
584 598
         String sortComp = dmoAlias + "." + DatabaseManager.PRIMARY_KEY + " " + (pKeyAscend ? "asc" : "desc");
585
         sortCriteria.add(new SortCriterion(dialect, sortComp, dmoInfo));
599
         sortCriteria.add(SortCriterionFactory.getInstance(dialect, sortComp, dmoInfo));
586 600
      }
587 601
      
588 602
      // eliminate superfluous sort components if sort criteria define a superset of a unique
......
930 944
      // ASC:   ASC + NULLS LAST    (CASE WHEN [Order] IS NULL THEN 1 ELSE 0 END), [Order] ASC  
931 945
      // DESC:  DESC + NULLS FIRST  (CASE WHEN [Order] IS NULL THEN 0 ELSE 1 END), [Order] DESC
932 946
      
947
      int cacheKey = 0;
948
      cacheKey += (invert ? 1 << 3 : 0);
949
      cacheKey += (useTableAlias ? 1 << 2 : 0);
950
      cacheKey += (uniqueAlias ? 1 << 1 : 0);
951
      cacheKey += (doSpecificProcessing ? 1 : 0);
952
      String cacheResult = null;
953
      cacheResult = toStringCache.get(cacheKey);
954
      
955
      if (cacheResult != null)
956
      {
957
         return cacheResult;
958
      }
959
      
933 960
      boolean mssqlFix = dialect instanceof P2JSQLServer2008Dialect;
934 961
      StringBuilder buf = new StringBuilder();
935 962
      StringBuilder sortExpr = mssqlFix ? new StringBuilder() : null;
......
983 1010
         buf.append(ascending ? "asc" : "desc");
984 1011
      }
985 1012
      
986
      return buf.toString();
1013
      String result = buf.toString();
1014
      toStringCache.setElementAt(result, cacheKey);
1015
      return result;
987 1016
   }
988 1017
   
989 1018
   /**
new/src/com/goldencode/p2j/persist/SortCriterionFactory.java 2023-01-05 10:49:48 +0000
1
/*
2
** Module   : SortCriterionFactory.java
3
** Abstract : Factory for an object which encapsulates a single "order by" sort criterion
4
**
5
** Copyright (c) 2004-2022, Golden Code Development Corporation.
6
**
7
** -#- -I- --Date-- --JPRM-- -----------------------------------Description-----------------------------------
8
** 001 RAA 20221214          Created initial version.
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;
65

  
66
import java.util.logging.*;
67

  
68
import com.goldencode.cache.*;
69
import com.goldencode.p2j.persist.dialect.*;
70
import com.goldencode.p2j.persist.orm.*;
71
import com.goldencode.p2j.util.*;
72

  
73
/**
74
 * Factory design pattern for a {@link SortCriterion}. Its purpose is to quickly retrieve an already created
75
 * {@code SortCriterion}. If that does not happen, the instance is created and then stored in cache. 
76
 */
77
public final class SortCriterionFactory
78
{
79
   /** Logger for {@code SortCriterionFactory}. */
80
   private static final Logger log = LogHelper.getLogger(SortCriterionFactory.class.getName());
81
   
82
   /** Cache for quickly returning an already created {@code SortCriterion}. */
83
   static final Cache<Key, SortCriterion> cache = new LRUCache<>(2048);
84
   
85
   /**
86
    * Function which simulates the behavior of the {@link SortCriterion} constructor with
87
    * {@link RecordBuffer} and {@code String} parameters.
88
    * 
89
    * It is strongly advised to use this function because the instance has a chance to be retrieved
90
    * from cache, meaning that it would be faster than creating a new one.
91
    * 
92
    * @param   buffer
93
    *          Record buffer.
94
    * @param   text
95
    *          Text representation of sort component, as provided by query.
96
    *          
97
    * @return  The instance of the requested {@code SortCriterion}.
98
    */
99
   static SortCriterion getInstance(RecordBuffer buffer, String text)
100
   throws PersistenceException
101
   {
102
      return getInstance(buffer.getDialect(), text, buffer.getDmoInfo(), false);
103
   }
104
   
105
   /**
106
    * Function which simulates the behavior of the {@link SortCriterion} constructor with
107
    * {@link Dialect}, {@code String} and {@link DmoMeta} parameters.
108
    * 
109
    * It is strongly advised to use this function because the instance has a chance to be retrieved
110
    * from cache, meaning that it would be faster than creating a new one.
111
    * 
112
    * @param   dialect
113
    *          Database dialect.
114
    * @param   text
115
    *          Text representation of sort component, as provided by query.
116
    * @param   dmoInfo
117
    *          Metadata for the table.
118
    *          
119
    * @return  The instance of the requested {@code SortCriterion}.
120
    */
121
   static SortCriterion getInstance(Dialect dialect, String text, DmoMeta dmoInfo)
122
   throws PersistenceException
123
   {
124
      return getInstance(dialect, text, dmoInfo, true);
125
   }
126
   
127
   /**
128
    * Function which simulates the behavior of the {@link SortCriterion} constructor with
129
    * {@link Dialect}, {@code String}, {@link DmoMeta} and {@code boolean} parameters.
130
    * 
131
    * It is strongly advised to use this function because the instance has a chance to be retrieved
132
    * from cache, meaning that it would be faster than creating a new one.
133
    * 
134
    * @param   dialect
135
    *          Database dialect.
136
    * @param   text
137
    *          Text representation of sort component, as provided by query.
138
    * @param   dmoInfo
139
    *          Metadata for the table.
140
    * @param   internStrings
141
    *          {@code true} to intern string fields, else {@code false}.
142
    *          
143
    * @return  The instance of the requested {@code SortCriterion}.
144
    */
145
   static SortCriterion getInstance(Dialect dialect, String text,
146
                                    DmoMeta dmoInfo, boolean internStrings)
147
   throws PersistenceException
148
   {
149
      Key key = new Key(dialect, text, dmoInfo, internStrings);
150
      synchronized (cache)
151
      {
152
         return cache.computeIfAbsent(key, k -> {
153
            try
154
            {
155
               return new SortCriterion(dialect, text, dmoInfo, internStrings);
156
            }
157
            catch (PersistenceException e)
158
            {
159
               if (log.isLoggable(Level.WARNING))
160
               {
161
                  log.log(Level.WARNING, "Could not create a new SortCriterion", e);
162
               }
163
            }
164
            
165
            return null;
166
         });
167
      }
168
   }
169
   
170
   /**
171
    * Function which simulates the behavior of the {@link SortCriterion} constructor with
172
    * {@code SortCriterion} and {@code String} parameters.
173
    * 
174
    * There are two reasons as to why this method does not involve caching:
175
    * 1. It is rarely used.
176
    * 2. Adding another element to the cache key (alias) and storing multiple {@code SortCriterion} with
177
    * just different aliases might provide more negative than positive aspects. It would make the
178
    * common cases (>95%) more difficult to obtain and the ones that are rarer easier to find.
179
    * 
180
    * @param   other
181
    *          The source {@code SortCriterion} to copy.
182
    * @param   alias
183
    *          The new alias to be used.
184
    *          
185
    * @return  The instance of the requested {@code SortCriterion}.
186
    */
187
   static SortCriterion getInstance(SortCriterion other, String alias)
188
   {
189
      return new SortCriterion(other, alias);
190
   }
191
   
192
   /**
193
    * Key for cached {@code SortCriterion}s.
194
    */
195
   private static class Key
196
   {
197
      /** Database dialect. */
198
      private final Dialect dialect;
199
      
200
      /** Text representation of sort component, as provided by query. */
201
      private final String text;
202
      
203
      /** Metadata for the table. */
204
      private final DmoMeta dmoInfo;
205
      
206
      /** Database dialect. */
207
      private final boolean internStrings;
208
      
209
      /** {@code true} to intern string fields, else {@code false}. */
210
      private final int hashCode;
211
      
212
      /**
213
       * Constructor.
214
       * 
215
       * @param   dialect
216
       *          Database dialect.
217
       * @param   text
218
       *          Text representation of sort component, as provided by query.
219
       * @param   dmoInfo
220
       *          Metadata for the table.
221
       * @param   internStrings
222
       *          {@code true} to intern string fields, else {@code false}.
223
       */
224
      Key(Dialect dialect, String text, DmoMeta dmoInfo, boolean internStrings)
225
      {
226
         this.dialect = dialect;
227
         this.text = text;
228
         this.dmoInfo = dmoInfo;
229
         this.internStrings = internStrings;
230
         
231
         int result = 17;
232
         result = 37 * result + (dialect == null ? 0 : dialect.hashCode());
233
         result = 37 * result + (text == null ? 0 : text.hashCode());
234
         result = 37 * result + (dmoInfo == null ? 0 : dmoInfo.hashCode());
235
         result = 37 * result + (internStrings == false ? 0 : 1);
236
         
237
         this.hashCode = result;
238
      }
239
      
240
      /**
241
       * Override of the default hash code algorithm. Consistent with {@link #equals(Object)}.
242
       * 
243
       * @return  Hash code.
244
       */
245
      @Override
246
      public int hashCode()
247
      {
248
         return hashCode;
249
      }
250
      
251
      /**
252
       * Override of the default equivalence algorithm. Consistent with {@link #hashCode()}.
253
       * 
254
       * return   {@code true} if this object is equivalent to the given object, else {@code
255
       *          false}.
256
       */
257
      @Override
258
      public boolean equals(Object o)
259
      {
260
         if (!(o instanceof Key))
261
         {
262
            return false;
263
         }
264
         
265
         Key that = (Key) o;
266
         if (this.dialect != that.dialect)
267
         {
268
            return false;
269
         }
270
         
271
         if ((this.text == null && that.text != null) ||
272
             (this.text != null && that.text == null) ||
273
             (!this.text.equals(that.text)))
274
         {
275
            return false;
276
         }
277
         
278
         if (this.dmoInfo != that.dmoInfo)
279
         {
280
            return false;
281
         }
282
         
283
         if (this.internStrings != that.internStrings)
284
         {
285
            return false;
286
         }
287
         
288
         return true;
289
      }
290
   }
291
}