7061-2-fwd.patch
new/src/com/goldencode/p2j/persist/AdaptiveQuery.java 2023-02-15 10:28:21 +0000 | ||
---|---|---|
345 | 345 |
** SVL 20230108 Improved performance by replacing some "for-each" loops with indexed "for" loops. |
346 | 346 |
** SVL 20230109 Reflected PreselectQuery.components() now providing direct access to the |
347 | 347 |
** components array in order to improve performance. |
348 |
** RAA 20230213 Added the possibility to execute a query in a lazy manner. Also, when a result |
|
349 |
** set is created, ProgressiveResults can no longer be the type returned. |
|
348 | 350 |
*/ |
349 | 351 | |
350 | 352 |
/* |
... | ... | |
403 | 405 |
package com.goldencode.p2j.persist; |
404 | 406 | |
405 | 407 |
import java.lang.reflect.*; |
408 |
import java.sql.*; |
|
406 | 409 |
import java.util.*; |
407 | 410 |
import java.util.function.*; |
408 | 411 |
import java.util.logging.*; |
412 | ||
409 | 413 |
import com.goldencode.p2j.persist.event.*; |
410 | 414 |
import com.goldencode.p2j.persist.lock.*; |
411 | 415 |
import com.goldencode.p2j.persist.orm.*; |
... | ... | |
2956 | 2960 |
/** |
2957 | 2961 |
* Execute the query to create a first level result set. |
2958 | 2962 |
* <p> |
2963 |
* This case is for persistent databases: |
|
2959 | 2964 |
* If we are executing the query after a re-validation, or if the query's sort phrase does not match |
2960 | 2965 |
* any legacy index, fall back to the superclass' implementation. Otherwise, this implementation creates |
2961 | 2966 |
* an instance of {@code ProgressiveResults}, which defers actually executing the query until |
2962 | 2967 |
* the first request to fetch a result. |
2968 |
* |
|
2969 |
* This case is for temporary tables: |
|
2970 |
* This implementation can create either a {@code ScrollingResults} or a type of {@code Results} |
|
2971 |
* determined by the execution of the super function. |
|
2963 | 2972 |
* |
2964 | 2973 |
* @param persistence |
2965 | 2974 |
* Object which is used to execute the query. |
... | ... | |
2970 | 2979 |
* string. |
2971 | 2980 |
* |
2972 | 2981 |
* @return An instance of {@code ProgressiveResults} which fetches progressively larger |
2973 |
* brackets of query results as the results are accessed. |
|
2982 |
* brackets of query results as the results are accessed (for persistent cases), |
|
2983 |
* Or |
|
2984 |
* An instance of {@code ScrollingResults}, possibly fetched in a lazy manner (for temporary |
|
2985 |
* cases). |
|
2974 | 2986 |
* |
2975 | 2987 |
* @throws PersistenceException |
2976 | 2988 |
* not thrown by this implementation. |
... | ... | |
2980 | 2992 |
throws PersistenceException |
2981 | 2993 |
{ |
2982 | 2994 |
Results results = null; |
2995 |
boolean temporary = persistence.isTemporary(); |
|
2996 |
if (temporary) |
|
2997 |
{ |
|
2998 |
Long templateRowid = getTemplateQueryRowid(args); |
|
2999 |
if (templateRowid != null) |
|
3000 |
{ |
|
3001 |
results = super.executeQuery(persistence, fql, args); |
|
3002 |
return results; |
|
3003 |
} |
|
3004 |
|
|
3005 |
return new ScrollingResults(persistence, executeScroll(persistence, fql, args, temporary)); |
|
3006 |
} |
|
2983 | 3007 |
|
2984 | 3008 |
if (cacheOnReval || probablyRequiresResort()) |
2985 | 3009 |
{ |
... | ... | |
3008 | 3032 |
} |
3009 | 3033 |
|
3010 | 3034 |
return results; |
3035 |
|
|
3036 |
} |
|
3037 |
|
|
3038 |
/** |
|
3039 |
* Function responsible with returning a list of {@code ScrollableResults}. |
|
3040 |
* It converts the FQL into SQL and executes the query against the SQL server. |
|
3041 |
* If the query is intended for temporary tables, then the execution could be done in a lazy manner. |
|
3042 |
* |
|
3043 |
* @param <T> |
|
3044 |
* The type of the rows returned in the list. |
|
3045 |
* @param persistence |
|
3046 |
* Object which is used to execute the query. |
|
3047 |
* @param fql |
|
3048 |
* FQL query string. |
|
3049 |
* @param args |
|
3050 |
* Substitution parameters to be inserted into placeholders within the FQL query |
|
3051 |
* string. |
|
3052 |
* @param lazyMode |
|
3053 |
* Flag that indicates whether the query should be executed in a lazy manner. |
|
3054 |
* Not all queries can be executed with this option, but the checks for that are not done here. |
|
3055 |
* |
|
3056 |
* @return A scrollable list of rows of type {@code <T>} from SQL server, as specified by the |
|
3057 |
* {@code fql} query. |
|
3058 |
* |
|
3059 |
* @throws PersistenceException |
|
3060 |
* when an error occurred while performing the requested operations. |
|
3061 |
*/ |
|
3062 |
protected <T> ScrollableResults<T> executeScroll(Persistence persistence, |
|
3063 |
String fql, |
|
3064 |
Object[] args, |
|
3065 |
boolean lazyMode) |
|
3066 |
throws PersistenceException |
|
3067 |
{ |
|
3068 |
int scrollMode = ResultSet.TYPE_SCROLL_INSENSITIVE; |
|
3069 |
return persistence.scroll(getEntities(), fql, args, 0, 0, scrollMode, lazyMode); |
|
3011 | 3070 |
} |
3012 | 3071 |
|
3013 | 3072 |
/** |
new/src/com/goldencode/p2j/persist/Persistence.java 2023-02-14 11:06:38 +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 20230213 Added the possibility to create a query defined with lazy. |
|
587 | 588 |
*/ |
588 | 589 | |
589 | 590 |
/* |
... | ... | |
1311 | 1312 |
int scrollMode) |
1312 | 1313 |
throws PersistenceException |
1313 | 1314 |
{ |
1315 |
return scroll(entities, fql, values, maxResults, startOffset, scrollMode, false); |
|
1316 |
} |
|
1317 |
|
|
1318 |
/** |
|
1319 |
* Execute an FQL query and get a scrollable cursor on the result set. Records represented by the result |
|
1320 |
* set are not locked by this method. |
|
1321 |
* |
|
1322 |
* @param entities |
|
1323 |
* Names of the DMO entities associated with this query statement. |
|
1324 |
* @param fql |
|
1325 |
* FQL query statement to be parsed and executed by ORM framework. |
|
1326 |
* @param values |
|
1327 |
* Substitution values for the query. If none, this should be an empty array. |
|
1328 |
* @param maxResults |
|
1329 |
* The maximum number of elements to be returned in the list. If this value is non-positive, no |
|
1330 |
* upper limit is applied. |
|
1331 |
* @param startOffset |
|
1332 |
* The 0-based offset of the first record to retrieve. If this value is non-positive, an offset |
|
1333 |
* of 0 is used by default. |
|
1334 |
* @param scrollMode |
|
1335 |
* Scroll mode which should apply to the result set. |
|
1336 |
* @param lazyMode |
|
1337 |
* Flag that indicates whether the query should be executed in a lazy manner. |
|
1338 |
* |
|
1339 |
* @return An object which provides access to a scrollable cursor on the result set. |
|
1340 |
* |
|
1341 |
* @throws PersistenceException |
|
1342 |
* if there was an error executing the query. |
|
1343 |
*/ |
|
1344 |
public <T> ScrollableResults<T> scroll(String[] entities, |
|
1345 |
String fql, |
|
1346 |
Object[] values, |
|
1347 |
int maxResults, |
|
1348 |
int startOffset, |
|
1349 |
int scrollMode, |
|
1350 |
boolean lazyMode) |
|
1351 |
throws PersistenceException |
|
1352 |
{ |
|
1314 | 1353 |
boolean trace = LOG.isLoggable(Level.FINEST); |
1315 | 1354 |
boolean warn = (trace || LOG.isLoggable(Level.WARNING)); |
1316 | 1355 |
long start = (warn ? System.currentTimeMillis() : 0L); |
... | ... | |
1321 | 1360 |
|
1322 | 1361 |
try |
1323 | 1362 |
{ |
1324 |
Query query = getQuery(local, fql, maxResults, startOffset, values, readOnly); |
|
1363 |
Query query = getQuery(local, fql, maxResults, startOffset, values, readOnly, lazyMode);
|
|
1325 | 1364 |
results = query.scroll(local.getSession(), scrollMode); |
1326 | 1365 |
} |
1327 | 1366 |
catch(UdfException exc) |
... | ... | |
3947 | 3986 |
boolean readOnly) |
3948 | 3987 |
throws PersistenceException |
3949 | 3988 |
{ |
3950 |
Query query = local.getQuery(fql, maxResults, startOffset, readOnly); |
|
3989 |
return getQuery(local, fql, maxResults, startOffset, values, readOnly, false); |
|
3990 |
} |
|
3991 |
|
|
3992 |
/** |
|
3993 |
* Get or create a query for the given FQL query string. A cached query is used if it exists. |
|
3994 |
* |
|
3995 |
* @param local |
|
3996 |
* Context-local state. |
|
3997 |
* @param fql |
|
3998 |
* FQL query string. |
|
3999 |
* @param maxResults |
|
4000 |
* The maximum number of results to be returned by the query. If this value is |
|
4001 |
* non-positive, no upper limit is applied. |
|
4002 |
* @param startOffset |
|
4003 |
* The 0-based offset of the first record to retrieve. If this value is non-positive, |
|
4004 |
* an offset of 0 is used by default. |
|
4005 |
* @param values |
|
4006 |
* Substitution values for the query. If none, this should be an empty array. |
|
4007 |
* @param readOnly |
|
4008 |
* {@code true} to prepare the query in read-only mode, else {@code false}. |
|
4009 |
* @param lazyMode |
|
4010 |
* Flag that indicates whether the query should be executed in a lazy manner. |
|
4011 |
* |
|
4012 |
* @return FQL query object. |
|
4013 |
* |
|
4014 |
* @throws PersistenceException |
|
4015 |
* if there was an error creating an ORM session. |
|
4016 |
*/ |
|
4017 |
private Query getQuery(Context local, |
|
4018 |
String fql, |
|
4019 |
int maxResults, |
|
4020 |
int startOffset, |
|
4021 |
Object[] values, |
|
4022 |
boolean readOnly, |
|
4023 |
boolean lazyMode) |
|
4024 |
throws PersistenceException |
|
4025 |
{ |
|
4026 |
Query query = local.getQuery(fql, maxResults, startOffset, readOnly, lazyMode); |
|
3951 | 4027 |
|
3952 | 4028 |
// Bind parameters, if any. It is OK to do this with cached queries, because each use is |
3953 | 4029 |
// guaranteed to reset the parameters. |
... | ... | |
4251 | 4327 |
Query getQuery(String fql, int maxResults, int startOffset, boolean readOnly) |
4252 | 4328 |
throws PersistenceException |
4253 | 4329 |
{ |
4330 |
return getQuery(fql, maxResults, startOffset, readOnly, false); |
|
4331 |
} |
|
4332 |
|
|
4333 |
/** |
|
4334 |
* Get a cached query for the given combination of FQL query string, maximum result rows, |
|
4335 |
* and starting row offset, creating and caching a new query if no such query already is |
|
4336 |
* cached. The cache is fixed size. When it is full and a new query needs to be added, the |
|
4337 |
* least recently used query is removed. |
|
4338 |
* |
|
4339 |
* @param fql |
|
4340 |
* FQL query string. |
|
4341 |
* @param maxResults |
|
4342 |
* The maximum number of results to be returned. |
|
4343 |
* @param startOffset |
|
4344 |
* Zero-based offset of first result to be returned (undefined results unless |
|
4345 |
* {@code fql} includes an {@code order by} phrase). |
|
4346 |
* @param readOnly |
|
4347 |
* {@code true} to execute the query in read-only mode, else {@code false}. |
|
4348 |
* @param lazyMode |
|
4349 |
* Flag that indicates whether the query should be executed in a lazy manner. |
|
4350 |
* |
|
4351 |
* @return A {@code Query} object for the requested {@code fql}. |
|
4352 |
* |
|
4353 |
* @throws PersistenceException |
|
4354 |
* if there was an error creating an ORM session. |
|
4355 |
*/ |
|
4356 |
Query getQuery(String fql, int maxResults, int startOffset, boolean readOnly, boolean lazyMode) |
|
4357 |
throws PersistenceException |
|
4358 |
{ |
|
4254 | 4359 |
Query query = null; |
4255 | 4360 |
fql = fql.trim(); // just in case |
4256 | 4361 |
|
... | ... | |
4281 | 4386 |
// an incorrect value from a previous usage |
4282 | 4387 |
query.setMaxResults((maxResults > 0) ? maxResults : -1); |
4283 | 4388 |
query.setFirstResult(Math.max(startOffset, 0)); |
4389 |
query.setLazyMode(lazyMode); |
|
4284 | 4390 |
} |
4285 | 4391 |
|
4286 | 4392 |
query.setReadOnly(readOnly); |
new/src/com/goldencode/p2j/persist/orm/FqlToSqlConverter.java 2023-02-14 11:06:38 +0000 | ||
---|---|---|
47 | 47 |
** SVL 20230108 Improved performance by replacing some "for-each" loops with indexed "for" loops. |
48 | 48 |
** 20221031 Collected fields of "select" query. |
49 | 49 |
** 20221101 Fixed possible NPE. |
50 |
** RAA 20230213 Added the keyword "lazy". This can be appended to the SQL that gets generated. |
|
50 | 51 |
*/ |
51 | 52 | |
52 | 53 |
/* |
... | ... | |
192 | 193 |
/** The maximum numbers of results in a query. Used for paging results. */ |
193 | 194 |
private int maxResults = -1; |
194 | 195 |
|
196 |
/** Flag for selecting results in a lazy manner. Used in {@code Select} statements. */ |
|
197 |
private boolean lazyMode = false; |
|
198 |
|
|
195 | 199 |
/** The offset of the record to start a result with. Used for paging results. */ |
196 | 200 |
private int startOffset = -1; |
197 | 201 |
|
... | ... | |
530 | 534 |
* @param fql |
531 | 535 |
* A valid {@code FQL} statement. |
532 | 536 |
* @param maxResults |
533 |
* The maximum numbers of results tobe returned. |
|
537 |
* The maximum numbers of results to be returned.
|
|
534 | 538 |
* @param startOffset |
535 | 539 |
* The start offset. Together with {@code maxResults} this parameter helps |
536 | 540 |
* implementing paging of a bigger the result set. |
... | ... | |
543 | 547 |
*/ |
544 | 548 |
public String toSQL(String fql, int maxResults, int startOffset, List<RowStructure> rowStructure) |
545 | 549 |
{ |
550 |
return toSQL(fql, maxResults, startOffset, rowStructure, false); |
|
551 |
} |
|
552 |
|
|
553 |
/** |
|
554 |
* Converts a {@code FQL} statement into its SQL representation. |
|
555 |
* |
|
556 |
* @param fql |
|
557 |
* A valid {@code FQL} statement. |
|
558 |
* @param maxResults |
|
559 |
* The maximum numbers of results to be returned. |
|
560 |
* @param startOffset |
|
561 |
* The start offset. Together with {@code maxResults} this parameter helps |
|
562 |
* implementing paging of a bigger the result set. |
|
563 |
* @param rowStructure |
|
564 |
* (Acts as an output parameter). If not {@code null}, at return, the map will contain |
|
565 |
* the list of entities returned and the number of fields coded in the generated |
|
566 |
* {@code SELECT} statement for each of them. |
|
567 |
* @param lazyMode |
|
568 |
* Should the returned SQL execute the statement in a lazy manner? |
|
569 |
* {@code true} if the answer is yes, {@code false} otherwise. |
|
570 |
* |
|
571 |
* @return The SQL representation of the {@code fql}. |
|
572 |
*/ |
|
573 |
public String toSQL(String fql, |
|
574 |
int maxResults, |
|
575 |
int startOffset, |
|
576 |
List<RowStructure> rowStructure, |
|
577 |
boolean lazyMode) |
|
578 |
{ |
|
546 | 579 |
if (LOG.isLoggable(Level.FINE)) |
547 | 580 |
{ |
548 | 581 |
LOG.log(Level.FINE, |
... | ... | |
578 | 611 |
sb = new StringBuilder(fql.length()); |
579 | 612 |
this.maxResults = maxResults; |
580 | 613 |
this.startOffset = startOffset; |
614 |
this.lazyMode = lazyMode; |
|
581 | 615 |
processStatement(root); |
582 | 616 |
|
583 | 617 |
String sql = sb.toString(); |
... | ... | |
1423 | 1457 |
{ |
1424 | 1458 |
generatePaging(); |
1425 | 1459 |
} |
1460 |
|
|
1461 |
if (lazyMode) |
|
1462 |
{ |
|
1463 |
generateLazyMode(); |
|
1464 |
} |
|
1426 | 1465 |
} |
1427 | 1466 |
|
1428 | 1467 |
if (aliases.pop() != topLevelAliasMap) |
... | ... | |
1564 | 1603 |
} |
1565 | 1604 |
|
1566 | 1605 |
/** |
1606 |
* Add the keyword "lazy" to the end of the SQL. This works only when there is a |
|
1607 |
* {@code Select} statement that is intended to be executed in a lazy manner. |
|
1608 |
*/ |
|
1609 |
private void generateLazyMode() |
|
1610 |
{ |
|
1611 |
sb.append("\n"); |
|
1612 |
sb.append("lazy"); |
|
1613 |
} |
|
1614 |
|
|
1615 |
/** |
|
1567 | 1616 |
* Generates the {@code ORDER BY} part of the query. The output is directly appended to |
1568 | 1617 |
* {@code sb}. |
1569 | 1618 |
* |
new/src/com/goldencode/p2j/persist/orm/Query.java 2023-02-14 11:06:38 +0000 | ||
---|---|---|
26 | 26 |
** SBI 20221023 Added a usage of SQLQuery.uniqueResult(Session session, RowStructure rowStructure). |
27 | 27 |
** CA 20221031 Javadoc fixes. |
28 | 28 |
** IAS 20230209 Fixed ${TZ} placeholder value |
29 |
** RAA 20230213 Added lazyMode flag. This is useful if the intention is to execute the query in a lazy |
|
30 |
** manner. |
|
29 | 31 |
*/ |
30 | 32 | |
31 | 33 |
/* |
... | ... | |
127 | 129 |
private int maxResults = -1; |
128 | 130 |
|
129 | 131 |
/** |
132 |
* Flag accepted by the {@code Select} query. If it is set to {@code true}, then the query will be |
|
133 |
* executed in a lazy manner. |
|
134 |
* By default, queries are executed in a preselect manner. |
|
135 |
*/ |
|
136 |
private boolean lazyMode = false; |
|
137 |
|
|
138 |
/** |
|
130 | 139 |
* The index of the first matching results accepted by the (SELECT) query. Used in combination |
131 | 140 |
* with {@code firstResult} to configure result pagination. |
132 | 141 |
*/ |
... | ... | |
176 | 185 |
} |
177 | 186 |
|
178 | 187 |
/** |
188 |
* Setter for the {@code lazyMode} flag. |
|
189 |
* Note: this method should only be called if the {@code Query} executed is a {@code Select}. |
|
190 |
* |
|
191 |
* @param lazyMode |
|
192 |
* If {@code true}, the {@code Query} will be executed in a lazy manner. |
|
193 |
*/ |
|
194 |
public void setLazyMode(boolean lazyMode) |
|
195 |
{ |
|
196 |
this.lazyMode = lazyMode; |
|
197 |
} |
|
198 |
|
|
199 |
/** |
|
179 | 200 |
* Configures the index of the first row to be returned by this query. Depending on the SQL |
180 | 201 |
* dialect from the {@code session}, the value is injected as the parameter of SQL |
181 | 202 |
* {@code limit} clause. |
... | ... | |
349 | 370 |
{ |
350 | 371 |
FqlToSqlConverter fql2sql = FqlToSqlConverter.getInstance(dialect, db, params); |
351 | 372 |
rowStructure = new ArrayList<>(2); // this should be enough for most cases |
352 |
String sql = fql2sql.toSQL(fql, maxResults, firstResult, rowStructure); |
|
373 |
String sql = fql2sql.toSQL(fql, maxResults, firstResult, rowStructure, lazyMode);
|
|
353 | 374 |
paramCount = fql2sql.getLastConversionParamCount(); |
354 | 375 |
wasRewritten = fql2sql.isQueryWasRewritten(); |
355 | 376 |
userTableStatRead = fql2sql.isUserTableStatRead(); |