# 機能設計書 15-PostgreSQL アダプター

## 概要

本ドキュメントは、Payload CMSにおけるPostgreSQL アダプター（`@payloadcms/db-postgres`）の設計を定義する。PostgreSQLデータベースとの接続、CRUD操作、スキーマ管理をDrizzle ORMを通じて提供する。

### 本機能の処理概要

PostgreSQL アダプターは、Payload CMSとPostgreSQLデータベース間のインターフェースを提供するデータベースアダプターである。Drizzle ORMを使用してPostgreSQLへの接続とスキーマ管理を行い、PayloadのOperations層からのデータ操作要求をSQLクエリに変換して実行する。

**業務上の目的・背景**：PostgreSQLはオープンソースの高機能リレーショナルデータベースであり、ACID準拠のトランザクション、高度なクエリ機能、拡張機能（PostGIS等）を提供する。エンタープライズ環境での信頼性と、クラウドプロバイダー（AWS RDS、Google Cloud SQL、Supabase等）での広範なサポートにより、本番環境に適したデータベース選択肢となる。

**機能の利用シーン**：
- 本番環境でのPostgreSQLクラスター接続
- リードレプリカを活用した負荷分散
- PostGIS等の拡張機能を使用した地理空間データ処理
- AWS RDS、Google Cloud SQL、Supabase等のマネージドサービス利用
- 既存PostgreSQLデータベースとの統合

**主要な処理内容**：
1. 接続管理：PostgreSQL接続プール管理、再接続処理
2. スキーマ生成：PayloadフィールドからDrizzleスキーマを自動生成
3. CRUD操作：create、find、findOne、update、delete
4. トランザクション：セッション管理、コミット、ロールバック
5. マイグレーション：スキーマ変更のマイグレーション生成・実行
6. リードレプリカ：読み取り負荷分散の設定

**関連システム・外部連携**：
- PostgreSQL / AWS RDS / Google Cloud SQL
- Supabase、Neon等のサーバーレスPostgreSQL
- Drizzle ORM
- node-postgres（pg）ライブラリ

**権限による制御**：
- PostgreSQL接続文字列での認証
- データベースユーザー権限に基づくアクセス制御

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| - | （該当なし） | - | データベース層のため画面連携なし |

## 機能種別

データベースアダプター / ORM統合

## 入力仕様

### アダプター設定パラメータ

| パラメータ名 | 型 | 必須 | 説明 | デフォルト |
|-------------|-----|-----|------|-----------|
| pool | PoolConfig | Yes | 接続プール設定 | - |
| idType | 'serial' / 'uuid' | No | ID生成タイプ | 'serial' |
| allowIDOnCreate | boolean | No | create時にカスタムIDを許可 | false |
| blocksAsJSON | boolean | No | ブロックをJSON列で保存 | false |
| disableCreateDatabase | boolean | No | 自動データベース作成無効化 | false |
| schemaName | string | No | PostgreSQLスキーマ名 | 'public' |
| localesSuffix | string | No | ロケールテーブルの接尾辞 | '_locales' |
| relationshipsSuffix | string | No | リレーションテーブルの接尾辞 | '_rels' |
| versionsSuffix | string | No | バージョンテーブルの接尾辞 | '_v' |
| transactionOptions | PgTransactionConfig / false | No | トランザクションオプション | undefined |
| push | boolean | No | 開発時のスキーマプッシュ | true |
| logger | DrizzleConfig['logger'] | No | ロガー設定 | false |
| extensions | string[] | No | 有効化するPostgreSQL拡張 | [] |
| readReplicas | string[] | No | リードレプリカ接続文字列 | - |
| migrationDir | string | No | マイグレーションディレクトリ | 自動検出 |
| prodMigrations | Migration[] | No | 本番用マイグレーション | - |
| beforeSchemaInit | PostgresSchemaHook[] | No | スキーマ初期化前フック | [] |
| afterSchemaInit | PostgresSchemaHook[] | No | スキーマ初期化後フック | [] |
| generateSchemaOutputFile | string | No | スキーマ出力ファイルパス | - |
| tablesFilter | string[] | No | 対象テーブルフィルタ | - |
| pg | PgDependency | No | カスタムpgライブラリ | pg |

## 出力仕様

### 出力データ

PayloadのドキュメントフォーマットでJSON形式のデータを返却。

### 出力先

- Operations層への戻り値
- PostgreSQLデータベース（書き込み操作時）

## 処理フロー

### 初期化処理シーケンス

```
1. postgresAdapter()呼び出し
   └─ アダプター設定オブジェクトを受け取る

2. createDatabaseAdapter()
   └─ 共通アダプターインターフェースでラップ

3. init()実行（@payloadcms/drizzle/postgres）
   └─ Drizzleスキーマ生成
   └─ テーブル定義、リレーション定義
   └─ beforeSchemaInit/afterSchemaInitフック実行

4. connect()実行
   └─ Pool接続確立
   └─ drizzle()でDrizzleインスタンス生成
   └─ リードレプリカ設定（オプション）
   └─ 拡張機能作成
   └─ 開発環境でpushDevSchema実行
   └─ 本番環境でマイグレーション実行
```

### フローチャート

```mermaid
flowchart TD
    A[postgresAdapter設定] --> B[createDatabaseAdapter]
    B --> C[init実行]
    C --> D[Drizzleスキーマ生成]
    D --> E[connect実行]
    E --> F[Pool接続確立]
    F --> G[drizzleインスタンス生成]
    G --> H{リードレプリカ?}
    H -->|Yes| I[withReplicas設定]
    H -->|No| J[拡張機能作成]
    I --> J
    J --> K{本番環境?}
    K -->|No| L[pushDevSchema]
    K -->|Yes| M[マイグレーション実行]
    L --> N[接続完了]
    M --> N
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-01 | IDタイプ変換 | idType='serial'→'number'、'uuid'→'text' | 全コレクション |
| BR-02 | 自動データベース作成 | データベースが存在しない場合は自動作成 | disableCreateDatabase=false時 |
| BR-03 | 開発時スキーマプッシュ | 本番環境以外ではpushDevSchemaでスキーマ同期 | push=true且つNODE_ENV!='production' |
| BR-04 | 再接続処理 | ECONNRESETエラー時に自動再接続 | 接続エラー時 |
| BR-05 | リードレプリカ自動選択 | withReplicasで読み取りクエリを自動分散 | readReplicas設定時 |
| BR-06 | テーブル命名規則 | コレクション名にsuffix付加（_locales、_rels、_v） | 多言語、リレーション、バージョン |

### 計算ロジック

該当なし

## データベース操作仕様

### テーブル構造

PostgreSQLアダプターは以下のテーブル構造を生成：

| テーブル種別 | 命名規則 | 用途 |
|------------|---------|------|
| メインテーブル | {collection} | ドキュメント本体 |
| ロケールテーブル | {collection}_locales | ローカライズフィールド |
| リレーションテーブル | {collection}_rels | 多対多リレーション |
| バージョンテーブル | {collection}_v | バージョン管理データ |

### 操作別PostgreSQL実行内容

| Payload操作 | PostgreSQL操作 | 備考 |
|------------|----------------|------|
| create | INSERT | Drizzle insert() |
| find | SELECT | Drizzle select() + where() |
| findOne | SELECT | LIMIT 1 |
| updateOne | UPDATE | Drizzle update() |
| updateMany | UPDATE | 複数行更新 |
| deleteOne | DELETE | Drizzle delete() |
| deleteMany | DELETE | 複数行削除 |
| count | SELECT COUNT(*) | countDistinct使用 |
| upsert | INSERT...ON CONFLICT | Drizzle onConflict() |
| queryDrafts | SELECT | バージョンテーブルから最新ドラフト |

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| 23505 | UniqueViolation | 一意制約違反 | handleErrorで変換 |
| ECONNREFUSED | 接続エラー | PostgreSQL接続失敗 | 接続設定を確認 |
| ECONNRESET | 接続リセット | ネットワーク断 | 自動再接続 |
| 42P01 | UndefinedTable | テーブル未存在 | マイグレーション実行 |

### リトライ仕様

`connectWithReconnect`関数で`ECONNRESET`エラー時に1秒後再接続を試行。

## トランザクション仕様

### トランザクション管理

- `beginTransaction`: セッションIDを生成し、drizzle.transactionを開始
- `commitTransaction`: トランザクションをコミット
- `rollbackTransaction`: トランザクションをロールバック
- `transactionOptions: false`でトランザクション無効化

### 注意事項

- Drizzleのトランザクションはネストをサポート
- `PgTransactionConfig`でisolationLevel等を設定可能

## パフォーマンス要件

- 接続プールによる接続再利用
- リードレプリカによる読み取り負荷分散
- `countDistinct`による効率的なカウント
- インデックス自動生成

## セキュリティ考慮事項

- 接続プール設定での認証（user/password/ssl）
- PostgreSQLのRLS（Row Level Security）との併用可能
- スキーマ分離によるマルチテナント対応
- SSL/TLS接続オプション

## 備考

- Drizzle ORM v0.30+に依存
- `@payloadcms/drizzle`パッケージで共通ロジック提供
- PostGIS等の拡張はextensionsオプションで有効化

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

まず、PostgreSQLアダプターの型定義とインターフェースを理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | types.ts | `packages/db-postgres/src/types.ts` | Args型、PostgresAdapter型の定義 |

**読解のコツ**: `Args`型（23-79行目）でアダプター設定オプションを定義。`PostgresAdapter`型（93-98行目）でBasePostgresAdapterを拡張し、drizzle、pg、pool、poolOptionsを追加。`declare module 'payload'`（100-140行目）でPayloadのDatabaseAdapter型を拡張。

#### Step 2: エントリーポイントを理解する

アダプター生成のメイン関数を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | index.ts | `packages/db-postgres/src/index.ts` | postgresAdapter関数 |

**主要処理フロー**:
- **70-72行目**: idType、payloadIDType、allowIDOnCreateの初期化
- **74行目**: 内部adapter関数の定義
- **75行目**: migrationDirの検出
- **80-83行目**: initializingプロミスの生成
- **85-90行目**: schemaName設定でpgSchemaを決定
- **92-98行目**: extensions配列をオブジェクトに変換
- **112-225行目**: createDatabaseAdapterでアダプターオブジェクト生成
- **144行目**: `idType: postgresIDType`（'serial'または'uuid'）
- **183行目**: `defaultIDType: payloadIDType`（'number'または'text'）
- **236-241行目**: DatabaseAdapterObjを返却

#### Step 3: 接続処理を理解する

connect関数の実装を確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | connect.ts | `packages/db-postgres/src/connect.ts` | connect関数、connectWithReconnect関数 |

**主要処理フロー**（connect.ts）:
- **10-45行目**: connectWithReconnect関数（再接続ロジック）
- **36-44行目**: ECONNRESETエラー時の再接続処理
- **56-59行目**: Poolインスタンス生成と接続
- **61-62行目**: drizzle()でDrizzleインスタンス生成
- **64-79行目**: リードレプリカ設定（withReplicas）
- **81-87行目**: PAYLOAD_DROP_DATABASEでテーブル削除
- **88-111行目**: データベース未存在時の自動作成
- **114行目**: createExtensions()で拡張機能作成
- **117-123行目**: 開発環境でpushDevSchema実行
- **129-131行目**: 本番環境でマイグレーション実行

#### Step 4: Drizzle統合を理解する

@payloadcms/drizzleパッケージの共通ロジックを確認する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | index.ts | `packages/drizzle/src/index.ts` | エクスポート一覧 |
| 4-2 | postgres/index.ts | `packages/drizzle/src/postgres/index.ts` | PostgreSQL固有ロジック |

### プログラム呼び出し階層図

```
Operations層 (payload.db.create/find/update/delete)
    │
    └─ PostgreSQL Adapter
           │
           ├─ @payloadcms/drizzle (共通ロジック)
           │      ├─ create.ts
           │      ├─ find.ts
           │      ├─ updateOne.ts
           │      ├─ deleteOne.ts
           │      └─ ...
           │
           ├─ @payloadcms/drizzle/postgres (PostgreSQL固有)
           │      ├─ init.ts
           │      ├─ createDatabase.ts
           │      ├─ createExtensions.ts
           │      └─ ...
           │
           └─ drizzle-orm/node-postgres
                  └─ PostgreSQL接続
```

### データフロー図

```
[Payload Operations]           [PostgreSQL Adapter]        [PostgreSQL]

payload.db.create() ───▶ @payloadcms/drizzle ───▶ drizzle.insert()
                              create()                    │
                                  │                       ▼
                                  │               PostgreSQL INSERT
                                  │                       │
                    ◀─────────────┴───────────────────────┘
                                  │
                                  ▼
                           Document (Payload形式)
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| index.ts | `packages/db-postgres/src/index.ts` | ソース | アダプターエントリーポイント |
| types.ts | `packages/db-postgres/src/types.ts` | ソース | 型定義 |
| connect.ts | `packages/db-postgres/src/connect.ts` | ソース | PostgreSQL接続処理 |
| index.ts | `packages/drizzle/src/index.ts` | ソース | Drizzle共通ロジックエクスポート |
| create.ts | `packages/drizzle/src/create.ts` | ソース | ドキュメント作成 |
| find.ts | `packages/drizzle/src/find.ts` | ソース | ドキュメント検索 |
| findOne.ts | `packages/drizzle/src/findOne.ts` | ソース | 単一ドキュメント取得 |
| updateOne.ts | `packages/drizzle/src/updateOne.ts` | ソース | 単一ドキュメント更新 |
| deleteOne.ts | `packages/drizzle/src/deleteOne.ts` | ソース | 単一ドキュメント削除 |
| index.ts | `packages/drizzle/src/postgres/index.ts` | ソース | PostgreSQL固有ロジック |
| init.ts | `packages/drizzle/src/postgres/init.ts` | ソース | 初期化処理 |
| createDatabase.ts | `packages/drizzle/src/postgres/createDatabase.ts` | ソース | データベース作成 |
| createExtensions.ts | `packages/drizzle/src/postgres/createExtensions.ts` | ソース | 拡張機能作成 |
| pushDevSchema.ts | `packages/drizzle/src/utilities/pushDevSchema.ts` | ソース | 開発スキーマプッシュ |
