ドキュメント
データベース
スコープ指定された接続所有権、型付きクエリの失敗、移行バックアップ、およびローカル ドキュメント ストレージを備えた SQLite 永続性。
英語から自動翻訳されています。 英語の原文を見る
永続モデル
@cadcamfun/db sQLite を開く bun:sqlite エフェクト内で Drizzle データベースを公開します Database サービス。サイドカーはアプリケーション データベースを次の場所に保存します。 <dataDir>/cadcamfun.db。この接続により、移行を適用する前に、先行書き込みログと外部キーの強制が有効になります。
データベースには、プロジェクトとその関連リソースの作業コピーが含まれています。プロジェクトのバージョン管理オブジェクト、プル リクエスト、およびワークフローの実行もローカルに保存されます。 AI プロバイダーの認証情報は、所有者が読み取り可能な別の JSON ファイルを使用します。アプリケーションベアラートークンは、設定として保持されるのではなく、起動時に提供されます。
スキーマとエントリポイント
packages/db/src/schema.ts テーブル、インデックス、リレーションシップ、デフォルト、および推論された選択/挿入タイプを定義します。 src/index.ts 輸出 Database, DatabaseError、霧雨 Db タイプ、およびスキーマの名前空間。 src/upgrade.ts 移行計画、バックアップ、およびトランザクション アプリケーションを所有します。 src/migrate.ts コマンドラインのエントリポイントです。生成された SQL と移行ジャーナルは、 packages/db/drizzle.
によって開かれたすべての接続 packages/db/src/index.ts sQLite を次のように切り替えます journal_mode = WAL そしてオンになります foreign_keys。データベースを閉じると実行されます wal_checkpoint(TRUNCATE) したがって、プロセスが終了するとメインファイルが完成します。チェックポイントがビジーであるか失敗すると、警告がログに記録され、シャットダウンが続行されます。
保存された値
ドメイン識別子は、テーブルによって構成されている場合、デフォルトで UUID になります。 Drizzle はタイムスタンプ列を JavaScript 日付にマップし、JSON 列を宣言されたレコード タイプにマップします。 HTTP リポジトリは日付をワイヤー表現に変換し、ドメイン境界で JSON を検証します。保存された JSON 列の TypeScript 型アノテーション自体は、インポートされたデータまたは古いデータが現在のドメイン スキーマと一致することを証明するものではありません。
独自の図面、コンポーネント、ツールパスを投影します。ツールパスは、図面、材料、カッター、および機械の構成を参照できます。彼らの data 現場では、ポータブルな製造文書を持ち運ぶことができます。個別の図面、コンポーネント、およびツールパスのバージョン テーブルには、プロジェクト全体の Git 履歴に加えて、リソースのバージョンも保持されます。
サービスを通じてクエリを実行する
Database.layer ファイルパスを受け入れるか、 :memory:、オプションの移行フォルダー、およびオプションのバックアップ ディレクトリ。 Effect スコープの SQLite ハンドルを取得します。発信者が使用する query(operation, run) 同期または非同期の Drizzle 作業を実行します。外国の例外は DatabaseError 操作名と元の原因を含めます。
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"
const loadProjects = Effect.gen(function* () {
const database = yield* Database
return yield* database.query("projects.list", (db) =>
db.select().from(schema.projectTable).all())
}) リポジトリは独自のアクセス ルールと行解釈を行います。共有クエリ ヘルパーは失敗の属性を提供します。クエリを自動的に承認したり、サービス呼び出しの任意のシーケンスに基づいてトランザクションを作成したりすることはありません。
アップグレード動作
移行フォルダーを含むレイヤーを開くと、移行ファイルが読み取られ、その名前が次のものと比較されます。 __drizzle_migrations。古い形式のジャーナルは、移行タイムスタンプとハッシュを一致させることによってバックフィルされます。一致しないジャーナルは、移行 ID を推測するのではなく、アップグレードに失敗します。
既存のオンディスク データベースのアップグレードが必要な場合、サービスは次を使用して移行前の SQLite バックアップを作成します。 VACUUM INTO。部分ファイルに書き込み、完成したスナップショットの名前を変更し、一致する最新の 5 つの移行前バックアップを保持します。デフォルトのバックアップディレクトリは、 backups データベースの横にあります。新しいデータベースとインメモリ データベースには、このアップグレード前のバックアップは必要ありません。
ジャーナルの変更と保留中の SQL ステートメントは 1 つの即時トランザクションで実行されます。移行が失敗するとトランザクションがロールバックされ、レイヤーの構築が失敗します。データベースの初期化が失敗した場合、アプリケーションは準備完了を通知すべきではありません。
移行コマンド
bun run db:generate
bun run db:migrate 変更後に移行を生成する schema.ts、生成された SQL とジャーナルを一緒に保持します。移行 CLI が使用するのは、 DATABASE_URL 指定すると、イニシャルが削除されます file: プレフィックス、それ以外の場合は開きます ./data/cadcamfun.db。ルート ラッパーはこのスクリプトを次のように実行します。 packages/db 作業ディレクトリとして使用されるため、デフォルトのターゲットは packages/db/data/cadcamfun.db。 Webスタジオで使用するもの data/cadcamfun.db リポジトリのルートにあります。パッケージ化されたサイドカーは次のものを使用します CADCAMFUN_DATA_DIR。メンテナンスを実行するときは、これらのエントリ ポイントが異なるファイルに対して意図せずに動作しないように、明示的なデータベース パスを選択してください。
移行フォルダーがないと、 Database.layer データベースは開きますが、アプリケーションテーブルは作成されません。完全なスキーマを必要とするテストでは、移行ディレクトリにメモリ内データベースが提供されます。
データドメイン
- アイデンティティとコラボレーション: ユーザー、アカウント、セッション、認証子、検証トークン、サブスクリプション、組織、メンバーシップ、招待、会話、メッセージ、通知、プッシュ サブスクリプション。
- 設計と製造: プロジェクト、図面、コンポーネント、そのバージョンとコメント、材料、ツール、機械構成、ツールパス、および生産コストの記録。
- アプリケーションサービス: ファイル、ライブラリ項目、プラグインのレジストリ/構成、API キー、分析とアクティビティの記録、マーケットプレイスのデータ、MCP 構成、およびローカルのキーと値のエントリ。
- プロジェクトの歴史: Git リポジトリ、オブジェクトと参照、プル リクエストとレビュー、およびワークフローの実行。
テーブルの存在により、永続化モデルが確立されます。ネットワーク統合とアプリケーション機能は、テーブルの存在だけではなく、実際のハンドラーとサービスに依存します。
ローカルのキーと値のストレージ
LocalKeyValue jSON 互換のアプリケーションの状態を保持します。サイドカー KvStore 取得、更新/挿入、削除、およびプレフィックスでフィルターされたリストをサポートします。キーには空白以外のテキストが含まれ、最大でも 512 文字である必要があります。値には、null、文字列、ブール値、有限数、配列、およびプレーン オブジェクトが許可されます。サイクル、関数、非有限数、非単純オブジェクトは拒否されます。
エントリには更新タイムスタンプが含まれます。 Upsert は同じキーの値を置き換えます。プレフィックス リストはメモリ内のキーとフィルターによって並べ替えられます。このストアは設定とアシスタントのコンテキストに使用します。資格情報は専用の資格情報サービスに属します。
シャットダウンとリカバリ
データベース スコープ ファイナライザー リクエスト PRAGMA wal_checkpoint(TRUNCATE) ハンドルを閉める前に。ビジーチェックポイントまたはクローズ失敗は警告として記録されるため、残りのファイナライザーを実行できます。個々の HTTP ハンドラーではなく、接続所有者がハンドルを閉じます。
一貫した手動バックアップを行うには、データ ディレクトリをコピーする前にアプリケーションを完全に停止するか、実行中に SQLite のバックアップ機能を使用します。ライブデータベースは、WAL ファイルに状態を含めることができます。移行前のバックアップを検査するには、コピーを作成し、回復が確認されるまで元のデータベースとバックアップを保存します。
リポジトリの責任
SQLite 接続の作成と移行ロジックをこのパッケージに保持します。サイドカー リポジトリは、提供されたデータベース サービスを使用し、ストレージの障害をそのリソースの HTTP エラーにマッピングします。ジオメトリ計算をコアに保持し、シークレットを資格情報またはトークン サービスに保持します。データベースのバックアップにはプロジェクト データとアカウント データが含まれており、アクティブ データ ディレクトリと同じアクセス制御が必要です。
データベースを変更せずに検査する
クリーン シャットダウン後、明示的なデータベース パスに対して SQLite CLI を使用します。読み取り専用モードでは、パスが間違っている場合に誤って新しい空のファイルが作成されることを回避します。これらのチェックでは、アカウントや資格情報の行を公開することなく、構造と一貫性を検査します。
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA quick_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db 'PRAGMA foreign_key_check;'
sqlite3 -readonly /absolute/path/cadcamfun.db "SELECT name FROM sqlite_master WHERE type = 'table' ORDER BY name;"
sqlite3 -readonly /absolute/path/cadcamfun.db 'SELECT name, applied_at FROM __drizzle_migrations ORDER BY id;' quick_check 普通に報告する ok; foreign_key_check 違反の行のみを返します。のない日記 name/applied_at columns は古い形式であり、アプリケーション アップグレード コードは最後のクエリを使用する前にこの形式を調整する必要があります。修復を試みる前に、パスと移行 ID を確認してください。
アプリケーションは、独自の接続に外部キーの適用を設定します。別途開かれたメンテナンス接続は、許可された書き込みを行う前に明示的に有効にする必要があります。 PRAGMA は接続固有です。
ソース: packages/db/src/index.ts, upgrade.ts、 そして database.test.ts.
コードで使い捨てデータベースを使用する
完全なインメモリ サービス スコープは、リポジトリの例や分離された診断に役立ちます。スコープは接続を所有し、エフェクトがサービスを読み取る前に、チェックインされた移行を適用します。
import { Database, schema } from "@cadcamfun/db"
import { Effect } from "effect"
const migrationsFolder = new URL("../../packages/db/drizzle", import.meta.url).pathname
const program = Effect.gen(function* () {
const database = yield* Database
return yield* database.query("diagnostic.projects", (db) =>
db.select({ id: schema.projectTable.id }).from(schema.projectTable).all())
}).pipe(Effect.provide(Database.layer({ path: ":memory:", migrationsFolder })))
console.log(await Effect.runPromise(program)) // [] このスクリプトを次の名前で保存します apps/sidecar/diagnostic.ts そして走ります bun apps/sidecar/diagnostic.ts リポジトリのルートから。このパッケージはデータベースとエフェクトの依存関係を提供し、移行 URL はそのファイルから解決されます。欠落している、または間違った移行ディレクトリは、空のアプリケーション スキーマと同等ではありません。霧雨 query コールバックは同期クエリを実行することも、Promise を返すこともできます。ヘルパーは、指定された操作ラベルの下で失敗をラップします。
ソース: packages/db/src/database.test.ts そして index.ts.
ストレージ障害を診断する
- そのようなテーブルはありません: 正確なデータベース ファイルと移行フォルダーを確認してください。移行せずに開くことはサービスによって許可されていますが、アプリケーション テーブルのクエリは失敗します。
- 外部キー制約が失敗しました: 参照されている所有者/プロジェクト/リソースを検査します。削除制限はスキーマ固有です。所有されているプロジェクトを持つユーザーの削除は制限されていますが、ユーザーが削除されるとセッション行はカスケードされます。
- アップグレード中にバックアップが失敗しました: データベースとバックアップ ディレクトリの空き領域と書き込みアクセスを確認します。バックアップが失敗すると移行が停止します。元のデータベースを削除してバイパスしないでください。
- レガシー移行ジャーナルはローカル移行と一致しません。 データベースを保存し、一致する移行セットを回復します。ジャーナル行の名前を変更または削除すると、互換性のないアップグレード履歴が非表示になります。
- シャットダウン時にチェックポイントがビジー状態になります: データベース ファイルのみをコピーする前に、他の開いている接続を検査してください。クリーンなチェックポイントまたは一貫性のある SQLite バックアップが完了するまで、WAL/SHM 兄弟を保持します。
移行バックアップは、5 つのファイル保持ポリシーを備えたアップグレード前の回復ポイントであり、後続の編集内容の継続的なバックアップではありません。インストールのリカバリ要件に従って、完全なデータ ディレクトリを個別にバックアップします。
ソース: packages/db/src/index.ts, upgrade.ts, schema.ts、 そして database.test.ts.
移行履歴
チェックインされた移行はフォルダー順に適用されます。各フォルダーには、 migration.sql そして霧雨キット snapshot.json 次の差分を計算するために使用されます。ジャーナル行には、フォルダー名、そのハッシュ、フォルダーのタイムスタンプ (ミリ秒単位) が保存されます。created_at)、およびそれが適用された ISO 時間 (applied_at).
| フォルダ | サイズ | それが生み出すもの |
|---|---|---|
20261004223029_careful_monster_badoon | 771 行 | 従来の Prisma モデルから変換されたベースライン スキーマ: ID、組織、プロジェクト、図面、コンポーネント、ツールパス、ライブラリ、コスト、マーケットプレイス、プラグイン、MCP 構成。テーブル名と列名は変更されていないため、古いままです dev.db データは読み取り可能な状態を維持します。 |
20261008030518_redundant_dagger | 73 行 | プロジェクトの歴史: GitRepo, GitObject, GitRef, PullRequest, PullRequestReview, WorkflowRun. |
20261009012351_moaning_loki | 28 行 | ジオメトリ カーネル レコード: MeshAsset そして CadOperationLog. |
20261009171602_brown_mad_thinker | 5 行 | LocalKeyValue (key テキストの主キー、 value テキストが null ではない、 updatedAt null ではない整数)。 |
フォルダー名は drizzle-kit に由来しており、UTC タイムスタンプの後にランダムな名前が続きます。一度適用したフォルダーの名前を変更したり編集したりしないでください。アップグレード プランナーは、適用された移行を名前で識別し、古いジャーナルの場合はタイムスタンプとハッシュで識別します。
アップグレードの決定フロー
| 状況 | バックアップ | 結果 |
|---|---|---|
| 新しいファイル、指定された移行フォルダー | いいえ (ファイルを開く前には存在しませんでした) | すべての移行が 1 つのトランザクションで適用される |
| 既存のファイル、最新のジャーナル | いいえ | そのまま開封しました。 needed 偽です |
| 既存のファイル、保留中の移行 | はい、変更前 | 保留中の移行が適用されました。で挿入されたジャーナル行 applied_at |
既存のファイル、ジャーナルなし name カラム | はい | コラム name そして applied_at 追加、行のバックフィル、保留中の移行の適用 |
| レガシー行はローカル移行に一致しません | バックアップは取られません: 最初に計画が失敗します | DatabaseError 操作付き migrate: 「レガシー移行ジャーナルがローカル移行と一致しません」 |
:memory: | 一度もない | フォルダーが指定されている場合に適用される移行 |
| 移行フォルダーがありません | 一度もない | テーブルを作成せずに開いた |
バックフィル マッチングは次のように機能します。レガシー行の created_at 整数秒に切り捨てられ、各移行のフォルダーのタイムスタンプと比較されます。正確に 1 つの移行が一致する場合、それが使用されます。それ以外の場合は、ハッシュによって結合が解除され、最後の手段として、同じハッシュを使用した移行が使用されます。
バックアップファイルの構造
backups/cadcamfun-20261015-034512.123-6f1c2d4e-8a90-4b7c-9e21-3f5a6b7c8d9e-pre-migrate.db
└ prefix ┘ └ UTC timestamp ┘ └ random UUID ┘ - プレフィックスは、データベース ファイル名に接頭辞を付けないものです。
.db、ダッシュが続きます。 - The timestamp is UTC with millisecond precision.最新の既存のバックアップのタイムスタンプが同じかそれ以降の場合、新しいバックアップはそれから 1 ミリ秒後に設定されるため、字句の順序は常に作成順序と等しくなります。
- スナップショットの書き込み先は、
<name>.partialとVACUUM INTO、その後、名前が変更されました。クラッシュが発生すると、最大でも.partialファイル。finallyブロック。 - バックアップが成功すると、正確なパターンに一致する 5 つの最新ファイルのみが保持されます。他の名前でディレクトリにコピーしたファイルは削除されません。
VACUUM INTOwAL 兄弟のないコンパクトな自己完結型データベースを作成するため、バックアップを直接開くことができます。
DATA="/absolute/path/to/data"
cp "$DATA/cadcamfun.db" "$DATA/cadcamfun.db.broken" # keep the failed state
rm -f "$DATA/cadcamfun.db-wal" "$DATA/cadcamfun.db-shm"
cp "$DATA/backups/cadcamfun-<timestamp>-<uuid>-pre-migrate.db" "$DATA/cadcamfun.db"
sqlite3 -readonly "$DATA/cadcamfun.db" 'PRAGMA quick_check;' # expect: ok 復元するとスキーマとデータがロールバックされます
復元されたバックアップはアップグレード直前の状態です。そのアップグレード後に行われた編集は含まれていないため、次回の起動時に同じ移行が再度試行されます。再起動する前に、障害の原因を解決してください。
コアテーブルの形状
これらは、デスクトップ エディタ、履歴、およびカーネルが最も頻繁に読み取るテーブルです。列名は SQL 名です。 Drizzle はそれらを同じキーの下で公開します。
| テーブル | コラム |
|---|---|
Project | id, name, description, createdAt, updatedAt, ownerId, organizationId, isPublic |
Drawing | id, name, description, data, thumbnail, createdAt, updatedAt, projectId |
Component | id, name, description, data, thumbnail, type, isPublic, createdAt, updatedAt, projectId |
Toolpath | id, name, description, data, type, operationType, gcode, thumbnail, isPublic, projectId, createdAt, updatedAt, createdBy, drawingId, materialId, toolId, machineConfigId |
GitRepo | projectId, head, remote, createdAt |
GitObject | projectId, oid, type, body |
GitRef | projectId, name, oid |
PullRequest | id, projectId, number, title, body, head, base, state, authorId, authorKind, mergeCommit, remoteNumber, createdAt, updatedAt |
WorkflowRun | id, projectId, workflow, event, ref, sha, status, events, startedAt, finishedAt |
MeshAsset | id, projectId, kind, inputs, stats, mesh, createdAt |
CadOperationLog | id, projectId, operation, meshAssetId, outcome, durationMs, inputs, error, traceId, spanId, createdAt |
LocalKeyValue | key, value, updatedAt |
プロジェクトを削除すると、その図面、コンポーネント、ツールパス、履歴オブジェクトと参照、プル リクエスト、ワークフロー実行、メッシュ アセット、操作ログが削除されます。バージョン行とコメント行は、親リソースからカスケードされます。あ CadOperationLog 行はメッシュ アセットの削除後も存続します。 meshAssetId null に設定します。材料、ツール、および機械構成へのツールパス リンクにはカスケードがないため、ツールパスが参照している材料をポイントしている間は参照材料を削除できません。
完全なテーブルカタログ
スキーマは 60 テーブルを定義します。以下のリストは、Drizzle テーブル定義から作成されたものです: SQL 名、エクスポートされた定数、列数、宣言されたインデックスの数、および削除ルールを含む外部キー。 「アクションなし」は SQLite のデフォルトです。参照行が存在する間は削除は失敗します (アプリケーション接続では外部キーが適用されます)。
| SQLテーブル | 輸出 | コル | いずみ | 外部キー |
|---|---|---|---|---|
Account | accountTable | 13 | 1 | userId → ユーザー(カスケード) |
ActivityLog | activityLogTable | 7 | 0 | userId → ユーザー (アクションなし) |
AIAnalysisLog | aIAnalysisLogTable | 7 | 0 | userId → ユーザー (アクションなし) |
ApiKey | apiKeyTable | 7 | 1 | userId → ユーザー(カスケード) |
authenticators | authenticatorTable | 8 | 0 | userId → ユーザー(カスケード) |
CadOperationLog | cadOperationLogTable | 11 | 1 | projectId → プロジェクト(カスケード)meshAssetId → MeshAsset (null を設定) |
Component | componentTable | 10 | 0 | projectId → プロジェクト(カスケード) |
ComponentComment | componentCommentTable | 6 | 0 | componentId → コンポーネント(カスケード)userId → ユーザー (アクションなし) |
ComponentVersion | componentVersionTable | 6 | 0 | componentId → コンポーネント(カスケード)userId → ユーザー (アクションなし) |
Conversation | conversationTable | 6 | 0 | organizationId → 組織化(カスケード) |
ConversationParticipant | conversationParticipantTable | 5 | 1 | userId → ユーザー(カスケード)conversationId → 会話(カスケード) |
CostSettings | costSettingsTable | 11 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
Drawing | drawingTable | 8 | 0 | projectId → プロジェクト(カスケード) |
DrawingVersion | drawingVersionTable | 5 | 0 | drawingId → 描画(カスケード) |
FileUpload | fileUploadTable | 12 | 0 | — |
GitObject | gitObjectTable | 4 | 1 | projectId → プロジェクト(カスケード) |
GitRef | gitRefTable | 3 | 1 | projectId → プロジェクト(カスケード) |
GitRepo | gitRepoTable | 4 | 0 | projectId → プロジェクト(カスケード) |
LibraryItem | libraryItemTable | 14 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
LocalKeyValue | localKeyValueTable | 3 | 0 | — |
MachineConfig | machineConfigTable | 9 | 0 | ownerId → ユーザー (アクションなし) |
MarketplaceCategory | marketplaceCategoryTable | 10 | 1 | parentId → MarketplaceCategory (アクションなし) |
MarketplaceCollection | marketplaceCollectionTable | 10 | 1 | sellerId → マーケットプレイス販売者 (アクションなし) |
MarketplaceCollectionItem | marketplaceCollectionItemTable | 5 | 1 | collectionId → マーケットプレイスコレクション (カスケード)itemId → マーケットプレイスアイテム (カスケード) |
MarketplaceItem | marketplaceItemTable | 30 | 5 | sellerId → マーケットプレイス販売者 (アクションなし) |
MarketplaceOrder | marketplaceOrderTable | 23 | 4 | buyerId → ユーザー (アクションなし)sellerId → マーケットプレイス販売者 (アクションなし)itemId → MarketplaceItem (アクションなし) |
MarketplacePayout | marketplacePayoutTable | 12 | 1 | sellerId → マーケットプレイス販売者 (アクションなし) |
MarketplaceReview | marketplaceReviewTable | 12 | 2 | itemId → MarketplaceItem (アクションなし)buyerId → ユーザー (アクションなし)orderId → マーケットプレイス注文 (アクションなし) |
MarketplaceSeller | marketplaceSellerTable | 19 | 0 | userId → ユーザー (アクションなし) |
MarketplaceWishlist | marketplaceWishlistTable | 4 | 1 | userId → ユーザー(カスケード)itemId → マーケットプレイスアイテム (カスケード) |
Material | materialTable | 9 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
MaterialCost | materialCostTable | 10 | 0 | materialId → マテリアル(カスケード)ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
MCPServerConfig | mCPServerConfigTable | 11 | 1 | — |
MeshAsset | meshAssetTable | 7 | 1 | projectId → プロジェクト(カスケード) |
Message | messageTable | 8 | 1 | senderId → ユーザー(カスケード)conversationId → 会話(カスケード)fileId → FileUpload (nullを設定) |
Notification | notificationTable | 12 | 0 | userId → ユーザー(カスケード)organizationId → 組織化(カスケード)projectId → プロジェクト(カスケード)componentId → コンポーネント(カスケード)messageId →メッセージ(カスケード) |
OperationCost | operationCostTable | 10 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
Organization | organizationTable | 5 | 0 | — |
OrganizationInvitation | organizationInvitationTable | 7 | 0 | organizationId → 組織化(カスケード) |
passkey | passkeyTable | 11 | 0 | userId → ユーザー(カスケード) |
PluginConfiguration | pluginConfigurationTable | 2 | 0 | pluginId → PluginRegistryEntry (カスケード) |
PluginRegistryEntry | pluginRegistryEntryTable | 9 | 0 | — |
ProductionCostEstimate | productionCostEstimateTable | 17 | 0 | toolpathId → ツールパス(カスケード)operationCostId → 運用コスト (アクションなし)ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
Project | projectTable | 8 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
PullRequest | pullRequestTable | 14 | 1 | projectId → プロジェクト(カスケード)authorId → ユーザー (アクションなし) |
PullRequestReview | pullRequestReviewTable | 8 | 1 | pullRequestId →プルリクエスト(カスケード)reviewerId → ユーザー (アクションなし) |
PushSubscription | pushSubscriptionTable | 6 | 0 | userId → ユーザー(カスケード) |
Session | sessionTable | 8 | 0 | userId → ユーザー(カスケード) |
Subscription | subscriptionTable | 24 | 3 | referenceId → ユーザー(カスケード) |
Token | tokenTable | 8 | 2 | userId → ユーザー(カスケード) |
Tool | toolTable | 17 | 0 | ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
Toolpath | toolpathTable | 17 | 0 | projectId → プロジェクト(カスケード)createdBy → ユーザー (アクションなし)drawingId → 描画(アクションなし)materialId → マテリアル(アクションなし)toolId → ツール(アクションなし)machineConfigId → MachineConfig (アクションなし) |
ToolpathComment | toolpathCommentTable | 6 | 0 | toolpathId → ツールパス(カスケード)userId → ユーザー (アクションなし) |
ToolpathVersion | toolpathVersionTable | 7 | 0 | toolpathId → ツールパス(カスケード)userId → ユーザー (アクションなし) |
ToolWearCost | toolWearCostTable | 10 | 0 | toolId → ツール(カスケード)ownerId → ユーザー (アクションなし)organizationId →組織化(何もしない) |
twoFactor | twoFactorTable | 4 | 0 | userId → ユーザー(カスケード) |
User | userTable | 17 | 0 | — |
UserOrganization | userOrganizationTable | 5 | 1 | userId → ユーザー(カスケード)organizationId → 組織化(カスケード) |
verification_tokens | verificationTokenTable | 3 | 1 | — |
WorkflowRun | workflowRunTable | 10 | 1 | projectId → プロジェクト(カスケード) |
安全に書く
- Drizzle トランザクション内の複数のステートメントの変更を 1 つのトランザクション内にラップします
query電話:database.query("op", (db) => db.transaction((tx) => ...))。別query呼び出しは別のトランザクションです。 - 各クエリに安定したドット付きの操作名を付けます (
projects.list,toolpaths.update)。それが唯一のコンテキストですDatabaseError原因以外にも原因があり、それがログに表示されます。 - ID を生成する
newId()(uUID) または列のデフォルトに任せます。名前から ID を構築しないでください。 - JSON 列を読み取るときに検証します。宣言された TypeScript 型は SQLite によってチェックされません。
- 使用
LocalKeyValue行を直接書き込むのではなく、サイドカーのキーと値のサービスを通じて行うため、キーと値の検証が適用されます。
import { Database, schema } from "@cadcamfun/db"
import { eq } from "drizzle-orm"
import { Effect } from "effect"
const renameProject = (id: string, name: string) => Effect.gen(function* () {
const database = yield* Database
return yield* database.query("projects.rename", (db) =>
db.transaction((tx) => {
const updated = tx.update(schema.projectTable)
.set({ name, updatedAt: new Date() })
.where(eq(schema.projectTable.id, id))
.returning().all()
if (updated.length === 0) throw new Error(`project ${id} not found`)
return updated[0]
}))
}) 明示的な ID を持つ行を作成するには、次を使用します。 schema.newId(); @cadcamfun/db スキーマモジュールを schema トップレベルの名前としてではなく、名前空間。