Mengapa Static Analysis PL/SQL Harus Ada di Pipeline CI

Kode basis data seperti package PL/SQL, trigger, dan migration script kerap diperlakukan berbeda dari kode backend application. Verifikasi sintaks sering kali baru terjadi saat deployment runtime ke staging database. Praktik ini lambat dan berisiko meloloskan anti-pattern kritis ke production.

Oracle SQLcl menyediakan utilitas CodeScan bawaan yang menjalankan static code analysis langsung pada file sumber SQL dan PL/SQL tanpa memerlukan koneksi database aktif. Engine ini mengevaluasi Abstract Syntax Tree (AST) kode menggunakan aturan berbasis Arbori (turunan aturan Trivadis), memungkinkan tim mendeteksi bug fungsional, celah keamanan, dan inefisiensi performa sebelum kode masuk ke proses merge.

Arsitektur CI untuk Verifikasi Kode PL/SQL

Arsitektur pipeline beroperasi dengan model pre-merge validation pada Pull Request:

  1. Filter Perubahan (Delta Detection): Runner mengecek file dengan ekstensi .sql, .pks, atau .pkb yang berubah terhadap target branch.
  2. Headless Execution: Menjalankan SQLcl dalam mode silent (/nolog) di dalam container ephemeral.
  3. Scan Execution: CodeScan memindai file target menggunakan aturan Trivadis/Arbori yang telah disepakati.
  4. Report Parsing & Severity Threshold: Runner mem-parsing laporan output (JSON/XML) dan menghitung jumlah violation berdasarkan level keparahan (Severe, Warning, Info).
  5. Status Check: Pipeline mengembalikan exit code 1 jika ditemukan pelanggaran pada level Severe/High, memblokir pull request secara otomatis.

Menyiapkan SQLcl Headless di Container GitHub Actions

SQLcl membutuhkan runtime Java (JDK 11 atau 17). Eksekusi paling efisien di CI adalah menggunakan container Oracle Linux resmi atau image Java standar dengan binary SQLcl yang di-cache.

Perintah minimal untuk menjalankan pemindaian headless tanpa kredensial database adalah:

sql -s /nolog <<EOF
codescan -input ./src/database -output ./reports/codescan-report.json -format json
exit
EOF

Flag -s mengaktifkan silent mode untuk meredam banner interaktif SQLcl, sedangkan /nolog mencegah SQLcl meminta autentikasi database saat inisialisasi.

Kustomisasi Aturan Arbori: Menangkap Anti-Pattern Kritis

CodeScan mengevaluasi ekspresi Arbori terhadap parse tree code. Berikut tiga anti-pattern umum yang wajib diblokir di pipeline CI beserta implementasi target deteksinya.

1. Swallowed Exception (WHEN OTHERS Tanpa RAISE)

Menangkap exception dengan WHEN OTHERS tanpa melakukan re-raise (RAISE atau RAISE_APPLICATION_ERROR) menyembunyikan fatal error di runtime dan merusak integritas transaksi data.

-- Anti-pattern:
EXCEPTION
  WHEN OTHERS THEN
    NULL; -- Error ditelan tanpa trace
END;

-- Target Arbori check:
whenOthersWithoutRaise:
  [node) exception_handler
  & [node) includes 'WHEN'
  & [node) includes 'OTHERS'
  & ! [node) includes 'RAISE'
  & ! [node) includes 'RAISE_APPLICATION_ERROR';

2. Penggunaan SELECT *

Klausa SELECT * di dalam package PL/SQL rentan terhadap error kompilasi ketika ada modifikasi kolom tabel dan menyebabkan overhead network buffer yang tidak terpakai.

-- Anti-pattern:
SELECT * INTO l_record FROM employees WHERE department_id = 10;

-- Arbori query pattern:
selectStarCheck:
  [query) query_block
  & [query) includes '*'
  & ! [query) includes 'COUNT(*)';

3. Hardcoded Literals

String SQL dinamis dengan literal hardcode memicu SQL injection dan memenuhi SGA shared pool karena ketiadaan bind variables.

Workflow CI Minimal (GitHub Actions)

Workflow berikut mendemonstrasikan eksekusi scan pada GitHub Actions runner, mengekspor hasil ke JSON, dan mengevaluasi severity menggunakan jq.

name: PL/SQL Static Analysis

on:
  pull_request:
    paths:
      - 'database/**.sql'
      - 'database/**.pks'
      - 'database/**.pkb'

jobs:
  sqlcl-lint:
    runs-on: ubuntu-latest
    steps:
      - name: Checkout Code
        uses: actions/checkout@v4

      - name: Set up Java JDK
        uses: actions/setup-java@v4
        with:
          java-version: '17'
          distribution: 'temurin'

      - name: Cache SQLcl
        id: cache-sqlcl
        uses: actions/cache@v4
        with:
          path: ~/sqlcl
          key: sqlcl-latest

      - name: Install SQLcl
        if: steps.cache-sqlcl.outputs.cache-hit != 'true'
        run: |
          curl -s -o sqlcl.zip https://download.oracle.com/otn_software/java/sqldev/sqlcl-latest.zip
          unzip -q sqlcl.zip -d ~/sqlcl
          chmod +x ~/sqlcl/sqlcl/bin/sql

      - name: Run CodeScan
        run: |
          export PATH="$HOME/sqlcl/sqlcl/bin:$PATH"
          mkdir -p reports
          sql -s /nolog <<EOF
          codescan -input database/ -output reports/scan-results.json -format json
          exit
          EOF

      - name: Validate Severity Threshold
        run: |
          if [ ! -f reports/scan-results.json ]; then
            echo "Report file not generated!"
            exit 1
          fi

          # Hitung issue dengan severity 1 (Severe) atau 2 (High)
          CRITICAL_ISSUES=$(jq '[.issues[] | select(.severity == "1" or .severity == "Severe")] | length' reports/scan-results.json)
          WARNING_ISSUES=$(jq '[.issues[] | select(.severity == "2" or .severity == "Warning")] | length' reports/scan-results.json)

          echo "Scan Summary: $CRITICAL_ISSUES critical issues, $WARNING_ISSUES warnings."

          if [ "$CRITICAL_ISSUES" -gt 0 ]; then
            echo "ERROR: Ditemukan $CRITICAL_ISSUES pelanggaran kritis pada PL/SQL."
            jq '.issues[] | select(.severity == "1" or .severity == "Severe") | {rule: .ruleId, file: .fileName, line: .line, desc: .description}' reports/scan-results.json
            exit 1
          fi

Trade-offs dan Limitasi

  • Dynamic SQL: CodeScan membedah token sintaks statis. Dynamic SQL di dalam EXECUTE IMMEDIATE yang dirakit secara modular tidak dapat dianalisis semantik isinya secara menyeluruh.
  • Kebutuhan Resource: Pemindaian direktori monorepo database besar membutuhkan alokasi memori heap JVM yang cukup. Jika runner OOM (Out Of Memory), batasi scan hanya pada file yang diubah menggunakan parameter git diff --name-only.
  • Database Dependency: CodeScan mengecek kaidah sintaks dan konvensi penulisan. Alat ini tidak memverifikasi validitas nama tabel atau relasi skema terhadap database riil.