APEX 26.1 platform issues – reports for the Oracle APEX development team

The issues below were found while building a custom model-driven APEX region plugin using the public apex.model JavaScript API and the APEX_EXEC PL/SQL package (WWV_FLOW_EXEC_API). Each one is reproducible in isolation, on a minimal model/table, independent of any specific application. Root cause has been verified by reading the actual client/ server source involved, not only inferred from the symptom.

Each numbered item below is written to be posted as its own forum thread / SR. A suggested workaround is included where one was found and applied.


Bug reports

1. apex.model, shape "record": a second save after a first successful save sends stale data

  • Componentapex.model (client JavaScript), internal function _updateData().
  • Environment: Oracle APEX 26.1, any browser (verified via network capture — not browser-specific).
  • Steps to reproduce:
    1. Create an apex.model instance with shape: "record".
    2. Call setValue() on a field, then model.save(). Let it complete successfully.
    3. Call setValue() again (on any field, including the same one), then model.save() a second time.
  • Expected: the second save request carries the record’s current value.
  • Observed: the second save silently sends the value as it stood right after the first save, not the value set afterward. model.getRecord() becomes stale from that point on.
  • Root cause (verified in source): after a successful save, _updateData() reconciles with this._data[iNode.index] = rec — correct only for shape: "table", where _data is an indexed collection. For shape: "record"getRecord() returns this._data directly (getRecord = function() { return that._data; }), never this._data[index] — so this reconciliation line never updates the object getRecord() returns.
  • Suggested workaround: after every successful save on a shape: "record" model, call model.clearData() followed by model.fetch() instead of relying on the model’s own post-save reconciliation.

2. apex.model, shape "record"revertRecords() does not update the reference returned by getRecord()

  • Componentapex.model (client JavaScript), revertRecords().
  • Environment: Oracle APEX 26.1.
  • Steps to reproduce:
    1. Create an apex.model with shape: "record".
    2. Modify a field with setValue().
    3. Call model.revertRecords() on the record.
  • ExpectedgetRecord() returns the original, pre-modification value.
  • ObservedgetRecord() still returns the just-modified value.
  • Root cause (verified in source): in the shape: "record" branch of revertRecords(), the code updates metadata.record = metadata.original but never reassigns this._data (what getRecord() returns for this shape). The line that would populate this._data is explicitly skipped by a shape !== RECORD guard, and no alternate branch populates it for shape "record".
  • Suggested workaround: after calling revertRecords() on a shape: "record" model, force model.clearData() + model.fetch() instead of trusting the in-memory revert.

3. apex.modelrecordIsArray: trueinsertNewRecord() does not validate or normalize the init value

  • Componentapex.model (modelViewBase.min.js), internal function _initRecord.
  • Environment: Oracle APEX 26.1.
  • Steps to reproduce:
    1. Create a model with recordIsArray: true.
    2. Call model.insertNewRecord(index, false, initValue), where initValue is a plain object (not an array).
  • Expected: either the init value is normalized to an array consistent with recordIsArray: true, or the public documentation states that a non-array third argument is unsupported in this mode.
  • Observed: the value is used verbatim as the new record, with no validation — leading to a runtime error the first time the record is treated as an array (row.values.forEach is not a function).
  • Root cause (verified in source)_initRecord uses any truthy third argument directly as the record, with no shape check against recordIsArray.
  • Suggested workaround: omit the third argument when recordIsArray: true — the model then builds a well-formed, empty array-shaped record on its own.

4. APEX_EXEC.EXECUTE_DML raises ORA-06592 for a DELETE added via OPEN_LOCAL_DML_CONTEXT with query type TABLE

  • ComponentAPEX_EXEC (PL/SQL), internally WWV_FLOW_EXEC_API.
  • Environment: Oracle APEX 26.1, Oracle Database 19c and later.
  • Severity: high — an internal ORA- error surfaces from inside a public, documented package, not an application-level error.
  • Steps to reproduce:
    1. On any scratch table, call APEX_EXEC.OPEN_LOCAL_DML_CONTEXT with p_query_type => APEX_EXEC.C_QUERY_TYPE_TABLE.
    2. Call APEX_EXEC.ADD_DML_ROW with p_dml_operation => APEX_EXEC.C_DML_OPERATION_DELETE, supplying only the primary key column and value.
    3. Call APEX_EXEC.EXECUTE_DML.
  • Expected: the row is deleted, or an application-level error is raised (permission denied, row not found).
  • ObservedORA-06592: CASE not found while executing CASE statement, raised from inside WWV_FLOW_EXEC_API itself.
  • Verification: reproduced in isolation on a dedicated scratch table, unrelated to any specific application. The public documentation for this API shows only INSERT examples via ADD_DML_ROW — no DELETE example with p_query_type => C_QUERY_TYPE_TABLE.
  • Suggested workaround: use p_query_type => APEX_EXEC.C_QUERY_TYPE_SQL_QUERY with an equivalent query and the primary key’s real datatype declared explicitly — this path deletes correctly.

5. apex.model: a save batch with more than one DELETE leaves the model/UI inconsistent even after an explicit fetch()

  • Componentapex.model (client JavaScript), post-save reconciliation (_updateData) and model.fetch().
  • Environment: Oracle APEX 26.1.
  • Steps to reproduce:
    1. In the same browser session, insert and save several rows in an apex.model-backed table region.
    2. In a single, separate save batch, mark two or more of those rows for deletion and save.
  • Expected: after the save completes, the table reflects the correct remaining rows — if necessary, after an explicit model.fetch().
  • Observed: both the model’s automatic post-save reconciliation and an explicit model.fetch() called immediately afterward are unreliable — the table can appear completely empty despite a successful save with no real conflict. Only a full page reload restores the correct state.
  • Note for triage: please test the exact sequence above (N inserts+save, then a separate save with M ≥ 2 deletes in the same batch) — the explicit fetch() call is also affected, not only the automatic reconciliation, so this should not be dismissed as “just call fetch() after save.”
  • Root cause: not further isolable from outside the platform — the client source is minified beyond the point reached during investigation. This is exactly the kind of case that needs access to the unminified source for further diagnosis.

Documentation clarification request

6. apex.model, shape "table": the post-save identity reconciliation contract does not appear to be publicly documented

  • Nature: not a functional defect — the behavior is consistent and stable, but the contract does not appear to be documented publicly. Included here as a documentation request, not a bug.
  • Observed behavior: after a save, the client only reconciles a row’s identity (e.g. replacing a temporary client-side id with the real server-generated id after an insert, or removing a row after a delete) if the save response includes a meta object — at the same position as the corresponding request row — containing recordId equal to the identity previously known to the client. Without this meta object, a successful insert leaves the temporary id visible client-side until the next fetch, and a successful delete never removes the row from the client-side model.
  • Request: could the exact shape of this meta/recordId contract (expected per save-response row, for insert and delete) be documented publicly for apex.model consumers implementing a custom Ajax save handler?
  • Verified by: reading _updateData() in the client source; no reference to this contract found in the public apex.model (aexjs) documentation.

Categories:

Tagged:

No responses yet

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *

Categories

Latest Comments

Nessun commento da mostrare.