Technical-Specification
Jump to navigation
Jump to search
Technical Specification[edit]
Document ID: PGCBL-TSD-001 · Version: 1.0 · Status: Approved · Last updated: 2026-03-17
← Business Requirements | Data Dictionary →
Contents[edit]
- System Architecture
- Component Inventory
- Pseudocode
- Database Connection Parameters
- SQL Statement Inventory
- File I/O Specification
- Known Technical Issues
1. System Architecture[edit]
1.1 Three-Tier Model[edit]
┌──────────────────────────────────────────────────────┐
│ primesmain │
│ Orchestrator · session controller · entry point │
│ Source: primesmain.cbl │
└───────────────────────┬──────────────────────────────┘
│ CALL "primesgen" USING primes-session
┌─────────▼──────────────┐
│ primesgen │
│ Business Logic tier │
│ Sieve algorithm or │
│ cursor read-loop │
│ Source: primesgen.cbl │
└──────┬─────────────────┘
CALL "primes" │ │ CALL "primesui"
USING primes-dal │ │ USING primes-ui
┌─────────▼──┐ ┌───▼──────────────┐
│ primes │ │ primesui │
│ Data │ │ Presentation │
│ Access │ │ Print file │
│ Layer │ │ Console log │
│ All SQL │ │ primesui.cbl │
│ primes.cbl│ └───────────────────┘
└──────┬─────┘ │
│ EXEC SQL │ WRITE to
│ via GixSQL │ primes.prt
┌──────▼─────────────┐ │
│ PostgreSQL 11 │ │
│ schema: primes │ ▼
│ table: primes │ (file system)
└────────────────────┘
Design rule: SQL appears only in primes.cbl. File I/O appears only in primesui.cbl. All other programs use the method-dispatch pattern to delegate to these two.
1.2 Inter-Tier Communication Pattern[edit]
Every program-to-program call uses the same protocol:
Step 1 Caller MOVEs a verb string into the control block's methods field
MOVE "connect" TO dal-methods
Step 2 Caller invokes the subprogram, passing the control block by reference
CALL "primes" USING primes-dal
Step 3 Callee EVALUATEs the verb and performs the matching paragraph
EVALUATE TRUE
WHEN db-connect → PERFORM s00-connect
...
Step 4 Callee sets a result code before EXIT PROGRAM
MOVE 0 TO dal-result ← success
Step 5 Caller inspects the result code
IF dal-method-ok THEN ...
| Tier boundary | Control block | Verbs | Result codes |
|---|---|---|---|
| primesmain → primesgen | primes-session
|
report · generate
|
0=ok · 1=nok · 9=eof |
| primesgen → primes | primes-dal
|
connect · cursor · next-prime · next-divider · write · disconnect
|
0=ok · 1=nok · 99=eof |
| any → primesui | primes-ui
|
start · write · log-message · stop
|
0=ok · 1=nok |
2. Component Inventory[edit]
Programs[edit]
| Program | Source file | Tier | Contains SQL | Opens files |
|---|---|---|---|---|
primesmain
|
primesmain.cbl
|
Orchestrator | No | No |
primesgen
|
primesgen.cbl
|
Business logic | No | No (FD declared but unused) |
primes
|
primes.cbl
|
Data access | Yes — via GixSQL | No |
primesui
|
primesui.cbl
|
Presentation | No | Yes — primes.prt
|
Copybooks[edit]
| Copybook | Root record | Used by | Purpose |
|---|---|---|---|
primes-session.cpy
|
primes-session
|
primesmain · primesgen | Session-tier control block |
primes-dal.cpy
|
primes-dal
|
primesgen · primes | DAL-tier control block |
primes-ui.cpy
|
primes-ui
|
all four | UI-tier control block |
primes-table.cpy
|
primes-table
|
primes | SQL host variables — character format |
primes_table.cpy
|
primes_table
|
primes | SQL host variables — COMP-3 format ⚠ duplicate |
SQLCA.cpy
|
SQLCA
|
primes | GixSQL SQL Communications Area |
Full field definitions: Data Dictionary → Control Blocks
3. Pseudocode[edit]
3.1 primesmain[edit]
PROGRAM primesmain
ACCEPT commandline-args FROM COMMAND-LINE ← PIC X(32)
MOVE commandline-args TO methods ← load session control block
MOVE "primesmain" TO program-name
CALL primesui("start") ← open primes.prt
IF ui-method-result ≠ 0 THEN
DISPLAY "Emergency: UI failed" ON console
STOP RUN ← hard abort, no cleanup
LOG "UI initialisation succeeded."
LOG commandline-args
EVALUATE commandline-args
WHEN "report"
LOG "Primes report generation starts."
MOVE "report" TO methods
CALL primesgen USING primes-session ← report path
WHEN "generate"
LOG "Primes generation starts."
MOVE "generate" TO methods
── CALL primesgen commented out ── ← ⚠ Defect T1
WHEN OTHER
LOG "Bad parameter, program initialisation failed."
CALL primesui("stop")
STOP RUN
END-EVALUATE
LOG "Primes run complete, program stops."
CALL primesui("stop") ← flush + close primes.prt
STOP RUN
END PROGRAM
3.2 primesgen — Generate Path[edit]
PROCEDURE r91-start-primes-generation
CALL primes("connect")
IF dal-method-ok THEN
test-divider = 2 ← first divisor
old-ident = 1
test-number = 3 ← first candidate (odd)
test-number-sqr = SQRT(3) ← COMPUTE test-number ** 0.5
LOG "Database initialisation succeeded."
ELSE
LOG "Database initialisation failed."
session-result = 1
RETURN ← falls through to r99-close-primes
MAIN LOOP — PERFORM r80-test-number UNTIL test-number = 999999999
PROCEDURE r80-test-number
DIVIDE test-number BY test-divider
GIVING test-quot REMAINDER test-rest
EVALUATE TRUE
WHEN test-rest = 0 ← divisible → composite
PERFORM r82-next-test-number
WHEN test-divider > test-number-sqr ← no factor ≤ √N → prime
PERFORM r85-write-prime
PERFORM r82-next-test-number
WHEN OTHER ← keep trying
PERFORM r89-get-next-divider
PROCEDURE r82-next-test-number
ADD 2 TO test-number ← skip even numbers
COMPUTE test-number-sqr = test-number ** 0.5
MOVE 1 TO old-ident
PERFORM r89-get-next-divider ← reload first divisor
PROCEDURE r85-write-prime
MOVE "write" TO ui-methods
CALL primes USING primes-dal ← INSERT INTO primes (prime)
CALL primesui USING primes-ui ← accumulate in print buffer
PROCEDURE r89-get-next-divider
MOVE "get" TO gen-methods ← verb for the local primes group
CALL primes USING primes ← SELECT prime WHERE ident = old-ident+1
← sets test-divider
END LOOP
CALL primes("disconnect")
EXIT PROGRAM
3.3 primesgen — Report Path[edit]
PROCEDURE r90-start-primes-report
CALL primes("connect")
IF NOT dal-method-ok THEN
LOG "Database initialisation failed."
session-result = 1
GOTO r99-close-primes
CALL primes("cursor") ← START TRANSACTION; OPEN primescursor
IF NOT dal-method-ok THEN
LOG "Cursor initialisation failed."
session-result = 1
GOTO r99-close-primes
PERFORM r94-fetch ← position cursor on first row
IF dal-method-ok THEN
LOG "Fetch first row ok."
MOVE 0 TO dal-result
ELSE
LOG "Fetch first row nok."
MOVE 1 TO dal-result
REPORT LOOP — PERFORM r86-report-primes UNTIL session-method-eof
PROCEDURE r86-report-primes
MOVE "write" TO ui-methods
MOVE primes-data TO u-primes ← primes-sequence + prime-number → u-sequence + u-number
CALL primesui USING primes-ui ← write to 6-column print buffer
PERFORM r94-fetch ← advance cursor
PROCEDURE r94-fetch
MOVE "next-prime" TO dal-methods
CALL primes USING primes-dal ← FETCH primescursor INTO :primes-row
IF NOT dal-method-ok THEN
LOG "Fetch failed."
session-result = 1 ← ⚠ sets nok (1), not eof (9) — loop may not terminate
← See Defect T7 in defect register
END LOOP
CALL primes("disconnect")
EXIT PROGRAM
3.4 primes (DAL)[edit]
PROGRAM primes USING primes-dal
EVALUATE TRUE on dal-methods
WHEN db-connect → PERFORM s00-connect
WHEN db-cursor → PERFORM s01-cursor
WHEN next-prime → PERFORM r80-get-next-prime (→ s02-fetch)
WHEN next-divider → PERFORM r81-get-next-divider
WHEN write-prime → PERFORM r83-write-prime
WHEN db-disconnect → PERFORM s99-disconnect
WHEN OTHER → MOVE 1 TO dal-result
END-EVALUATE
EXIT PROGRAM
── s00-connect ──────────────────────────────────────────────────────
EXEC SQL
CONNECT TO :DATASRC AS primes USER :DBUSR USING :DBPWD
END-EXEC
IF SQLCODE = 0 THEN dal-result = 0
ELSE LOG error; dal-result = 1
── s01-cursor ───────────────────────────────────────────────────────
EXEC SQL AT primes START TRANSACTION END-EXEC
IF SQLCODE = 0 THEN LOG "Start transaction ok."
ELSE dal-result = 1
IF dal-method-ok THEN
EXEC SQL OPEN primescursor END-EXEC
IF SQLCODE = 0 THEN LOG "Start primescursor ok."
ELSE dal-result = 1
── s02-fetch (called by r80-get-next-prime) ─────────────────────────
EXEC SQL FETCH primescursor INTO :primes-row END-EXEC
IF SQLCODE = 0 THEN
primes-sequence = r-ident
prime-number = r-prime
dal-result = 0
ELSE LOG error; dal-result = 1
── r81-get-next-divider ─────────────────────────────────────────────
ADD 1 TO old-ident GIVING new-ident
EXEC SQL AT primes
SELECT prime INTO :test-divider
FROM primes WHERE ident = :new-ident
END-EXEC
IF SQLCODE ≠ 0 THEN DISPLAY SQLCODE; DISPLAY "select new-divider nok"
MOVE new-ident TO old-ident
── r83-write-prime ──────────────────────────────────────────────────
EXEC SQL AT primes
INSERT INTO primes (prime) VALUES (:prime)
END-EXEC
IF SQLCODE ≠ 0 THEN DISPLAY SQLCODE; DISPLAY "insert nok: " prime-number
── COMMIT is commented out ── ← ⚠ Defect T3
── s99-disconnect ───────────────────────────────────────────────────
EXEC SQL CONNECT RESET primes END-EXEC ← ⚠ Defect T2: cbsql.out shows "primesdb"
IF SQLCODE = 0 THEN dal-result = 0
ELSE dal-result = 1
DISPLAY "s99 disconnect from database"
DISPLAY SQLCODE
3.5 primesui[edit]
PROGRAM primesui USING primes-ui
EVALUATE TRUE on ui-methods
WHEN start-ui → PERFORM r90-start-primesui
WHEN write-ui → PERFORM r92-write-primesui
WHEN message-ui → PERFORM r98-message-ui
WHEN stop-ui → PERFORM r99-stop-primesui
WHEN OTHER → MOVE 1 TO ui-method-result
END-EVALUATE
EXIT PROGRAM
── r90-start-primesui ───────────────────────────────────────────────
OPEN OUTPUT fprinter (primes.prt)
IF primes-prt-status = "00" THEN
SET primes-idx TO 1
MOVE 0 TO ui-method-result
LOG "Open printer Ok"
ELSE
MOVE 1 TO ui-method-result
DISPLAY primes-prt-status ON console
── r92-write-primesui ───────────────────────────────────────────────
t-ident(primes-idx) = u-sequence ← load into current cell
t-prime(primes-idx) = u-number
SET primes-idx UP BY 1 ← advance index
IF new-page THEN PERFORM r93-new-page ← heading + column header
IF primes-idx = 7 THEN ← 6 entries filled
WRITE file-buffer FROM primes-table ← 132-char data line
SET primes-idx TO 1
IF linage-counter = 53 THEN ← 3 lines from page end
PERFORM r94-eop ← footing + page increment
── r93-new-page ─────────────────────────────────────────────────────
WRITE primes-heading (="primes overview")
WRITE table-header (="Sequence Prime " × 6)
MOVE ZERO TO print-new-page ← clear flag
── r94-eop ──────────────────────────────────────────────────────────
WRITE blank line
WRITE primes-footing (right-aligned "page: ZZZZ9")
ADD 1 TO page-number
MOVE 1 TO print-new-page ← trigger heading on next write
── r98-message-ui ───────────────────────────────────────────────────
DISPLAY process-message UPON scherm ← program-name + paragraph + message
── r99-stop-primesui ────────────────────────────────────────────────
MOVE 0 TO u-sequence
MOVE 0 TO u-number
PERFORM r92-write-primesui UNTIL new-page ← flush partial line with zeros
LOG "close printer"
CLOSE fprinter
MOVE primes-prt-status TO ui-method-result
4. Database Connection Parameters[edit]
| Parameter | Value |
|---|---|
| Driver | pgsql (GixSQL native PostgreSQL driver)
|
| Host | localhost
|
| Port | 5432
|
| Database name | primes
|
| Schema | primes (set via default_schema=primes in connection string)
|
| Connection alias | primes (used in all EXEC SQL AT primes clauses)
|
| User | primes_user
|
| Password | pr1mes_user ⚠ hard-coded — see Defect T5
|
| Full connection string | pgsql://localhost:5432/primes&default_schema=primes
|
5. SQL Statement Inventory[edit]
| ID | Statement | Paragraph | Path |
|---|---|---|---|
| S1 | CONNECT TO :DATASRC AS primes USER :DBUSR USING :DBPWD
|
s00-connect
|
Both |
| S2 | START TRANSACTION
|
s01-cursor
|
Report only |
| S3 | DECLARE primescursor CURSOR FOR SELECT * FROM primes
|
Compile-time | Report only |
| S4 | OPEN primescursor
|
s01-cursor
|
Report only |
| S5 | FETCH primescursor INTO :primes-row
|
s02-fetch
|
Report only |
| S6 | SELECT prime INTO :test-divider FROM primes WHERE ident = :new-ident
|
r81-get-next-divider
|
Generate only |
| S7 | INSERT INTO primes (prime) VALUES (:prime)
|
r83-write-prime
|
Generate only |
| S8 | CONNECT RESET primes
|
s99-disconnect
|
Both |
Host variable mappings:
| SQL host variable | COBOL field | PICTURE | Source |
|---|---|---|---|
:DATASRC
|
DATASRC
|
PIC X(64)
|
WS — primes.cbl
|
:DBUSR
|
DBUSR
|
PIC X(64)
|
WS — primes.cbl
|
:DBPWD
|
DBPWD
|
PIC X(64)
|
WS — primes.cbl
|
:primes-row
|
primes-row group
|
r-ident + r-prime COMP-3
|
WS — primes.cbl
|
:test-divider
|
test-divider
|
PIC 9(9)
|
WS — primes.cbl local primes group
|
:new-ident
|
new-ident
|
PIC 9(9)
|
WS — primes.cbl local primes group
|
:prime
|
prime-number
|
PIC 9(9)
|
Linkage — primes-dal.primes-data
|
6. File I/O Specification[edit]
Print file primes.prt[edit]
| Property | Value |
|---|---|
| Logical name | fprinter
|
| Physical name | primes.prt (relative to working directory)
|
| Organisation | Line-sequential |
| Record length | 132 characters |
| Linage | 56 lines per page |
| Footing area | Lines 55–56 (last 2 of linage) |
| Bottom margin | 2 lines |
| Top margin | 0 (not specified) |
| Open mode | OUTPUT (write-only; created fresh each run)
|
| File-status field | primes-prt-status PIC X(2)
|
| EOP trigger | linage-counter = 53 (3 lines before page end)
|
Record types[edit]
| Record | Content | Width |
|---|---|---|
| Page heading | "primes overview" left-justified
|
118 + 14 padding = 132 |
| Column header | "Sequence Prime " × 6
|
132 |
| Data line | 6 × (Z(9) seq + X(2) sp + Z(9) prime + X(2) sp)
|
132 |
| Blank separator | Spaces | 132 |
| Page footing | 118 spaces + "page: " + Z(3)9
|
132 |
7. Known Technical Issues[edit]
| ID | Severity | Description | Impact |
|---|---|---|---|
| T1 | 🔴 High | Generate loop (PERFORM r80-test-number) is commented out in primesmain.cbl line 51
|
generate mode does not execute the sieve
|
| T2 | 🔴 High | primes_cbsql.out shows disconnect targeting alias primesdb; runtime code uses alias primes
|
Disconnect call will fail with alias-not-found error at runtime |
| T3 | 🟡 Medium | COMMIT after INSERT is commented out in r83-write-prime
|
Relies on PostgreSQL auto-commit; INSERTs will not commit if an explicit transaction is open |
| T4 | 🟡 Medium | Prime 2 is not seeded by the algorithm; sieve starts at 3 with old-ident = 1, assuming a row exists at ident = 2
|
Divider lookup will fail or return wrong value on a fresh empty database |
| T5 | 🟠 Medium | Database credentials hard-coded in primes.cbl WORKING-STORAGE
|
Credential rotation requires recompile; credentials visible in binary |
| T6 | 🔵 Low | Two near-duplicate host-variable copybooks: primes-table.cpy (char) and primes_table.cpy (COMP-3, underscore)
|
Ambiguity about which is canonical; risk of one being stale |
| T7 | 🟡 Medium | On fetch failure, session-result is set to 1 (nok), not 9 (eof); the report loop condition session-method-eof (value 9) is never triggered by normal cursor exhaustion
|
Report loop may not terminate cleanly at end of data |