querying-local-postgres について
PostHog 開発者向けのローカル PostgreSQL 読み取り専用クエリガイドです。SQL による調査、デバッグ、実行計画の分析を扱い、接続を読み取り専用にする手順を定めています。
主な機能
- 01読み取り専用 SQL のみを許可
- 02ローカル PostgreSQL の接続方法
- 03EXPLAIN による実行計画分析
- 04PostHog のテーブルと DB の案内
- 05クエリを使ったデバッグ例
活用シーン
- 01チーム、プロジェクト、フラグ行の確認
- 02マイグレーション、制約、重複キーの調査
- 03Django クエリ計画の分析
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ posthog/ posthog --skill querying-local-postgresインストール後、Claude Code で/querying-local-postgresを入力して読み込まれたか確認します。
詳細
ローカル Postgres のクエリ (読み取り専用) — PostHog リポジトリ
ユーザーのクエリ: $ARGUMENTS
スコープ: このリポジトリはアプリのメタデータ (チーム、プロジェクト、フラグ、Django モデルなど) に PostgreSQL を使用しています。分析イベントデータは ClickHouse にあり、Postgres にはありません。ユーザーが明示的に Postgres を希望しない限り、events 系の質問には HogQL / ClickHouse ツールを使用してください。
使用する場面
- ユーザーがデータベースへのクエリ、テーブルの調査、または Postgres に対する SQL の実行を依頼した場合
- デバッグ: 行レベルの確認 (例: チーム/プロジェクト/フラグの行がなぜおかしいのか)、マイグレーション、制約、重複キー
- パフォーマンス: Django またはアプリのテーブルに対する 読み取り専用の
SELECTでのEXPLAIN/EXPLAIN (ANALYZE, …)
手順
- 変更を厳格に禁止 — 以下の「変更は厳格に禁止」を参照してください。ユーザーが書き込みや変更を求めた場合は拒否し、このスキルが読み取り専用であることを説明してください。
- ユーザーの質問を 1 つ以上の読み取り専用 SQL ステートメントに変換します。
- 実行する前にSQL を表示します (コードブロック内)。
- 以下のコマンドパターンを使用して実行します (読み取り専用接続を強制するため、常に
PGOPTIONS='-c default_transaction_read_only=on'を付けます)。 - 結果を表示し、簡単な解釈を添えます (特にデバッグやプランレビューで使用する場合)。
変更は厳格に禁止
以下を実行、提案、または生成しないでください。 拒否し、このスキルが読み取り専用であることを述べてください。
- DML:
INSERT、UPDATE、DELETE、MERGE、TRUNCATE - DDL:
CREATE、DROP、ALTER、RENAME - その他の書き込み:
COPY ... TO program、CALL(変更を伴う場合)、GRANT/REVOKE - 読み取り専用の
SELECT以外に対するEXPLAIN ANALYZE(WITH … SELECTを含む)。DML をEXPLAIN ANALYZEでラップしないでください。書き込みが実行されてしまいます。以下の読み取り専用接続は書き込みを拒否しますが、エージェントはこのパターンを試みてはなりません。 - データ、スキーマ、またはロールを変更するあらゆるステートメント
許可されるもの:
SELECT(WITH … SELECTを含む)EXPLAIN…SELECT(推定のみのプラン。実行なし)EXPLAIN (ANALYZE, …) SELECT—SELECTを 1 回実行します。パフォーマンス分析にのみ使用してください。以下の読み取り専用接続で実行する必要があります。SHOW、カタログビュー (pg_stat_*、information_schemaなど) からの読み取り専用のSELECT
ユーザーが書き込み操作を要求した場合は、次のように伝えてください: 「このスキルは読み取り専用です。INSERT/UPDATE/DELETE などの変更は実行できません。書き込みには DB クライアントまたはマイグレーションツールを使用してください。」
EXPLAIN と EXPLAIN ANALYZE (パフォーマンス)
| 目的 | 使用するもの |
|---|---|
| プランの形状、推定コスト、実行なし | EXPLAIN (FORMAT TEXT, COSTS) または VERBOSE を追加 |
| 実際のタイミング、行数、バッファヒット | SELECT に対する EXPLAIN (ANALYZE, BUFFERS, FORMAT TEXT) |
| バッファ + WAL 統計 | BUFFERS には ANALYZE が必要。WAL には ANALYZE が必要 (PostgreSQL 13 以降) |
安全なパターン: 分析対象のステートメントは必ず SELECT (または WITH … SELECT) のみとし、読み取り専用接続で実行する必要があります (以下の使用法を参照)。例:
PGOPTIONS='-c default_transaction_read_only=on' psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c "
EXPLAIN (ANALYZE, BUFFERS, VERBOSE, FORMAT TEXT)
SELECT … LIMIT 100;"
任意のフラグ (有用な場合): SETTINGS (デフォルト以外の GUC を表示)、WAL (ANALYZE と併用)、TIMING (最近のバージョンでは ANALYZE に対してデフォルトで有効)。
注意点:
EXPLAIN ANALYZEはクエリを実行します — 大規模なスキャンでは遅くなったり負荷が高くなったりする可能性があります。探索する場合は、現実的なWHEREや本番の形状に合わせたLIMITなど、範囲を限定したSELECTを優先してください。- 本番 / 共有 DB — ホットなクエリや幅の広いクエリを分析すると負荷が増える可能性があります。影響を気にする場合は、ステージング、レプリカ、またはオフピーク時の使用を優先してください。
ANALYZEなしのEXPLAIN— 内部のステートメントは実行されません (一部の特殊なケースを除く)。それでも読み取り専用の SQL のみをラップしてください。
PostHog: 接続と DATABASE_URL
ローカル Postgres (ホストマシン) — このスキルのデフォルト
日々のローカルクエリには、このハードコードされた URL を使用してください (一般的な Docker Compose + localhost のポート 5432、SSL オフに一致します):
| 設定 | 値 |
|---|---|
| ホスト | localhost |
| ポート | 5432 |
| ユーザー | posthog |
| パスワード | posthog |
| データベース | posthog |
| SSL | off |
# Prefer this unless the user says their local password/db differs
LOCAL_POSTGRES_URL='postgres://posthog:posthog@localhost:5432/posthog'
同等: postgresql://posthog:posthog@localhost:5432/posthog
同じサーバー上の他のローカル DB: パスのみを置き換えます。例: ...5432/posthog_persons。
設定の信頼できる情報源 (アプリ): posthog/settings/data_stores.py (Django の DATABASES、オプションのレプリカ POSTHOG_POSTGRES_READ_HOST、直接接続用 POSTHOG_POSTGRES_DIRECT_HOST、PERSONS_DB_WRITER_URL、products/db_routing.yaml からのプロダクト DB ルーティング)。
ハードコードされた URL を使用しない場合: 同じ認証情報でホストから接続する方法は Developing locally に記載されています (fe_sendauth のトラブルシューティング)。コンテナが起動していることを確認してください。
DEBUG がオンのときのデフォルト env: Django は PGHOST (デフォルトは db)、PGUSER / PGPASSWORD、PGPORT、PGDATABASE からデフォルトの DATABASE_URL を構築します — これはコンテナ内のホスト名に一致します。ホストからは、シェルがすでに DATABASE_URL をエクスポートしていない限り、localhost と同じユーザー / パスワード / データベース名を使用してください。
複数の PostgreSQL データベース (ローカル compose では同じサーバー、論理的に別々の DB):
- メインアプリ DB: 通常は
posthog - Persons DB:
posthog_persons(PERSONS_DB_WRITER_URL/PERSONS_DB_READER_URL) - プロダクト分離 DB:
products/db_routing.yamlに従ったposthog_<name>(docker/postgres-init-scripts/create-product-dbs.shによって作成されます) - 他の init スクリプトが追加の DB (例: cyclotron) を作成する場合があります — 必要に応じて
docker/postgres-init-scripts/を確認してください
DATABASE_URL のパスを変更することで、psql を適切なデータベースに向けられます (例: .../posthog_persons)。
Rust / sqlx: posthog/rust から作業する場合、一部のサービスは DATABASE_URL に rust/.env を使用します — rust/README.md を参照してください。
使用方法 (コマンドパターン)
生成された SQL が誤っていても Postgres が書き込みを拒否するように、常に PGOPTIONS='-c default_transaction_read_only=on' を介して接続を読み取り専用に強制してください。
なぜ
SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLYではなくPGOPTIONSなのか? 複数のステートメントを含むpsql -c "..."文字列は、単一の暗黙的なトランザクションとして実行されます。SET SESSION CHARACTERISTICS AS TRANSACTION READ ONLYは、_後続の_トランザクションのデフォルトを設定するだけです — 実行中のトランザクションはBEGIN時に与えられた読み書きモードを維持するため、同じ-c内の書き込みは拒否されません。PGOPTIONS='-c default_transaction_read_only=on'は接続開始時に GUC を設定するため、すべてのトランザクション (暗黙的な-cのトランザクションを含む) が読み取り専用で開始されます。インラインでの同等の方法は、-c文字列の最初のステートメントとしてSET TRANSACTION READ ONLY;を実行することです (SET SESSION CHARACTERISTICSとは異なり、現在のトランザクションに影響します)。
相対的な env パスが解決されるように、PostHog リポジトリのルートから実行してください。
デフォルト — ローカルのハードコードされた URL (posthog / posthog @ localhost:5432 / db posthog):
PGOPTIONS='-c default_transaction_read_only=on' psql "postgres://posthog:posthog@localhost:5432/posthog" -v ON_ERROR_STOP=1 -c "SELECT 1;"
オプション A — DATABASE_URL がすでにシェルにある場合 (例: flox activate 後や手動の export 後):
PGOPTIONS='-c default_transaction_read_only=on' psql "$DATABASE_URL" -v ON_ERROR_STOP=1 -c "SELECT 1;"
オプション B — リポジトリルートの gitignore された env ファイルから読み込む場合 (DATABASE_URL がそこで設定されている場合):
npx dotenv -e .env -- bash -c "PGOPTIONS='-c default_transaction_read_only=on' psql \"\$DATABASE_URL\" -c 'SELECT ...'"
- シェル内の SQL の文字列リテラルには通常どおりシングルクォートを使用してください。
-c内でクォートをネストする場合は慎重にエスケープしてください。 - ユーザーが別途指定しない限り、デフォルトは
LIMIT 100です。 - 横幅の広い行には
-xを使用してください:psql ... -x -c "..."。
スキーマリファレンス (PostHog)
- Django モデル → テーブル:
posthog/models/(およびproducts/配下のプロダクトパッケージ) を参照してください。テーブル名は通常posthog_というプレフィックスが付き、スネークケースになります (例:posthog_team、posthog_user)。psql で\dt posthog_*を実行して確認するか、非標準の場合はモデルのMeta.db_tableを確認してください。 - マイグレーション:
posthog/migrations/(およびプロダクトのマイグレーションパス) が、時系列に沿った正式な DDL を定義しています。 - Person テーブル名:
PERSON_TABLE_NAMEで設定可能です (data_stores.pyを参照)。デフォルトはposthog_personです。
クエリランナーを使ったデバッグ (PostHog 向け)
- チーム、プロジェクト、ユーザー、またはフィーチャーフラグの関連付けに対応する行が存在することを確認してください。該当する場合はソフトデリート /
deletedフィールドも確認してください。 - カウントや JOIN を、アプリが想定している内容 (メンバーシップ、プロジェクトアクセスなど) と比較してください。
- レプリカとプライマリの読み取り差分は、ユーザーが正しいホストに接続している場合にのみ検証してください (レプリカ:
POSTHOG_POSTGRES_READ_HOST)。 - 遅い Django クエリで SQL としてレプリケートされた
SELECTに対してはEXPLAIN ANALYZEを使用してください — 本番規模のデータを読み込む点に注意してください。
相互参照
- ローカルセットアップと DB の注意点:
docs/published/handbook/engineering/developing-locally.md - リポジトリ CLI:
hogli(.agents/skills/hogli/SKILL.mdを参照)