Reviewing Oracle To Postgres Migration
GitHubOfficialFirst committed by PrimedPaulDistributed by GitHub
- Skill
- Workflow
- Software development
- MIT
- No issues found
AutoGineer summary
Written by AutoGineer from the skill’s own files, not by the author
This skill provides a systematic approach to managing database migrations from Oracle to PostgreSQL. It centers on a reference-based workflow that helps developers identify behavioral differences—such as empty string handling, type coercion, and pagination logic—during the planning phase and verify that these risks are addressed during the validation phase. By cross-referencing migration artifacts against known technical discrepancies, it ensures that application logic and database schemas are correctly adapted for the PostgreSQL environment.
What it does
- Provides a risk assessment workflow for planning database migrations.
- Offers a validation workflow to confirm migration work meets PostgreSQL requirements.
- Maintains a library of known behavioral differences between Oracle and PostgreSQL.
- Guides users through documenting risks and recommended fix patterns.
- Ensures integration tests cover critical Oracle-to-PostgreSQL behavioral gaps.
- Supports decision-making for handling specific database object migrations.
When to use it
- When planning the migration of database objects like procedures, triggers, or views.
- When reviewing completed migration artifacts to ensure all behavioral risks are mitigated.
- When validating that integration tests adequately cover Oracle-to-PostgreSQL differences.
- When you need a structured checklist to gate the success of a database migration task.
Advantages
- Reduces the risk of runtime errors caused by subtle database behavioral differences.
- Standardizes the migration process across different database objects.
- Provides clear, actionable guidance for common migration pitfalls.
- Facilitates thorough testing by highlighting specific scenarios that require validation.
Usage examples
- “I am planning to migrate a set of Oracle stored procedures to PostgreSQL. How should I start?”
- The agent will guide you through the Risk Assessment workflow, helping you list affected objects and screen them against the reference index to document necessary changes.
- “I have finished migrating a query that uses ROWNUM. How do I validate this?”
- The agent will initiate the Validation workflow, prompting you to map the artifact, check it against the ROWNUM-specific reference, and verify that your integration tests cover the new LIMIT/OFFSET logic.
Install
Download the skill as a ZIP:
https://autogineer.com/agent-skills/github/reviewing-oracle-to-postgres-migration/downloadUnzip it into the cross-client skills folder of your project:
mkdir -p .agents/skills && unzip reviewing-oracle-to-postgres-migration.zip -d .agents/skills
Documentation by PrimedPaul
Oracle-to-PostgreSQL Database Migration
Surfaces migration risks and validates migration work against known Oracle/PostgreSQL behavioral differences documented in the references/ folder.
When to use
Planning — Before starting migration work on a procedure, trigger, query, or refcursor client. Identify which reference insights apply so risks are addressed up front.
Validating — After migration work is done, confirm every applicable insight was addressed and integration tests cover the new PostgreSQL semantics.
Workflow
Determine the task type:
Planning a migration? Follow the risk assessment workflow. Validating completed work? Follow the validation workflow.
Risk assessment workflow (planning)
Risk Assessment:
- [ ] Step 1: Identify the migration scope
- [ ] Step 2: Screen each insight for applicability
- [ ] Step 3: Document risks and recommended actionsStep 1: Identify the migration scope
List the affected database objects (procedures, triggers, queries, views) and the application code that calls them.
Step 2: Screen each insight for applicability
Review the reference index in references/REFERENCE.md. For each entry, determine whether the migration scope contains patterns affected by that insight. Read the full reference file only when the insight is potentially relevant.
Step 3: Document risks and recommended actions
For each applicable insight, note the specific risk and the recommended fix pattern from the reference file. Flag any insight that requires a design decision (e.g., whether to preserve Oracle empty-string-as-NULL semantics or adopt PostgreSQL behavior).
Validation workflow (post-migration)
Validation:
- [ ] Step 1: Map the migration artifact
- [ ] Step 2: Cross-check applicable insights
- [ ] Step 3: Verify integration test coverage
- [ ] Step 4: Gate the resultStep 1: Map the migration artifact
Identify the migrated object and summarize the change set.
Step 2: Cross-check applicable insights
For each reference in references/REFERENCE.md, confirm the behavior or test requirement is acknowledged and addressed in the migration work.
Step 3: Verify integration test coverage
Confirm tests exercise both the happy path and the failure scenarios highlighted in applicable insights (exceptions, sorting, UNION ALL behavior/performance risks, refcursor consumption, concurrent transactions, timestamps, materialized-view freshness, etc.).
Step 4: Gate the result
Return a checklist asserting each applicable insight was addressed, migration scripts run, and integration tests pass.
Files
- ATTRIBUTION.mdAdded by AutoGineer913 B
- LICENSELicence file added by AutoGineer1.0 KiB
- SKILL.md3.1 KiB
- references/REFERENCE.md2.8 KiB
- references/empty-strings-handling.md2.0 KiB
- references/no-data-found-exceptions.md2.6 KiB
- references/oracle-nvl-decode-functions.md2.4 KiB
- references/oracle-parentheses-from-clause.md4.8 KiB
- references/oracle-rownum-pagination.md2.1 KiB
- references/oracle-sysdate-sequences-dual.md3.2 KiB
- references/oracle-to-postgres-sorting.md2.7 KiB
- references/oracle-to-postgres-timestamp-timezone.md8.4 KiB
- references/oracle-to-postgres-to-char-numeric.md4.2 KiB
- references/oracle-to-postgres-type-coercion.md4.9 KiB
- references/postgres-concurrent-transactions.md9.4 KiB
- references/postgres-materialized-view-refresh.md1.4 KiB
- references/postgres-refcursor-handling.md5.7 KiB
- references/postgres-union-all-planner.md1.6 KiB
references/postgres-concurrent-transactions.md
Oracle to PostgreSQL: Concurrent Transaction Handling
Contents
Overview
The Core Difference
Common Error Symptoms
Problem Scenarios
Solutions — materialize results, separate connections, single query
Detection Strategy
Error Messages to Watch For
Comparison Table
Best Practices
Migration Checklist
Overview
When migrating from Oracle to PostgreSQL, a critical difference exists in how concurrent operations on a single database connection are handled. Oracle's ODP.NET driver allows multiple active commands and result sets on the same connection simultaneously, while PostgreSQL's Npgsql driver enforces a strict one active command per connection rule. Code that worked seamlessly in Oracle will throw runtime exceptions in PostgreSQL if concurrent operations share a connection.
The Core Difference
Oracle Behavior:
A single connection can have multiple active commands executing concurrently
Opening a second
DataReaderwhile another is still open is permittedNested or overlapping database calls on the same connection work transparently
PostgreSQL Behavior:
A connection supports only one active command at a time
Attempting to execute a second command while a
DataReaderis open throws an exceptionLazy-loaded navigation properties or callback-driven reads that trigger additional queries on the same connection will fail
Common Error Symptoms
When migrating Oracle code without accounting for this difference:
System.InvalidOperationException: An operation is already in progress.Npgsql.NpgsqlOperationInProgressException: A command is already in progress: <SQL text>These occur when application code attempts to execute a new command on a connection that already has an active DataReader or uncommitted command in flight.
Problem Scenarios
Scenario 1: Iterating a DataReader While Executing Another Command
using (var reader = command1.ExecuteReader())
{
while (reader.Read())
{
// PROBLEM: executing a second command on the same connection
// while the reader is still open
using (var command2 = new NpgsqlCommand("SELECT ...", connection))
{
var value = command2.ExecuteScalar(); // FAILS
}
}
}Scenario 2: Lazy Loading / Deferred Execution in Data Access Layers
// Oracle: works because ODP.NET supports concurrent readers
var items = repository.GetItems(); // returns IEnumerable backed by open DataReader
foreach (var item in items)
{
// PROBLEM: triggers a second query on the same connection
var details = repository.GetDetails(item.Id); // FAILS on PostgreSQL
}Scenario 3: Nested Stored Procedure Calls via Application Code
// Oracle: ODP.NET handles multiple active commands
command1.ExecuteNonQuery(); // starts a long-running operation
command2.ExecuteScalar(); // FAILS on PostgreSQL — command1 still in progressSolutions
Solution 1: Materialize Results Before Issuing New Commands (Recommended)
Close the first result set by loading it into memory before executing subsequent commands on the same connection.
// Load all results into a list first
var items = new List<Item>();
using (var reader = command1.ExecuteReader())
{
while (reader.Read())
{
items.Add(MapItem(reader));
}
} // reader is closed and disposed here
// Now safe to execute another command on the same connection
foreach (var item in items)
{
using (var command2 = new NpgsqlCommand("SELECT ...", connection))
{
command2.Parameters.AddWithValue("id", item.Id);
var value = command2.ExecuteScalar(); // Works
}
}For LINQ / EF Core scenarios, force materialization with .ToList():
// Before (fails on PostgreSQL — deferred execution keeps connection busy)
var items = dbContext.Items.Where(i => i.Active);
foreach (var item in items)
{
var details = dbContext.Details.FirstOrDefault(d => d.ItemId == item.Id);
}
// After (materializes first query before issuing second)
var items = dbContext.Items.Where(i => i.Active).ToList();
foreach (var item in items)
{
var details = dbContext.Details.FirstOrDefault(d => d.ItemId == item.Id);
}Solution 2: Use Separate Connections for Concurrent Operations
When operations genuinely need to run concurrently, open a dedicated connection for each.
using (var reader = command1.ExecuteReader())
{
while (reader.Read())
{
// Use a separate connection for the nested query
using (var connection2 = new NpgsqlConnection(connectionString))
{
connection2.Open();
using (var command2 = new NpgsqlCommand("SELECT ...", connection2))
{
var value = command2.ExecuteScalar(); // Works — different connection
}
}
}
}Solution 3: Restructure to a Single Query
Where possible, combine nested lookups into a single query using JOINs or subqueries to eliminate the need for concurrent commands entirely.
// Before: two sequential queries on the same connection
var order = GetOrder(orderId); // query 1
var details = GetOrderDetails(orderId); // query 2 (fails if query 1 reader still open)
// After: single query with JOIN
using (var command = new NpgsqlCommand(
"SELECT o.*, d.* FROM orders o JOIN order_details d ON o.id = d.order_id WHERE o.id = @id",
connection))
{
command.Parameters.AddWithValue("id", orderId);
using (var reader = command.ExecuteReader())
{
// Process combined result set
}
}Detection Strategy
Code Review Checklist
[ ] Search for methods that open a
DataReaderand call other database methods before closing it[ ] Look for
IEnumerablereturn types from data access methods that defer execution (indicate open readers)[ ] Identify EF Core queries without
.ToList()/.ToArray()that are iterated while issuing further queries[ ] Check for nested stored procedure calls in application code that share a connection
Common Locations to Search
Data access layers and repository classes
Service methods that orchestrate multiple repository calls
Code paths that iterate query results and perform lookups per row
Event handlers or callbacks triggered during data iteration
Search Patterns
ExecuteReader\(.*\)[\s\S]*?Execute(Scalar|NonQuery|Reader)\(\.Where\(.*\)[\s\S]*?foreach[\s\S]*?dbContext\.Error Messages to Watch For
| Error Message | Likely Cause |
|---|---|
An operation is already in progress | Second command executed while a DataReader is open on the same connection |
A command is already in progress: <SQL> | Npgsql detected overlapping command execution on a single connection |
The connection is already in state 'Executing' | Connection state conflict from concurrent usage |
Comparison Table: Oracle vs. PostgreSQL
| Aspect | Oracle (ODP.NET) | PostgreSQL (Npgsql) |
|---|---|---|
| Concurrent commands | Multiple active commands per connection | One active command per connection |
| Multiple open DataReaders | Supported | Not supported — must close/materialize first |
| Nested DB calls during iteration | Transparent | Throws InvalidOperationException |
| Deferred execution safety | Safe to iterate and query | Must materialize (.ToList()) before issuing new queries |
| Connection pooling impact | Lower connection demand | May need more pooled connections if using Solution 2 |
Best Practices
Materialize early — Call
.ToList()or.ToArray()on query results before iterating and issuing further database calls. This is the simplest and most reliable fix.Audit data access patterns — Review all repository and data access methods for deferred-execution return types (
IEnumerable,IQueryable) that callers iterate while issuing additional queries.Prefer single queries — Where feasible, combine nested lookups into JOINs or subqueries to eliminate the concurrent-command pattern entirely.
Isolate connections when necessary — If concurrent operations are genuinely required, use separate connections rather than attempting to share one.
Test iterative workflows — Integration tests should cover scenarios where code iterates result sets and performs additional database operations per row, as these are the most common failure points.
Migration Checklist
[ ] Identify all code paths that execute multiple commands on a single connection concurrently
[ ] Locate
IEnumerable-backed data access methods that defer execution with open readers[ ] Add
.ToList()/.ToArray()materialization where deferred results are iterated alongside further queries[ ] Refactor nested database calls to use separate connections or combined queries where appropriate
[ ] Verify EF Core navigation properties and lazy loading do not trigger concurrent connection usage
[ ] Update integration tests to cover iterative data access patterns
[ ] Load-test connection pool sizing if Solution 2 (separate connections) is used extensively
References
Requirements and compatibility
Written for: GitHub Copilot
Works with: Claude Code, Claude apps, OpenAI Codex, GitHub Copilot, Cursor, Gemini CLI, Kiro, OpenCode, Goose, Amp, Roo Code, JetBrains Junie, Factory Droid, Devin (ex-Windsurf), Mistral Vibe, OpenHands, Letta
“Works with” means the tool reads the open Agent Skills format; a skill written for one tool may use features others lack.
Security
No issues found by our static scan· No issues found
A static scan is not a guarantee. Skills are third-party content: review a skill before you let an agent use it.
Attribution and licence
- First committed by
PrimedPaul
- Publisher
- GitHub (Official)
- Distributed by
- GitHub
- Source
- github/awesome-copilot · skills/reviewing-oracle-to-postgres-migration · commit 797337f
- Licence
- MIT License (redistribution allowed)
- LICENSE (added by AutoGineer from LICENSE)
- ATTRIBUTION.md (written by AutoGineer)
- Synced
- Contributors
- PrimedPaul
AutoGineer is not affiliated with or endorsed by PrimedPaul or GitHub.
Download ZIP
The ZIP holds the skill’s files, its licence files and an ATTRIBUTION.md. Download reviewing-oracle-to-postgres-migration.zip