Modernizing a custom Ellucian reporting layer from Oracle to PostgreSQL in Ellucian Insights
07 Sep 2026
A higher-education institution was moving to Ellucian SaaS while also transitioning its reporting environment from Cognos to Argos. But more than 200 existing reports depended on institution-specific Oracle database objects and data-loading logic. That meant the custom data layer underlying those reports also had to be modernized.
The migration scope focused on those customizations rather than baseline functionality already expected in the Ellucian Insights PostgreSQL environment. ABCloudz needed to identify the custom database behavior the institution still required for reporting, adapt it for PostgreSQL and move deployment and data refresh into Ellucian Data Connect, the Ellucian data orchestration environment used here for database-object deployment and data-loading pipelines.
Modernize the institution’s custom reporting data layer for Ellucian SaaS while preserving the business behavior its reports still required.
Why the reporting migration depended on custom Oracle logic
The legacy reporting environment relied on a custom schema in an Oracle 19 Operational Data Store. An initial inventory identified 136 custom objects used directly by reports, including tables, views, materialized views and functions. Dependency analysis uncovered additional objects behind that first layer.
The database objects were only part of the dependency chain. Custom tables also had to be populated. Some were loaded by existing Oracle Data Integrator (ODI) packages. Others derived their data from materialized-view logic or objects in other schemas. For another group, ABCloudz first had to determine how the tables were populated before defining the future-state approach.
That distinction mattered. The central migration problem was not the standard Ellucian data model. It was the institution-specific layer built around it and the business behavior encoded across database logic, dependencies and ETL.
The migration had to reproduce custom reporting behavior encoded across database objects, Oracle-specific constructs, dependencies and legacy ETL, not simply convert SQL syntax.
How do you move institution-specific Oracle reporting logic into Ellucian SaaS without carrying the legacy architecture with it?
Re-engineering the custom data layer for PostgreSQL
The generated code still had to be reviewed for behavior. The work included datatype corrections and explicit casts, Oracle compatibility functions, cursor and package-function calls, table-returning functions, row identifiers, synonyms, cross-schema references and function-based indexes.
The objective was not simply to make converted SQL compile. Each custom object had to preserve the behavior the reporting layer depended on.
See how Oracle-specific database behavior was rewritten for PostgreSQL
Replacing compatibility-layer calls with PostgreSQL-native behavior
AWS SCT could represent Oracle datatypes and built-in functions through its Oracle Extension Pack, a target-side compatibility layer that emulates source-database functionality needed by converted objects. The migration reduced those dependencies by converting applicable references to native PostgreSQL datatypes, functions and expressions.
Representative mappings included:
Converted dependency
PostgreSQL pattern
aws_oracle_ext.substr(…)
substr(…)
aws_oracle_ext.instr(…)
strpos(…)
aws_oracle_ext.to_char(…)
to_char(…)
aws_oracle_ext.to_date(…)
to_date(…)
aws_oracle_ext.sysdate
now()::timestamp
Oracle-style date truncation
date_trunc(‘day’, …)
Datatype review was also necessary because the type generated by AWS SCT was not always the right PostgreSQL type.
Explicit casting was another recurring case. A converted function call or join could look compatible by column name yet still fail because PostgreSQL enforced the target types differently.
A sanitized version of the correction pattern looks like this:
JOIN person
ON term.person_uid = person.person_uid::numeric
The important change is not the identifier itself. It is the explicit preservation of the expected function and comparison types.
Rebuilding cursor functions
Oracle functions using cursors created another conversion problem. AWS SCT could split one source function into a main function plus a generated cursor helper function. In some cases, parts of the generated PostgreSQL code remained unresolved.
ABCloudz consolidated the generated helper pattern into a single PL/pgSQL function, using PostgreSQL’s procedural language for stored database logic, with the cursor defined and consumed within that function.
A simplified structure is:
CREATE OR REPLACE FUNCTION custom_lookup(
lookup_type text,
lookup_value text
)
RETURNS varchar
AS $$
DECLARE
result_value varchar(255);
result_cursor CURSOR FOR
SELECT value_description
FROM validation_table
WHERE table_name = lookup_type
AND value = lookup_value;
BEGIN
OPEN result_cursor;
FETCH result_cursor INTO result_value;
CLOSE result_cursor;
RETURN result_value;
END;
$$ LANGUAGE plpgsql;
This restored the intended one-function behavior instead of retaining conversion-generated helper functions as part of the application design.
Correcting package-function calls
Package-function references also required review. Automatic conversion could leave the source function name in place rather than use the converted PostgreSQL function, or omit required function parameters.
The remediation pattern was to resolve the correct target function and restore its arguments:
Oracle pipelined functions did not map directly to the required PostgreSQL behavior.
The source pattern:
RETURN table_type PIPELINED
was reworked as a PostgreSQL function with an explicit table return definition:
RETURNS TABLE (
column_a datatype,
column_b datatype
)
AS $$
BEGIN
RETURN QUERY
SELECT ...
FROM ...;
END;
$$ LANGUAGE plpgsql;
Column references inside the query also had to be qualified where return-column names could otherwise make a reference ambiguous.
Repairing function-based indexes
Function-based indexes exposed another semantic difference. PostgreSQL requires functions used in index expressions to meet immutability requirements.
For expressions that needed a wrapper, the implementation introduced an IMMUTABLE PostgreSQL function and indexed the wrapper result:
CREATE OR REPLACE FUNCTION normalized_index_value(...)
RETURNS varchar
AS $$
BEGIN
RETURN ...;
END;
$$ LANGUAGE plpgsql
IMMUTABLE;
Custom-function indexes also had to be corrected when AWS SCT generated an internal expression rather than the intended converted custom function.
Together, these cases illustrate why automatic conversion served as a starting point rather than the definition of the final PostgreSQL implementation.
Replace Oracle-specific behavior with PostgreSQL-native patterns
Where the Oracle implementation relied on platform-specific behavior, ABCloudz selected PostgreSQL patterns that preserved the required semantics.
For transaction-local uses of Oracle ROWID, the design used PostgreSQL ctid and tid where their semantics matched the required behavior. Synonym-based references were resolved with fully qualified object names or wrapper objects. Database-link dependencies were analyzed separately, with direct references to objects in the same PostgreSQL database used where applicable.
See how ROWID, synonyms, and database links were handled
ROWID without creating a permanent surrogate
One option considered for dependencies on Oracle ROWID, a database-provided row locator rather than an application-level business identifier, was to create an additional row-identifier column, sequence and primary key in PostgreSQL.
For this project, the preferred pattern used PostgreSQL’s ctid, a system column that identifies the physical location of a row version, for the documented transaction-scoped ROWID usage rather than introducing an additional persistent column.
The mapping depended on object type:
Legacy usage
PostgreSQL treatment
Table access to ROWID
Use the table’s ctid
table.rowid in views or function calls
Replace with table.ctid
Function return type carrying a row identifier
Use tid
Function variable carrying a row identifier
Declare it as tid
This is intentionally a scoped pattern. ctid can change when a row is updated or moved, including by VACUUM FULL, so it should not be treated as a durable business identifier.
Resolving Oracle synonyms
Oracle synonyms hid the physical schema behind referenced objects. PostgreSQL required those relationships to become explicit.
For table-like objects, two patterns were available:
Create a view that exposes the underlying table under the expected name.
Replace the synonym reference with a fully qualified object name.
A simplified example:
-- Legacy logic
SELECT ...
FROM enrollment_source;
could become either:
CREATE VIEW custom_schema.enrollment_source AS
SELECT *
FROM delivered_schema.enrollment_source;
or the consuming view could reference the delivered object directly:
SELECT ...
FROM delivered_schema.enrollment_source;
Functions and package-style calls had a similar choice between wrappers and explicit references to the converted target function. The implementation used the pattern appropriate to the dependency rather than carrying Oracle synonym semantics into PostgreSQL.
Reworking database-link dependencies
Some legacy materialized-view logic referenced data through Oracle database links, database-level references that let one database access objects in another. Three technical patterns were evaluated for PostgreSQL:
foreign servers and foreign tables;
direct references when the required objects existed in the same database;
an external data-movement mechanism such as Data Connect.
Where the source data could be referenced within the same PostgreSQL database, direct schema-qualified access removed the database-link dependency altogether.
The same analysis also helped simplify some legacy copied tables: when an object only duplicated a table already available in another schema, the future state could reference that delivered object directly rather than preserve another custom copy.
For customized enterprise databases, schema conversion is only the starting point. Preserving behavior across dependencies, functions, indexes and data-loading logic requires additional engineering.
Moving deployment and data refresh into Ellucian Data Connect
Converting the objects was only one part of the future state. The custom layer also needed a deployment and data-population model aligned with the new Ellucian environment.
Deploy custom objects in dependency order
Each database object was represented as a Data Connect sub-pipeline. Table pipelines could create the table and add its indexes and primary key, while views and functions used their corresponding creation operations.
Those sub-pipelines were then combined into a consolidated deployment pipeline so dependent objects could be created in the correct order.
Legacy materialized views were represented as tables in this deployment model. Their refresh behavior moved into data pipelines rather than remaining tied to database-native materialized-view refresh logic.
See how custom database objects were deployed through Data Connect
The deployment design separates the definition of one object from orchestration across the entire custom layer.
For a table, one sub-pipeline can contain:
Data Connect operation
Role
Create Table
Create the target table definition
Add Index
Create one or more indexes
Add Primary Key
Add the table’s primary-key constraint
A view sub-pipeline contains its Create View operation, including the SQL query that defines the view.
A function sub-pipeline contains a Create Function operation with its arguments, return type and PL/pgSQL body.
This keeps each database object independently deployable while letting the consolidated pipeline enforce dependency order.
A view may depend on another view or table, and a function may reference delivered or custom objects. The consolidated pipeline runs the object-level pipelines in the order those dependencies require.
Materialized views receive a different treatment. Because the final Data Connect deployment model does not create them as materialized views, those objects are deployed as tables. Their data population becomes a separate ETL concern.
Rebuild legacy data population as managed pipelines
ABCloudz then re-engineered the data-loading layer. Existing ODI packages, materialized-view definitions and other source relationships provided the logic needed to determine where data came from and how each target should be refreshed.
The resulting Data Connect design uses database tables and views or flat files as sources. Most pipelines run daily, several run hourly and flat-file loads can run ad hoc. Depending on the dataset, refresh behavior is full or scoped by a business key such as an academic period. For larger source datasets, the design included a planned chunking strategy to reduce the risk of long-running pipeline execution.
See how legacy ODI loading logic became Data Connect pipelines
Classify the existing population logic first
The final scope included 38 tables that required a data-population decision.
They fell into three broad groups:
Legacy situation
Population decision
13 materialized-view-based objects
Derive the future load from the materialized-view query or simplify the dependency where possible
14 tables with identified ODI loaders
Use the existing package logic as the basis for the new Data Connect ETL
11 tables without an identified loader
Determine whether the object can be replaced by an existing cross-schema source or establish the required population logic
Within the last group, several tables simply duplicated data available in another schema. Those copies could be replaced with direct, fully qualified references rather than reproduced as separate custom data-loading processes.
This classification prevented the migration from treating every legacy table as if it required the same type of ETL.
Map source, refresh behavior and schedule
The future-state design includes 26 Data Connect ETL pipelines:
19 scheduled daily;
3 scheduled hourly;
4 driven by CSV files and executed ad hoc.
Database-sourced pipelines use tables or views already present in the environment. Flat-file pipelines use files from the Data Lake.
Refresh behavior also varies by target. Some datasets use a full refresh. Others are scoped by an academic period or another business key carried by the legacy process.
A simplified mapping looks like this:
Legacy input
Future source
Refresh pattern
Execution
ODI package using a database view
Database view/table
Full or scoped
Scheduled
Materialized-view query
Database objects
Recreated as table-loading logic
Scheduled
Cross-schema copy
Existing delivered table
Direct reference where appropriate
No duplicate custom loader
Managed CSV dataset
Flat file
Full
Ad hoc
Plan for larger loads with configurable chunks
The future-state design also accounts for Data Connect execution limits on long-running jobs. For workloads at risk of exceeding those limits, the planned mitigation is to split source datasets into chunks, with the chunk size configured for the individual pipeline.
This keeps the operational concern inside the ETL design rather than forcing the database migration to reproduce legacy loading mechanics unchanged.
The future state brings the two parts of the migration together: institution-specific database behavior runs as PostgreSQL logic, while Data Connect manages how the custom objects are deployed and how their data is refreshed.
What the new architecture changes for the institution
The project moved the custom reporting foundation into the Ellucian SaaS architecture rather than preserving its legacy Oracle operating model.
The institution now has a PostgreSQL-based custom data layer in Ellucian Insights, with Oracle-specific behavior re-engineered where required. Database objects are deployed through Data Connect with dependency-aware orchestration. Legacy ODI population logic has been translated into a set of Data Connect ETL patterns with defined sources, schedules and refresh behavior.
Together, these changes provide the custom data foundation required for the institution’s broader transition from Cognos reporting to Argos without retaining the previous Oracle-specific architecture underneath it.
The institution’s custom reporting layer was re-platformed into the Ellucian SaaS architecture with PostgreSQL-native database logic and Data Connect-based deployment and data refresh.
Frequently asked questions
Why couldn’t the custom reporting layer follow the standard Ellucian SaaS migration path?
The migration scope included institution-specific database objects and loading logic layered on top of the baseline environment. Those custom tables, views, functions, materialized-view behaviors and ETL dependencies had to be analyzed and re-engineered separately for the PostgreSQL-based target.
What did AWS Schema Conversion Tool automate, and what still required manual engineering?
AWS SCT provided the initial Oracle-to-PostgreSQL conversion. Manual engineering was still required for cases such as datatype and cast corrections, Oracle Extension Pack dependencies, cursor functions, package-function calls, table-returning functions, row identifiers and function-based indexes. The converted output had to preserve behavior, not only produce syntactically valid PostgreSQL.
How were Oracle ROWID references handled in PostgreSQL?
For the affected logic, ROWID was used within a transaction rather than as a permanent business identifier. PostgreSQL ctid and the tid type were used as short-lived row-location values for those scoped cases. Because ctid can change when a row is updated or moved, including by VACUUM FULL, it was not treated as a durable identifier.
How were Oracle materialized views handled in Ellucian Data Connect?
In the final deployment model, the relevant legacy materialized views were represented as PostgreSQL tables. Their creation followed the same Data Connect deployment pattern as other tables, while their data-refresh behavior was implemented through Data Connect ETL pipelines.
How was legacy ODI ETL migrated to Data Connect?
The team classified how each custom table received its data. Existing ODI packages could provide the basis for new Data Connect pipelines, materialized-view queries could define equivalent loading logic, and some copied objects could be replaced with direct cross-schema references. Flat-file-managed datasets became ad hoc Data Connect loads. Each pipeline was then assigned the appropriate source, schedule and refresh pattern.
How does Data Connect handle dependencies between custom database objects?
Each object is represented by its own sub-pipeline, allowing a table, view or function to be managed as an individual deployment unit. A consolidated pipeline then invokes those sub-pipelines in the order required by object dependencies.
How did the Cognos-to-Argos migration relate to this work?
Cognos-to-Argos reporting was the broader modernization context. Because the existing reports depended directly on the custom Oracle data layer, that underlying layer also had to move to the SaaS target. This project focused on the custom database and data-population foundation required to support that transition.
Planning a similar Ellucian SaaS modernization?
Custom reporting schemas, direct database dependencies and legacy ETL can make an Ellucian SaaS move much more than a platform migration. ABCloudz can help identify the custom behavior your institution still needs, map its dependencies, design the appropriate PostgreSQL and Data Connect patterns and carry the modernization through implementation.
If your Ellucian modernization includes custom Oracle logic or reporting dependencies that need a clear path to SaaS, contact ABCloudz to discuss your environment.