# DDL Migrations: Column Operations

Step-by-step migration patterns for column-level changes using the Table Recreation Pattern.

**MUST read [overview.md](overview.md) first** for destructive operation warnings and the common verify & swap pattern.

---

## DROP COLUMN Migration

**Goal:** Remove a column from an existing table.

### Pre-Migration Validation

```sql
SELECT COUNT(*) as total_rows FROM target_table;
SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'target_table' ORDER BY ordinal_position;
```

### Migration Steps

#### Step 1: Create new table excluding the column

```sql
CREATE TABLE target_table_new (
  id UUID PRIMARY KEY,
  tenant_id VARCHAR(255) NOT NULL,
  kept_column1 VARCHAR(255),
  kept_column2 INTEGER
  -- dropped_column is NOT included
);
```

#### Step 2: Migrate data

```sql
INSERT INTO target_table_new (id, tenant_id, kept_column1, kept_column2)
SELECT id, tenant_id, kept_column1, kept_column2
FROM target_table;
```

For tables > 3,000 rows, use [Batched Migration Pattern](batched-migration.md).

**Step 3: Verify and swap** (see [Common Pattern](overview.md#common-verify--swap-pattern))

---

## ALTER COLUMN TYPE Migration

**Goal:** Change a column's data type.

### Pre-Migration Validation

**MUST validate data compatibility BEFORE migration** to prevent data loss.

```sql
-- Example: VARCHAR to INTEGER - check for non-numeric values
SELECT COUNT(*) as invalid_count FROM target_table
WHERE column_to_change !~ '^-?[0-9]+$';
-- MUST abort if invalid_count > 0

-- Show problematic rows
SELECT id, column_to_change FROM target_table
WHERE column_to_change !~ '^-?[0-9]+$' LIMIT 100;
```

### Data Type Compatibility Matrix

| From Type | To Type    | Validation                                              |
| --------- | ---------- | ------------------------------------------------------- |
| VARCHAR   | INTEGER    | MUST validate all values are numeric                    |
| VARCHAR   | BOOLEAN    | MUST validate values are 'true'/'false'/'t'/'f'/'1'/'0' |
| INTEGER   | VARCHAR    | Safe conversion                                         |
| TEXT      | VARCHAR(n) | MUST validate max length ≤ n                            |
| TIMESTAMP | DATE       | Safe (truncates time)                                   |
| INTEGER   | DECIMAL    | Safe conversion                                         |

### Migration Steps

#### Step 1: Create new table with changed type

```sql
CREATE TABLE target_table_new (
  id UUID PRIMARY KEY,
  converted_column INTEGER,  -- Changed from VARCHAR
  other_column TEXT
);
```

#### Step 2: Copy data with type casting

```sql
INSERT INTO target_table_new (id, converted_column, other_column)
SELECT id, CAST(converted_column AS INTEGER), other_column
FROM target_table;
```

**Step 3: Verify and swap** (see [Common Pattern](overview.md#common-verify--swap-pattern))

---

## ALTER COLUMN SET/DROP NOT NULL Migration

**Goal:** Change a column's nullability constraint.

### Pre-Migration Validation (for SET NOT NULL)

```sql
SELECT COUNT(*) as null_count FROM target_table
WHERE target_column IS NULL;
-- MUST ABORT if null_count > 0, or plan to provide default values
```

### Migration Steps

#### Step 1: Create new table with changed constraint

```sql
CREATE TABLE target_table_new (
  id UUID PRIMARY KEY,
  target_column VARCHAR(255) NOT NULL,  -- Changed from nullable
  other_column TEXT
);
```

#### Step 2: Copy data (with default for NULLs if needed)

```sql
INSERT INTO target_table_new (id, target_column, other_column)
SELECT id, COALESCE(target_column, 'default_value'), other_column
FROM target_table;
```

**Step 3: Verify and swap** (see [Common Pattern](overview.md#common-verify--swap-pattern))

---

## ALTER COLUMN SET/DROP DEFAULT Migration

**Goal:** Add or remove a default value for a column.

### Pre-Migration Validation

```sql
SELECT column_name, data_type, is_nullable FROM information_schema.columns WHERE table_name = 'target_table' ORDER BY ordinal_position;
-- Identify current column definition and any existing defaults
```

### Migration Steps (SET DEFAULT)

#### Step 1: Create new table with default value

```sql
CREATE TABLE target_table_new (
  id UUID PRIMARY KEY,
  status VARCHAR(50) DEFAULT 'pending',  -- Added default
  other_column TEXT
);
```

#### Step 2: Copy data

```sql
INSERT INTO target_table_new (id, status, other_column)
SELECT id, status, other_column
FROM target_table;
```

**Step 3: Verify and swap** (see [Common Pattern](overview.md#common-verify--swap-pattern))

### Migration Steps (DROP DEFAULT)

#### Step 1: Create new table without default

```sql
CREATE TABLE target_table_new (
  id UUID PRIMARY KEY,
  status VARCHAR(50),  -- Removed DEFAULT
  other_column TEXT
);
```

#### Step 2: Copy data

```sql
INSERT INTO target_table_new (id, status, other_column)
SELECT id, status, other_column
FROM target_table;
```

**Step 3: Verify and swap** (see [Common Pattern](overview.md#common-verify--swap-pattern))
