IBM i: The SQL Way · #11

Create a Data Journal Reader for an IBM i Table

Use QSYS2.CREATE_DATA_JOURNAL_READER to generate a table function that returns journaled record images as individual columns instead of an unparsed entry-data value.

Related native optionDSPJRN or QSYS2.DISPLAY_JOURNAL
IBM iSQLJournalingCREATE_DATA_JOURNAL_READERDISPLAY_JOURNALDb2 for iChange Data

DISPLAY_JOURNAL can return record-level journal entries, but the record image is normally contained in entry-specific data. CREATE_DATA_JOURNAL_READER generates a file-specific table function that exposes supported table fields as individual SQL columns.

The service is:

QSYS2.CREATE_DATA_JOURNAL_READER

It is a scalar function that creates another table function.

The generated table function is based on the column definition of one journaled file.

Why this matters

Without a file-specific reader, a query may return:

ENTRY_DATA

as a large binary value that must be interpreted according to the record format.

The generated reader can return:

CUSTOMER_ID
ORDER_NUMBER
ORDER_STATUS
ORDER_TOTAL

alongside journal metadata such as:

ENTRY_TIMESTAMP
OPERATION
JOURNAL_ENTRY_TYPE
JOB_NAME
USER_NAME
PROGRAM_NAME
COMMIT_CYCLE

That makes the journal easier to query using normal SQL.

Example source table

Assume:

Library: PRODLIB
File:    ORDERS

The file must already be journaled.

The FILE_NAME parameter requires the system object name.

Generate the reader

VALUES QSYS2.CREATE_DATA_JOURNAL_READER(
    LIBRARY_NAME   => 'PRODLIB',
    FILE_NAME      => 'ORDERS',
    OUTPUT_LIBRARY => 'MYTOOLS'
);

A successful call returns:

1

An unsuccessful call returns:

-1

The generated table function is named:

MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS

If a function with the generated name already exists, IBM replaces it.

Query the generated reader

SELECT *
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS()
);

This can return many rows.

For normal investigation, restrict the time or receiver range.

Query the last hour

SELECT
    ENTRY_TIMESTAMP,
    OPERATION,
    JOURNAL_ENTRY_TYPE,
    USER_NAME,
    JOB_NUMBER,
    JOB_USER,
    JOB_NAME,
    PROGRAM_LIBRARY,
    PROGRAM_NAME,
    ORDER_ID,
    CUSTOMER_ID,
    ORDER_STATUS,
    ORDER_TOTAL
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 1 HOUR
    )
)
ORDER BY ENTRY_TIMESTAMP DESC;

Replace the example application columns with the actual columns in the journaled file.

Limit record-operation types

The generated reader supports a JOURNAL_ENTRY_TYPES parameter.

SELECT
    ENTRY_TIMESTAMP,
    OPERATION,
    JOURNAL_ENTRY_TYPE,
    USER_NAME,
    JOB_NUMBER,
    JOB_USER,
    JOB_NAME,
    ORDER_ID,
    ORDER_STATUS
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 24 HOURS,
        JOURNAL_ENTRY_TYPES =>
            'DL,PT,PX,UP'
    )
)
ORDER BY ENTRY_TIMESTAMP DESC;

Common record-operation entry types include:

DL   Delete
PT   Record added
PX   Record added with previous-image context
UP   Update after image

The exact entries available depend on the journaling configuration and operation.

Use QSYS2.JOURNAL_CODE_INFO when a descriptive entry-type reference is needed.

Find changes to one business key

SELECT
    ENTRY_TIMESTAMP,
    OPERATION,
    USER_NAME,
    JOB_NUMBER,
    JOB_USER,
    JOB_NAME,
    PROGRAM_NAME,
    ORDER_ID,
    ORDER_STATUS,
    ORDER_TOTAL,
    COMMIT_CYCLE
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 7 DAYS
    )
)
WHERE ORDER_ID = 100245
ORDER BY
    ENTRY_TIMESTAMP,
    SEQUENCE_NUMBER;

This can help answer:

Compare before and after images

Whether both images are available depends on the journaled object and journal configuration.

A query can return entry types in sequence:

SELECT
    ENTRY_TIMESTAMP,
    SEQUENCE_NUMBER,
    JOURNAL_ENTRY_TYPE,
    OPERATION,
    ORDER_ID,
    ORDER_STATUS,
    ORDER_TOTAL
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 1 DAY
    )
)
WHERE ORDER_ID = 100245
ORDER BY SEQUENCE_NUMBER;

Do not assume every update has both a before and after image. Confirm the journal settings and entry types being deposited.

Available filter parameters

The generated reader supports many of the same filters as QSYS2.DISPLAY_JOURNAL, including:

STARTING_RECEIVER_LIBRARY
STARTING_RECEIVER_NAME
STARTING_TIMESTAMP
STARTING_SEQUENCE
JOURNAL_ENTRY_TYPES
OBJECT_MEMBER
USER
JOB
PROGRAM
ENDING_RECEIVER_LIBRARY
ENDING_RECEIVER_NAME
ENDING_TIMESTAMP
ENDING_SEQUENCE

A value cannot be supplied for both a starting timestamp and starting sequence in the same request.

Query one job

SELECT
    ENTRY_TIMESTAMP,
    OPERATION,
    ORDER_ID,
    ORDER_STATUS,
    PROGRAM_NAME,
    COMMIT_CYCLE
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 1 DAY,
        JOB =>
            '123456/APPUSER/ORDERJOB'
    )
)
ORDER BY ENTRY_TIMESTAMP DESC;

Query one program

SELECT
    ENTRY_TIMESTAMP,
    OPERATION,
    USER_NAME,
    JOB_NUMBER,
    JOB_USER,
    JOB_NAME,
    ORDER_ID,
    ORDER_STATUS
FROM TABLE(
    MYTOOLS.DISPLAY_JOURNAL_PRODLIB_ORDERS(
        STARTING_TIMESTAMP =>
            CURRENT TIMESTAMP - 1 DAY,
        PROGRAM =>
            'ORDUPD'
    )
)
ORDER BY ENTRY_TIMESTAMP DESC;

Unsupported application column types

IBM documents that the generated reader does not return application columns with these data types:

CLOB
DBCLOB
BLOB
XML
DATALINK
user-defined types

The journal metadata columns are still part of the generated interface, but those file columns are not exposed as decoded application columns.

Row and column access control

Journal data can contain sensitive historical values.

QSYS2.DISPLAY_JOURNAL recognizes active row permissions and column masks for the target table when returning entry data.

Still, do not assume journal access is harmless.

Journal history may expose:

Apply least privilege to the generated function and underlying journal objects.

Creation authority

The caller creating the reader needs:

The generated table function is:

Owned by the caller
Created with *PUBLIC *EXCLUDE

That is a good secure default.

Grant access deliberately.

Reader authority

A user invoking the generated function needs authority to:

A missing-authority error can occur even when the user can query the live table.

Regenerate after a schema change

The generated function is based on the file definition at creation time.

After adding, removing, renaming, or changing table columns, regenerate it:

VALUES QSYS2.CREATE_DATA_JOURNAL_READER(
    LIBRARY_NAME   => 'PRODLIB',
    FILE_NAME      => 'ORDERS',
    OUTPUT_LIBRARY => 'MYTOOLS'
);

Because the existing function is replaced, review dependent objects and grants as part of the deployment process.

SQL long names

FILE_NAME must be the system object name.

For an SQL table with a long name, find the system name:

SELECT
    TABLE_SCHEMA,
    TABLE_NAME,
    SYSTEM_TABLE_SCHEMA,
    SYSTEM_TABLE_NAME
FROM QSYS2.SYSTABLES
WHERE TABLE_SCHEMA = 'PRODLIB'
  AND TABLE_NAME = 'CUSTOMER_ORDERS';

Pass SYSTEM_TABLE_NAME to CREATE_DATA_JOURNAL_READER.

Native option versus generated reader

Use DSPJRN when:

Use QSYS2.DISPLAY_JOURNAL when:

Use a generated data journal reader when:

Release and PTF requirement

IBM lists the service as new with:

IBM i 7.6 — Db2 Group PTF SF99960 Level 3
IBM i 7.5 — Db2 Group PTF SF99950 Level 12

It is not listed as supported on IBM i 7.4.

A practical workflow

1. Confirm the file is journaled.
2. Identify its system library and system file name.
3. Choose a controlled output library.
4. Generate the reader.
5. Grant only required users access.
6. Query a narrow timestamp or receiver range.
7. Filter by business key, job, user, or program.
8. Validate entry types and journal configuration.
9. Regenerate after file-definition changes.
10. Protect journal history as sensitive data.

Final takeaway

CREATE_DATA_JOURNAL_READER converts a file’s journal history from generic entry data into a file-aware SQL interface.

It does not replace journaling knowledge, receiver management, or authority controls—but it makes record-level investigation dramatically easier when the business columns are available directly in the result.

References

IBM documentation and support references used for this entry.

Comments

Share your thoughts, questions, or real-world IBM i experiences related to this article.