Generated banner for Reviewing Oracle To Postgres Migration
Generated logo for Reviewing Oracle To Postgres Migration

Reviewing Oracle To Postgres Migration

GitHubOfficialFirst committed by PrimedPaulDistributed by GitHub

  • Skill
  • Workflow
  • Software development
  • MIT
  • No issues found

From the author

Identifies Oracle-to-PostgreSQL migration risks by cross-referencing code against known behavioral differences (empty strings, refcursors, type coercion, sorting/collations, UNION ALL planner risks, materialized-view refresh requirements, timestamps, concurrent transactions, etc.). Use when planning a database migration, reviewing migration artifacts, or validating that integration tests cover Oracle/PostgreSQL differences.
— the description by PrimedPaul

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

  1. Download the skill as a ZIP:

    https://autogineer.com/agent-skills/github/reviewing-oracle-to-postgres-migration/download
  2. Unzip 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

  1. Planning — Before starting migration work on a procedure, trigger, query, or refcursor client. Identify which reference insights apply so risks are addressed up front.

  2. 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 actions

Step 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 result

Step 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

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 DataReader while another is still open is permitted

  • Nested 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 DataReader is open throws an exception

  • Lazy-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 progress

Solutions

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 DataReader and call other database methods before closing it

  • [ ] Look for IEnumerable return 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

  • 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 MessageLikely Cause
An operation is already in progressSecond 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

AspectOracle (ODP.NET)PostgreSQL (Npgsql)
Concurrent commandsMultiple active commands per connectionOne active command per connection
Multiple open DataReadersSupportedNot supported — must close/materialize first
Nested DB calls during iterationTransparentThrows InvalidOperationException
Deferred execution safetySafe to iterate and queryMust materialize (.ToList()) before issuing new queries
Connection pooling impactLower connection demandMay need more pooled connections if using Solution 2

Best Practices

  1. Materialize early — Call .ToList() or .ToArray() on query results before iterating and issuing further database calls. This is the simplest and most reliable fix.

  2. 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.

  3. Prefer single queries — Where feasible, combine nested lookups into JOINs or subqueries to eliminate the concurrent-command pattern entirely.

  4. Isolate connections when necessary — If concurrent operations are genuinely required, use separate connections rather than attempting to share one.

  5. 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
GitHub avatar of PrimedPaulPrimedPaul
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