Portabilitas database
Sistem: setiap instance INACT harus jalan di MySQL, PostgreSQL dan SQL Server dari satu code base. Itulah inti INA01, "Refactor INACT For Database Abstraction". Instance-instance memang berbeda: Medco dan Tomori menjalankan SQL Server, sisanya MySQL, dan QA master menjalankan ketiganya. SQL khusus MySQL rusak diam-diam di engine lain.
Cara halaman ini diverifikasi
Lapisan PDO, pemetaan driver dan helper legacy dibaca di master pada 2026-09-06. Fork mana yang masih punya helper legacy yang bekerja diperiksa di hari yang sama.
Lapisan-lapisannya
| Bagian | Apa itu |
|---|---|
DB_CONNECTION di .env | mysql, postgres atau mssql. |
Konstanta DB_DRIVER, db_config.php | Nama driver PDO: mysql, pgsql atau sqlsrv. Inilah yang menjadi cabang di kode. |
InactPDO::connect(), libraries/InactPDO.php | Membangun DSN per driver dan membuka PDO biasa. |
$pdo = new PDOLogActivity(), libraries/PDOLogActivity.php | Handle global. Pembungkus yang meneruskan setiap method PDO lewat __call() dan menambah ->log($text): $pdo->log("...")->exec($sql) menulis baris log aktivitas untuk insert, update dan delete. ->save() dan ->writeLog() adalah bagian di baliknya. |
phinx, phinx.php | Pengelolaan skema. Ia membaca .env yang sama, jadi migrasi ditulis sekali dalam DSL phinx dan berjalan di ketiga engine. |
InactPDO::getLimit() dan InactPDO::getAggregatedColumnNames() adalah helper portabilitasnya.
Helper legacy sudah hilang
iw_mysql_query() di libraries/inweb.lib.php dinonaktifkan: baris pertamanya throw new Exception("Error Processing Request", 1). Pemanggilan mysqli_* dan $dbLink ikut mati. Itu berlaku di master, Jadestone, Medco dan Timas. JOTRE masih punya versi yang bekerja, itu sebabnya beberapa layar legacy berjalan di sana dan tidak di tempat lain.
Jadi sebuah fitur bisa terlihat ada dan mati. Sebelum berasumsi apa pun bekerja, periksa bahwa jalur aktifnya memakai $pdo. Halaman modul mencantumkan jalur mati per modul. Layar yang gagal dengan "Error Processing Request" mencapai helper legacy.
Menulis SQL portabel
Pola yang bekerja di ketiga engine:
| Lakukan | Bukan | Kenapa |
|---|---|---|
$pdo->query($sql), lalu $stmt->fetch(), while ($r = $stmt->fetch()), $stmt->rowCount(); $pdo->exec($sql) untuk penulisan; $pdo->prepare($sql)->execute([...]) untuk nilai | iw_mysql_query(), mysqli_fetch_array() | helper legacy melempar exception |
$pdo->quote($v) | '$v' dengan mysqli_real_escape_string() | quote() mengembalikan nilai dengan tanda kutipnya; jangan tambah sendiri |
AND | && | di PostgreSQL && adalah operator array-overlap: error, bukan warning |
$limit = InactPDO::getLimit(1) setelah ORDER BY | LIMIT 1 | SQL Server butuh OFFSET 0 ROWS FETCH NEXT n ROWS ONLY, dan mewajibkan ORDER BY |
COALESCE() | IFNULL() | hanya MySQL |
| identifier polos | backtick | hanya MySQL |
| ekspresi tanggal yang sadar driver | NOW(), DATE_ADD(... INTERVAL ...), date(now()) | tidak portabel; kode bercabang pada DB_DRIVER untuk ini |
kutip nilai yang dibandingkan dengan kolom varchar: rout_indicate IN ('11','15','16') | IN (11,15,16) | PostgreSQL menolak character varying = integer. INA01-751 memperbaiki tepat ini di aturan tutup sign-off. |
GROUP BY eksplisit untuk setiap kolom yang dipilih | GROUP BY longgar dengan SET sql_mode = '' | trik sql_mode adalah artefak MySQL 5.7 |
if (DB_DRIVER == 'sqlsrv') { ... TOP n ... } else if (DB_DRIVER == 'pgsql') { ... CAST(...) ... } | satu string | pola percabangan kode yang ada; tiru |
Batasan engine yang perlu diingat:
| Engine | Versi produksi | Batasan |
|---|---|---|
| MySQL | 5.7.44 | tanpa CTE (WITH), tanpa window function; sintaks LIMIT a,b hanya MySQL |
| PostgreSQL | 14.22 | pengetikan ketat; &&; pelipatan huruf identifier |
| SQL Server | 2019 | tanpa LIMIT; TOP n atau OFFSET ... FETCH; kolom ntext perlu perhatian; identity insert butuh SET IDENTITY_INSERT ... ON |
Identity insert juga berbeda: SET IDENTITY_INSERT di SQL Server, OVERRIDING SYSTEM VALUE di PostgreSQL. Seed yang memasukkan id eksplisit harus menangani keduanya.
Mode fetch
$pdo->query() mengembalikan PDOStatement biasa; fetch() memberikan key numerik dan asosiatif kecuali kamu meneruskan PDO::FETCH_ASSOC. Kode yang dulu memakai mysqli_fetch_array() bergantung pada key ganda yang sama, itu sebabnya sebagian besar port mempertahankan default. Prepared statement di SQL Server kadang butuh PDO::ATTR_CURSOR => PDO::CURSOR_SCROLL supaya rowCount() bermakna; handler Document Library menunjukkan polanya.
Memverifikasi perubahan di tiga engine
Menalar dialek bukan bukti. Database uji master inact-v3-eris ada di ketiga engine di host dev bersama (MySQL di port 3357, PostgreSQL di 54314, SQL Server di 1433). Tulis satu skrip PHP yang:
- terhubung ke setiap engine lewat PDO dengan aturan DSN yang sama seperti
InactPDO::connect(); - mengisi baris yang sama ke
inact-v3-erisdi masing-masing; - menjalankan query lama dan query baru dan mencetak keduanya;
- menghapus baris ujinya.
Itu menangkap perbedaan identity insert dan perbedaan ketatnya tipe sekaligus. Untuk bug yang bergantung pada data nyata, jalankan juga query baru di snapshot SQL Server Medco dan snapshot MySQL Jadestone; database master hampir kosong.
Hal yang perlu diwaspadai
quote()menyertakan tanda kutip.WHERE x = '" . $pdo->quote($v) . "'menghasilkan''value''.rowCount()setelahSELECTandal di MySQL, tidak andal di tempat lain. Hitung di SQL atau fetch lalu hitung.lastInsertId()butuh nama sequence di PostgreSQL untuk beberapa tabel. Periksa cara kode tetangga melakukannya sebelum menyalin pemanggilan gaya MySQL.- Tanggal kembali sebagai string, dalam format engine-nya. Format saat keluar, jangan bandingkan mentah.
- Pembungkus
PDOLogActivitymenyembunyikan nama method dari analisis statis.$pdo->log()->exec()nyata; IDE-mu mungkin tidak berpikir begitu.