Published 20. November 2025
Introduction
In this article, we demonstrate how to migrate an Oracle Database to PostgreSQL.
Why would you want to do that? A common motivation is to make your application database-agnostic, allowing it to run on multiple database systems and thereby increasing your potential customer base.
There are several tools on the market that claim to automate this migration. KeepTool is one of them—easy to use and designed to automate migration tasks without requiring you to hand over full control to a “black-box” tool.
Over the past year, Hora’s built-in migration components have been refined through numerous real-world Oracle-to-PostgreSQL migration projects. This article explains how to use these capabilities efficiently.
Migration Tasks
Migrating an Oracle database to PostgreSQL involves several tasks:
-
Creating an equivalent table structure in PostgreSQL, including foreign keys, primary/unique keys, check constraints, and comments
-
Re-creating views using ANSI SQL and addressing differences in SQL syntax
-
Copying data from Oracle to PostgreSQL
-
Re-implementing PL/SQL logic using PL/pgSQL
We cover each of these steps in the following sections.
Creating a Table Structure in PostgreSQL
Before starting, review your Oracle schema and decide which tables actually need to be migrated. Some tables—such as temporary backup tables—may not be relevant in the target environment.
Hora’s flagging feature helps you mark the tables you want to migrate. In our example, we use a yellow bullet to identify migration candidates.

Flagged tables and the “DDL for PostgreSQL DB” menu item
Once your tables are flagged, select one or more of them and choose DDL for PostgreSQL DB. To understand the process, let’s begin with a single table.
Hora displays a dialog with PostgreSQL-compatible DDL. It starts by setting the PostgreSQL search_path, which plays a role similar to—but not identical with—Oracle’s current schema.
set search_path = hr;
Next, Hora generates a CREATE TABLE statement. For each Oracle column, Hora selects the closest PostgreSQL data type. While similar, there are important differences, which Hora handles automatically.
Example: PostgreSQL Table Definition
DROP TABLE IF EXISTS jobs;
CREATE TABLE jobs
(
job_id varchar(10) NOT NULL,
job_title varchar(35) NOT NULL,
min_salary int4,
max_salary int4
);
Hora also includes table and column comments if they exist in the Oracle schema:
COMMENT ON TABLE jobs IS
'jobs table with job titles and salary ranges. Contains 19 rows.
References with employees and job_history table.';
COMMENT ON COLUMN jobs.job_id IS
'Primary key of jobs table.';
COMMENT ON COLUMN jobs.job_title IS
'A not null column that shows job title, e.g. AD_VP, FI_ACCOUNTANT';
Constraints are generated next, each with its own comment:
ALTER TABLE jobs ADD CONSTRAINT job_id_pk PRIMARY KEY ( job_id)
NOT DEFERRABLE INITIALLY IMMEDIATE;
COMMENT ON CONSTRAINT job_id_pk ON jobs IS
'The values in the column JOB_ID should be unique for each JOBS data record.';
Creating the Stuctures for Multiple Tables
When generating DDL for multiple tables, the order of statements becomes important. For example, foreign keys referencing a table require that the referenced table’s primary key already exists.
Hora automatically determines the correct order.
Simply select multiple tables and choose DDL for PostgreSQL DB again.
The generated script is organized as follows:
-
DDL for tables, including comments, primary/unique keys, and indexes
-
DDL for all foreign keys
-
Trigger logic
-
Grants
This structured output lets you run the entire script at once without worrying about dependencies.
Migrating Trigger Logic
For each Oracle trigger, Hora creates:
Example:
create or replace function departments_bir_fnc()
returns trigger as
$body$
begin
IF coalesce( new.DEPARTMENT_ID,0) = 0 THEN
select nextval('departments_seq') INTO new.DEPARTMENT_ID ;
end IF;
return new;
end;
$body$ language plpgsql;
create or replace trigger departments_bir
before insert
on departments
for each row
execute function departments_bir_fnc();
Creating Views in PostgreSQL
Switch the overview context from tables to views. Any flagged views can also be processed with the DDL for PostgreSQL DB menu option.

Flagged view and DDL for PostgreSQL DB menu item
Hora generates a complete PostgreSQL-compatible definition, replacing Oracle-specific joins with ANSI-compliant syntax. In complex cases, Hora also adjusts built-in function calls and other Oracle-specific constructs.
Example output:
set search_path = hr;
-- VIEW: emp_details_view
drop view if exists emp_details_view;
create or replace view emp_details_view
(
employee_id, job_id, manager_id, department_id, first_name, last_name,
salary, commission_pct, location_id, department_name, country_id, city,
state_province, job_title, country_name, region_name
)
AS
select
E.EMPLOYEE_ID, E.JOB_ID, E.MANAGER_ID, E.DEPARTMENT_ID,
E.FIRST_NAME, E.LAST_NAME, E.SALARY, E.COMMISSION_PCT,
D.LOCATION_ID, D.DEPARTMENT_NAME,
L.COUNTRY_ID, L.CITY, L.STATE_PROVINCE,
J.JOB_TITLE,
C.COUNTRY_NAME,
R.REGION_NAME
from EMPLOYEES E
join DEPARTMENTS D on E.DEPARTMENT_ID = D.DEPARTMENT_ID
join LOCATIONS L on D.LOCATION_ID = L.LOCATION_ID
join COUNTRIES C on L.COUNTRY_ID = C.COUNTRY_ID
join REGIONS R on C.REGION_ID = R.REGION_ID
join JOBS J on J.JOB_ID = E.JOB_ID;
COMMENT ON VIEW emp_details_view IS
'Employees details view';
-- Grant privileges
grant read on emp_details_view to keeptool;
grant select on emp_details_view to keeptool;
You may also select multiple views to generate all definitions in a single script.
Copying Data from Oracle to PostgreSQL
Hora makes data migration straightforward by generating PostgreSQL-compatible INSERT scripts.
Start by opening the table in Tables → Data Content (or the Data Content Browser).
Right-click the grid and select Create INSERT Script.

Data content of the JOBS table and Create INSERT script menu item
In the dialog that appears, deselect all Oracle-specific options. Ensure that Use timestamp literals remains enabled. These settings produce PostgreSQL-compatible output.

Create INSERT script dialog using PostgreSQL compatible options
Example excerpt:
INSERT INTO JOBS( JOB_ID, JOB_TITLE, MIN_SALARY, MAX_SALARY)
VALUES( 'AD_PRES', 'President', 20080, 40000);
INSERT INTO JOBS( JOB_ID, JOB_TITLE, MIN_SALARY, MAX_SALARY)
VALUES( 'AD_VP', 'Administration Vice President', 15000, 30000);
INSERT INTO JOBS( JOB_ID, JOB_TITLE, MIN_SALARY, MAX_SALARY)
...
COMMIT;
This method can be applied to any flagged table.
Implementing Oracle PL/SQL Logic using PL/pgSQL
We’ve already seen how Hora migrates trigger logic. Most Oracle databases also contain stored procedures, functions, and packages. Migrating these requires a deeper analysis of your PL/SQL code.
A follow-up blog post will cover how to migrate PL/SQL logic to PL/pgSQL, including common patterns, differences in exception handling, and best practices.
Rewriting SQL Queries for PostgreSQL
Hora provides a built-in tool for automatically converting Oracle SQL queries to PostgreSQL, including adjustments for ANSI compliance. This feature is useful in several situations:
-
Queries inside PL/SQL code that are not automatically transformed when converting to PL/pgSQL.
These need to be converted individually using our tool.
-
Queries used in applications that are also being migrated from Oracle to PostgreSQL.
In all cases, the workflow is straightforward:
Select any query inside any SQL editor window, right-click, and choose “Rewrite SQL for PostgreSQL.”

“Rewrite SQL for PostgreSQL” menu item
Example
Once selected, KeepTool attempts to convert the query automatically.
For example, the following rewritten query is produced:
select
D.DEPARTMENT_NAME, D.DEPARTMENT_ID,
E.EMPLOYEE_ID, E.FIRST_NAME, E.LAST_NAME, E.DEPARTMENT_ID
from DEPARTMENTS D
right join EMPLOYEES E on D.DEPARTMENT_ID = E.DEPARTMENT_ID
The algorithm handles most queries reliably.
However, very complex SQL statements may not be fully convertible in a single step.
In such cases, try converting subqueries, UNION blocks, or other components separately to achieve the best results.
Summary
Migrating an Oracle database to PostgreSQL involves schema conversion, view translation, data transfer, and procedural logic migration. KeepTool’s Hora simplifies all these steps while giving you complete control over the generated SQL.
Stay tuned for our next article, which will dive into converting complex PL/SQL code to PostgreSQL’s PL/pgSQL.
➡️ Try KeepTool for free
KeepTool's Oracle tools are designed specifically for developers, DBAs, and support teams.
Based on over 25 years of experience, we continuously optimize our software to make your database analysis and documentation faster, more efficient, and clearer.