# 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
