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:
ReplaceISwithAS $$ -
ERROR: syntax error at or near "AS" - LINE 2: AS $$:
Insert the missing()betweenPROCEDURE secure_dmlandAS $$ -
ERROR: unterminated dollar-quoted string at or near "$$ ... - LINE 2: AS $$:
ReplaceEND secure_dmlwithEND\n$$ LANGUAGE plpgsql -
syntax error at or near "RAISE_APPLICATION_ERROR" - LINE 6: RAISE_APPLICATION_ERROR (-20205,:
Replace procedure callRAISE_APPLICATION_ERRORwithRAISE EXCEPTION 'You may only make changes during normal office hours' USING ERRCODE = '-20205';
Upon implementing the aforementioned modifications, thesecure_dmlprocedure 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
SYSDATEwithclock_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: 1SYSDATE: 2TO_CHAR: 2
- Code metrics:
Lines of code: 9Number of statements: 2Cyclomatic 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 BYDECODE- 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.