Proof of concept tooling library to migrate PL/SQL code to PL/pgSQL, written in Rust.
A Rust WebAssembly (WASM) application that parses Oracle PL/SQL code, assesses its structure, and transpiles it into equivalent PostgreSQL PL/pgSQL code.
- PL/SQL Parser: Parses Oracle PL/SQL code to build an appropriate AST.
- Code Assessment: Analyzes the structure and complexity of migrating the PL/SQL code.
- Transpiler: Converts Oracles PL/SQL code to PostgreSQLs PL/pgSQL.
- WebAssembly: Utilizes Rust's WebAssembly support for running in the browser.
Given a valid SQL DDL command to CREATE
a database object (DBO), this library should parse the statement, extract its
metadata, and calculate a quantitative measure (expressed in an abstract unit) representing the effort required to
migrate the DBO to PostgreSQL.
DBOs that typically demand significant effort for migration to PostgreSQL include:
- Functions
- Packages
- Procedures
- Triggers
- Views
In addition to the aforementioned DBOs, it may be necessary to evaluate the effort involved in migrating other language constructs to PostgreSQL, such as:
- Column check constraint expressions
- Column default expressions
- Expressions in functional indexes
The error handling mechanism should exhibit leniency. In instances where the analyzer fails to parse a particular language construct, it should provide a descriptive error output and promptly attempt to continue the analysis of the remaining components.
To showcase the necessary modifications a user has to undertake during migration, we will utilize the SECURE_DML
procedure of Oracles HR
sample schema.
-- Oracle PL/SQL
CREATE PROCEDURE secure_dml
IS
BEGIN
IF
TO_CHAR (SYSDATE, 'HH24:MI') NOT BETWEEN '08:00' AND '18:00'
OR TO_CHAR (SYSDATE, 'DY') IN ('SAT', 'SUN') THEN
RAISE_APPLICATION_ERROR (-20205, 'You may only make changes during normal office hours');
END IF;
END secure_dml;
Listed below are the error messages emitted by the PosgreSQL server, accompanied by the corresponding manual changes required to resolve said errors:
-
ERROR: syntax error at or near "IS" - LINE 2: IS
:
ReplaceIS
withAS $$
-
ERROR: syntax error at or near "AS" - LINE 2: AS $$
:
Insert the missing()
betweenPROCEDURE secure_dml
andAS $$
-
ERROR: unterminated dollar-quoted string at or near "$$ ... - LINE 2: AS $$
:
ReplaceEND secure_dml
withEND\n$$ LANGUAGE plpgsql
-
syntax error at or near "RAISE_APPLICATION_ERROR" - LINE 6: RAISE_APPLICATION_ERROR (-20205,
:
Replace procedure callRAISE_APPLICATION_ERROR
withRAISE EXCEPTION 'You may only make changes during normal office hours' USING ERRCODE = '-20205';
Upon implementing the aforementioned modifications, thesecure_dml
procedure can be created PostgreSQL. However, we will encounter an error when invoking the procedure:postgres=# CALL hr.secure_dml(); ERROR: column "sysdate" does not exist LINE 1: SELECT TO_CHAR (SYSDATE, 'HH24:MI') NOT BETWEEN '08:00' AND ... ^ QUERY: SELECT TO_CHAR (SYSDATE, 'HH24:MI') NOT BETWEEN '08:00' AND '18:00' OR TO_CHAR (SYSDATE, 'DY') IN ('SAT', 'SUN') CONTEXT: PL/pgSQL function hr.secure_dml() line 3 at IF
-
Replacing
SYSDATE
withclock_timestamp()
leads to a functional equivalent on PostgreSQL with PL/pgSQL.-- Migrated procedure to PL/pgSQL CREATE PROCEDURE secure_dml() AS $$ BEGIN IF TO_CHAR (clock_timestamp(), 'HH24:MI') NOT BETWEEN '08:00' AND '18:00' OR TO_CHAR (clock_timestamp(), 'DY') IN ('SAT', 'SUN') THEN RAISE EXCEPTION 'You may only make changes during normal office hours' USING ERRCODE = '-20205'; END IF; END $$ LANGUAGE plpgsql;
Objective: Evaluate the effort required to migrate the DBOs along with its corresponding code to PostgreSQL.
Using secure_dml
as an example, the metadata would be:
- Signature:
() -> ()
- Function/procedure invocations:
RAISE_APPLICATION_ERROR: 1
SYSDATE: 2
TO_CHAR: 2
- Code metrics:
Lines of code: 9
Number of statements: 2
Cyclomatic complexity: 2
Note
The user may choose to use Orafce - Oracle's compatibility functions and packages to reduce the migration effort. This choice will have an impact on the assessment figures.
Given a valid CREATE
SQL DDL command:
Construct | Supported |
---|---|
Constraints | ❌ |
DB Links | ❌ |
Functions | ✅ |
Indexes | ❌ |
Operators | ❌ |
Packages | ❌ |
Procedures | ✅ |
Queues | ❌ |
Referential constraints | ❌ |
Sequences | ❌ |
Tables | ❌ |
Triggers | ✅ |
User-defined types | ❌ |
Views | ✅ |
Most Oracle code in one way or another makes use of
the BLOCK
,
allowing multiple statements within.
Statement | Supported |
---|---|
Assignment statement | ❌ |
Basic loop statement | ❌ |
Case statement | ❌ |
Close statement | ❌ |
Collection method call | ❌ |
Continue statement | ❌ |
Cursor for loop statement | ❌ |
Declare section | ❌ |
Execute immediate statement | ❌ |
Exit statement | ❌ |
Fetch statement | ❌ |
For loop statement | ❌ |
Forall statement | ❌ |
Goto statement | ❌ |
If statement | ✅ |
Null statement | ✅ |
Open for statement | ❌ |
Open statement | ❌ |
Nested PL/SQL block | ✅ |
Pipe row statement | ❌ |
Procedure call | ✅ |
Raise statement | ❌ |
Return statement | ✅ |
SQL statement | Partially |
Select into statement | ✅ |
While loop statement | ❌ |
Statement | Supported |
---|---|
Collection type definition | ❌ |
Collection variable declaration | ❌ |
Constant declaration | ❌ |
Cursor declaration | ❌ |
Cursor variable declaration | ❌ |
Exception declaration | ❌ |
Function declaration | ❌ |
Procedure declaration | ❌ |
Record type definition | ❌ |
Record variable declaration | ❌ |
Ref cursor type definition | ❌ |
Subtype definition | ❌ |
Variable declaration | ❌ |
Statement | Supported |
---|---|
Collection method call | ❌ |
Commit statement | ❌ |
Delete statement | ❌ |
Insert statement | ✅ |
Lock table statement | ❌ |
Merge statement | ❌ |
Rollback statement | ❌ |
Savepoint statement | ❌ |
Set transaction statement | ❌ |
Update statement | ❌ |
- Code metrics
- Lines of code
- Database specific functionality
- Outer joins using the
(+)
syntax
- Outer joins using the
- Function signature
- Called functions/procedures
- Code metrics
- Number of statements
- Cyclomatic complexity (code path)
- Used SQL commands
- DDL (Data Definition Language)
- DML (Data Manipulation Language)
- DCL (Data Control Language)
- TCL (Transactional Control Language)
- Database specific functionality
- External modules (e.g.
DBMS
) CONNECT BY
DECODE
- and many more.
- External modules (e.g.
- Unsupported language constructs
- Global variables in packages
- Anonymous/initialization block in package
- Nested functions
Refer to the Development README
.
This project is licensed under the MIT License. See the LICENSE file for details.