Skip to content
PYD ENTERPRISES

PL/SQL & T-SQL

The business logic hiding in your database.

Packages, triggers, cursors and stored procedures hold fee formulas and status rules nobody remembers. PYD ENTERPRISES extracts them, with every table each one really changes.

LOANDB · SCHEMA SCANOracle 19c DDL

Packages

64

Triggers

118

Cursor loops

402

  • LOAN.FEE_BALwritten by 3 procs, 1 trigger
  • LOAN.WATCH_FLAGwritten only by TRG_LOAN_FEE_GL
  • GL_ENTRY11 writers, 9 of them triggers
  • RATE_INDEXread-only from PL/SQL

Mutation lineage

Every table a procedure changes, including the ones it does not mention.

Toggle trigger side effects and dynamic SQL resolution to see how much of a procedure's real behavior lives outside its own body.

PL/SQL

PKG_LOAN.NIGHTLY_LATE_FEES

Cursor loop over active loans past due; assesses the greater of 15.00 or 5% of the payment when days late exceed product grace days.

Tables written
4
Outside the proc body
3
  1. READ

    LOAN

    CURSOR c_due

  2. READ

    PRODUCT

    CURSOR c_due (JOIN)

  3. INSERT

    LOAN_FEE

    line 12, per row

  4. ↳ INSERT

    GL_ENTRY

    TRG_LOAN_FEE_GL, AFTER INSERT ON LOAN_FEE

    Credits 4410-LATE-FEE-INC. Not visible in the package source.

  5. ↳ UPDATE

    LOAN

    TRG_LOAN_FEE_GL, when amount > 100

    Sets WATCH_FLAG = 'Y'. A second rule hidden in a trigger.

  6. UPDATE

    LOAN

    line 14, FEE_BAL

  7. ↳ INSERT

    LOAN_AUDIT

    TRG_LOAN_AUD, AFTER UPDATE ON LOAN

    Fires for both LOAN updates above.

  8. COMMIT

    (transaction)

    line 18, once after loop

    All fees for the night commit or roll back together.

PKG_LOAN body (Oracle PL/SQL)SQL
1CREATE OR REPLACE PACKAGE BODY pkg_loan AS
2 PROCEDURE nightly_late_fees(p_as_of IN DATE) IS
3 CURSOR c_due IS
4 SELECT l.loan_id, l.next_due_dt, l.pmt_amt, p.grace_days
5 FROM loan l JOIN product p ON p.product_cd = l.product_cd
6 WHERE l.status = 'A' AND l.next_due_dt < p_as_of;
7 v_fee NUMBER(9,2);
8 BEGIN
9 FOR r IN c_due LOOP
10 IF p_as_of - r.next_due_dt > r.grace_days THEN
11 v_fee := GREATEST(15, ROUND(r.pmt_amt * 0.05, 2));
12 INSERT INTO loan_fee (loan_id, fee_type, amount, assessed_dt)
13 VALUES (r.loan_id, 'LATE', v_fee, p_as_of);
14 UPDATE loan SET fee_bal = fee_bal + v_fee
15 WHERE loan_id = r.loan_id;
16 END IF;
17 END LOOP;
18 COMMIT;
19 END nightly_late_fees;
20END pkg_loan;
TRG_LOAN_FEE_GLSQL
1CREATE OR REPLACE TRIGGER trg_loan_fee_gl
2AFTER INSERT ON loan_fee
3FOR EACH ROW
4BEGIN
5 INSERT INTO gl_entry (acct, dr_cr, amount, src_ref, posted_dt)
6 VALUES ('4410-LATE-FEE-INC', 'C', :NEW.amount,
7 'FEE:' || :NEW.loan_id, :NEW.assessed_dt);
8 IF :NEW.amount > 100 THEN
9 UPDATE loan SET watch_flag = 'Y' WHERE loan_id = :NEW.loan_id;
10 END IF;
11END;

ORACLE PL/SQL

One package, three rules, two of them elsewhere.

Rule 1, in the package. For active loans past due by more than the product's grace days, assess a late fee of GREATEST(15, ROUND(pmt_amt * 0.05, 2)).

Rule 2, in a trigger. Every late fee credits income account 4410. Move the package logic into a service without the trigger and the general ledger stops balancing.

Rule 3, also in the trigger. A fee above 100.00 puts the loan on a watch list. No package, screen or report mentions it.

The single COMMIT after the loop is recorded too: the night's fees are all-or-nothing today, and the target design has to decide whether to keep that.

Hidden transaction side effects

What a stored procedure does that its name does not say.

MICROSOFT T-SQL

Dynamic table names, resolved to candidates.

The INSERT target is built from @PayType. PYD ENTERPRISES matches the pattern against the DDL, lists Pay_CHK, Pay_EFT and Pay_WIR as candidates, links the reserve trigger, and flags the concatenated identifier for security review.

dbo.usp_ApplyClaimPayment (SQL Server T-SQL)SQL
1CREATE PROCEDURE dbo.usp_ApplyClaimPayment
2 @ClaimId INT, @Amount DECIMAL(12,2), @PayType CHAR(3)
3AS
4BEGIN
5 SET NOCOUNT ON;
6 BEGIN TRY
7 BEGIN TRANSACTION;
8 UPDATE dbo.ClaimReserve
9 SET Outstanding = Outstanding - @Amount
10 WHERE ClaimId = @ClaimId;
11 IF @@ROWCOUNT = 0
12 THROW 50001, 'No reserve for claim', 1;
13 DECLARE @sql NVARCHAR(400) =
14 N'INSERT INTO dbo.Pay_' + @PayType + N' (ClaimId, Amount) VALUES (@c, @a)';
15 EXEC sp_executesql @sql, N'@c INT, @a DECIMAL(12,2)', @c = @ClaimId, @a = @Amount;
16 COMMIT TRANSACTION;
17 END TRY
18 BEGIN CATCH
19 IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
20 THROW;
21 END CATCH
22END

COMMIT inside loops

Partial commit points that change failure semantics when the logic moves to a service.

Autonomous transactions

PRAGMA AUTONOMOUS_TRANSACTION blocks that persist even when the caller rolls back.

Trigger side effects

Writes performed by row and statement triggers, attributed to the statement that fired them.

File and queue output

UTL_FILE writes, DBMS_AQ enqueues and xp_cmdshell calls recorded as external effects.

Database links and synonyms

Remote writes over DB links and synonym chains resolved to the real object.

Scheduled entry points

DBMS_SCHEDULER jobs and SQL Agent steps linked to the procedures they run.

How stored procedure analysis works

Four passes over code and schema.

  1. 01

    Parse with the DDL

    Procedures, packages, functions and triggers are parsed against the schema DDL you export, so every column reference resolves to a typed column and every %ROWTYPE expands.

  2. 02

    Bind triggers to statements

    Each INSERT, UPDATE, DELETE and MERGE is linked to the row and statement triggers it fires, including triggers fired by triggers, with the column filters (UPDATE OF) that apply.

  3. 03

    Resolve dynamic SQL

    EXECUTE IMMEDIATE and sp_executesql strings are evaluated where they are built from literals and bounded parameters; candidate targets are listed, the rest flagged.

  4. 04

    Separate rules from mechanics

    Fee formulas, eligibility predicates and status transitions become rules; cursor housekeeping, logging tables and error wrappers are classified as plumbing.

Planning a move off procedures? See PL/SQL to microservices and T-SQL to .NET services.

FAQ

Database logic questions.

Do you need access to the production database?

No. PYD ENTERPRISES works from exported source and DDL: package specs and bodies, procedures, triggers, views and table definitions. Most teams export them with the tooling their DBAs already use and copy the files into the PYD ENTERPRISES ingest volume.

How are cursor loops turned into rules?

The loop body is analyzed per row with the cursor query as its input set. The WHERE clause becomes the population a rule applies to, and the statements inside become the rule's condition and effect, for example a late fee of the greater of 15.00 or 5% of the payment when days late exceed grace days.

Can PYD ENTERPRISES tell which procedures are still used?

It links procedures to their callers in application code that is part of the scan (COBOL EXEC SQL, Java JDBC, .NET ADO), to scheduler jobs and to triggers. Procedures with no caller in the scan are reported as orphans for your DBAs to confirm.

What about Informix 4GL and embedded SQL?

Informix 4GL programs, DB2 embedded SQL in COBOL and SQL in Java and .NET data access layers all feed the same lineage graph, so a column can be traced from the screen or batch program to every procedure and trigger that changes it.

Find the rules your DBAs inherited.

Run PYD ENTERPRISES on 100,000 lines of COBOL or Java in your private VPC and review OpenAPI specs and DMN tables tomorrow.