Библиотека на C++/Qt для автоматического перевода SQL‑диалектов между Oracle и PostgreSQL. Подходит для миграций, статического анализа и полуавтоматической конвертации DDL/DML в приложениях.
- Oracle → PostgreSQL и PostgreSQL → Oracle (двунаправленный перевод).
- Регулярные выражения case‑insensitive, аккуратные границы слов — меньше ложных срабатываний.
- Сохранение аргументов функций/типов:
NUMBER(10,2) → NUMERIC(10,2),SUBSTR(s,2,5) → SUBSTRING(s FROM 2 FOR 5)и т. п. - Корректные соответствия распространённых конструкций:
NVL → COALESCEDECODE(...) → CASE ... END(с поддержкойELSE)TO_NUMBER(...) → to_number(...)TO_DATE(str, fmt) → to_date(...) | to_timestamp(...)(автоопределение по наличиюHH/MI/SS)TRUNC(date, 'unit') → date_trunc('unit', date)и обратноINSTR(s, sub) ↔ POSITION(sub IN s)- Типы данных:
NUMBER/NVARCHAR2/VARCHAR2/RAW/BLOB/CLOB/LONG/NCHAR/NCLOB/INTERVAL↔NUMERIC/VARCHAR/BYTEA/TEXT/... EXPLAIN PLAN FOR↔EXPLAINSYSTIMESTAMP/SYSDATE↔CURRENT_TIMESTAMP
- Понятные подсказки там, где нужен ручной рефакторинг:
LEVEL,CONNECT BY↔WITH RECURSIVE— вставляются комментарии‑заглушки.
- Опциональная вставка/удаление
FROM DUAL:- Oracle:
SELECT 1 FROM DUAL;→ PostgreSQL:SELECT 1; - PostgreSQL:
SELECT 1;→ Oracle:SELECT 1 FROM DUAL;
- Oracle:
- C++17+
- Qt 5.15+ или Qt 6.x (модули Core)
#include "translateSQL.h"
#include <QString>
#include <iostream>
int main() {
translateSQL tr;
QString sql = "SELECT NVL(name,'n/a'), TRUNC(hire_date,'MONTH') FROM dual";
QString res = tr.translateString(sql, translateSQL::oracleToPostgres);
std::cout << res.toStdString() << std::endl;
// → SELECT COALESCE(name,'n/a'), date_trunc('month', hire_date)
QString pg = "SELECT SUBSTRING(title FROM 5 FOR 3), POSITION('x' IN title);";
QString res2 = tr.translateString(pg, translateSQL::postgresToOracle);
std::cout << res2.toStdString() << std::endl;
// → SELECT SUBSTR(title, 5, 3), INSTR(title, 'x');
}Примечание про
FROM DUAL:
- В Oracle
DUALиспользуется для выборки «из ниоткуда». В PostgreSQLFROMможно опускать.- В библиотеке предусмотрены правила для удаления
FROM DUALпри переводе в PostgreSQL и добавления при обратном преобразовании уSELECTбезFROM(в т. ч. послеUNION/INTERSECT/EXCEPT). Их можно включить/выключить, оставив/закомментировав соответствующие правила вrulesO2P_иrulesP2O_.
- Типы:
NUMBER(p,s) → NUMERIC(p,s)•VARCHAR2(n [BYTE|CHAR]) → VARCHAR(n)•NVARCHAR2(n) → VARCHAR(n)
DATE(type) → TIMESTAMP•BLOB → BYTEA•CLOB/LONG/NCLOB → TEXT•RAW(n) → BYTEA•NCHAR(n) → CHAR(n)
INTERVAL DAY TO SECOND|YEAR TO MONTH → INTERVAL - Функции:
NVL(a,b) → COALESCE(a,b)•DECODE(expr, s1, r1, [s2, r2]..., [else]) → CASE expr WHEN s1 THEN r1 ... [ELSE ...] END
TO_NUMBER(x[, fmt]) → to_number(x[, fmt])
TO_DATE(str, fmt) → to_date(str, fmt)(если естьHH/MI/SS→to_timestamp(...))
TRUNC(dt[, 'unit']) → date_trunc('unit', dt)
SUBSTR(s, n[, m]) → SUBSTRING(s FROM n [FOR m])
INSTR(s, sub) → POSITION(sub IN s)
SYSTIMESTAMP|SYSDATE → CURRENT_TIMESTAMP
EXPLAIN PLAN FOR → EXPLAIN - Иерархия:
LEVEL,CONNECT BY→ комментарии с подсказкой переписать на рекурсивный CTE.
- Типы:
NUMERIC(p,s) → NUMBER(p,s)•VARCHAR(n) → VARCHAR2(n)•BYTEA → BLOB•TEXT → CLOB
INTERVAL → INTERVAL DAY TO SECOND•TIMESTAMP [WITH/WITHOUT TIME ZONE] → TIMESTAMP[/WITH TIME ZONE] - Функции:
COALESCE(a,b) → NVL(a,b)(только 2 аргумента; при 3+ лучше использоватьCOALESCEв Oracle)
TO_TIMESTAMP(...) → TO_TIMESTAMP(...)
DATE_TRUNC('unit', dt) → TRUNC(dt[, 'unit'])
SUBSTRING(s FROM n [FOR m]) → SUBSTR(s, n[, m])
POSITION(sub IN s) → INSTR(s, sub)
CURRENT_TIMESTAMP → SYSTIMESTAMP
EXPLAIN → EXPLAIN PLAN FOR - Без FROM:
ВставкаFROM DUALвSELECTбез источника (в начале оператора и послеUNION/INTERSECT/EXCEPT).
CREATE TABLE t_emp (
id NUMBER(10),
name VARCHAR2(100 CHAR),
nick NVARCHAR2(40),
hire_date DATE,
photo BLOB,
note CLOB,
raw_col RAW(16),
long_col LONG,
nchar_col NCHAR(5),
nclob_col NCLOB,
dur INTERVAL DAY TO SECOND
);
SELECT SYSDATE, SYSTIMESTAMP FROM dual;
SELECT NVL(phone, 'n/a') AS p FROM t_emp;
SELECT DECODE(status, 'OK', 1, 'N/A, TBD', 0, -1) AS s FROM t_status;
SELECT TO_NUMBER('1,234.56', '9G999D99') AS v;
SELECT TO_DATE('2025-08-22','YYYY-MM-DD') d1,
TO_DATE('2025-08-22 13:45','YYYY-MM-DD HH24:MI') d2;
SELECT TRUNC(hire_date, 'MONTH') m, TRUNC(hire_date) d FROM t_emp;
SELECT SUBSTR(name, 2, 5), SUBSTR(name, 3), INSTR(name, 'a') FROM t_emp;
SELECT LEVEL, id FROM t_tree CONNECT BY PRIOR id = parent_id START WITH parent_id IS NULL;
EXPLAIN PLAN FOR SELECT 1 FROM dual;CREATE TABLE t_doc (
id NUMERIC(10,2),
title VARCHAR(200),
payload BYTEA,
body TEXT,
created_at TIMESTAMP,
period INTERVAL
);
SELECT CURRENT_TIMESTAMP;
SELECT COALESCE(phone, 'n/a') FROM t_emp;
SELECT TO_TIMESTAMP('2025-08-22 12:34','YYYY-MM-DD HH24:MI');
SELECT DATE_TRUNC('day', created_at), DATE_TRUNC('month', created_at) FROM t_emp;
SELECT SUBSTRING(title FROM 5 FOR 3), SUBSTRING(title FROM 3), POSITION('x' IN title) FROM t_doc;
WITH RECURSIVE r(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM r WHERE n < 3) SELECT * FROM r;
EXPLAIN SELECT * FROM t_emp;
SELECT 1 UNION ALL SELECT 2; -- должно превратиться в ... FROM DUAL- Иерархические запросы Oracle (
CONNECT BY,LEVEL) требуют ручной перекладки на рекурсивные CTE в PostgreSQL. - Расширенные формы
INSTRс 3–4 аргументами пока не конвертируются (позиция/вхождение). COALESCEс >2 аргументами лучше оставлять как есть при переводе в Oracle (Oracle его поддерживает; если нужно строгоNVL— только для 2 арг.).- Форматы дат обрабатываются эвристикой: наличие
HH/MI/SSтрактуется как время →to_timestamp. - README описывает «модернизированную» версию класса. Если используется первоначальный вариант, часть возможностей может отсутствовать, обновите
translateSQL.cpp/.hдо актуальной версии.