mirror of
https://github.com/github/awesome-copilot.git
synced 2026-03-12 04:05:12 +00:00
Adds the 'Oracle-to-PostgreSQL Migration Expert' Custom Agent, Asociated Skills, and Plugin Manifest (#950)
* Add the 'Oracle-to-PostgreSQL Migration Expert' Custom Agent, its associated skills, plugin manifest * Update READMEs using 'npm run build' * Resolve PR comments: - Fix BOM characters - Rerun 'npm run build' - Clarify timestampz date kind - Remove consufing text for SELECT INTO exception - Remove dangerous VB.NET example * Update README and refcursor handling documentation for clarity and consistency * Update skills/creating-oracle-to-postgres-master-migration-plan/SKILL.md Add .slnx to discovery of projects Co-authored-by: Aaron Powell <me@aaron-powell.com> --------- Co-authored-by: TCPrimedPaul <paul.delannoy@tc.gc.ca> Co-authored-by: Aaron Powell <me@aaron-powell.com>
This commit is contained in:
6
.github/plugin/marketplace.json
vendored
6
.github/plugin/marketplace.json
vendored
@@ -209,6 +209,12 @@
|
||||
"description": "Generate production-ready FastAPI applications from OpenAPI specifications. Includes project scaffolding, route generation, dependency injection, and Python best practices for async APIs.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"name": "oracle-to-postgres-migration-expert",
|
||||
"source": "oracle-to-postgres-migration-expert",
|
||||
"description": "Expert agent for Oracle-to-PostgreSQL application migrations in .NET solutions. Performs code edits, runs commands, and invokes extension tools to migrate .NET/Oracle data access patterns to PostgreSQL.",
|
||||
"version": "1.0.0"
|
||||
},
|
||||
{
|
||||
"name": "ospo-sponsorship",
|
||||
"source": "ospo-sponsorship",
|
||||
|
||||
55
agents/oracle-to-postgres-migration-expert.agent.md
Normal file
55
agents/oracle-to-postgres-migration-expert.agent.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
description: 'Agent for Oracle-to-PostgreSQL application migrations. Educates users on migration concepts, pitfalls, and best practices; makes code edits and runs commands directly; and invokes extension tools on user confirmation.'
|
||||
model: 'Claude Sonnet 4.6 (copilot)'
|
||||
tools: [vscode/installExtension, vscode/memory, vscode/runCommand, vscode/extensions, vscode/askQuestions, execute, read, edit, search, ms-ossdata.vscode-pgsql/pgsql_migration_oracle_app, ms-ossdata.vscode-pgsql/pgsql_migration_show_report, todo]
|
||||
name: 'Oracle-to-PostgreSQL Migration Expert'
|
||||
---
|
||||
|
||||
## Your Expertise
|
||||
|
||||
You are an expert **Oracle-to-PostgreSQL migration agent** with deep knowledge in database migration strategies, Oracle/PostgreSQL behavioral differences, .NET/C# data access patterns, and integration testing workflows. You directly make code edits, run commands, and perform migration tasks.
|
||||
|
||||
## Your Approach
|
||||
|
||||
- **Educate first.** Explain migration concepts clearly before suggesting actions.
|
||||
- **Suggest, don't assume.** Present recommended next steps as options. Explain the purpose and expected outcome of each step. Do not chain tasks automatically.
|
||||
- **Confirm before invoking extension tools.** Before invoking any extension tool, ask the user if they want to proceed. Use `vscode/askQuestions` for structured confirmation when appropriate.
|
||||
- **One step at a time.** After completing a step, summarize what was produced and suggest the logical next step. Do not auto-advance to the next task.
|
||||
- **Act directly.** Use `edit`, `runInTerminal`, `read`, and `search` tools to analyze the workspace, make code changes, and run commands. You perform migration tasks yourself rather than delegating to subagents.
|
||||
|
||||
## Guidelines
|
||||
|
||||
- Keep to existing .NET and C# versions used by the solution; do not introduce newer language/runtime features.
|
||||
- Minimize changes — map Oracle behaviors to PostgreSQL equivalents carefully; prioritize well-tested libraries.
|
||||
- Preserve comments and application logic unless absolutely necessary to change.
|
||||
- PostgreSQL schema is immutable — no DDL alterations to tables, views, indexes, constraints, or sequences. The only permitted DDL changes are `CREATE OR REPLACE` of stored procedures and functions.
|
||||
- Oracle is the source of truth for expected application behavior during validation.
|
||||
- Be concise and clear in your explanations. Use tables and lists to structure advice.
|
||||
- When reading reference files, synthesize the guidance for the user — don't just dump raw content.
|
||||
- Ask only for missing prerequisites; do not re-ask known info.
|
||||
|
||||
## Migration Phases
|
||||
|
||||
Present this as a guide — the user decides which steps to take and when.
|
||||
|
||||
1. **Discovery & Planning** — Discover projects in the solution, classify migration eligibility, set up DDL artifacts under `.github/oracle-to-postgres-migration/DDL/`.
|
||||
2. **Code Migration** *(per project)* — Convert application code Oracle data access patterns to PostgreSQL equivalents; translate stored procedures from PL/SQL to PL/pgSQL.
|
||||
3. **Validation** *(per project)* — Plan integration testing, scaffold test infrastructure, create and run tests, document defects.
|
||||
4. **Reporting** — Generate a final migration summary report per project.
|
||||
|
||||
## Extension Tools
|
||||
|
||||
Two workflow steps can be performed by the `ms-ossdata.vscode-pgsql` extension:
|
||||
|
||||
- `pgsql_migration_oracle_app` — Scans application code and converts Oracle data access patterns to PostgreSQL equivalents.
|
||||
- `pgsql_migration_show_report` — Produces a final migration summary report.
|
||||
|
||||
Before invoking either tool: explain what it does, verify the extension is installed, and confirm with the user.
|
||||
|
||||
## Working Directory
|
||||
|
||||
Migration artifacts should be stored under `.github/oracle-to-postgres-migration/`, if not, ask the user where to find what you need to be of help:
|
||||
|
||||
- `DDL/Oracle/` — Oracle DDL definitions (pre-migration)
|
||||
- `DDL/Postgres/` — PostgreSQL DDL definitions (post-migration)
|
||||
- `Reports/` — Migration plans, testing plans, bug reports, and final reports
|
||||
@@ -121,6 +121,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-agents) for guidelines on how to
|
||||
| [Next.js Expert](../agents/expert-nextjs-developer.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fexpert-nextjs-developer.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fexpert-nextjs-developer.agent.md) | Expert Next.js 16 developer specializing in App Router, Server Components, Cache Components, Turbopack, and modern React patterns with TypeScript | |
|
||||
| [Octopus Release Notes With Mcp](../agents/octopus-deploy-release-notes-mcp.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Foctopus-deploy-release-notes-mcp.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Foctopus-deploy-release-notes-mcp.agent.md) | Generate release notes for a release in Octopus Deploy. The tools for this MCP server provide access to the Octopus Deploy APIs. | octopus<br />[](https://aka.ms/awesome-copilot/install/mcp-vscode?name=octopus&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%2540octopusdeploy%252Fmcp-server%22%5D%2C%22env%22%3A%7B%7D%7D)<br />[](https://aka.ms/awesome-copilot/install/mcp-vscodeinsiders?name=octopus&config=%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%2540octopusdeploy%252Fmcp-server%22%5D%2C%22env%22%3A%7B%7D%7D)<br />[](https://aka.ms/awesome-copilot/install/mcp-visualstudio/mcp-install?%7B%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%2540octopusdeploy%252Fmcp-server%22%5D%2C%22env%22%3A%7B%7D%7D) |
|
||||
| [OpenAPI to Application Generator](../agents/openapi-to-application.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fopenapi-to-application.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fopenapi-to-application.agent.md) | Expert assistant for generating working applications from OpenAPI specifications | |
|
||||
| [Oracle To PostgreSQL Migration Expert](../agents/oracle-to-postgres-migration-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Foracle-to-postgres-migration-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Foracle-to-postgres-migration-expert.agent.md) | Agent for Oracle-to-PostgreSQL application migrations. Educates users on migration concepts, pitfalls, and best practices; makes code edits and runs commands directly; and invokes extension tools on user confirmation. | |
|
||||
| [PagerDuty Incident Responder](../agents/pagerduty-incident-responder.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fpagerduty-incident-responder.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fpagerduty-incident-responder.agent.md) | Responds to PagerDuty incidents by analyzing incident context, identifying recent code changes, and suggesting fixes via GitHub PRs. | [pagerduty](https://github.com/mcp/io.github.PagerDuty/pagerduty-mcp)<br />[](https://aka.ms/awesome-copilot/install/mcp-vscode?name=pagerduty&config=%7B%22url%22%3A%22https%3A%2F%2Fmcp.pagerduty.com%2Fmcp%22%2C%22headers%22%3A%7B%7D%7D)<br />[](https://aka.ms/awesome-copilot/install/mcp-vscodeinsiders?name=pagerduty&config=%7B%22url%22%3A%22https%3A%2F%2Fmcp.pagerduty.com%2Fmcp%22%2C%22headers%22%3A%7B%7D%7D)<br />[](https://aka.ms/awesome-copilot/install/mcp-visualstudio/mcp-install?%7B%22url%22%3A%22https%3A%2F%2Fmcp.pagerduty.com%2Fmcp%22%2C%22headers%22%3A%7B%7D%7D) |
|
||||
| [PHP MCP Expert](../agents/php-mcp-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fphp-mcp-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fphp-mcp-expert.agent.md) | Expert assistant for PHP MCP server development using the official PHP SDK with attribute-based discovery | |
|
||||
| [Pimcore Expert](../agents/pimcore-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fpimcore-expert.agent.md)<br />[](https://aka.ms/awesome-copilot/install/agent?url=vscode-insiders%3Achat-agent%2Finstall%3Furl%3Dhttps%3A%2F%2Fraw.githubusercontent.com%2Fgithub%2Fawesome-copilot%2Fmain%2Fagents%2Fpimcore-expert.agent.md) | Expert Pimcore development assistant specializing in CMS, DAM, PIM, and E-Commerce solutions with Symfony integration | |
|
||||
|
||||
@@ -54,6 +54,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-plugins) for guidelines on how t
|
||||
| [openapi-to-application-java-spring-boot](../plugins/openapi-to-application-java-spring-boot/README.md) | Generate production-ready Spring Boot applications from OpenAPI specifications. Includes project scaffolding, REST controller generation, service layer organization, and Spring Boot best practices. | 2 items | openapi, code-generation, api, java, spring-boot |
|
||||
| [openapi-to-application-nodejs-nestjs](../plugins/openapi-to-application-nodejs-nestjs/README.md) | Generate production-ready NestJS applications from OpenAPI specifications. Includes project scaffolding, controller and service generation, TypeScript best practices, and enterprise patterns. | 2 items | openapi, code-generation, api, nodejs, typescript, nestjs |
|
||||
| [openapi-to-application-python-fastapi](../plugins/openapi-to-application-python-fastapi/README.md) | Generate production-ready FastAPI applications from OpenAPI specifications. Includes project scaffolding, route generation, dependency injection, and Python best practices for async APIs. | 2 items | openapi, code-generation, api, python, fastapi |
|
||||
| [oracle-to-postgres-migration-expert](../plugins/oracle-to-postgres-migration-expert/README.md) | Expert agent for Oracle-to-PostgreSQL application migrations in .NET solutions. Performs code edits, runs commands, and invokes extension tools to migrate .NET/Oracle data access patterns to PostgreSQL. | 8 items | oracle, postgresql, database-migration, dotnet, sql, migration, integration-testing, stored-procedures |
|
||||
| [ospo-sponsorship](../plugins/ospo-sponsorship/README.md) | Tools and resources for Open Source Program Offices (OSPOs) to identify, evaluate, and manage sponsorship of open source dependencies through GitHub Sponsors, Open Collective, and other funding platforms. | 1 items | |
|
||||
| [partners](../plugins/partners/README.md) | Custom agents that have been created by GitHub partners | 20 items | devops, security, database, cloud, infrastructure, observability, feature-flags, cicd, migration, performance |
|
||||
| [pcf-development](../plugins/pcf-development/README.md) | Complete toolkit for developing custom code components using Power Apps Component Framework for model-driven and canvas apps | 0 items | power-apps, pcf, component-framework, typescript, power-platform |
|
||||
|
||||
@@ -86,6 +86,9 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
|
||||
| [create-technical-spike](../skills/create-technical-spike/SKILL.md) | Create time-boxed technical spike documents for researching and resolving critical development decisions before implementation. | None |
|
||||
| [create-tldr-page](../skills/create-tldr-page/SKILL.md) | Create a tldr page from documentation URLs and command examples, requiring both URL and command name. | None |
|
||||
| [create-web-form](../skills/create-web-form/SKILL.md) | Create robust, accessible web forms with best practices for HTML structure, CSS styling, JavaScript interactivity, form validation, and server-side processing. Use when asked to "create a form", "build a web form", "add a contact form", "make a signup form", or when building any HTML form with data handling. Covers PHP and Python backends, MySQL database integration, REST APIs, XML data exchange, accessibility (ARIA), and progressive web apps. | `references/accessibility.md`<br />`references/aria-form-role.md`<br />`references/css-styling.md`<br />`references/form-basics.md`<br />`references/form-controls.md`<br />`references/form-data-handling.md`<br />`references/html-form-elements.md`<br />`references/html-form-example.md`<br />`references/hypertext-transfer-protocol.md`<br />`references/javascript.md`<br />`references/php-cookies.md`<br />`references/php-forms.md`<br />`references/php-json.md`<br />`references/php-mysql-database.md`<br />`references/progressive-web-app.md`<br />`references/python-as-web-framework.md`<br />`references/python-contact-form.md`<br />`references/python-flask-app.md`<br />`references/python-flask.md`<br />`references/security.md`<br />`references/styling-web-forms.md`<br />`references/web-api.md`<br />`references/web-performance.md`<br />`references/xml.md` |
|
||||
| [creating-oracle-to-postgres-master-migration-plan](../skills/creating-oracle-to-postgres-master-migration-plan/SKILL.md) | Discovers all projects in a .NET solution, classifies each for Oracle-to-PostgreSQL migration eligibility, and produces a persistent master migration plan. Use when starting a multi-project Oracle-to-PostgreSQL migration, creating a migration inventory, or assessing which .NET projects contain Oracle dependencies. | None |
|
||||
| [creating-oracle-to-postgres-migration-bug-report](../skills/creating-oracle-to-postgres-migration-bug-report/SKILL.md) | Creates structured bug reports for defects found during Oracle-to-PostgreSQL migration. Use when documenting behavioral differences between Oracle and PostgreSQL as actionable bug reports with severity, root cause, and remediation steps. | `references/BUG-REPORT-TEMPLATE.md` |
|
||||
| [creating-oracle-to-postgres-migration-integration-tests](../skills/creating-oracle-to-postgres-migration-integration-tests/SKILL.md) | Creates integration test cases for .NET data access artifacts during Oracle-to-PostgreSQL database migrations. Generates DB-agnostic xUnit tests with deterministic seed data that validate behavior consistency across both database systems. Use when creating integration tests for a migrated project, generating test coverage for data access layers, or writing Oracle-to-PostgreSQL migration validation tests. | None |
|
||||
| [csharp-async](../skills/csharp-async/SKILL.md) | Get best practices for C# async programming | None |
|
||||
| [csharp-docs](../skills/csharp-docs/SKILL.md) | Ensure that C# types are documented with XML comments and follow best practices for documentation. | None |
|
||||
| [csharp-mcp-server-generator](../skills/csharp-mcp-server-generator/SKILL.md) | Generate a complete MCP server project in C# with tools, prompts, and proper configuration | None |
|
||||
@@ -156,6 +159,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
|
||||
| [microsoft-code-reference](../skills/microsoft-code-reference/SKILL.md) | Look up Microsoft API references, find working code samples, and verify SDK code is correct. Use when working with Azure SDKs, .NET libraries, or Microsoft APIs—to find the right method, check parameters, get working examples, or troubleshoot errors. Catches hallucinated methods, wrong signatures, and deprecated patterns by querying official docs. | None |
|
||||
| [microsoft-docs](../skills/microsoft-docs/SKILL.md) | Query official Microsoft documentation to find concepts, tutorials, and code examples across Azure, .NET, Agent Framework, Aspire, VS Code, GitHub, and more. Uses Microsoft Learn MCP as the default, with Context7 and Aspire MCP for content that lives outside learn.microsoft.com. | None |
|
||||
| [microsoft-skill-creator](../skills/microsoft-skill-creator/SKILL.md) | Create agent skills for Microsoft technologies using Learn MCP tools. Use when users want to create a skill that teaches agents about any Microsoft technology, library, framework, or service (Azure, .NET, M365, VS Code, Bicep, etc.). Investigates topics deeply, then generates a hybrid skill storing essential knowledge locally while enabling dynamic deeper investigation. | `references/skill-templates.md` |
|
||||
| [migrating-oracle-to-postgres-stored-procedures](../skills/migrating-oracle-to-postgres-stored-procedures/SKILL.md) | Migrates Oracle PL/SQL stored procedures to PostgreSQL PL/pgSQL. Translates Oracle-specific syntax, preserves method signatures and type-anchored parameters, leverages orafce where appropriate, and applies COLLATE "C" for Oracle-compatible text sorting. Use when converting Oracle stored procedures or functions to PostgreSQL equivalents during a database migration. | None |
|
||||
| [mkdocs-translations](../skills/mkdocs-translations/SKILL.md) | Generate a language translation for a mkdocs documentation stack. | None |
|
||||
| [model-recommendation](../skills/model-recommendation/SKILL.md) | Analyze chatmode or prompt files and recommend optimal AI models based on task complexity, required capabilities, and cost-efficiency | None |
|
||||
| [msstore-cli](../skills/msstore-cli/SKILL.md) | Microsoft Store Developer CLI (msstore) for publishing Windows applications to the Microsoft Store. Use when asked to configure Store credentials, list Store apps, check submission status, publish submissions, manage package flights, set up CI/CD for Store publishing, or integrate with Partner Center. Supports Windows App SDK/WinUI, UWP, .NET MAUI, Flutter, Electron, React Native, and PWA applications. | None |
|
||||
@@ -171,6 +175,7 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
|
||||
| [pdftk-server](../skills/pdftk-server/SKILL.md) | Skill for using the command-line tool pdftk (PDFtk Server) for working with PDF files. Use when asked to merge PDFs, split PDFs, rotate pages, encrypt or decrypt PDFs, fill PDF forms, apply watermarks, stamp overlays, extract metadata, burst documents into pages, repair corrupted PDFs, attach or extract files, or perform any PDF manipulation from the command line. | `references/download.md`<br />`references/pdftk-cli-examples.md`<br />`references/pdftk-man-page.md`<br />`references/pdftk-server-license.md`<br />`references/third-party-materials.md` |
|
||||
| [penpot-uiux-design](../skills/penpot-uiux-design/SKILL.md) | Comprehensive guide for creating professional UI/UX designs in Penpot using MCP tools. Use this skill when: (1) Creating new UI/UX designs for web, mobile, or desktop applications, (2) Building design systems with components and tokens, (3) Designing dashboards, forms, navigation, or landing pages, (4) Applying accessibility standards and best practices, (5) Following platform guidelines (iOS, Android, Material Design), (6) Reviewing or improving existing Penpot designs for usability. Triggers: "design a UI", "create interface", "build layout", "design dashboard", "create form", "design landing page", "make it accessible", "design system", "component library". | `references/accessibility.md`<br />`references/component-patterns.md`<br />`references/platform-guidelines.md`<br />`references/setup-troubleshooting.md` |
|
||||
| [php-mcp-server-generator](../skills/php-mcp-server-generator/SKILL.md) | Generate a complete PHP Model Context Protocol server project with tools, resources, prompts, and tests using the official PHP SDK | None |
|
||||
| [planning-oracle-to-postgres-migration-integration-testing](../skills/planning-oracle-to-postgres-migration-integration-testing/SKILL.md) | Creates an integration testing plan for .NET data access artifacts during Oracle-to-PostgreSQL database migrations. Analyzes a single project to identify repositories, DAOs, and service layers that interact with the database, then produces a structured testing plan. Use when planning integration test coverage for a migrated project, identifying which data access methods need tests, or preparing for Oracle-to-PostgreSQL migration validation. | None |
|
||||
| [plantuml-ascii](../skills/plantuml-ascii/SKILL.md) | Generate ASCII art diagrams using PlantUML text mode. Use when user asks to create ASCII diagrams, text-based diagrams, terminal-friendly diagrams, or mentions plantuml ascii, text diagram, ascii art diagram. Supports: Converting PlantUML diagrams to ASCII art, Creating sequence diagrams, class diagrams, flowcharts in ASCII format, Generating Unicode-enhanced ASCII art with -utxt flag | None |
|
||||
| [playwright-automation-fill-in-form](../skills/playwright-automation-fill-in-form/SKILL.md) | Automate filling in a form using Playwright MCP | None |
|
||||
| [playwright-explore-website](../skills/playwright-explore-website/SKILL.md) | Website exploration for testing using Playwright MCP | None |
|
||||
@@ -199,8 +204,10 @@ See [CONTRIBUTING.md](../CONTRIBUTING.md#adding-skills) for guidelines on how to
|
||||
| [remember-interactive-programming](../skills/remember-interactive-programming/SKILL.md) | A micro-prompt that reminds the agent that it is an interactive programmer. Works great in Clojure when Copilot has access to the REPL (probably via Backseat Driver). Will work with any system that has a live REPL that the agent can use. Adapt the prompt with any specific reminders in your workflow and/or workspace. | None |
|
||||
| [repo-story-time](../skills/repo-story-time/SKILL.md) | Generate a comprehensive repository summary and narrative story from commit history | None |
|
||||
| [review-and-refactor](../skills/review-and-refactor/SKILL.md) | Review and refactor code in your project according to defined instructions | None |
|
||||
| [reviewing-oracle-to-postgres-migration](../skills/reviewing-oracle-to-postgres-migration/SKILL.md) | Identifies Oracle-to-PostgreSQL migration risks by cross-referencing code against known behavioral differences (empty strings, refcursors, type coercion, sorting, timestamps, concurrent transactions, etc.). Use when planning a database migration, reviewing migration artifacts, or validating that integration tests cover Oracle/PostgreSQL differences. | `references/REFERENCE.md`<br />`references/empty-strings-handling.md`<br />`references/no-data-found-exceptions.md`<br />`references/oracle-parentheses-from-clause.md`<br />`references/oracle-to-postgres-sorting.md`<br />`references/oracle-to-postgres-timestamp-timezone.md`<br />`references/oracle-to-postgres-to-char-numeric.md`<br />`references/oracle-to-postgres-type-coercion.md`<br />`references/postgres-concurrent-transactions.md`<br />`references/postgres-refcursor-handling.md` |
|
||||
| [ruby-mcp-server-generator](../skills/ruby-mcp-server-generator/SKILL.md) | Generate a complete Model Context Protocol server project in Ruby using the official MCP Ruby SDK gem. | None |
|
||||
| [rust-mcp-server-generator](../skills/rust-mcp-server-generator/SKILL.md) | Generate a complete Rust Model Context Protocol server project with tools, prompts, resources, and tests using the official rmcp SDK | None |
|
||||
| [scaffolding-oracle-to-postgres-migration-test-project](../skills/scaffolding-oracle-to-postgres-migration-test-project/SKILL.md) | Scaffolds an xUnit integration test project for validating Oracle-to-PostgreSQL database migration behavior in .NET solutions. Creates the test project, transaction-rollback base class, and seed data manager. Use when setting up test infrastructure before writing migration integration tests, or when a test project is needed for Oracle-to-PostgreSQL validation. | None |
|
||||
| [scoutqa-test](../skills/scoutqa-test/SKILL.md) | This skill should be used when the user asks to "test this website", "run exploratory testing", "check for accessibility issues", "verify the login flow works", "find bugs on this page", or requests automated QA testing. Triggers on web application testing scenarios including smoke tests, accessibility audits, e-commerce flows, and user flow validation using ScoutQA CLI. IMPORTANT: Use this skill proactively after implementing web application features to verify they work correctly - don't wait for the user to ask for testing. | None |
|
||||
| [shuffle-json-data](../skills/shuffle-json-data/SKILL.md) | Shuffle repetitive JSON objects safely by validating schema consistency before randomising entries. | None |
|
||||
| [snowflake-semanticview](../skills/snowflake-semanticview/SKILL.md) | Create, alter, and validate Snowflake semantic views using Snowflake CLI (snow). Use when asked to build or troubleshoot semantic views/semantic layer definitions with CREATE/ALTER SEMANTIC VIEW, to validate semantic-view DDL against Snowflake via CLI, or to guide Snowflake CLI installation and connection setup. | None |
|
||||
|
||||
32
plugins/oracle-to-postgres-migration-expert/.github/plugin/plugin.json
vendored
Normal file
32
plugins/oracle-to-postgres-migration-expert/.github/plugin/plugin.json
vendored
Normal file
@@ -0,0 +1,32 @@
|
||||
{
|
||||
"name": "oracle-to-postgres-migration-expert",
|
||||
"description": "Expert agent for Oracle-to-PostgreSQL application migrations in .NET solutions. Performs code edits, runs commands, and invokes extension tools to migrate .NET/Oracle data access patterns to PostgreSQL.",
|
||||
"version": "1.0.0",
|
||||
"author": {
|
||||
"name": "Awesome Copilot Community"
|
||||
},
|
||||
"repository": "https://github.com/github/awesome-copilot",
|
||||
"license": "MIT",
|
||||
"keywords": [
|
||||
"oracle",
|
||||
"postgresql",
|
||||
"database-migration",
|
||||
"dotnet",
|
||||
"sql",
|
||||
"migration",
|
||||
"integration-testing",
|
||||
"stored-procedures"
|
||||
],
|
||||
"agents": [
|
||||
"./agents/oracle-to-postgres-migration-expert.md"
|
||||
],
|
||||
"skills": [
|
||||
"./skills/creating-oracle-to-postgres-master-migration-plan/",
|
||||
"./skills/creating-oracle-to-postgres-migration-bug-report/",
|
||||
"./skills/creating-oracle-to-postgres-migration-integration-tests/",
|
||||
"./skills/migrating-oracle-to-postgres-stored-procedures/",
|
||||
"./skills/planning-oracle-to-postgres-migration-integration-testing/",
|
||||
"./skills/reviewing-oracle-to-postgres-migration/",
|
||||
"./skills/scaffolding-oracle-to-postgres-migration-test-project/"
|
||||
]
|
||||
}
|
||||
117
plugins/oracle-to-postgres-migration-expert/README.md
Normal file
117
plugins/oracle-to-postgres-migration-expert/README.md
Normal file
@@ -0,0 +1,117 @@
|
||||
# Oracle-to-PostgreSQL Migration Expert Plugin
|
||||
|
||||
Expert agent for Oracle-to-PostgreSQL application migrations in .NET solutions. Performs code edits, runs commands, and invokes extension tools to migrate .NET/Oracle data access patterns to PostgreSQL.
|
||||
|
||||
## Installation
|
||||
|
||||
```bash
|
||||
# Using Copilot CLI
|
||||
copilot plugin install oracle-to-postgres-migration-expert@awesome-copilot
|
||||
```
|
||||
|
||||
## What's Included
|
||||
|
||||
### Agents
|
||||
|
||||
| Agent | Description |
|
||||
|-------|-------------|
|
||||
| `Oracle-to-PostgreSQL Migration Expert` | Expert agent for Oracle→PostgreSQL migrations. Makes code edits and runs commands directly, educates users on migration concepts and pitfalls, and invokes extension tools on user confirmation. |
|
||||
|
||||
### Skills
|
||||
|
||||
| Skill | Description |
|
||||
|-------|-------------|
|
||||
| `reviewing-oracle-to-postgres-migration` | Identifies Oracle-to-PostgreSQL migration risks by cross-referencing code against known behavioral differences (empty strings, refcursors, type coercion, sorting, timestamps, concurrent transactions, etc.). |
|
||||
| `creating-oracle-to-postgres-master-migration-plan` | Discovers all projects in a .NET solution, classifies each for Oracle-to-PostgreSQL migration eligibility, and produces a persistent master migration plan. |
|
||||
| `migrating-oracle-to-postgres-stored-procedures` | Migrates Oracle PL/SQL stored procedures to PostgreSQL PL/pgSQL. Translates Oracle-specific syntax, preserves method signatures and type-anchored parameters, and applies `COLLATE "C"` for Oracle-compatible text sorting. |
|
||||
| `planning-oracle-to-postgres-migration-integration-testing` | Creates an integration testing plan for .NET data access artifacts, identifying repositories, DAOs, and service layers that need validation coverage. |
|
||||
| `scaffolding-oracle-to-postgres-migration-test-project` | Scaffolds an xUnit integration test project with a transaction-rollback base class and seed data manager for Oracle-to-PostgreSQL migration validation. |
|
||||
| `creating-oracle-to-postgres-migration-integration-tests` | Generates DB-agnostic xUnit integration tests with deterministic seed data that validate behavior consistency across both database systems. |
|
||||
| `creating-oracle-to-postgres-migration-bug-report` | Creates structured bug reports for defects discovered during Oracle-to-PostgreSQL migration validation, with severity, root cause, and remediation steps. |
|
||||
|
||||
## Features
|
||||
|
||||
### Educational Guidance
|
||||
|
||||
The expert agent educates users throughout the migration journey:
|
||||
|
||||
- **Migration Concepts**: Explains Oracle→PostgreSQL differences (empty strings vs NULL, NO_DATA_FOUND exceptions, sort order, TO_CHAR conversions, type coercion strictness, REF CURSOR handling, concurrent transactions, timestamp/timezone behavior)
|
||||
- **Pitfall Reference**: Surfaces insights from migration knowledge so users understand why changes are needed
|
||||
- **Best Practices**: Advises on minimizing changes, preserving logic, and ensuring schema immutability
|
||||
- **Workflow Guidance**: Presents a four-phase migration workflow as a guide users can follow at their own pace
|
||||
|
||||
### Suggest-Then-Act Pattern
|
||||
|
||||
The expert suggests actionable next steps and only proceeds with user confirmation:
|
||||
|
||||
1. **Educate** on the migration topic and why it matters
|
||||
2. **Suggest** a recommended action with expected outcomes
|
||||
3. **Confirm** the user wants to proceed
|
||||
4. **Act** — make edits, run commands, or invoke extension tools directly
|
||||
5. **Summarize** what was produced and suggest the next step
|
||||
|
||||
No autonomous chaining — the user controls the pace and sequence.
|
||||
|
||||
## Migration Workflow
|
||||
|
||||
The expert guides users through a four-phase workflow:
|
||||
|
||||
**Phase 1 — Discovery & Planning**
|
||||
|
||||
1. Create a master migration plan (classifies all projects in the solution)
|
||||
2. Set up Oracle and PostgreSQL DDL artifacts
|
||||
|
||||
**Phase 2 — Code Migration** *(per project)*
|
||||
3. Migrate application codebase (via `ms-ossdata.vscode-pgsql` extension)
|
||||
4. Migrate stored procedures (Oracle PL/SQL → PostgreSQL PL/pgSQL)
|
||||
|
||||
**Phase 3 — Validation** *(per project)*
|
||||
5. Plan integration testing
|
||||
6. Scaffold the xUnit test project
|
||||
7. Create integration tests
|
||||
8. Run tests against Oracle (baseline) and PostgreSQL (target)
|
||||
9. Validate test results
|
||||
10. Create bug reports for any failures
|
||||
|
||||
**Phase 4 — Reporting**
|
||||
11. Generate final migration report (via `ms-ossdata.vscode-pgsql` extension)
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- Visual Studio Code with GitHub Copilot
|
||||
- PostgreSQL Extension (`ms-ossdata.vscode-pgsql`) — required for application code migration and report generation
|
||||
- .NET solution with Oracle dependencies to migrate
|
||||
|
||||
## Directory Structure
|
||||
|
||||
The agent expects and creates the following structure in your repository:
|
||||
|
||||
```
|
||||
.github/
|
||||
└── oracle-to-postgres-migration/
|
||||
├── Reports/
|
||||
│ ├── Master Migration Plan.md
|
||||
│ ├── {Project} Integration Testing Plan.md
|
||||
│ ├── {Project} Application Migration Report.md
|
||||
│ ├── BUG_REPORT_*.md
|
||||
│ └── TestResults/
|
||||
└── DDL/
|
||||
├── Oracle/ # Oracle DDL scripts (pre-migration)
|
||||
└── Postgres/ # PostgreSQL DDL scripts (post-migration)
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
1. **Ask for Guidance**: Invoke the expert with a migration question or situation (e.g., *"How should I approach migrating my .NET solution to PostgreSQL?"* or *"What does Oracle do with empty strings that's different from PostgreSQL?"*)
|
||||
2. **Learn & Plan**: The expert explains concepts, surfaces pitfall insights, and presents recommended workflow steps
|
||||
3. **Choose Your Next Step**: Decide which task to tackle (master plan, code migration, testing, etc.)
|
||||
4. **Confirm and Act**: Tell the expert to proceed, and it makes edits, runs commands, or invokes extension tools directly
|
||||
5. **Review & Continue**: Examine the results and ask for the next step
|
||||
|
||||
## Source
|
||||
|
||||
This plugin is part of [Awesome Copilot](https://github.com/github/awesome-copilot), a community-driven collection of GitHub Copilot extensions.
|
||||
|
||||
## License
|
||||
|
||||
MIT
|
||||
@@ -0,0 +1,83 @@
|
||||
---
|
||||
name: creating-oracle-to-postgres-master-migration-plan
|
||||
description: 'Discovers all projects in a .NET solution, classifies each for Oracle-to-PostgreSQL migration eligibility, and produces a persistent master migration plan. Use when starting a multi-project Oracle-to-PostgreSQL migration, creating a migration inventory, or assessing which .NET projects contain Oracle dependencies.'
|
||||
---
|
||||
|
||||
# Creating an Oracle-to-PostgreSQL Master Migration Plan
|
||||
|
||||
Analyze a .NET solution, classify every project for Oracle→PostgreSQL migration eligibility, and write a structured plan that downstream agents and skills can parse.
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Discover projects in the solution
|
||||
- [ ] Step 2: Classify each project
|
||||
- [ ] Step 3: Confirm with user
|
||||
- [ ] Step 4: Write the plan file
|
||||
```
|
||||
|
||||
**Step 1: Discover projects**
|
||||
|
||||
Find the Solution File (it has a `.sln` or `.slnx` extension) in the workspace root (ask the user if multiple exist). Parse it to extract all `.csproj` project references. For each project, note the name, path, and type (class library, web API, console, test, etc.).
|
||||
|
||||
**Step 2: Classify each project**
|
||||
|
||||
Scan every non-test project for Oracle indicators:
|
||||
|
||||
- NuGet references: `Oracle.ManagedDataAccess`, `Oracle.EntityFrameworkCore` (check `.csproj` and `packages.config`)
|
||||
- Config entries: Oracle connection strings in `appsettings.json`, `web.config`, `app.config`
|
||||
- Code usage: `OracleConnection`, `OracleCommand`, `OracleDataReader`
|
||||
- DDL cross-references under `.github/oracle-to-postgres-migration/DDL/Oracle/` (if present)
|
||||
|
||||
Assign one classification per project:
|
||||
|
||||
| Classification | Meaning |
|
||||
|---|---|
|
||||
| **MIGRATE** | Has Oracle interactions requiring conversion |
|
||||
| **SKIP** | No Oracle indicators (UI-only, shared utility, etc.) |
|
||||
| **ALREADY_MIGRATED** | A `-postgres` or `.Postgres` duplicate exists and appears processed |
|
||||
| **TEST_PROJECT** | Test project; handled by the testing workflow |
|
||||
|
||||
**Step 3: Confirm with user**
|
||||
|
||||
Present the classified list. Let the user adjust classifications or migration ordering before finalizing.
|
||||
|
||||
**Step 4: Write the plan file**
|
||||
|
||||
Save to: `.github/oracle-to-postgres-migration/Reports/Master Migration Plan.md`
|
||||
|
||||
Use this exact template — downstream consumers depend on the structure:
|
||||
|
||||
````markdown
|
||||
# Master Migration Plan
|
||||
|
||||
**Solution:** {solution file name}
|
||||
**Solution Root:** {REPOSITORY_ROOT}
|
||||
**Created:** {timestamp}
|
||||
**Last Updated:** {timestamp}
|
||||
|
||||
## Solution Summary
|
||||
|
||||
| Metric | Count |
|
||||
|--------|-------|
|
||||
| Total projects in solution | {n} |
|
||||
| Projects requiring migration | {n} |
|
||||
| Projects already migrated | {n} |
|
||||
| Projects skipped (no Oracle usage) | {n} |
|
||||
| Test projects (handled separately) | {n} |
|
||||
|
||||
## Project Inventory
|
||||
|
||||
| # | Project Name | Path | Classification | Notes |
|
||||
|---|---|---|---|---|
|
||||
| 1 | {name} | {relative path} | MIGRATE | {notes} |
|
||||
| 2 | {name} | {relative path} | SKIP | No Oracle dependencies |
|
||||
|
||||
## Migration Order
|
||||
|
||||
1. **{ProjectName}** — {rationale, e.g., "Core data access library; other projects depend on it."}
|
||||
2. **{ProjectName}** — {rationale}
|
||||
````
|
||||
|
||||
Order projects so that shared/foundational libraries are migrated before their dependents.
|
||||
@@ -0,0 +1,43 @@
|
||||
---
|
||||
name: creating-oracle-to-postgres-migration-bug-report
|
||||
description: 'Creates structured bug reports for defects found during Oracle-to-PostgreSQL migration. Use when documenting behavioral differences between Oracle and PostgreSQL as actionable bug reports with severity, root cause, and remediation steps.'
|
||||
---
|
||||
|
||||
# Creating Bug Reports for Oracle-to-PostgreSQL Migration
|
||||
|
||||
## When to Use
|
||||
|
||||
- Documenting a defect caused by behavioral differences between Oracle and PostgreSQL
|
||||
- Writing or reviewing a bug report for an Oracle-to-PostgreSQL migration project
|
||||
|
||||
## Bug Report Format
|
||||
|
||||
Use the template in [references/BUG-REPORT-TEMPLATE.md](references/BUG-REPORT-TEMPLATE.md). Each report must include:
|
||||
|
||||
- **Status**: ✅ RESOLVED, ⛔ UNRESOLVED, or ⏳ IN PROGRESS
|
||||
- **Component**: Affected endpoint, repository, or stored procedure
|
||||
- **Test**: Related automated test names
|
||||
- **Severity**: Low / Medium / High / Critical — based on impact scope
|
||||
- **Problem**: Expected Oracle behavior vs. observed PostgreSQL behavior
|
||||
- **Scenario**: Ordered reproduction steps with seed data, operation, expected result, and actual result
|
||||
- **Root Cause**: The specific Oracle/PostgreSQL behavioral difference causing the defect
|
||||
- **Solution**: Changes made or required, with explicit file paths
|
||||
- **Validation**: Steps to confirm the fix on both databases
|
||||
|
||||
## Oracle-to-PostgreSQL Guidance
|
||||
|
||||
- **Oracle is the source of truth** — frame expected behavior from the Oracle baseline
|
||||
- Call out data layer nuances explicitly: empty string vs. NULL, type coercion strictness, collation, sequence values, time zones, padding, constraints
|
||||
- Client code changes should be avoided unless required for correct behavior; when proposed, document and justify them clearly
|
||||
|
||||
## Writing Style
|
||||
|
||||
- Plain language, short sentences, clear next actions
|
||||
- Present or past tense consistently
|
||||
- Bullets and numbered lists for steps and validations
|
||||
- Minimal SQL excerpts and logs as evidence; omit sensitive data and keep snippets reproducible
|
||||
- Stick to existing runtime/language versions; avoid speculative fixes
|
||||
|
||||
## Filename Convention
|
||||
|
||||
Save bug reports as `BUG_REPORT_<DescriptiveSlug>.md` where `<DescriptiveSlug>` is a short PascalCase identifier (e.g., `EmptyStringNullHandling`, `RefCursorUnwrapFailure`).
|
||||
@@ -0,0 +1,79 @@
|
||||
# Bug Report Template
|
||||
|
||||
Use this template when creating bug reports for Oracle-to-PostgreSQL migration defects.
|
||||
|
||||
## Filename Format
|
||||
|
||||
```
|
||||
BUG_REPORT_<DescriptiveSlug>.md
|
||||
```
|
||||
|
||||
## Template Structure
|
||||
|
||||
```markdown
|
||||
# Bug Report: <Title>
|
||||
|
||||
**Status:** ✅ RESOLVED | ⛔ UNRESOLVED | ⏳ IN PROGRESS
|
||||
**Component:** <High-level component/endpoint and key method(s)>
|
||||
**Test:** <Related automated test names>
|
||||
**Severity:** Low | Medium | High | Critical
|
||||
|
||||
---
|
||||
|
||||
## Problem
|
||||
|
||||
<Observable incorrect behavior. State expected behavior (Oracle baseline)
|
||||
versus actual behavior (PostgreSQL). Be specific and factual.>
|
||||
|
||||
## Scenario
|
||||
|
||||
<Ordered steps to reproduce the defect. Include:
|
||||
1. Prerequisites and seed data
|
||||
2. Exact operation or API call
|
||||
3. Expected result (Oracle)
|
||||
4. Actual result (PostgreSQL)>
|
||||
|
||||
## Root Cause
|
||||
|
||||
<Minimal, concrete technical cause. Reference the specific Oracle/PostgreSQL
|
||||
behavioral difference (e.g., empty string vs NULL, type coercion strictness).>
|
||||
|
||||
## Solution
|
||||
|
||||
<Changes made or required. Be explicit about data access layer changes,
|
||||
tracking flags, and any client code modifications. Note whether changes
|
||||
are already applied or still needed.>
|
||||
|
||||
## Validation
|
||||
|
||||
<Bullet list of passing tests or manual checks that confirm the fix:
|
||||
- Re-run reproduction steps on both Oracle and PostgreSQL
|
||||
- Compare row/column outputs
|
||||
- Check error handling parity>
|
||||
|
||||
## Files Modified
|
||||
|
||||
<Bullet list with relative file paths and short purpose for each change:
|
||||
- `src/DataAccess/FooRepository.cs` — Added explicit NULL check for empty string parameter>
|
||||
|
||||
## Notes / Next Steps
|
||||
|
||||
<Follow-ups, environment caveats, risks, or dependencies on other fixes.>
|
||||
```
|
||||
|
||||
## Status Values
|
||||
|
||||
| Status | Meaning |
|
||||
|--------|---------|
|
||||
| ✅ RESOLVED | Defect has been fixed and verified |
|
||||
| ⛔ UNRESOLVED | Defect has not been addressed yet |
|
||||
| ⏳ IN PROGRESS | Defect is being investigated or fix is underway |
|
||||
|
||||
## Style Rules
|
||||
|
||||
- Keep wording concise and factual
|
||||
- Use present or past tense consistently
|
||||
- Prefer bullets and numbered lists for steps and validation
|
||||
- Call out data layer nuances (tracking, padding, constraints) explicitly
|
||||
- Keep to existing runtime/language versions; avoid speculative fixes
|
||||
- Include minimal SQL excerpts and logs as evidence; omit sensitive data
|
||||
@@ -0,0 +1,60 @@
|
||||
---
|
||||
name: creating-oracle-to-postgres-migration-integration-tests
|
||||
description: 'Creates integration test cases for .NET data access artifacts during Oracle-to-PostgreSQL database migrations. Generates DB-agnostic xUnit tests with deterministic seed data that validate behavior consistency across both database systems. Use when creating integration tests for a migrated project, generating test coverage for data access layers, or writing Oracle-to-PostgreSQL migration validation tests.'
|
||||
---
|
||||
|
||||
# Creating Integration Tests for Oracle-to-PostgreSQL Migration
|
||||
|
||||
Generates integration test cases for data access artifacts in a single target project. Tests validate behavior consistency when running against Oracle or PostgreSQL.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
- The test project must already exist and compile (scaffolded separately).
|
||||
- Read the existing base test class and seed manager conventions before writing tests.
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
Test Creation:
|
||||
- [ ] Step 1: Discover the test project conventions
|
||||
- [ ] Step 2: Identify testable data access artifacts
|
||||
- [ ] Step 3: Create seed data
|
||||
- [ ] Step 4: Write test cases
|
||||
- [ ] Step 5: Review determinism
|
||||
```
|
||||
|
||||
**Step 1: Discover the test project conventions**
|
||||
|
||||
Read the base test class, seed manager, and project file to understand inheritance patterns, transaction management, and seed file conventions.
|
||||
|
||||
**Step 2: Identify testable data access artifacts**
|
||||
|
||||
Scope to the target project only. List data access methods that interact with the database — repositories, DAOs, stored procedure callers, query builders.
|
||||
|
||||
**Step 3: Create seed data**
|
||||
|
||||
- Follow seed file location and naming conventions from the existing project.
|
||||
- Reuse existing seed files when possible.
|
||||
- Avoid `TRUNCATE TABLE` — keep existing database data intact.
|
||||
- Do not commit seed data; tests run in transactions that roll back.
|
||||
- Ensure seed data does not conflict with other tests.
|
||||
- Load and verify seed data before assertions depend on it.
|
||||
|
||||
**Step 4: Write test cases**
|
||||
|
||||
- Inherit from the base test class to get automatic transaction create/rollback.
|
||||
- Assert logical outputs (rows, columns, counts, error types), not platform-specific messages.
|
||||
- Assert specific expected values — never assert that a value is merely non-null or non-empty when a concrete value is available from seed data.
|
||||
- Avoid testing code paths that do not exist or asserting behavior that cannot occur.
|
||||
- Avoid redundant assertions across tests targeting the same method.
|
||||
|
||||
**Step 5: Review determinism**
|
||||
|
||||
Re-examine every assertion against non-null values. Confirm each is deterministic against the seeded data. Fix any assertion that depends on database state outside the test's control.
|
||||
|
||||
## Key Constraints
|
||||
|
||||
- **Oracle is the golden source** — tests capture Oracle's expected behavior.
|
||||
- **DB-agnostic assertions** — no platform-specific error messages or syntax in assertions.
|
||||
- **Seed only against Oracle** — test project will be migrated to PostgreSQL later.
|
||||
- **Scoped to one project** — do not create tests for artifacts outside the target project.
|
||||
@@ -0,0 +1,42 @@
|
||||
---
|
||||
name: migrating-oracle-to-postgres-stored-procedures
|
||||
description: 'Migrates Oracle PL/SQL stored procedures to PostgreSQL PL/pgSQL. Translates Oracle-specific syntax, preserves method signatures and type-anchored parameters, leverages orafce where appropriate, and applies COLLATE "C" for Oracle-compatible text sorting. Use when converting Oracle stored procedures or functions to PostgreSQL equivalents during a database migration.'
|
||||
---
|
||||
|
||||
# Migrating Stored Procedures from Oracle to PostgreSQL
|
||||
|
||||
Translate Oracle PL/SQL stored procedures and functions to PostgreSQL PL/pgSQL equivalents.
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Read the Oracle source procedure
|
||||
- [ ] Step 2: Translate to PostgreSQL PL/pgSQL
|
||||
- [ ] Step 3: Write the migrated procedure to Postgres output directory
|
||||
```
|
||||
|
||||
**Step 1: Read the Oracle source procedure**
|
||||
|
||||
Read the Oracle stored procedure from `.github/oracle-to-postgres-migration/DDL/Oracle/Procedures and Functions/`. Consult the Oracle table/view definitions at `.github/oracle-to-postgres-migration/DDL/Oracle/Tables and Views/` for type resolution.
|
||||
|
||||
**Step 2: Translate to PostgreSQL PL/pgSQL**
|
||||
|
||||
Apply these translation rules:
|
||||
|
||||
- Translate all Oracle-specific syntax to PostgreSQL equivalents.
|
||||
- Preserve original functionality and control flow logic.
|
||||
- Keep type-anchored input parameters (e.g., `PARAM_NAME IN table_name.column_name%TYPE`).
|
||||
- Use explicit types (`NUMERIC`, `VARCHAR`, `INTEGER`) for output parameters passed to other procedures — do not type-anchor these.
|
||||
- Do not alter method signatures.
|
||||
- Do not prefix object names with schema names unless already present in the Oracle source.
|
||||
- Leave exception handling and rollback logic unchanged.
|
||||
- Do not generate `COMMENT` or `GRANT` statements.
|
||||
- Use `COLLATE "C"` when ordering by text fields for Oracle-compatible sorting.
|
||||
- Leverage the `orafce` extension when it improves clarity or fidelity.
|
||||
|
||||
Consult the PostgreSQL table/view definitions at `.github/oracle-to-postgres-migration/DDL/Postgres/Tables and Views/` for target schema details.
|
||||
|
||||
**Step 3: Write the migrated procedure to Postgres output directory**
|
||||
|
||||
Place each migrated procedure in its own file under `.github/oracle-to-postgres-migration/DDL/Postgres/Procedures and Functions/{PACKAGE_NAME_IF_APPLICABLE}/`. One procedure per file.
|
||||
@@ -0,0 +1,44 @@
|
||||
---
|
||||
name: planning-oracle-to-postgres-migration-integration-testing
|
||||
description: 'Creates an integration testing plan for .NET data access artifacts during Oracle-to-PostgreSQL database migrations. Analyzes a single project to identify repositories, DAOs, and service layers that interact with the database, then produces a structured testing plan. Use when planning integration test coverage for a migrated project, identifying which data access methods need tests, or preparing for Oracle-to-PostgreSQL migration validation.'
|
||||
---
|
||||
|
||||
# Planning Integration Testing for Oracle-to-PostgreSQL Migration
|
||||
|
||||
Analyze a single target project to identify data access artifacts that require integration testing, then produce a structured, actionable testing plan.
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Identify data access artifacts
|
||||
- [ ] Step 2: Classify testing priorities
|
||||
- [ ] Step 3: Write the testing plan
|
||||
```
|
||||
|
||||
**Step 1: Identify data access artifacts**
|
||||
|
||||
Scope to the target project only. Find classes and methods that interact directly with the database — repositories, DAOs, stored procedure callers, service layers performing CRUD operations.
|
||||
|
||||
**Step 2: Classify testing priorities**
|
||||
|
||||
Rank artifacts by migration risk. Prioritize methods that use Oracle-specific features (refcursors, `TO_CHAR`, implicit type coercion, `NO_DATA_FOUND`) over simple CRUD.
|
||||
|
||||
**Step 3: Write the testing plan**
|
||||
|
||||
Write a markdown plan covering:
|
||||
- List of testable artifacts with method signatures
|
||||
- Recommended test cases per artifact
|
||||
- Seed data requirements
|
||||
- Known Oracle→PostgreSQL behavioral differences to validate
|
||||
|
||||
## Output
|
||||
|
||||
Write the plan to: `.github/oracle-to-postgres-migration/Reports/{TARGET_PROJECT} Integration Testing Plan.md`
|
||||
|
||||
## Key Constraints
|
||||
|
||||
- **Single project scope** — only plan tests for artifacts within the target project.
|
||||
- **Database interactions only** — skip business logic that does not touch the database.
|
||||
- **Oracle is the golden source** — tests should capture Oracle's expected behavior for comparison against PostgreSQL.
|
||||
- **No multi-connection harnessing** — migrated applications are copied and renamed (e.g., `MyApp.Postgres`), so each instance targets one database.
|
||||
67
skills/reviewing-oracle-to-postgres-migration/SKILL.md
Normal file
67
skills/reviewing-oracle-to-postgres-migration/SKILL.md
Normal file
@@ -0,0 +1,67 @@
|
||||
---
|
||||
name: reviewing-oracle-to-postgres-migration
|
||||
description: 'Identifies Oracle-to-PostgreSQL migration risks by cross-referencing code against known behavioral differences (empty strings, refcursors, type coercion, sorting, timestamps, concurrent transactions, etc.). Use when planning a database migration, reviewing migration artifacts, or validating that integration tests cover Oracle/PostgreSQL differences.'
|
||||
---
|
||||
|
||||
# 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](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](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, refcursor consumption, concurrent transactions, timestamps, etc.).
|
||||
|
||||
**Step 4: Gate the result**
|
||||
|
||||
Return a checklist asserting each applicable insight was addressed, migration scripts run, and integration tests pass.
|
||||
@@ -0,0 +1,13 @@
|
||||
# Reference Index
|
||||
|
||||
| File | Brief description |
|
||||
| --- | --- |
|
||||
| [empty-strings-handling.md](empty-strings-handling.md) | Oracle treats '' as NULL; PostgreSQL keeps empty strings distinct—patterns to align behavior in code, tests, and migrations. |
|
||||
| [no-data-found-exceptions.md](no-data-found-exceptions.md) | Oracle SELECT INTO raises "no data found"; PostgreSQL doesn’t—add explicit NOT FOUND handling to mirror Oracle behavior. |
|
||||
| [oracle-parentheses-from-clause.md](oracle-parentheses-from-clause.md) | Oracle allows `FROM(TABLE_NAME)` syntax; PostgreSQL requires `FROM TABLE_NAME`—remove unnecessary parentheses around table names. |
|
||||
| [oracle-to-postgres-sorting.md](oracle-to-postgres-sorting.md) | How to preserve Oracle-like ordering in PostgreSQL using COLLATE "C" and DISTINCT wrapper patterns. |
|
||||
| [oracle-to-postgres-to-char-numeric.md](oracle-to-postgres-to-char-numeric.md) | Oracle allows TO_CHAR(numeric) without format; PostgreSQL requires format string—use CAST(numeric AS TEXT) instead. |
|
||||
| [oracle-to-postgres-type-coercion.md](oracle-to-postgres-type-coercion.md) | PostgreSQL strict type checks vs. Oracle implicit coercion—fix comparison errors by quoting or casting literals. |
|
||||
| [postgres-concurrent-transactions.md](postgres-concurrent-transactions.md) | PostgreSQL allows only one active command per connection—materialize results or use separate connections to avoid concurrent operation errors. |
|
||||
| [postgres-refcursor-handling.md](postgres-refcursor-handling.md) | Differences in refcursor handling; PostgreSQL requires fetching by cursor name—C# patterns to unwrap and read results. |
|
||||
| [oracle-to-postgres-timestamp-timezone.md](oracle-to-postgres-timestamp-timezone.md) | CURRENT_TIMESTAMP / NOW() return UTC-normalised timestamptz in PostgreSQL; Npgsql surfaces DateTime.Kind=Unspecified—force UTC at connection open and in application code. |
|
||||
@@ -0,0 +1,69 @@
|
||||
# Oracle to PostgreSQL: Empty String Handling Differences
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle automatically converts empty strings (`''`) to `NULL` in VARCHAR2 columns. PostgreSQL preserves empty strings as distinct from `NULL`. This difference can cause application logic errors and test failures during migration.
|
||||
|
||||
## Behavior Comparison
|
||||
|
||||
**Oracle:**
|
||||
- Empty string (`''`) is **always** treated as `NULL` in VARCHAR2 columns
|
||||
- `WHERE column = ''` never matches rows; use `WHERE column IS NULL`
|
||||
- Cannot distinguish between explicit empty string and `NULL`
|
||||
|
||||
**PostgreSQL:**
|
||||
- Empty string (`''`) and `NULL` are **distinct** values
|
||||
- `WHERE column = ''` matches empty strings
|
||||
- `WHERE column IS NULL` matches `NULL` values
|
||||
|
||||
## Code Example
|
||||
|
||||
```sql
|
||||
-- Oracle behavior
|
||||
INSERT INTO table (varchar_column) VALUES ('');
|
||||
SELECT * FROM table WHERE varchar_column IS NULL; -- Returns the row
|
||||
|
||||
-- PostgreSQL behavior
|
||||
INSERT INTO table (varchar_column) VALUES ('');
|
||||
SELECT * FROM table WHERE varchar_column IS NULL; -- Returns nothing
|
||||
SELECT * FROM table WHERE varchar_column = ''; -- Returns the row
|
||||
```
|
||||
|
||||
## Migration Actions
|
||||
|
||||
### 1. Stored Procedures
|
||||
Update logic that assumes empty strings convert to `NULL`:
|
||||
|
||||
```sql
|
||||
-- Preserve Oracle behavior (convert empty to NULL):
|
||||
column = NULLIF(param, '')
|
||||
|
||||
-- Or accept PostgreSQL behavior (preserve empty string):
|
||||
column = param
|
||||
```
|
||||
|
||||
### 2. Application Code
|
||||
Review code that checks for `NULL` and ensure it handles empty strings appropriately:
|
||||
|
||||
```csharp
|
||||
// Before (Oracle-specific)
|
||||
if (value == null) { }
|
||||
|
||||
// After (PostgreSQL-compatible)
|
||||
if (string.IsNullOrEmpty(value)) { }
|
||||
```
|
||||
|
||||
### 3. Tests
|
||||
Update assertions to be compatible with both behaviors:
|
||||
|
||||
```csharp
|
||||
// Migration-compatible test pattern
|
||||
var value = reader.IsDBNull(columnIndex) ? null : reader.GetString(columnIndex);
|
||||
Assert.IsTrue(string.IsNullOrEmpty(value));
|
||||
```
|
||||
|
||||
### 4. Data Migration
|
||||
Decide whether to:
|
||||
- Convert existing `NULL` values to empty strings
|
||||
- Convert empty strings to `NULL` using `NULLIF(column, '')`
|
||||
- Leave values as-is and update application logic
|
||||
@@ -0,0 +1,99 @@
|
||||
# PostgreSQL Exception Handling: SELECT INTO No Data Found
|
||||
|
||||
## Overview
|
||||
|
||||
A common issue when migrating from Oracle to PostgreSQL involves `SELECT INTO` statements that expect to raise an exception when no rows are found. This pattern difference can cause integration tests to fail and application logic to behave incorrectly if not properly handled.
|
||||
|
||||
---
|
||||
|
||||
## Problem Description
|
||||
|
||||
### Scenario
|
||||
|
||||
A stored procedure performs a lookup operation using `SELECT INTO` to retrieve a required value:
|
||||
|
||||
```sql
|
||||
SELECT column_name
|
||||
INTO variable_name
|
||||
FROM table1, table2
|
||||
WHERE table1.id = table2.id AND table1.id = parameter_value;
|
||||
```
|
||||
|
||||
### Oracle Behavior
|
||||
|
||||
When a `SELECT INTO` statement in Oracle does **not find any rows**, it automatically raises:
|
||||
|
||||
```
|
||||
ORA-01403: no data found
|
||||
```
|
||||
|
||||
This exception is caught by the procedure's exception handler and re-raised to the calling application.
|
||||
|
||||
### PostgreSQL Behavior (Pre-Fix)
|
||||
|
||||
When a `SELECT INTO` statement in PostgreSQL does **not find any rows**, it:
|
||||
|
||||
- Sets the `FOUND` variable to `false`
|
||||
- **Silently continues** execution without raising an exception
|
||||
|
||||
This fundamental difference can cause tests to fail silently and logic errors in production code.
|
||||
|
||||
---
|
||||
|
||||
## Root Cause Analysis
|
||||
|
||||
The PostgreSQL version was missing explicit error handling for the `NOT FOUND` condition after the `SELECT INTO` statement.
|
||||
|
||||
**Original Code (Problematic):**
|
||||
|
||||
```plpgsql
|
||||
SELECT column_name
|
||||
INTO variable_name
|
||||
FROM table1, table2
|
||||
WHERE table1.id = table2.id AND table1.id = parameter_value;
|
||||
|
||||
IF variable_name = 'X' THEN
|
||||
result_variable := 1;
|
||||
ELSE
|
||||
result_variable := 2;
|
||||
END IF;
|
||||
```
|
||||
|
||||
**Problem:** No check for `NOT FOUND` condition. When an invalid parameter is passed, the SELECT returns no rows, `FOUND` becomes `false`, and execution continues with an uninitialized variable.
|
||||
|
||||
---
|
||||
|
||||
## Key Differences: Oracle vs PostgreSQL
|
||||
|
||||
Add explicit `NOT FOUND` error handling to match Oracle behavior.
|
||||
|
||||
**Fixed Code:**
|
||||
|
||||
```plpgsql
|
||||
SELECT column_name
|
||||
INTO variable_name
|
||||
FROM table1, table2
|
||||
WHERE table1.id = table2.id AND table1.id = parameter_value;
|
||||
|
||||
-- Explicitly raise exception if no data found (matching Oracle behavior)
|
||||
IF NOT FOUND THEN
|
||||
RAISE EXCEPTION 'no data found';
|
||||
END IF;
|
||||
|
||||
IF variable_name = 'X' THEN
|
||||
result_variable := 1;
|
||||
ELSE
|
||||
result_variable := 2;
|
||||
END IF;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Migration Notes for Similar Issues
|
||||
|
||||
When fixing this issue, verify:
|
||||
|
||||
1. **Success path tests** - Confirm valid parameters still work correctly
|
||||
2. **Exception tests** - Verify exceptions are raised with invalid parameters
|
||||
3. **Transaction rollback** - Ensure proper cleanup on errors
|
||||
4. **Data integrity** - Confirm all fields are populated correctly in success cases
|
||||
@@ -0,0 +1,190 @@
|
||||
# Oracle to PostgreSQL: Parentheses in FROM Clause
|
||||
|
||||
## Contents
|
||||
|
||||
- Problem
|
||||
- Root Cause
|
||||
- Solution Pattern
|
||||
- Examples
|
||||
- Migration Checklist
|
||||
- Common Locations
|
||||
- Application Code Examples
|
||||
- Error Messages to Watch For
|
||||
- Testing Recommendations
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle allows optional parentheses around table names in the FROM clause:
|
||||
|
||||
```sql
|
||||
-- Oracle: Both are valid
|
||||
SELECT * FROM (TABLE_NAME) WHERE id = 1;
|
||||
SELECT * FROM TABLE_NAME WHERE id = 1;
|
||||
```
|
||||
|
||||
PostgreSQL does **not** allow extra parentheses around a single table name in the FROM clause without it being a derived table or subquery. Attempting to use this pattern results in:
|
||||
|
||||
```
|
||||
Npgsql.PostgresException: 42601: syntax error at or near ")"
|
||||
```
|
||||
|
||||
## Root Cause
|
||||
|
||||
- **Oracle**: Treats `FROM(TABLE_NAME)` as equivalent to `FROM TABLE_NAME`
|
||||
- **PostgreSQL**: Parentheses in the FROM clause are only valid for:
|
||||
- Subqueries: `FROM (SELECT * FROM table)`
|
||||
- Explicit table references that are part of join syntax
|
||||
- Common Table Expressions (CTEs)
|
||||
- Without a valid SELECT or join context, PostgreSQL raises a syntax error
|
||||
|
||||
## Solution Pattern
|
||||
|
||||
Remove the unnecessary parentheses around the table name:
|
||||
|
||||
```sql
|
||||
-- Oracle (problematic in PostgreSQL)
|
||||
SELECT col1, col2
|
||||
FROM (TABLE_NAME)
|
||||
WHERE id = 1;
|
||||
|
||||
-- PostgreSQL (correct)
|
||||
SELECT col1, col2
|
||||
FROM TABLE_NAME
|
||||
WHERE id = 1;
|
||||
```
|
||||
|
||||
## Examples
|
||||
|
||||
### Example 1: Simple Table Reference
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
SELECT employee_id, employee_name
|
||||
FROM (EMPLOYEES)
|
||||
WHERE department_id = 10;
|
||||
|
||||
-- PostgreSQL (fixed)
|
||||
SELECT employee_id, employee_name
|
||||
FROM EMPLOYEES
|
||||
WHERE department_id = 10;
|
||||
```
|
||||
|
||||
### Example 2: Join with Parentheses
|
||||
|
||||
```sql
|
||||
-- Oracle (problematic)
|
||||
SELECT e.employee_id, d.department_name
|
||||
FROM (EMPLOYEES) e
|
||||
JOIN (DEPARTMENTS) d ON e.department_id = d.department_id;
|
||||
|
||||
-- PostgreSQL (fixed)
|
||||
SELECT e.employee_id, d.department_name
|
||||
FROM EMPLOYEES e
|
||||
JOIN DEPARTMENTS d ON e.department_id = d.department_id;
|
||||
```
|
||||
|
||||
### Example 3: Valid Subquery Parentheses (Works in Both)
|
||||
|
||||
```sql
|
||||
-- Both Oracle and PostgreSQL
|
||||
SELECT *
|
||||
FROM (SELECT employee_id, employee_name FROM EMPLOYEES WHERE department_id = 10) sub;
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When fixing this issue, verify:
|
||||
|
||||
1. **Identify all problematic FROM clauses**:
|
||||
- Search for `FROM (` pattern in SQL
|
||||
- Verify the opening parenthesis is immediately after `FROM` followed by a table name
|
||||
- Confirm it's **not** a subquery (no SELECT keyword inside)
|
||||
|
||||
2. **Distinguish valid parentheses**:
|
||||
- ✅ `FROM (SELECT ...)` - Valid subquery
|
||||
- ✅ `FROM (table_name` followed by a join - Check if JOIN keyword follows
|
||||
- ❌ `FROM (TABLE_NAME)` - Invalid, remove parentheses
|
||||
|
||||
3. **Apply the fix**:
|
||||
- Remove the parentheses around the table name
|
||||
- Keep parentheses for legitimate subqueries
|
||||
|
||||
4. **Test thoroughly**:
|
||||
- Execute the query in PostgreSQL
|
||||
- Verify result set matches original Oracle query
|
||||
- Include in integration tests
|
||||
|
||||
## Common Locations
|
||||
|
||||
Search for `FROM (` in:
|
||||
|
||||
- ✅ Stored procedures and functions (DDL scripts)
|
||||
- ✅ Application data access layers (DAL classes)
|
||||
- ✅ Dynamic SQL builders
|
||||
- ✅ Reporting queries
|
||||
- ✅ Views and materialized views
|
||||
- ✅ Complex queries with multiple joins
|
||||
|
||||
## Application Code Examples
|
||||
|
||||
### VB.NET
|
||||
|
||||
```vb
|
||||
' Before (Oracle)
|
||||
StrSQL = "SELECT employee_id, NAME " _
|
||||
& "FROM (EMPLOYEES) e " _
|
||||
& "WHERE e.department_id = 10"
|
||||
|
||||
' After (PostgreSQL)
|
||||
StrSQL = "SELECT employee_id, NAME " _
|
||||
& "FROM EMPLOYEES e " _
|
||||
& "WHERE e.department_id = 10"
|
||||
```
|
||||
|
||||
### C #
|
||||
|
||||
```csharp
|
||||
// Before (Oracle)
|
||||
var sql = "SELECT id, name FROM (USERS) WHERE status = @status";
|
||||
|
||||
// After (PostgreSQL)
|
||||
var sql = "SELECT id, name FROM USERS WHERE status = @status";
|
||||
```
|
||||
|
||||
## Error Messages to Watch For
|
||||
|
||||
```
|
||||
Npgsql.PostgresException: 42601: syntax error at or near ")"
|
||||
ERROR: syntax error at or near ")"
|
||||
LINE 1: SELECT * FROM (TABLE_NAME) WHERE ...
|
||||
^
|
||||
```
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
1. **Syntax Verification**: Parse all migrated queries to ensure they run without syntax errors
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public void GetEmployees_ExecutesWithoutSyntaxError()
|
||||
{
|
||||
// Should not throw PostgresException with error code 42601
|
||||
var employees = dal.GetEmployees(departmentId: 10);
|
||||
Assert.NotEmpty(employees);
|
||||
}
|
||||
```
|
||||
|
||||
2. **Result Comparison**: Verify that result sets are identical before and after migration
|
||||
3. **Regex-based Search**: Use pattern `FROM\s*\(\s*[A-Za-z_][A-Za-z0-9_]*\s*\)` to identify candidates
|
||||
|
||||
## Related Files
|
||||
|
||||
- Reference: [oracle-to-postgres-type-coercion.md](oracle-to-postgres-type-coercion.md) - Other syntax differences
|
||||
- PostgreSQL Documentation: [SELECT Statement](https://www.postgresql.org/docs/current/sql-select.html)
|
||||
|
||||
## Migration Notes
|
||||
|
||||
- This is a straightforward syntactic fix with no semantic implications
|
||||
- No data conversion required
|
||||
- Safe to apply automated find-and-replace, but manually verify complex queries
|
||||
- Update integration tests to exercise the migrated queries
|
||||
@@ -0,0 +1,51 @@
|
||||
# Oracle to PostgreSQL Sorting Migration Guide
|
||||
|
||||
Purpose: Preserve Oracle-like sorting semantics when moving queries to PostgreSQL.
|
||||
|
||||
## Key points
|
||||
- Oracle often treats plain `ORDER BY` as binary/byte-wise, giving case-insensitive ordering for ASCII.
|
||||
- PostgreSQL defaults differ; to match Oracle behavior, use `COLLATE "C"` on sort expressions.
|
||||
|
||||
## 1) Standard `SELECT … ORDER BY`
|
||||
**Goal:** Keep Oracle-style ordering.
|
||||
|
||||
**Pattern:**
|
||||
```sql
|
||||
SELECT col1
|
||||
FROM your_table
|
||||
ORDER BY col1 COLLATE "C";
|
||||
```
|
||||
|
||||
**Notes:**
|
||||
- Apply `COLLATE "C"` to each sort expression that must mimic Oracle.
|
||||
- Works with ascending/descending and multi-column sorts, e.g. `ORDER BY col1 COLLATE "C", col2 COLLATE "C" DESC`.
|
||||
|
||||
## 2) `SELECT DISTINCT … ORDER BY`
|
||||
**Issue:** PostgreSQL enforces that `ORDER BY` expressions appear in the `SELECT` list for `DISTINCT`, raising:
|
||||
`Npgsql.PostgresException: 42P10: for SELECT DISTINCT, ORDER BY expressions must appear in select list`
|
||||
|
||||
**Oracle difference:** Oracle allowed ordering by expressions not projected when using `DISTINCT`.
|
||||
|
||||
**Recommended pattern (wrap and sort):**
|
||||
```sql
|
||||
SELECT *
|
||||
FROM (
|
||||
SELECT DISTINCT col1, col2
|
||||
FROM your_table
|
||||
) AS distinct_results
|
||||
ORDER BY col2 COLLATE "C";
|
||||
```
|
||||
|
||||
**Why:**
|
||||
- The inner query performs the `DISTINCT` projection.
|
||||
- The outer query safely orders the result set and adds `COLLATE "C"` to align with Oracle sorting.
|
||||
|
||||
**Tips:**
|
||||
- Ensure any columns used in the outer `ORDER BY` are included in the inner projection.
|
||||
- For multi-column sorts, collate each relevant expression: `ORDER BY col2 COLLATE "C", col3 COLLATE "C" DESC`.
|
||||
|
||||
## Validation checklist
|
||||
- [ ] Added `COLLATE "C"` to every `ORDER BY` that should follow Oracle sorting rules.
|
||||
- [ ] For `DISTINCT` queries, wrapped the projection and sorted in the outer query.
|
||||
- [ ] Confirmed ordered columns are present in the inner projection.
|
||||
- [ ] Re-ran tests or representative queries to verify ordering matches Oracle outputs.
|
||||
@@ -0,0 +1,187 @@
|
||||
# Oracle to PostgreSQL: CURRENT_TIMESTAMP and NOW() Timezone Handling
|
||||
|
||||
## Contents
|
||||
|
||||
- Problem
|
||||
- Behavior Comparison
|
||||
- PostgreSQL Timezone Precedence
|
||||
- Common Error Symptoms
|
||||
- Migration Actions — Npgsql config, DateTime normalization, stored procedures, session timezone, application code
|
||||
- Integration Test Patterns
|
||||
- Checklist
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle's `CURRENT_TIMESTAMP` returns a value in the **session timezone** and stores it in the column's declared precision. When .NET reads this value back via ODP.NET, it is surfaced as a `DateTime` with `Kind=Local`, reflecting the OS timezone of the client.
|
||||
|
||||
PostgreSQL's `CURRENT_TIMESTAMP` and `NOW()` both return a `timestamptz` (timestamp with time zone) anchored to **UTC**, regardless of the session timezone setting. How Npgsql surfaces this value depends on the driver version and configuration:
|
||||
|
||||
- **Npgsql < 6 / legacy mode (`EnableLegacyTimestampBehavior = true`):** `timestamptz` columns are returned as `DateTime` with `Kind=Unspecified`. This is the source of silent timezone bugs when migrating from Oracle.
|
||||
- **Npgsql 6+ with legacy mode disabled (the new default):** `timestamptz` columns are returned as `DateTime` with `Kind=Utc`, and writing a `Kind=Unspecified` value throws an exception at insertion time.
|
||||
|
||||
Projects that have not yet upgraded to Npgsql 6+, or that explicitly opt back into legacy mode, remain vulnerable to the `Kind=Unspecified` issue. This mismatch — and the ease of accidentally re-enabling legacy mode — causes silent data corruption, incorrect comparisons, and off-by-N-hours bugs that are extremely difficult to trace.
|
||||
|
||||
---
|
||||
|
||||
## Behavior Comparison
|
||||
|
||||
| Aspect | Oracle | PostgreSQL |
|
||||
|---|---|---|
|
||||
| `CURRENT_TIMESTAMP` type | `TIMESTAMP WITH LOCAL TIME ZONE` | `timestamptz` (UTC-normalised) |
|
||||
| Client `DateTime.Kind` via driver | `Local` | `Unspecified` (Npgsql < 6 / legacy mode); `Utc` (Npgsql 6+ default) |
|
||||
| Session timezone influence | Yes — affects stored/returned value | Affects *display* only; UTC stored internally |
|
||||
| NOW() equivalent | `SYSDATE` / `CURRENT_TIMESTAMP` | `NOW()` = `CURRENT_TIMESTAMP` (both return `timestamptz`) |
|
||||
| Implicit conversion on comparison | Oracle applies session TZ offset | PostgreSQL compares UTC; session TZ is display-only |
|
||||
|
||||
---
|
||||
|
||||
## PostgreSQL Timezone Precedence
|
||||
|
||||
PostgreSQL resolves the effective session timezone using the following hierarchy (highest priority wins):
|
||||
|
||||
| Level | How it is set |
|
||||
|---|---|
|
||||
| **Session** | `SET TimeZone = 'UTC'` sent at connection open |
|
||||
| **Role** | `ALTER ROLE app_user SET TimeZone = 'UTC'` |
|
||||
| **Database** | `ALTER DATABASE mydb SET TimeZone = 'UTC'` |
|
||||
| **Server** | `postgresql.conf` → `TimeZone = 'America/New_York'` |
|
||||
|
||||
The session timezone does **not** affect the stored UTC value of a `timestamptz` column — it only controls how `SHOW timezone` and `::text` casts format a value for display. Application code that relies on `DateTime.Kind` or compares timestamps without an explicit timezone can produce incorrect results if the server's default timezone is not UTC.
|
||||
|
||||
---
|
||||
|
||||
## Common Error Symptoms
|
||||
|
||||
- Timestamps read from PostgreSQL have `Kind=Unspecified`; comparisons with `DateTime.UtcNow` or `DateTime.Now` produce incorrect results.
|
||||
- Date-range queries return too few or too many rows because the WHERE clause comparison is evaluated in a timezone that differs from the stored UTC value.
|
||||
- Integration tests pass on a developer machine (UTC OS timezone) but fail in CI or production (non-UTC timezone).
|
||||
- Stored procedure output parameters carrying timestamps arrive with a session-offset applied by the server but are then compared to UTC values in the application.
|
||||
|
||||
---
|
||||
|
||||
## Migration Actions
|
||||
|
||||
### 1. Configure Npgsql for UTC via Connection String or AppContext
|
||||
|
||||
Npgsql 6+ ships with `EnableLegacyTimestampBehavior` set to `false` by default, which causes `timestamptz` values to be returned as `DateTime` with `Kind=Utc`. Explicitly setting the switch at startup is still recommended to guard against accidental opt-in to legacy mode (e.g., via a config file or a transitive dependency) and to make the intent visible to future maintainers:
|
||||
|
||||
```csharp
|
||||
// Program.cs / Startup.cs — apply once at application start
|
||||
AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", false);
|
||||
```
|
||||
|
||||
With this switch disabled, Npgsql throws if you try to write a `DateTime` with `Kind=Unspecified` to a `timestamptz` column, making timezone bugs loud and detectable at insertion time rather than silently at query time.
|
||||
|
||||
### 2. Normalise DateTime Values Before Persistence
|
||||
|
||||
Replace any `DateTime.Now` with `DateTime.UtcNow` throughout the migrated codebase. For values that originate from external input (e.g., user-provided dates deserialized from JSON), ensure they are converted to UTC before being saved:
|
||||
|
||||
```csharp
|
||||
// Before (Oracle-era code — relied on session/OS timezone)
|
||||
var timestamp = DateTime.Now;
|
||||
|
||||
// After (PostgreSQL-compatible)
|
||||
var timestamp = DateTime.UtcNow;
|
||||
|
||||
// For externally-supplied values
|
||||
var utcTimestamp = dateTimeInput.Kind == DateTimeKind.Utc
|
||||
? dateTimeInput
|
||||
: dateTimeInput.ToUniversalTime();
|
||||
```
|
||||
|
||||
### 3. Fix Stored Procedures Using CURRENT_TIMESTAMP / NOW()
|
||||
|
||||
Stored procedures that assign `CURRENT_TIMESTAMP` or `NOW()` to a `timestamp without time zone` (`timestamp`) column must be reviewed. Prefer `timestamptz` columns or cast explicitly:
|
||||
|
||||
```sql
|
||||
-- Ambiguous: server timezone influences interpretation
|
||||
INSERT INTO audit_log (created_at) VALUES (NOW()::timestamp);
|
||||
|
||||
-- Safe: always UTC
|
||||
INSERT INTO audit_log (created_at) VALUES (NOW() AT TIME ZONE 'UTC');
|
||||
|
||||
-- Or: use timestamptz column type and let PostgreSQL store UTC natively
|
||||
INSERT INTO audit_log (created_at) VALUES (CURRENT_TIMESTAMP);
|
||||
```
|
||||
|
||||
### 4. Force Session Timezone on Connection Open (Defence-in-Depth)
|
||||
|
||||
Regardless of role or database defaults, set the session timezone explicitly when opening a connection. This guarantees consistent behavior independent of server configuration:
|
||||
|
||||
```csharp
|
||||
// Npgsql connection string approach
|
||||
var connString = "Host=localhost;Database=mydb;Username=app;Password=...;Timezone=UTC";
|
||||
|
||||
// Or: apply via NpgsqlDataSourceBuilder
|
||||
var dataSource = new NpgsqlDataSourceBuilder(connString)
|
||||
.Build();
|
||||
|
||||
// Or: execute on every new connection
|
||||
await using var conn = new NpgsqlConnection(connString);
|
||||
await conn.OpenAsync();
|
||||
await using var cmd = new NpgsqlCommand("SET TimeZone = 'UTC'", conn);
|
||||
await cmd.ExecuteNonQueryAsync();
|
||||
```
|
||||
|
||||
### 5. Application Code — Avoid DateTime.Kind=Unspecified
|
||||
|
||||
Audit all repository and data-access code that reads timestamp columns. Where Npgsql returns `Unspecified`, either configure the data source globally (option 1 above) or wrap the read:
|
||||
|
||||
```csharp
|
||||
// Safe reader helper — convert Unspecified to Utc at the boundary
|
||||
DateTime ReadUtcDateTime(NpgsqlDataReader reader, int ordinal)
|
||||
{
|
||||
var dt = reader.GetDateTime(ordinal);
|
||||
return dt.Kind == DateTimeKind.Unspecified
|
||||
? DateTime.SpecifyKind(dt, DateTimeKind.Utc)
|
||||
: dt.ToUniversalTime();
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Integration Test Patterns
|
||||
|
||||
### Test: Verify timestamps persist and return as UTC
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public async Task InsertedTimestamp_ShouldRoundTripAsUtc()
|
||||
{
|
||||
var before = DateTime.UtcNow;
|
||||
|
||||
await repository.InsertAuditEntryAsync(/* ... */);
|
||||
|
||||
var retrieved = await repository.GetLatestAuditEntryAsync();
|
||||
|
||||
Assert.Equal(DateTimeKind.Utc, retrieved.CreatedAt.Kind);
|
||||
Assert.True(retrieved.CreatedAt >= before,
|
||||
"Persisted CreatedAt should not be earlier than the pre-insert UTC timestamp.");
|
||||
}
|
||||
```
|
||||
|
||||
### Test: Verify timestamp comparisons across Oracle and PostgreSQL baselines
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public async Task TimestampComparison_ShouldReturnSameRowsAsOracle()
|
||||
{
|
||||
var cutoff = DateTime.UtcNow.AddDays(-1);
|
||||
|
||||
var oracleResults = await oracleRepository.GetEntriesAfter(cutoff);
|
||||
var postgresResults = await postgresRepository.GetEntriesAfter(cutoff);
|
||||
|
||||
Assert.Equal(oracleResults.Count, postgresResults.Count);
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Checklist
|
||||
|
||||
- [ ] `AppContext.SetSwitch("Npgsql.EnableLegacyTimestampBehavior", false)` applied at application startup.
|
||||
- [ ] All `DateTime.Now` usages in data-access code replaced with `DateTime.UtcNow`.
|
||||
- [ ] Connection string or connection-open hook sets `Timezone=UTC` / `SET TimeZone = 'UTC'`.
|
||||
- [ ] Stored procedures that use `CURRENT_TIMESTAMP` or `NOW()` reviewed; `timestamp without time zone` columns explicitly cast or replaced with `timestamptz`.
|
||||
- [ ] Integration tests assert `DateTime.Kind == Utc` on retrieved timestamp values.
|
||||
- [ ] Tests cover date-range queries to confirm row counts match Oracle baseline.
|
||||
@@ -0,0 +1,145 @@
|
||||
# Oracle to PostgreSQL: TO_CHAR() Numeric Conversions
|
||||
|
||||
## Contents
|
||||
|
||||
- Problem
|
||||
- Root Cause
|
||||
- Solution Patterns — CAST, format string, concatenation
|
||||
- Migration Checklist
|
||||
- Application Code Review
|
||||
- Testing Recommendations
|
||||
- Common Locations
|
||||
- Error Messages to Watch For
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle allows `TO_CHAR()` to convert numeric types to strings without a format specifier:
|
||||
|
||||
```sql
|
||||
-- Oracle: Works fine
|
||||
SELECT TO_CHAR(vessel_id) FROM vessels;
|
||||
SELECT TO_CHAR(fiscal_year) FROM certificates;
|
||||
```
|
||||
|
||||
PostgreSQL requires a format string when using `TO_CHAR()` with numeric types, otherwise it raises:
|
||||
|
||||
```
|
||||
42883: function to_char(numeric) does not exist
|
||||
```
|
||||
|
||||
## Root Cause
|
||||
|
||||
- **Oracle**: `TO_CHAR(number)` without a format mask implicitly converts the number to a string using default formatting
|
||||
- **PostgreSQL**: `TO_CHAR()` always requires an explicit format string for numeric types (e.g., `'999999'`, `'FM999999'`)
|
||||
|
||||
## Solution Patterns
|
||||
|
||||
### Pattern 1: Use CAST (Recommended)
|
||||
|
||||
The cleanest migration approach is to replace `TO_CHAR(numeric_column)` with `CAST(numeric_column AS TEXT)`:
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
SELECT TO_CHAR(vessel_id) AS vessel_item FROM vessels;
|
||||
|
||||
-- PostgreSQL (preferred)
|
||||
SELECT CAST(vessel_id AS TEXT) AS vessel_item FROM vessels;
|
||||
```
|
||||
|
||||
**Advantages:**
|
||||
|
||||
- More idiomatic in PostgreSQL
|
||||
- Clearer intent
|
||||
- No format string needed
|
||||
|
||||
### Pattern 2: Provide Format String
|
||||
|
||||
If you need specific numeric formatting, use an explicit format mask:
|
||||
|
||||
```sql
|
||||
-- PostgreSQL with format
|
||||
SELECT TO_CHAR(vessel_id, 'FM999999') AS vessel_item FROM vessels;
|
||||
SELECT TO_CHAR(amount, 'FM999999.00') AS amount_text FROM payments;
|
||||
```
|
||||
|
||||
**Format masks:**
|
||||
|
||||
- `'FM999999'`: Fixed-width integer (FM = Fill Mode, removes leading spaces)
|
||||
- `'FM999999.00'`: Decimal with 2 places
|
||||
- `'999,999.00'`: With thousand separators
|
||||
|
||||
### Pattern 3: String Concatenation
|
||||
|
||||
For simple concatenation where numeric conversion is implicit:
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
WHERE TO_CHAR(fiscal_year) = '2024'
|
||||
|
||||
-- PostgreSQL (using concatenation)
|
||||
WHERE fiscal_year::TEXT = '2024'
|
||||
-- or
|
||||
WHERE CAST(fiscal_year AS TEXT) = '2024'
|
||||
```
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
When migrating SQL containing `TO_CHAR()`:
|
||||
|
||||
1. **Identify all TO_CHAR() calls**: Search for `TO_CHAR\(` in SQL strings, stored procedures, and application queries
|
||||
2. **Check the argument type**:
|
||||
- **DATE/TIMESTAMP**: Keep `TO_CHAR()` with format string (e.g., `TO_CHAR(date_col, 'YYYY-MM-DD')`)
|
||||
- **NUMERIC/INTEGER**: Replace with `CAST(... AS TEXT)` or add format string
|
||||
3. **Test the output**: Verify that the string representation matches expectations (no unexpected spaces, decimals, etc.)
|
||||
4. **Update comparison logic**: If comparing numeric-to-string, ensure consistent types on both sides
|
||||
|
||||
## Application Code Review
|
||||
|
||||
### C# Example
|
||||
|
||||
```csharp
|
||||
// Before (Oracle)
|
||||
var sql = "SELECT TO_CHAR(id) AS id_text FROM entities WHERE TO_CHAR(status) = @status";
|
||||
|
||||
// After (PostgreSQL)
|
||||
var sql = "SELECT CAST(id AS TEXT) AS id_text FROM entities WHERE CAST(status AS TEXT) = @status";
|
||||
```
|
||||
|
||||
## Testing Recommendations
|
||||
|
||||
1. **Unit Tests**: Verify numeric-to-string conversions return expected values
|
||||
|
||||
```csharp
|
||||
[Fact]
|
||||
public void GetVesselNumbers_ReturnsVesselIdsAsStrings()
|
||||
{
|
||||
var results = dal.GetVesselNumbers(certificateType);
|
||||
Assert.All(results, item => Assert.True(int.TryParse(item.DISPLAY_MEMBER, out _)));
|
||||
}
|
||||
```
|
||||
|
||||
2. **Integration Tests**: Ensure queries with `CAST()` execute without errors
|
||||
3. **Comparison Tests**: Verify WHERE clauses with numeric-to-string comparisons filter correctly
|
||||
|
||||
## Common Locations
|
||||
|
||||
Search for `TO_CHAR` in:
|
||||
|
||||
- ✅ Stored procedures and functions (DDL scripts)
|
||||
- ✅ Application data access layers (DAL classes)
|
||||
- ✅ Dynamic SQL builders
|
||||
- ✅ Reporting queries
|
||||
- ✅ ORM/Entity Framework raw SQL
|
||||
|
||||
## Error Messages to Watch For
|
||||
|
||||
```
|
||||
Npgsql.PostgresException: 42883: function to_char(numeric) does not exist
|
||||
Npgsql.PostgresException: 42883: function to_char(integer) does not exist
|
||||
Npgsql.PostgresException: 42883: function to_char(bigint) does not exist
|
||||
```
|
||||
|
||||
## See Also
|
||||
|
||||
- [oracle-to-postgres-type-coercion.md](oracle-to-postgres-type-coercion.md) - Related type conversion issues
|
||||
- PostgreSQL Documentation: [Data Type Formatting Functions](https://www.postgresql.org/docs/current/functions-formatting.html)
|
||||
@@ -0,0 +1,182 @@
|
||||
# Oracle to PostgreSQL Type Coercion Issues
|
||||
|
||||
## Contents
|
||||
|
||||
- Overview
|
||||
- The Problem — symptom, root cause, example
|
||||
- The Solution — string literals, explicit casting
|
||||
- Common Comparison Operators Affected
|
||||
- Detection Strategy
|
||||
- Real-World Example
|
||||
- Prevention Best Practices
|
||||
|
||||
## Overview
|
||||
|
||||
This document describes a common migration issue encountered when porting SQL code from Oracle to PostgreSQL. The issue stems from fundamental differences in how these databases handle implicit type conversions in comparison operators.
|
||||
|
||||
## The Problem
|
||||
|
||||
### Symptom
|
||||
|
||||
When migrating SQL queries from Oracle to PostgreSQL, you may encounter the following error:
|
||||
|
||||
```
|
||||
Npgsql.PostgresException: 42883: operator does not exist: character varying <> integer
|
||||
POSITION: [line_number]
|
||||
```
|
||||
|
||||
### Root Cause
|
||||
|
||||
PostgreSQL has **strict type enforcement** and does not perform implicit type coercion in comparison operators. Oracle, by contrast, automatically converts operands to compatible types during comparison operations.
|
||||
|
||||
#### Example Mismatch
|
||||
|
||||
**Oracle SQL (works fine):**
|
||||
|
||||
```sql
|
||||
AND physical_address.pcountry_cd <> 124
|
||||
```
|
||||
|
||||
- `pcountry_cd` is a `VARCHAR2`
|
||||
- `124` is an integer literal
|
||||
- Oracle silently converts `124` to a string for comparison
|
||||
|
||||
**PostgreSQL (fails):**
|
||||
|
||||
```sql
|
||||
AND physical_address.pcountry_cd <> 124
|
||||
```
|
||||
|
||||
```
|
||||
42883: operator does not exist: character varying <> integer
|
||||
```
|
||||
|
||||
- `pcountry_cd` is a `character varying`
|
||||
- `124` is an integer literal
|
||||
- PostgreSQL rejects the comparison because the types don't match
|
||||
|
||||
## The Solution
|
||||
|
||||
### Approach 1: Use String Literals (Recommended)
|
||||
|
||||
Convert integer literals to string literals:
|
||||
|
||||
```sql
|
||||
AND physical_address.pcountry_cd <> '124'
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Semantically correct (country codes are typically stored as strings)
|
||||
- Most efficient
|
||||
- Clearest intent
|
||||
|
||||
**Cons:**
|
||||
|
||||
- None
|
||||
|
||||
### Approach 2: Explicit Type Casting
|
||||
|
||||
Explicitly cast the integer to a string type:
|
||||
|
||||
```sql
|
||||
AND physical_address.pcountry_cd <> CAST(124 AS VARCHAR)
|
||||
```
|
||||
|
||||
**Pros:**
|
||||
|
||||
- Makes the conversion explicit and visible
|
||||
- Useful if the value is a parameter or complex expression
|
||||
|
||||
**Cons:**
|
||||
|
||||
- Slightly less efficient
|
||||
- More verbose
|
||||
|
||||
## Common Comparison Operators Affected
|
||||
|
||||
All comparison operators can trigger this issue:
|
||||
|
||||
- `<>` (not equal)
|
||||
- `=` (equal)
|
||||
- `<` (less than)
|
||||
- `>` (greater than)
|
||||
- `<=` (less than or equal)
|
||||
- `>=` (greater than or equal)
|
||||
|
||||
## Detection Strategy
|
||||
|
||||
When migrating from Oracle to PostgreSQL:
|
||||
|
||||
1. **Search for numeric literals in WHERE clauses** comparing against string/varchar columns
|
||||
2. **Look for patterns like:**
|
||||
- `column_name <> 123` (where column is VARCHAR/CHAR)
|
||||
- `column_name = 456` (where column is VARCHAR/CHAR)
|
||||
- `column_name IN (1, 2, 3)` (where column is VARCHAR/CHAR)
|
||||
|
||||
3. **Code review checklist:**
|
||||
- Are all comparison values correctly typed?
|
||||
- Do string columns always use string literals?
|
||||
- Are numeric columns always compared against numeric values?
|
||||
|
||||
## Real-World Example
|
||||
|
||||
**Original Oracle Query:**
|
||||
|
||||
```sql
|
||||
SELECT ac040.stakeholder_id,
|
||||
ac006.organization_etxt
|
||||
FROM ac040_stakeholder ac040
|
||||
INNER JOIN ac006_organization ac006 ON ac040.stakeholder_id = ac006.organization_id
|
||||
WHERE physical_address.pcountry_cd <> 124
|
||||
AND LOWER(ac006.organization_etxt) LIKE '%' || @orgtxt || '%'
|
||||
ORDER BY UPPER(ac006.organization_etxt)
|
||||
```
|
||||
|
||||
**Fixed PostgreSQL Query:**
|
||||
|
||||
```sql
|
||||
SELECT ac040.stakeholder_id,
|
||||
ac006.organization_etxt
|
||||
FROM ac040_stakeholder ac040
|
||||
INNER JOIN ac006_organization ac006 ON ac040.stakeholder_id = ac006.organization_id
|
||||
WHERE physical_address.pcountry_cd <> '124'
|
||||
AND LOWER(ac006.organization_etxt) LIKE '%' || @orgtxt || '%'
|
||||
ORDER BY UPPER(ac006.organization_etxt)
|
||||
```
|
||||
|
||||
**Change:** `124` → `'124'`
|
||||
|
||||
## Prevention Best Practices
|
||||
|
||||
1. **Use Type-Consistent Literals:**
|
||||
- For string columns: Always use string literals (`'value'`)
|
||||
- For numeric columns: Always use numeric literals (`123`)
|
||||
- For dates: Always use date literals (`DATE '2024-01-01'`)
|
||||
|
||||
2. **Leverage Database Tools:**
|
||||
- Use your IDE's SQL linter to catch type mismatches
|
||||
- Run PostgreSQL syntax validation during code review
|
||||
|
||||
3. **Test Early:**
|
||||
- Execute migration queries against PostgreSQL before deployment
|
||||
- Include integration tests that exercise all comparison operators
|
||||
|
||||
4. **Documentation:**
|
||||
- Document any type coercions in comments
|
||||
- Mark migrated code with revision history
|
||||
|
||||
## References
|
||||
|
||||
- [PostgreSQL Type Casting Documentation](https://www.postgresql.org/docs/current/sql-syntax.html)
|
||||
- [Oracle Type Conversion Documentation](https://docs.oracle.com/database/121/SQLRF/sql_elements003.htm)
|
||||
- [Npgsql Exception: Operator Does Not Exist](https://www.npgsql.org/doc/api/NpgsqlException.html)
|
||||
|
||||
## Related Issues
|
||||
|
||||
This issue is part of broader Oracle → PostgreSQL migration challenges:
|
||||
|
||||
- Implicit function conversions (e.g., `TO_CHAR`, `TO_DATE`)
|
||||
- String concatenation operator differences (`||` works in both, but behavior differs)
|
||||
- Numeric precision and rounding differences
|
||||
- NULL handling in comparisons
|
||||
@@ -0,0 +1,259 @@
|
||||
# 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
|
||||
|
||||
```csharp
|
||||
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
|
||||
|
||||
```csharp
|
||||
// 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
|
||||
|
||||
```csharp
|
||||
// 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.
|
||||
|
||||
```csharp
|
||||
// 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()`:
|
||||
|
||||
```csharp
|
||||
// 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.
|
||||
|
||||
```csharp
|
||||
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.
|
||||
|
||||
```csharp
|
||||
// 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
|
||||
|
||||
### 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
|
||||
|
||||
```regex
|
||||
ExecuteReader\(.*\)[\s\S]*?Execute(Scalar|NonQuery|Reader)\(
|
||||
```
|
||||
|
||||
```regex
|
||||
\.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
|
||||
|
||||
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
|
||||
|
||||
- [Npgsql Documentation: Basic Usage](https://www.npgsql.org/doc/basic-usage.html)
|
||||
- [PostgreSQL Documentation: Concurrency Control](https://www.postgresql.org/docs/current/mvcc.html)
|
||||
- [Npgsql GitHub: Multiple Active Result Sets Discussion](https://github.com/npgsql/npgsql/issues/462)
|
||||
@@ -0,0 +1,148 @@
|
||||
# Oracle to PostgreSQL: Refcursor Handling in Client Applications
|
||||
|
||||
## The Core Difference
|
||||
|
||||
Oracle's driver automatically unwraps `SYS_REFCURSOR` output parameters, exposing the result set directly in the data reader. PostgreSQL's Npgsql driver instead returns a **cursor name** (e.g., `"<unnamed portal 1>"`). The client must issue a separate `FETCH ALL FROM "<cursor_name>"` command to retrieve actual rows.
|
||||
|
||||
Failing to account for this causes:
|
||||
|
||||
```
|
||||
System.IndexOutOfRangeException: Field not found in row: <column_name>
|
||||
```
|
||||
|
||||
The reader contains only the cursor-name parameter — not the expected result columns.
|
||||
|
||||
> **Transaction requirement:** PostgreSQL refcursors are scoped to a transaction. Both the procedure call and the `FETCH` must execute within the same explicit transaction, or the cursor may be closed before the fetch completes under autocommit.
|
||||
|
||||
## Solution: Explicit Refcursor Unwrapping (C#)
|
||||
|
||||
```csharp
|
||||
public IEnumerable<User> GetUsers(int departmentId)
|
||||
{
|
||||
var users = new List<User>();
|
||||
using var connection = new NpgsqlConnection(connectionString);
|
||||
connection.Open();
|
||||
|
||||
// Refcursors are transaction-scoped — wrap both the call and FETCH in one transaction.
|
||||
using var tx = connection.BeginTransaction();
|
||||
|
||||
using var command = new NpgsqlCommand("get_users", connection, tx)
|
||||
{
|
||||
CommandType = CommandType.StoredProcedure
|
||||
};
|
||||
command.Parameters.AddWithValue("p_department_id", departmentId);
|
||||
var refcursorParam = new NpgsqlParameter("cur_result", NpgsqlDbType.Refcursor)
|
||||
{
|
||||
Direction = ParameterDirection.Output
|
||||
};
|
||||
command.Parameters.Add(refcursorParam);
|
||||
|
||||
// Execute the procedure to open the cursor.
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
// Retrieve the cursor name, then fetch the actual data.
|
||||
string cursorName = (string)refcursorParam.Value;
|
||||
using var fetchCommand = new NpgsqlCommand($"FETCH ALL FROM \"{cursorName}\"", connection, tx);
|
||||
using var reader = fetchCommand.ExecuteReader();
|
||||
while (reader.Read())
|
||||
{
|
||||
users.Add(new User
|
||||
{
|
||||
UserId = reader.GetInt32(reader.GetOrdinal("user_id")),
|
||||
UserName = reader.GetString(reader.GetOrdinal("user_name")),
|
||||
Email = reader.GetString(reader.GetOrdinal("email"))
|
||||
});
|
||||
}
|
||||
|
||||
tx.Commit();
|
||||
return users;
|
||||
}
|
||||
```
|
||||
|
||||
## Reusable Helper
|
||||
|
||||
Returning a live `NpgsqlDataReader` from a helper leaves the underlying `NpgsqlCommand` undisposed and creates ambiguous ownership. Prefer materializing results inside the helper instead:
|
||||
|
||||
```csharp
|
||||
public static class PostgresHelpers
|
||||
{
|
||||
public static List<T> ExecuteRefcursorProcedure<T>(
|
||||
NpgsqlConnection connection,
|
||||
NpgsqlTransaction transaction,
|
||||
string procedureName,
|
||||
Dictionary<string, object> parameters,
|
||||
string refcursorParameterName,
|
||||
Func<NpgsqlDataReader, T> map)
|
||||
{
|
||||
using var command = new NpgsqlCommand(procedureName, connection, transaction)
|
||||
{
|
||||
CommandType = CommandType.StoredProcedure
|
||||
};
|
||||
foreach (var (key, value) in parameters)
|
||||
command.Parameters.AddWithValue(key, value);
|
||||
|
||||
var refcursorParam = new NpgsqlParameter(refcursorParameterName, NpgsqlDbType.Refcursor)
|
||||
{
|
||||
Direction = ParameterDirection.Output
|
||||
};
|
||||
command.Parameters.Add(refcursorParam);
|
||||
command.ExecuteNonQuery();
|
||||
|
||||
string cursorName = (string)refcursorParam.Value;
|
||||
if (string.IsNullOrEmpty(cursorName))
|
||||
return new List<T>();
|
||||
|
||||
// fetchCommand is disposed here; results are fully materialized before returning.
|
||||
using var fetchCommand = new NpgsqlCommand($"FETCH ALL FROM \"{cursorName}\"", connection, transaction);
|
||||
using var reader = fetchCommand.ExecuteReader();
|
||||
|
||||
var results = new List<T>();
|
||||
while (reader.Read())
|
||||
results.Add(map(reader));
|
||||
return results;
|
||||
}
|
||||
}
|
||||
|
||||
// Usage:
|
||||
using var connection = new NpgsqlConnection(connectionString);
|
||||
connection.Open();
|
||||
using var tx = connection.BeginTransaction();
|
||||
|
||||
var users = PostgresHelpers.ExecuteRefcursorProcedure(
|
||||
connection, tx,
|
||||
"get_users",
|
||||
new Dictionary<string, object> { { "p_department_id", departmentId } },
|
||||
"cur_result",
|
||||
r => new User
|
||||
{
|
||||
UserId = r.GetInt32(r.GetOrdinal("user_id")),
|
||||
UserName = r.GetString(r.GetOrdinal("user_name")),
|
||||
Email = r.GetString(r.GetOrdinal("email"))
|
||||
});
|
||||
|
||||
tx.Commit();
|
||||
```
|
||||
|
||||
## Oracle vs. PostgreSQL Summary
|
||||
|
||||
| Aspect | Oracle (ODP.NET) | PostgreSQL (Npgsql) |
|
||||
|--------|------------------|---------------------|
|
||||
| **Cursor return** | Result set exposed directly in data reader | Cursor name string in output parameter |
|
||||
| **Data access** | `ExecuteReader()` returns rows immediately | `ExecuteNonQuery()` → get cursor name → `FETCH ALL FROM` |
|
||||
| **Transaction** | Transparent | CALL and FETCH must share the same transaction |
|
||||
| **Multiple cursors** | Automatic | Each requires a separate `FETCH` command |
|
||||
| **Resource lifetime** | Driver-managed | Cursor is open until fetched or transaction ends |
|
||||
|
||||
## Migration Checklist
|
||||
|
||||
- [ ] Identify all procedures returning `SYS_REFCURSOR` (Oracle) / `refcursor` (PostgreSQL)
|
||||
- [ ] Replace `ExecuteReader()` with `ExecuteNonQuery()` → cursor name → `FETCH ALL FROM`
|
||||
- [ ] Wrap each call-and-fetch pair in an explicit transaction
|
||||
- [ ] Ensure commands and readers are disposed (prefer materializing results inside a helper)
|
||||
- [ ] Update unit and integration tests
|
||||
|
||||
## References
|
||||
|
||||
- [PostgreSQL Documentation: Cursors](https://www.postgresql.org/docs/current/plpgsql-cursors.html)
|
||||
- [PostgreSQL FETCH Command](https://www.postgresql.org/docs/current/sql-fetch.html)
|
||||
- [Npgsql Refcursor Support](https://github.com/npgsql/npgsql/issues/1887)
|
||||
@@ -0,0 +1,54 @@
|
||||
---
|
||||
name: scaffolding-oracle-to-postgres-migration-test-project
|
||||
description: 'Scaffolds an xUnit integration test project for validating Oracle-to-PostgreSQL database migration behavior in .NET solutions. Creates the test project, transaction-rollback base class, and seed data manager. Use when setting up test infrastructure before writing migration integration tests, or when a test project is needed for Oracle-to-PostgreSQL validation.'
|
||||
---
|
||||
|
||||
# Scaffolding an Integration Test Project for Oracle-to-PostgreSQL Migration
|
||||
|
||||
Creates a compilable, empty xUnit test project with transaction management and seed data infrastructure for a single target project. Run once per project before writing tests.
|
||||
|
||||
## Workflow
|
||||
|
||||
```
|
||||
Progress:
|
||||
- [ ] Step 1: Inspect the target project
|
||||
- [ ] Step 2: Create the xUnit test project
|
||||
- [ ] Step 3: Implement transaction-rollback base class
|
||||
- [ ] Step 4: Implement seed data manager
|
||||
- [ ] Step 5: Verify the project compiles
|
||||
```
|
||||
|
||||
**Step 1: Inspect the target project**
|
||||
|
||||
Read the target project's `.csproj` to determine the .NET version and existing package references. Match these versions exactly — do not upgrade.
|
||||
|
||||
**Step 2: Create the xUnit test project**
|
||||
|
||||
- Target the same .NET version as the application under test.
|
||||
- Add NuGet packages for Oracle database connectivity and xUnit.
|
||||
- Add a project reference to the target project only — no other application projects.
|
||||
- Add an `appsettings.json` configured for Oracle database connectivity.
|
||||
|
||||
**Step 3: Implement transaction-rollback base class**
|
||||
|
||||
- Create a base test class that opens a transaction before each test and rolls it back after.
|
||||
- Catch and handle all exceptions to guarantee rollback.
|
||||
- Make the pattern inheritable by all downstream test classes.
|
||||
|
||||
**Step 4: Implement seed data manager**
|
||||
|
||||
- Create a global seed manager for loading test data within the transaction scope.
|
||||
- Do not commit seed data — transactions roll back after each test.
|
||||
- Do not use `TRUNCATE TABLE` — preserve existing database data.
|
||||
- Reuse existing seed files if available.
|
||||
- Establish a naming convention for seed file location that downstream test creation will follow.
|
||||
|
||||
**Step 5: Verify the project compiles**
|
||||
|
||||
Build the test project and confirm it compiles with zero errors before finishing.
|
||||
|
||||
## Key Constraints
|
||||
|
||||
- Oracle is the golden behavior source — scaffold for Oracle first.
|
||||
- Keep to existing .NET and C# versions; do not introduce newer language or runtime features.
|
||||
- Output is an empty test project with infrastructure only — no test cases.
|
||||
Reference in New Issue
Block a user