rbval.patch
src/com/goldencode/p2j/persist/RecordBuffer.java 2021-02-18 17:51:32 +0000 | ||
---|---|---|
1119 | 1119 |
** OM 20201218 Fixed implementation for error and rejected attributes/hidden fields. |
1120 | 1120 |
** OM 20210127 Replaced TableMapper.getLegacyName() triple map lookup with direct access to |
1121 | 1121 |
** local dmoMeta.legacyTable. |
1122 |
** ECF 20210117 Do not update DMO state when explicitly validating. Attempt to flush a transient |
|
1123 |
** record if still in the buffer when its scope ends. |
|
1122 | 1124 |
*/ |
1123 | 1125 | |
1124 | 1126 |
/* |
... | ... | |
3191 | 3193 |
{ |
3192 | 3194 |
// in batch assign mode, standard validation happens before assign triggers are fired |
3193 | 3195 |
Record dmo = buffer.getCurrentRecord(); |
3194 |
buffer.validateMaybeFlush(dmo, false); |
|
3196 |
buffer.validateMaybeFlush(dmo, false, false);
|
|
3195 | 3197 |
|
3196 | 3198 |
// fire assign triggers, if any |
3197 | 3199 |
TriggerTracker tracker = buffer.getTriggerTracker(); |
... | ... | |
6717 | 6719 |
// validate and persist the record |
6718 | 6720 |
if (currentRecord.checkState(DmoState.NEW) || currentRecord.checkState(DmoState.CHANGED)) |
6719 | 6721 |
{ |
6720 |
validateMaybeFlush(currentRecord, true); |
|
6722 |
validateMaybeFlush(currentRecord, true, false);
|
|
6721 | 6723 |
} |
6722 | 6724 |
else if (currentRecord.checkState(DmoState.INVALID)) |
6723 | 6725 |
{ |
... | ... | |
10039 | 10041 |
if (currentRecord.checkState(DmoState.INVALID)) |
10040 | 10042 |
{ |
10041 | 10043 |
// release the broken record as if undone |
10044 |
// TODO: is this still correct, now that we no longer update the DMO state to INVALID on a |
|
10045 |
// failed, explicit validation? |
|
10042 | 10046 |
setCurrentRecord(null, true, false, false, false); |
10043 | 10047 |
} |
10044 | 10048 |
else |
10045 | 10049 |
{ |
10046 |
try |
|
10047 |
{ |
|
10048 |
flush(); |
|
10049 |
} |
|
10050 |
catch (ValidationException exc) |
|
10051 |
{ |
|
10052 |
// if the current referenced record does not pass validation, |
|
10053 |
// it means it was never flushed when it was created in the |
|
10054 |
// first place. so, before allowing the validation exception |
|
10055 |
// to be processed, we must release this not-valid record |
|
10056 |
setCurrentRecord(null, true, false, false, false); |
|
10057 |
|
|
10058 |
throw exc; |
|
10059 |
} |
|
10050 |
// flush the transient record currently in the buffer, which will attempt validation; |
|
10051 |
// the create will be aborted if this record is invalid |
|
10052 |
flush(true); |
|
10060 | 10053 |
} |
10061 | 10054 |
} |
10062 | 10055 |
|
... | ... | |
10294 | 10287 |
throws ValidationException, |
10295 | 10288 |
PersistenceException |
10296 | 10289 |
{ |
10297 |
validateMaybeFlush(dmo, false); |
|
10290 |
validateMaybeFlush(dmo, false, true);
|
|
10298 | 10291 |
} |
10299 | 10292 |
|
10300 | 10293 |
/** |
... | ... | |
10821 | 10814 |
* Record whose data is to be validated and optionally flushed to the database. |
10822 | 10815 |
* @param flush |
10823 | 10816 |
* {@code true} to validate and persist DMO's data; {@code false} to validate only. |
10817 |
* @param explicit |
|
10818 |
* {@code true} if this validation was explicitly requested by business logic; {@code false} if |
|
10819 |
* it is implicit/organic (e.g., in response to a record being updated). |
|
10824 | 10820 |
* |
10825 | 10821 |
* @return The changed property, or {@code null} if there is no current record or if we are in |
10826 | 10822 |
* batch mode. |
... | ... | |
10832 | 10828 |
* if there was a validation error. This represents an application level data |
10833 | 10829 |
* validation failure and is recoverable. |
10834 | 10830 |
*/ |
10835 |
private Object[] validateMaybeFlush(Record dmo, boolean flush) |
|
10831 |
private Object[] validateMaybeFlush(Record dmo, boolean flush, boolean explicit)
|
|
10836 | 10832 |
throws PersistenceException, |
10837 | 10833 |
ValidationException |
10838 | 10834 |
{ |
... | ... | |
10851 | 10847 |
persistenceContext.getNursery(), |
10852 | 10848 |
uniqueTracker, |
10853 | 10849 |
uniqueTrackerCtx, |
10854 |
flush); |
|
10850 |
flush, |
|
10851 |
!explicit); |
|
10855 | 10852 |
try |
10856 | 10853 |
{ |
10857 | 10854 |
diffs = validation.validateMaybeFlush(); |
... | ... | |
11175 | 11172 |
{ |
11176 | 11173 |
activeScopeDepth = -1; |
11177 | 11174 |
|
11178 |
// Mark current record unavailable; however, a no-undo buffer appears to retain its |
|
11179 |
// current record and continue reporting errors with an invalid record beyond the |
|
11180 |
// buffer's scope |
|
11181 |
setCurrentRecord(null, false, false, false, false); |
|
11175 |
// attempt to flush a transient record in the buffer at the time its block scope ends; this will |
|
11176 |
// trigger validation and may report an error |
|
11177 |
if (isTransient()) |
|
11178 |
{ |
|
11179 |
try |
|
11180 |
{ |
|
11181 |
flush(true); |
|
11182 |
} |
|
11183 |
catch (ValidationException exc) |
|
11184 |
{ |
|
11185 |
reportValidationException(exc, this, false); |
|
11186 |
} |
|
11187 |
} |
|
11182 | 11188 |
|
11183 | 11189 |
// The snapshot is no longer available |
11184 | 11190 |
snapshot = null; |
... | ... | |
12297 | 12303 |
{ |
12298 | 12304 |
try |
12299 | 12305 |
{ |
12300 |
diffs = validateMaybeFlush(currentRecord, false); |
|
12306 |
diffs = validateMaybeFlush(currentRecord, false, false);
|
|
12301 | 12307 |
} |
12302 | 12308 |
catch (ValidationException exc) |
12303 | 12309 |
{ |
... | ... | |
13445 | 13451 |
} |
13446 | 13452 |
|
13447 | 13453 |
/** |
13448 |
* Set table statistics record |
|
13449 |
* @param tableStats |
|
13450 |
* table statistics record |
|
13454 |
* Set table statistics record. |
|
13455 |
* |
|
13456 |
* @param tableStats |
|
13457 |
* Table statistics record |
|
13451 | 13458 |
*/ |
13452 | 13459 |
private void setTableStats(TableStats tableStats) |
13453 | 13460 |
{ |
13454 | 13461 |
this.tableStats = tableStats; |
13455 | 13462 |
} |
13456 | ||
13463 |
|
|
13457 | 13464 |
/** |
13458 | 13465 |
* Getter for the {@link #propsBySetter} map. |
13459 | 13466 |
* |
... | ... | |
13463 | 13470 |
{ |
13464 | 13471 |
return propsBySetter; |
13465 | 13472 |
} |
13466 | ||
13473 |
|
|
13467 | 13474 |
/** |
13468 | 13475 |
* Getter for the {@link #propsByGetter} map. |
13469 | 13476 |
* |
... | ... | |
13473 | 13480 |
{ |
13474 | 13481 |
return propsByGetter; |
13475 | 13482 |
} |
13476 | ||
13483 |
|
|
13477 | 13484 |
/** |
13478 | 13485 |
* Getter for the {@link #setterDatums} map. |
13479 | 13486 |
* |
... | ... | |
13483 | 13490 |
{ |
13484 | 13491 |
return setterDatums; |
13485 | 13492 |
} |
13486 | ||
13493 |
|
|
13487 | 13494 |
/** |
13488 | 13495 |
* Getter for the {@link #getterDatums} map. |
13489 | 13496 |
* |
... | ... | |
13493 | 13500 |
{ |
13494 | 13501 |
return getterDatums; |
13495 | 13502 |
} |
13496 | ||
13503 |
|
|
13497 | 13504 |
/** |
13498 | 13505 |
* Information needed by a getter or setter method to affect the datum of one property (or one |
13499 | 13506 |
* element within an extent property), namely, the property's name and optional extent index, |
src/com/goldencode/p2j/persist/orm/RecordMeta.java 2021-02-04 20:37:19 +0000 | ||
---|---|---|
2 | 2 |
** Module : RecordMeta.java |
3 | 3 |
** Abstract : Information required to perform CRUD operations on a specific DMO type. |
4 | 4 |
** |
5 |
** Copyright (c) 2019-2020, Golden Code Development Corporation.
|
|
5 |
** Copyright (c) 2019-2021, Golden Code Development Corporation.
|
|
6 | 6 |
** |
7 | 7 |
** -#- -I- --Date-- ---------------------------------------Description--------------------------------------- |
8 | 8 |
** 001 ECF 20191015 First revision. Information required to perform basic CRUD operations on a |
... | ... | |
13 | 13 |
** OM 20201001 Improved DMO manipulation performance by caching slow Property annotation access. |
14 | 14 |
** OM 20201002 Use DmoMeta cached information instead of map lookups. |
15 | 15 |
** OM 20201007 Replaced inlined column name with TemporaryBuffer.MULTIPLEX_FIELD_NAME. |
16 |
** ECF 20210204 Removed word index unsupported log message. |
|
16 | 17 |
*/ |
17 | 18 | |
18 | 19 |
/* |
... | ... | |
710 | 711 |
{ |
711 | 712 |
if (next.word()) |
712 | 713 |
{ |
713 |
// TODO: handle word index |
|
714 |
|
|
715 |
// if (log.isLoggable(Level.WARNING)) |
|
716 |
// { |
|
717 |
// String msg = "Word indices are not supported in this version of FWD: " + |
|
718 |
// dmoIface.getCanonicalName() + |
|
719 |
// ": " + |
|
720 |
// next.name(); |
|
721 |
// log.log(Level.WARNING, msg); |
|
722 |
// } |
|
723 |
|
|
714 |
// TODO: handle word index; how do word indices affect validation? |
|
724 | 715 |
continue; |
725 | 716 |
} |
726 | 717 |
|
src/com/goldencode/p2j/persist/orm/Validation.java 2021-02-18 17:46:06 +0000 | ||
---|---|---|
16 | 16 |
** AIL 20201020 Closed unclosed prepared statement. |
17 | 17 |
** ECF 20201201 Fixed FFC invalidation for flush update case when record was validated previously. |
18 | 18 |
** 20210126 Ensure all unvalidated unique indices are validated at flush time. |
19 |
** ECF 20210117 Do not update DMO state when explicitly validating. |
|
19 | 20 |
*/ |
20 | 21 | |
21 | 22 |
/* |
... | ... | |
118 | 119 |
/** Whether to flush the record; {@code false} to validate only */ |
119 | 120 |
private final boolean flush; |
120 | 121 |
|
122 |
/** Whether to update the DMO state as a side effect of validation */ |
|
123 |
private final boolean updateState; |
|
124 |
|
|
121 | 125 |
/** Whether we are validating the most recently updated DMO property only, or the entire DMO */ |
122 | 126 |
private final boolean singleProp; |
123 | 127 |
|
... | ... | |
165 | 169 |
* Context-local API helper for unique constraint tracking. |
166 | 170 |
* @param flush |
167 | 171 |
* {@code true} to validate and persist DMO's data; {@code false} to validate only. |
172 |
* @param updateState |
|
173 |
* Update the DMO's index and validation state as a side effect of validation. This should be set |
|
174 |
* to {@code true} for implicit/organic validation; {@code false} for explicit validation. |
|
168 | 175 |
*/ |
169 | 176 |
public Validation(RecordBuffer buffer, |
170 | 177 |
BaseRecord dmo, |
... | ... | |
173 | 180 |
RecordNursery nursery, |
174 | 181 |
UniqueTracker uniqueTracker, |
175 | 182 |
UniqueTracker.Context uniqueTrackerCtx, |
176 |
boolean flush) |
|
183 |
boolean flush, |
|
184 |
boolean updateState) |
|
177 | 185 |
{ |
178 | 186 |
this.buffer = buffer; |
179 | 187 |
this.dmo = dmo; |
... | ... | |
183 | 191 |
this.uniqueTracker = uniqueTracker; |
184 | 192 |
this.uniqueTrackerCtx = uniqueTrackerCtx; |
185 | 193 |
this.flush = flush; |
194 |
this.updateState = updateState; |
|
186 | 195 |
this.singleProp = dmo.getActiveOffset() >= 0; |
187 | 196 |
} |
188 | 197 |
|
... | ... | |
300 | 309 |
// before the unique constraint checks, which will clear the changed property state |
301 | 310 |
Object[] diff = singleProp ? dmo.getActiveUpdateDiffs() : null; |
302 | 311 |
|
303 |
// validate fully updated unique indices, if any |
|
304 |
BitSet valUnique = dmo.getUnvalidatedIndices(offset, flush); |
|
312 |
// validate fully updated unique indices, if any, or all indices if flushing or not updating index |
|
313 |
// state |
|
314 |
BitSet valUnique = dmo.getUnvalidatedIndices(offset, flush || !updateState); |
|
305 | 315 |
if (!valUnique.isEmpty()) |
306 | 316 |
{ |
307 | 317 |
// validate unique indices (and flush, if flush was requested) |
... | ... | |
318 | 328 |
// update the nursery if we did not already flush and: |
319 | 329 |
// * the table has no remaining index needing update; or |
320 | 330 |
// * any indices for a new record were updated |
321 |
if (isNew && !flushed)
|
|
331 |
if (isNew && (!flushed && updateState))
|
|
322 | 332 |
{ |
323 | 333 |
if (dmo.getIndexState().isEmpty()) |
324 | 334 |
{ |
... | ... | |
340 | 350 |
} |
341 | 351 |
} |
342 | 352 |
|
343 |
if (!flushed) |
|
353 |
if (!flushed && updateState)
|
|
344 | 354 |
{ |
345 | 355 |
if (singleProp) |
346 | 356 |
{ |
... | ... | |
430 | 440 |
uniqueTrackerCtx.rollbackChange(uniqueTracker, token); |
431 | 441 |
} |
432 | 442 |
|
433 |
// mark record as having failed validation; it will stay this way until it passes |
|
434 |
dmo.updateState(session, INVALID, true); |
|
443 |
if (updateState) |
|
444 |
{ |
|
445 |
// mark record as having failed validation; it will stay this way until it passes |
|
446 |
dmo.updateState(session, INVALID, true); |
|
447 |
} |
|
435 | 448 |
|
436 | 449 |
throw exc; |
437 | 450 |
} |
... | ... | |
785 | 798 |
|
786 | 799 |
String msg = bufferName + "." + fi.legacy + " is mandatory, but has unknown (?) value"; |
787 | 800 |
|
788 |
dmo.updateState(buffer.getSession(false), INVALID, true); |
|
801 |
if (updateState) |
|
802 |
{ |
|
803 |
dmo.updateState(buffer.getSession(false), INVALID, true); |
|
804 |
} |
|
789 | 805 |
|
790 | 806 |
throw new ValidationException(msg, 110); |
791 | 807 |
} |