TAFC to TAFJ

Why a Select That Worked in TAFC Returned Nothing in TAFJ

A routine can compile, run, create the output file, and still be wrong. This batch export defect looked like a data problem at first, but it turned out to be a classic migration issue: a derived DICT field was being used in a select that TAFJ would not resolve the same way as TAFC.

The symptom

The batch export routine ran successfully, created the output file, and wrote the header and footer. The problem was that there were no detail lines in the file.

The original select logic looked straightforward. The names below are deliberately obfuscated, but the pattern is the same:

SELECT F.SOURCE.FILE WITH PRIMARY.KEY NE "" AND DERIVED.FLAG EQ "Y" BY PRIMARY.KEY

Under TAFJ, that returned no data. No obvious runtime error. Just an empty export.

The first clue

The field I was filtering on could be seen in the R19 TAFC DICT list, but it could not be found physically on the raw source record.

That is an important distinction in T24 work. Seeing a field name in DICT does not prove it is stored on the file itself. It may still be derived, calculated, or backed by subroutine logic.

The actual root cause

Once the dictionary definition was checked, the problem became clear.

SUBR("ENQ.TRANS","STATUS.ENQUIRY",@1,"DERIVED.FLAG")

That meant the field was an I-type derived item, not a physical attribute on the source file. The export was filtering on a value that was being resolved through enquiry logic rather than stored directly on the selected file.

What was really happening

Once the dependency chain was traced, the data flow was much simpler than the DICT field made it look:

F.SOURCE.FILE<1>   ->  STATUS.CODE
STATUS.CODE          ->  F.STATUS.LOOKUP.@ID
F.STATUS.LOOKUP<2>   ->  DERIVED.FLAG

In other words, the select was relying on hidden cross-file logic. The real value did not live on the file being selected at all.

Why TAFC and TAFJ diverged

TAFC often tolerated selection patterns built on derived dictionary items well enough that teams came to rely on them. TAFJ is much less forgiving when a select depends on enquiry-driven logic instead of real stored data.

The issue was not that ENQ.TRANS itself was unsupported. The issue was expecting the database selection layer to behave like the enquiry/runtime layer.

That is the migration trap. A routine can appear technically fine and still return the wrong result because a hidden assumption no longer holds.

There is also a wider TAFJ rule behind this. TAFJ does not support IDESC or I-descriptor behaviour in the same way older environments did. That matters because teams may still see the DICT item in metadata and assume it should behave normally at runtime. In practice, the dictionary entry can exist while the old descriptor-style behaviour is no longer a safe thing to build batch logic around.

That does not automatically mean every routine is broken for the same reason, but it is a strong warning sign. If a selection depends on derived DICT behaviour, the right question is not only "does this field exist?" but also "is this runtime behaviour actually supported in TAFJ?"

The fix

The right fix was to stop filtering on the derived DICT field in the select.

The old logic:

SELECT F.SOURCE.FILE WITH PRIMARY.KEY NE "" AND DERIVED.FLAG EQ "Y" BY PRIMARY.KEY

The corrected TAFJ-safe logic:

SELECT F.SOURCE.FILE WITH PRIMARY.KEY NE "" BY PRIMARY.KEY

Then, inside the processing loop:

  1. Read the source record.
  2. Get the status code from the source file.
  3. Read the status lookup file using that code.
  4. Check the real stored flag on the lookup record.
  5. Only continue when the value matches the required business rule.

The minimal replacement logic

READ SOURCE.REC FROM F.SOURCE.FILE, REC.ID ELSE CONTINUE

STATUS.CODE = SOURCE.REC<1>
IF STATUS.CODE EQ "" THEN CONTINUE

READ LOOKUP.REC FROM F.STATUS.LOOKUP, STATUS.CODE ELSE CONTINUE

IF LOOKUP.REC<2> NE "Y" THEN CONTINUE

This makes the dependency explicit and removes the hidden enquiry behaviour from the selection layer.

Why this is the better pattern anyway

It is clearer

Anyone reading the routine can now see that export eligibility depends on a real lookup file, not a DICT shortcut.

It is safer

Batch logic becomes less dependent on enquiry behaviour and more predictable in migration and audit scenarios.

It is easier to support

When a result set is wrong, you can test the stored fields directly instead of reverse engineering a DICT chain every time.

The wider lesson

This is not just one export story. It is a migration pattern worth actively checking for in older routines:

  • I-type DICT fields in SELECT statements
  • Old descriptor or IDESC-style assumptions carried over from TAFC
  • SUBR(...)-backed dictionary items
  • ENQ.TRANS-driven logic hidden behind a normal-looking field name
  • Cross-file business rules that are only visible once the DICT is opened

A routine that compiles and runs is not necessarily correct. Migration testing has to validate outputs, not just runtime success.

Practical takeaway

If a select worked in TAFC and suddenly returns nothing in TAFJ, do not stop at checking whether the field name exists in DICT.

Check what kind of DICT item it is. If it is derived, enquiry-driven, or backed by a subroutine, assume you may need to replace that selection logic with explicit reads against the real underlying file.

That habit will solve more migration defects than memorising any one specific fix.