mirror of
https://github.com/github/awesome-copilot.git
synced 2026-08-16 14:16:56 +00:00
chore: publish from main
This commit is contained in:
@@ -4,7 +4,10 @@
|
||||
| --- | --- |
|
||||
| [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-nvl-decode-functions.md](oracle-nvl-decode-functions.md) | Oracle NVL/NVL2/DECODE have no direct equivalents—replace with COALESCE, CASE WHEN, and NULL-safe comparisons. |
|
||||
| [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-rownum-pagination.md](oracle-rownum-pagination.md) | Oracle ROWNUM is assigned before ORDER BY; replace top-N and pagination patterns with LIMIT/OFFSET. |
|
||||
| [oracle-sysdate-sequences-dual.md](oracle-sysdate-sequences-dual.md) | Replace SYSDATE/SYSTIMESTAMP with NOW(), sequence .NEXTVAL with nextval('seq'), and remove FROM DUAL. |
|
||||
| [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. |
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
# Oracle to PostgreSQL: NVL, DECODE, and Null-Handling Functions
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle provides several functions — `NVL`, `NVL2`, `DECODE` — that have no direct equivalents in standard SQL but are commonly used in Oracle stored procedures and inline SQL. PostgreSQL uses standard SQL alternatives: `COALESCE`, `NULLIF`, and `CASE` expressions.
|
||||
|
||||
## Behavior Comparison
|
||||
|
||||
### NVL
|
||||
|
||||
**Oracle:** `NVL(expr, replacement)` — returns `replacement` if `expr` is `NULL`, otherwise `expr`.
|
||||
|
||||
**PostgreSQL:** Use `COALESCE(expr, replacement)` — semantically identical for two arguments.
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
NVL(column_name, 'default')
|
||||
|
||||
-- PostgreSQL
|
||||
COALESCE(column_name, 'default')
|
||||
```
|
||||
|
||||
### NVL2
|
||||
|
||||
**Oracle:** `NVL2(expr, not_null_val, null_val)` — returns `not_null_val` if `expr` IS NOT NULL, `null_val` if NULL.
|
||||
|
||||
**PostgreSQL:** No direct equivalent — use `CASE`:
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
NVL2(column_name, 'has value', 'no value')
|
||||
|
||||
-- PostgreSQL
|
||||
CASE WHEN column_name IS NOT NULL THEN 'has value' ELSE 'no value' END
|
||||
```
|
||||
|
||||
### DECODE
|
||||
|
||||
**Oracle:** `DECODE(expr, search1, result1, search2, result2, ..., default)` — equality-based switch.
|
||||
|
||||
**PostgreSQL:** No `DECODE` function — use `CASE WHEN`:
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
DECODE(status, 1, 'Active', 2, 'Inactive', 'Unknown')
|
||||
|
||||
-- PostgreSQL
|
||||
CASE status
|
||||
WHEN 1 THEN 'Active'
|
||||
WHEN 2 THEN 'Inactive'
|
||||
ELSE 'Unknown'
|
||||
END
|
||||
```
|
||||
|
||||
Note: `DECODE` in Oracle treats two `NULL` values as equal (unlike `=`). If any search value is `NULL`, use `IS NULL` in the `CASE` equivalent:
|
||||
|
||||
```sql
|
||||
-- Oracle: DECODE treats NULL = NULL
|
||||
DECODE(col, NULL, 'empty', col)
|
||||
|
||||
-- PostgreSQL
|
||||
CASE WHEN col IS NULL THEN 'empty' ELSE col END
|
||||
```
|
||||
|
||||
## Migration Actions
|
||||
|
||||
### 1. Stored Procedures
|
||||
|
||||
Apply the direct substitutions above. Pay special attention to:
|
||||
- `NVL` on numeric expressions — `COALESCE` is type-sensitive in PostgreSQL; ensure both arguments are the same type or cast explicitly.
|
||||
- `DECODE` with `NULL` search values — replace with `IS NULL` guard in the `CASE` expression.
|
||||
|
||||
### 2. Application Code (inline SQL strings)
|
||||
|
||||
Search C# string literals and query builders for `NVL(`, `NVL2(`, and `DECODE(`. Apply the same substitutions.
|
||||
|
||||
### 3. Tests
|
||||
|
||||
Write test cases that exercise `NULL` inputs specifically — the Oracle → PostgreSQL translation of `NVL`/`COALESCE` is straightforward, but edge cases around NULL equality in `DECODE` → `CASE` are a common source of silent behavioral differences.
|
||||
@@ -0,0 +1,75 @@
|
||||
# Oracle to PostgreSQL: ROWNUM Pagination vs LIMIT/OFFSET
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle uses `ROWNUM` pseudo-column for pagination and row-limiting. PostgreSQL uses standard `LIMIT` / `OFFSET` syntax. `ROWNUM` is also fundamentally different in *when* it is assigned, which affects filtering behavior.
|
||||
|
||||
## Behavior Comparison
|
||||
|
||||
**Oracle:**
|
||||
- `ROWNUM` is assigned before `ORDER BY` — filtering with `ROWNUM` on an unordered result set is non-deterministic
|
||||
- Common pattern to get "top N ordered rows" requires a subquery:
|
||||
```sql
|
||||
SELECT * FROM (SELECT * FROM t ORDER BY col) WHERE ROWNUM <= 10
|
||||
```
|
||||
- `ROWNUM BETWEEN n AND m` requires a double-wrapped subquery
|
||||
|
||||
**PostgreSQL:**
|
||||
- `LIMIT n` restricts result rows after `ORDER BY` is applied — straightforward and deterministic
|
||||
- `OFFSET n` skips rows; combine with `LIMIT` for pagination
|
||||
- No `ROWNUM` pseudo-column exists
|
||||
|
||||
## Code Example
|
||||
|
||||
```sql
|
||||
-- Oracle: top 10 rows by date
|
||||
SELECT * FROM (
|
||||
SELECT * FROM orders ORDER BY created_at DESC
|
||||
) WHERE ROWNUM <= 10;
|
||||
|
||||
-- PostgreSQL equivalent
|
||||
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10;
|
||||
|
||||
-- Oracle: rows 11–20 (keyset pagination via ROWNUM)
|
||||
SELECT * FROM (
|
||||
SELECT t.*, ROWNUM rn FROM (
|
||||
SELECT * FROM orders ORDER BY created_at DESC
|
||||
) t WHERE ROWNUM <= 20
|
||||
) WHERE rn > 10;
|
||||
|
||||
-- PostgreSQL equivalent
|
||||
SELECT * FROM orders ORDER BY created_at DESC LIMIT 10 OFFSET 10;
|
||||
```
|
||||
|
||||
## Migration Actions
|
||||
|
||||
### 1. Stored Procedures
|
||||
|
||||
Replace all `ROWNUM`-based limiting patterns with `LIMIT`/`OFFSET`:
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
WHERE ROWNUM = 1
|
||||
WHERE ROWNUM <= :n
|
||||
|
||||
-- PostgreSQL
|
||||
LIMIT 1
|
||||
LIMIT :n -- note: use $n parameter style in PL/pgSQL
|
||||
```
|
||||
|
||||
For subquery wrapping patterns:
|
||||
```sql
|
||||
-- Oracle
|
||||
SELECT * FROM (SELECT ... ORDER BY col) WHERE ROWNUM <= :n
|
||||
|
||||
-- PostgreSQL
|
||||
SELECT ... ORDER BY col LIMIT :n
|
||||
```
|
||||
|
||||
### 2. Application Code (inline SQL strings)
|
||||
|
||||
Search for `ROWNUM` in C# string literals, `StringBuilder`, and query-builder methods. Apply the same replacement patterns above.
|
||||
|
||||
### 3. Tests
|
||||
|
||||
Ensure integration tests validate that result set sizes are correct and that ordering is preserved (i.e., the correct *n* rows are returned, not just any *n* rows).
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
# Oracle to PostgreSQL: Date Functions, Sequences, and DUAL
|
||||
|
||||
## Problem
|
||||
|
||||
Oracle relies on several built-in constructs — `SYSDATE`, `SYSTIMESTAMP`, sequence `NEXTVAL` syntax, and the `DUAL` dummy table — that do not exist in PostgreSQL. Each requires a direct substitution.
|
||||
|
||||
## SYSDATE and SYSTIMESTAMP
|
||||
|
||||
**Oracle:**
|
||||
- `SYSDATE` — returns the current date and time (no time zone) as an Oracle `DATE` type
|
||||
- `SYSTIMESTAMP` — returns the current timestamp with time zone
|
||||
|
||||
**PostgreSQL:**
|
||||
- Use `NOW()` or `CURRENT_TIMESTAMP` for timestamp with time zone
|
||||
- Use `CURRENT_DATE` for date only
|
||||
- Use `LOCALTIMESTAMP` for timestamp without time zone (closer to Oracle's `SYSDATE` semantics)
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
SELECT SYSDATE FROM DUAL;
|
||||
INSERT INTO t (created_at) VALUES (SYSDATE);
|
||||
|
||||
-- PostgreSQL
|
||||
SELECT NOW();
|
||||
INSERT INTO t (created_at) VALUES (NOW());
|
||||
-- or, if the column is DATE-only:
|
||||
INSERT INTO t (created_at) VALUES (CURRENT_DATE);
|
||||
```
|
||||
|
||||
> **Warning:** Oracle `DATE` stores date *and* time; PostgreSQL `DATE` stores date only. If Oracle columns typed as `DATE` carry a time component, the PostgreSQL target column should be `TIMESTAMP`, not `DATE`.
|
||||
|
||||
## Sequence NEXTVAL Syntax
|
||||
|
||||
**Oracle:**
|
||||
```sql
|
||||
SELECT my_sequence.NEXTVAL FROM DUAL;
|
||||
INSERT INTO t (id) VALUES (my_sequence.NEXTVAL);
|
||||
```
|
||||
|
||||
**PostgreSQL:**
|
||||
```sql
|
||||
SELECT nextval('my_sequence');
|
||||
INSERT INTO t (id) VALUES (nextval('my_sequence'));
|
||||
```
|
||||
|
||||
Key differences:
|
||||
- PostgreSQL `nextval()` is a function call with the sequence name as a quoted string argument
|
||||
- Oracle uses dot notation: `sequence_name.NEXTVAL`
|
||||
- Oracle also has `CURRVAL` → PostgreSQL `currval('sequence_name')`
|
||||
- If the column uses a `DEFAULT nextval(...)` constraint (set during Phase 4 DDL migration), application code can omit the sequence call entirely and omit the column from the `INSERT`
|
||||
|
||||
## DUAL Table
|
||||
|
||||
Oracle requires a `FROM DUAL` clause in `SELECT` statements that evaluate expressions without a real table. PostgreSQL does not have `DUAL` — expressions can be selected without a `FROM` clause.
|
||||
|
||||
```sql
|
||||
-- Oracle
|
||||
SELECT 1 + 1 FROM DUAL;
|
||||
SELECT SYSDATE FROM DUAL;
|
||||
SELECT my_sequence.NEXTVAL FROM DUAL;
|
||||
|
||||
-- PostgreSQL
|
||||
SELECT 1 + 1;
|
||||
SELECT NOW();
|
||||
SELECT nextval('my_sequence');
|
||||
```
|
||||
|
||||
> **orafce extension:** If `orafce` is installed, it provides a `DUAL` view that makes Oracle-style `FROM DUAL` queries work without changes. This is a useful transitional aid but should not be relied on permanently.
|
||||
|
||||
## Migration Actions
|
||||
|
||||
### 1. Stored Procedures
|
||||
|
||||
- Replace all `SYSDATE` / `SYSTIMESTAMP` references with `NOW()` or `CURRENT_TIMESTAMP` (verify column type — use `LOCALTIMESTAMP` if the target is `TIMESTAMP WITHOUT TIME ZONE`)
|
||||
- Replace `sequence_name.NEXTVAL` with `nextval('sequence_name')`
|
||||
- Replace `sequence_name.CURRVAL` with `currval('sequence_name')`
|
||||
- Remove `FROM DUAL` from all expression-only `SELECT` statements
|
||||
|
||||
### 2. Application Code (inline SQL strings)
|
||||
|
||||
Search C# string literals for `SYSDATE`, `SYSTIMESTAMP`, `.NEXTVAL`, `.CURRVAL`, and `FROM DUAL`. Apply the same substitutions.
|
||||
|
||||
### 3. Tests
|
||||
|
||||
- Verify datetime assertions use timezone-safe comparisons (see `oracle-to-postgres-timestamp-timezone.md` for Npgsql-specific behavior)
|
||||
- Verify sequence-dependent IDs are correctly populated in assertions
|
||||
Reference in New Issue
Block a user