feat(FN-193): rewrite migrate.ts with hash-based runner (+4 more)
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Some checks failed
Sync dev → Gitea / Mirror dev to Gitea (push) Has been cancelled
Commits merged: - docs(FN-193): update migration runner docs - fix(FN-193): lint/typecheck fixes - test(FN-193): rewrite migrate tests for runMigrations - feat(FN-193): make baseline migration idempotent (IF NOT EXISTS) - feat(FN-193): rewrite migrate.ts with hash-based runner Files changed: apps/api/drizzle/0000_brief_guardian.sql | 364 ++++++++++++------------ apps/api/src/database/__tests__/migrate.spec.ts | 354 ++++++++++++----------- apps/api/src/database/migrate.ts | 173 +++++------ docs/INDEX.md | 17 +- 4 files changed, 466 insertions(+), 442 deletions(-) Fusion-Task-Id: FN-193
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
CREATE TABLE "accounts" (
|
||||
CREATE TABLE IF NOT EXISTS "accounts" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"account_id" text NOT NULL,
|
||||
@@ -14,7 +14,7 @@ CREATE TABLE "accounts" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "brands" (
|
||||
CREATE TABLE IF NOT EXISTS "brands" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"slug" varchar(100) NOT NULL,
|
||||
@@ -24,7 +24,7 @@ CREATE TABLE "brands" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "catalog_vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "catalog_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"source" varchar(20) DEFAULT 'pl24' NOT NULL,
|
||||
"service_name" varchar(100) NOT NULL,
|
||||
@@ -45,7 +45,7 @@ CREATE TABLE "catalog_vehicles" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "categories" (
|
||||
CREATE TABLE IF NOT EXISTS "categories" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vehicle_id" uuid,
|
||||
"catalog_vehicle_id" uuid,
|
||||
@@ -60,7 +60,7 @@ CREATE TABLE "categories" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "changelog_entries" (
|
||||
CREATE TABLE IF NOT EXISTS "changelog_entries" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"stage" varchar(10) DEFAULT 'prod' NOT NULL,
|
||||
"title" varchar(255) NOT NULL,
|
||||
@@ -70,7 +70,7 @@ CREATE TABLE "changelog_entries" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_category_translations" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_category_translations" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"original_name" varchar(500) NOT NULL,
|
||||
"translated_name" varchar(500) NOT NULL,
|
||||
@@ -79,7 +79,7 @@ CREATE TABLE "emex_category_translations" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "oem_code_copies" (
|
||||
CREATE TABLE IF NOT EXISTS "oem_code_copies" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
@@ -89,7 +89,7 @@ CREATE TABLE "oem_code_copies" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "parts" (
|
||||
CREATE TABLE IF NOT EXISTS "parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vehicle_id" uuid,
|
||||
"catalog_vehicle_id" uuid,
|
||||
@@ -111,7 +111,7 @@ CREATE TABLE "parts" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "password_reset_tokens" (
|
||||
CREATE TABLE IF NOT EXISTS "password_reset_tokens" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
@@ -120,7 +120,7 @@ CREATE TABLE "password_reset_tokens" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "payments" (
|
||||
CREATE TABLE IF NOT EXISTS "payments" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"subscription_id" uuid NOT NULL,
|
||||
@@ -135,7 +135,7 @@ CREATE TABLE "payments" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "plans" (
|
||||
CREATE TABLE IF NOT EXISTS "plans" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(100) NOT NULL,
|
||||
"brand_count" integer NOT NULL,
|
||||
@@ -146,7 +146,7 @@ CREATE TABLE "plans" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "query_logs" (
|
||||
CREATE TABLE IF NOT EXISTS "query_logs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
@@ -158,7 +158,7 @@ CREATE TABLE "query_logs" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "referrals" (
|
||||
CREATE TABLE IF NOT EXISTS "referrals" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"referrer_id" uuid NOT NULL,
|
||||
"referred_id" uuid NOT NULL,
|
||||
@@ -166,7 +166,7 @@ CREATE TABLE "referrals" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "schema_pics" (
|
||||
CREATE TABLE IF NOT EXISTS "schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"category_id" uuid NOT NULL,
|
||||
"image_url" text NOT NULL,
|
||||
@@ -175,7 +175,7 @@ CREATE TABLE "schema_pics" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "sessions" (
|
||||
CREATE TABLE IF NOT EXISTS "sessions" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"token" text NOT NULL,
|
||||
@@ -186,7 +186,7 @@ CREATE TABLE "sessions" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_brands" (
|
||||
CREATE TABLE IF NOT EXISTS "user_brands" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"subscription_id" uuid NOT NULL,
|
||||
@@ -194,7 +194,7 @@ CREATE TABLE "user_brands" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_subscriptions" (
|
||||
CREATE TABLE IF NOT EXISTS "user_subscriptions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"plan_id" uuid NOT NULL,
|
||||
@@ -207,7 +207,7 @@ CREATE TABLE "user_subscriptions" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "user_vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "user_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"user_id" uuid NOT NULL,
|
||||
"vehicle_id" uuid NOT NULL,
|
||||
@@ -215,7 +215,7 @@ CREATE TABLE "user_vehicles" (
|
||||
"last_accessed_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "users" (
|
||||
CREATE TABLE IF NOT EXISTS "users" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"name" varchar(255) NOT NULL,
|
||||
"email" varchar(255) NOT NULL,
|
||||
@@ -228,7 +228,7 @@ CREATE TABLE "users" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"brand_id" uuid,
|
||||
@@ -245,7 +245,7 @@ CREATE TABLE "vehicles" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "verifications" (
|
||||
CREATE TABLE IF NOT EXISTS "verifications" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"identifier" text NOT NULL,
|
||||
"value" text NOT NULL,
|
||||
@@ -254,7 +254,7 @@ CREATE TABLE "verifications" (
|
||||
"updated_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_catalogs" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_catalogs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" varchar(100) NOT NULL,
|
||||
"code" varchar(50),
|
||||
@@ -264,7 +264,7 @@ CREATE TABLE "emex_catalogs" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_groups" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_part_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_catalog_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
@@ -278,7 +278,7 @@ CREATE TABLE "emex_part_groups" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_images" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_part_images" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_part_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
@@ -286,7 +286,7 @@ CREATE TABLE "emex_part_images" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_part_numbers" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_part_numbers" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_part_id" uuid,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
@@ -294,7 +294,7 @@ CREATE TABLE "emex_part_numbers" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_parts" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_catalog_id" uuid,
|
||||
"group_id" uuid,
|
||||
@@ -310,7 +310,7 @@ CREATE TABLE "emex_parts" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_schema_pics" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"group_id" uuid,
|
||||
"image_url" text,
|
||||
@@ -323,7 +323,7 @@ CREATE TABLE "emex_schema_pics" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_scrape_sessions" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_scrape_sessions" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"status" varchar(20) DEFAULT 'pending' NOT NULL,
|
||||
@@ -335,7 +335,7 @@ CREATE TABLE "emex_scrape_sessions" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_group_links" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_vehicle_group_links" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid NOT NULL,
|
||||
"emex_group_id" uuid NOT NULL,
|
||||
@@ -343,7 +343,7 @@ CREATE TABLE "emex_vehicle_group_links" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_part_links" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_vehicle_part_links" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid NOT NULL,
|
||||
"emex_part_id" uuid NOT NULL,
|
||||
@@ -353,14 +353,14 @@ CREATE TABLE "emex_vehicle_part_links" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicle_vins" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_vehicle_vins" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"emex_vehicle_id" uuid,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "emex_vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "emex_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"vehicle_id" varchar(100) NOT NULL,
|
||||
@@ -381,7 +381,7 @@ CREATE TABLE "emex_vehicles" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_part_groups" (
|
||||
CREATE TABLE IF NOT EXISTS "pcat_part_groups" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"catalog_id" text NOT NULL,
|
||||
"parent_id" text,
|
||||
@@ -392,7 +392,7 @@ CREATE TABLE "pcat_part_groups" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_parts" (
|
||||
CREATE TABLE IF NOT EXISTS "pcat_parts" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"name" text NOT NULL,
|
||||
"notice" text,
|
||||
@@ -400,7 +400,7 @@ CREATE TABLE "pcat_parts" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_schema_pics" (
|
||||
CREATE TABLE IF NOT EXISTS "pcat_schema_pics" (
|
||||
"id" serial PRIMARY KEY NOT NULL,
|
||||
"group_id" text NOT NULL,
|
||||
"car_id" text NOT NULL,
|
||||
@@ -410,7 +410,7 @@ CREATE TABLE "pcat_schema_pics" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pcat_vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "pcat_vehicles" (
|
||||
"id" text PRIMARY KEY NOT NULL,
|
||||
"catalog_id" text NOT NULL,
|
||||
"vin" varchar(17),
|
||||
@@ -421,7 +421,7 @@ CREATE TABLE "pcat_vehicles" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_catalogs" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_catalogs" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" varchar(100) NOT NULL,
|
||||
"brand_name" varchar(100) NOT NULL,
|
||||
@@ -429,7 +429,7 @@ CREATE TABLE "pl24_catalogs" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_groups" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_part_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
@@ -439,7 +439,7 @@ CREATE TABLE "pl24_part_groups" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_images" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_part_images" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_part_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
@@ -447,7 +447,7 @@ CREATE TABLE "pl24_part_images" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_part_numbers" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_part_numbers" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_part_id" uuid,
|
||||
"oem_code" varchar(100) NOT NULL,
|
||||
@@ -455,7 +455,7 @@ CREATE TABLE "pl24_part_numbers" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_parts" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"group_id" uuid,
|
||||
@@ -469,7 +469,7 @@ CREATE TABLE "pl24_parts" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_schema_pics" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_schema_pics" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"group_id" uuid,
|
||||
"image_url" text NOT NULL,
|
||||
@@ -480,7 +480,7 @@ CREATE TABLE "pl24_schema_pics" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_groups" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_vehicle_groups" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"group_id" varchar(100) NOT NULL,
|
||||
@@ -489,7 +489,7 @@ CREATE TABLE "pl24_vehicle_groups" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_parts" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_vehicle_parts" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"pl24_part_id" uuid,
|
||||
@@ -497,14 +497,14 @@ CREATE TABLE "pl24_vehicle_parts" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicle_vins" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_vehicle_vins" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"pl24_vehicle_id" uuid,
|
||||
"vin" varchar(17) NOT NULL,
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
CREATE TABLE "pl24_vehicles" (
|
||||
CREATE TABLE IF NOT EXISTS "pl24_vehicles" (
|
||||
"id" uuid PRIMARY KEY DEFAULT gen_random_uuid() NOT NULL,
|
||||
"catalog_id" uuid,
|
||||
"vehicle_id" varchar(100) NOT NULL,
|
||||
@@ -520,139 +520,139 @@ CREATE TABLE "pl24_vehicles" (
|
||||
"created_at" timestamp with time zone DEFAULT now() NOT NULL
|
||||
);
|
||||
--> statement-breakpoint
|
||||
ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "catalog_vehicles" ADD CONSTRAINT "catalog_vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "categories" ADD CONSTRAINT "categories_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "oem_code_copies" ADD CONSTRAINT "oem_code_copies_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "parts" ADD CONSTRAINT "parts_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "payments" ADD CONSTRAINT "payments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "payments" ADD CONSTRAINT "payments_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referrer_id_users_id_fk" FOREIGN KEY ("referrer_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referred_id_users_id_fk" FOREIGN KEY ("referred_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "schema_pics" ADD CONSTRAINT "schema_pics_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_plan_id_plans_id_fk" FOREIGN KEY ("plan_id") REFERENCES "public"."plans"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_groups" ADD CONSTRAINT "emex_part_groups_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_images" ADD CONSTRAINT "emex_part_images_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_part_numbers" ADD CONSTRAINT "emex_part_numbers_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_schema_pics" ADD CONSTRAINT "emex_schema_pics_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicle_vins" ADD CONSTRAINT "emex_vehicle_vins_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "emex_vehicles" ADD CONSTRAINT "emex_vehicles_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_groups" ADD CONSTRAINT "pl24_part_groups_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_images" ADD CONSTRAINT "pl24_part_images_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_part_numbers" ADD CONSTRAINT "pl24_part_numbers_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_schema_pics" ADD CONSTRAINT "pl24_schema_pics_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_groups" ADD CONSTRAINT "pl24_vehicle_groups_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicle_vins" ADD CONSTRAINT "pl24_vehicle_vins_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
ALTER TABLE "pl24_vehicles" ADD CONSTRAINT "pl24_vehicles_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action;--> statement-breakpoint
|
||||
CREATE INDEX "accounts_user_id_idx" ON "accounts" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "brands_slug_idx" ON "brands" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "catalog_vehicles_source_svc_vid_idx" ON "catalog_vehicles" USING btree ("source","service_name","service_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "catalog_vehicles_brand_name_idx" ON "catalog_vehicles" USING btree ("brand_name");--> statement-breakpoint
|
||||
CREATE INDEX "catalog_vehicles_service_name_idx" ON "catalog_vehicles" USING btree ("service_name");--> statement-breakpoint
|
||||
CREATE INDEX "categories_vehicle_id_idx" ON "categories" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "categories_catalog_vehicle_id_idx" ON "categories" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "categories_parent_id_idx" ON "categories" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "categories_vehicle_name_source_idx" ON "categories" USING btree ("vehicle_id","catalog_vehicle_id","name","source");--> statement-breakpoint
|
||||
CREATE INDEX "changelog_entries_published_at_idx" ON "changelog_entries" USING btree ("published_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_translations_original_name_idx" ON "emex_category_translations" USING btree ("original_name");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_user_id_created_at_idx" ON "oem_code_copies" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_oem_code_idx" ON "oem_code_copies" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "oem_code_copies_created_at_idx" ON "oem_code_copies" USING btree ("created_at");--> statement-breakpoint
|
||||
CREATE INDEX "parts_vehicle_id_idx" ON "parts" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_catalog_vehicle_id_idx" ON "parts" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_category_id_idx" ON "parts" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE INDEX "parts_oem_code_idx" ON "parts" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "password_reset_tokens_token_idx" ON "password_reset_tokens" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX "payments_user_id_idx" ON "payments" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "payments_status_idx" ON "payments" USING btree ("status");--> statement-breakpoint
|
||||
CREATE INDEX "query_logs_user_id_created_at_idx" ON "query_logs" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX "query_logs_vin_idx" ON "query_logs" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "referrals_referrer_id_idx" ON "referrals" USING btree ("referrer_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "referrals_referred_id_idx" ON "referrals" USING btree ("referred_id");--> statement-breakpoint
|
||||
CREATE INDEX "schema_pics_category_id_idx" ON "schema_pics" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "sessions_token_idx" ON "sessions" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX "user_brands_user_id_idx" ON "user_brands" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "user_brands_unique_idx" ON "user_brands" USING btree ("user_id","subscription_id","brand_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_subscriptions_user_id_idx" ON "user_subscriptions" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_subscriptions_status_idx" ON "user_subscriptions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "user_vehicles_user_vehicle_idx" ON "user_vehicles" USING btree ("user_id","vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "user_vehicles_user_id_idx" ON "user_vehicles" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "users_email_idx" ON "users" USING btree ("email");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "users_referral_code_idx" ON "users" USING btree ("referral_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "vehicles_vin_unique_idx" ON "vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "verifications_identifier_idx" ON "verifications" USING btree ("identifier");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_catalogs_catalog_id_idx" ON "emex_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_catalogs_code_idx" ON "emex_catalogs" USING btree ("code");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_groups_catalog_id_idx" ON "emex_part_groups" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_groups_group_id_idx" ON "emex_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_part_groups_catalog_group_idx" ON "emex_part_groups" USING btree ("emex_catalog_id","group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_images_part_id_idx" ON "emex_part_images" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_numbers_part_id_idx" ON "emex_part_numbers" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_part_numbers_oem_code_idx" ON "emex_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_catalog_id_idx" ON "emex_parts" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_group_id_idx" ON "emex_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_part_number_idx" ON "emex_parts" USING btree ("part_number");--> statement-breakpoint
|
||||
CREATE INDEX "emex_parts_oem_number_idx" ON "emex_parts" USING btree ("oem_number");--> statement-breakpoint
|
||||
CREATE INDEX "emex_schema_pics_group_id_idx" ON "emex_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_schema_pics_group_path_idx" ON "emex_schema_pics" USING btree ("group_id","local_path");--> statement-breakpoint
|
||||
CREATE INDEX "emex_scrape_sessions_vin_idx" ON "emex_scrape_sessions" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "emex_scrape_sessions_status_idx" ON "emex_scrape_sessions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vgl_vehicle_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_vehicle_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vgl_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vpl_vehicle_part_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_vehicle_id","emex_part_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vpl_part_idx" ON "emex_vehicle_part_links" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vpl_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vehicle_vins_vin_idx" ON "emex_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicle_vins_vehicle_id_idx" ON "emex_vehicle_vins" USING btree ("emex_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "emex_vehicles_vehicle_id_idx" ON "emex_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_catalog_id_idx" ON "emex_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_name_idx" ON "emex_vehicles" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX "emex_vehicles_source_id_idx" ON "emex_vehicles" USING btree ("source_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_part_groups_catalog_id_idx" ON "pcat_part_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_part_groups_parent_id_idx" ON "pcat_part_groups" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_parts_name_idx" ON "pcat_parts" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_schema_pics_group_car_idx" ON "pcat_schema_pics" USING btree ("group_id","car_id");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_vehicles_vin_idx" ON "pcat_vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "pcat_vehicles_catalog_id_idx" ON "pcat_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "pl24_catalogs_catalog_id_idx" ON "pl24_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_groups_vehicle_id_idx" ON "pl24_part_groups" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_groups_group_id_idx" ON "pl24_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_images_part_id_idx" ON "pl24_part_images" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_numbers_part_id_idx" ON "pl24_part_numbers" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_part_numbers_oem_code_idx" ON "pl24_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_parts_vehicle_id_idx" ON "pl24_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_parts_group_id_idx" ON "pl24_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_schema_pics_group_id_idx" ON "pl24_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_groups_catalog_id_idx" ON "pl24_vehicle_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_groups_group_id_idx" ON "pl24_vehicle_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_parts_vehicle_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_parts_part_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_vins_vin_idx" ON "pl24_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicle_vins_vehicle_id_idx" ON "pl24_vehicle_vins" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX "pl24_vehicles_vehicle_id_idx" ON "pl24_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX "pl24_vehicles_catalog_id_idx" ON "pl24_vehicles" USING btree ("catalog_id");
|
||||
DO $$ BEGIN ALTER TABLE "accounts" ADD CONSTRAINT "accounts_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "catalog_vehicles" ADD CONSTRAINT "catalog_vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "categories" ADD CONSTRAINT "categories_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "categories" ADD CONSTRAINT "categories_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "oem_code_copies" ADD CONSTRAINT "oem_code_copies_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "parts" ADD CONSTRAINT "parts_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "parts" ADD CONSTRAINT "parts_catalog_vehicle_id_catalog_vehicles_id_fk" FOREIGN KEY ("catalog_vehicle_id") REFERENCES "public"."catalog_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "parts" ADD CONSTRAINT "parts_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "password_reset_tokens" ADD CONSTRAINT "password_reset_tokens_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "payments" ADD CONSTRAINT "payments_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "payments" ADD CONSTRAINT "payments_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "query_logs" ADD CONSTRAINT "query_logs_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referrer_id_users_id_fk" FOREIGN KEY ("referrer_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "referrals" ADD CONSTRAINT "referrals_referred_id_users_id_fk" FOREIGN KEY ("referred_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "schema_pics" ADD CONSTRAINT "schema_pics_category_id_categories_id_fk" FOREIGN KEY ("category_id") REFERENCES "public"."categories"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "sessions" ADD CONSTRAINT "sessions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_subscription_id_user_subscriptions_id_fk" FOREIGN KEY ("subscription_id") REFERENCES "public"."user_subscriptions"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_brands" ADD CONSTRAINT "user_brands_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_subscriptions" ADD CONSTRAINT "user_subscriptions_plan_id_plans_id_fk" FOREIGN KEY ("plan_id") REFERENCES "public"."plans"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_user_id_users_id_fk" FOREIGN KEY ("user_id") REFERENCES "public"."users"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "user_vehicles" ADD CONSTRAINT "user_vehicles_vehicle_id_vehicles_id_fk" FOREIGN KEY ("vehicle_id") REFERENCES "public"."vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "vehicles" ADD CONSTRAINT "vehicles_brand_id_brands_id_fk" FOREIGN KEY ("brand_id") REFERENCES "public"."brands"("id") ON DELETE no action ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_part_groups" ADD CONSTRAINT "emex_part_groups_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_part_images" ADD CONSTRAINT "emex_part_images_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_part_numbers" ADD CONSTRAINT "emex_part_numbers_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_emex_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("emex_catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_parts" ADD CONSTRAINT "emex_parts_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_schema_pics" ADD CONSTRAINT "emex_schema_pics_group_id_emex_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_group_links" ADD CONSTRAINT "emex_vehicle_group_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_part_id_emex_parts_id_fk" FOREIGN KEY ("emex_part_id") REFERENCES "public"."emex_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_part_links" ADD CONSTRAINT "emex_vehicle_part_links_emex_group_id_emex_part_groups_id_fk" FOREIGN KEY ("emex_group_id") REFERENCES "public"."emex_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicle_vins" ADD CONSTRAINT "emex_vehicle_vins_emex_vehicle_id_emex_vehicles_id_fk" FOREIGN KEY ("emex_vehicle_id") REFERENCES "public"."emex_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "emex_vehicles" ADD CONSTRAINT "emex_vehicles_catalog_id_emex_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."emex_catalogs"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_part_groups" ADD CONSTRAINT "pl24_part_groups_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_part_images" ADD CONSTRAINT "pl24_part_images_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_part_numbers" ADD CONSTRAINT "pl24_part_numbers_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_parts" ADD CONSTRAINT "pl24_parts_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_schema_pics" ADD CONSTRAINT "pl24_schema_pics_group_id_pl24_part_groups_id_fk" FOREIGN KEY ("group_id") REFERENCES "public"."pl24_part_groups"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_vehicle_groups" ADD CONSTRAINT "pl24_vehicle_groups_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_vehicle_parts" ADD CONSTRAINT "pl24_vehicle_parts_pl24_part_id_pl24_parts_id_fk" FOREIGN KEY ("pl24_part_id") REFERENCES "public"."pl24_parts"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_vehicle_vins" ADD CONSTRAINT "pl24_vehicle_vins_pl24_vehicle_id_pl24_vehicles_id_fk" FOREIGN KEY ("pl24_vehicle_id") REFERENCES "public"."pl24_vehicles"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
DO $$ BEGIN ALTER TABLE "pl24_vehicles" ADD CONSTRAINT "pl24_vehicles_catalog_id_pl24_catalogs_id_fk" FOREIGN KEY ("catalog_id") REFERENCES "public"."pl24_catalogs"("id") ON DELETE cascade ON UPDATE no action; EXCEPTION WHEN duplicate_object THEN null; END $$;--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "accounts_user_id_idx" ON "accounts" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "brands_slug_idx" ON "brands" USING btree ("slug");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "catalog_vehicles_source_svc_vid_idx" ON "catalog_vehicles" USING btree ("source","service_name","service_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "catalog_vehicles_brand_name_idx" ON "catalog_vehicles" USING btree ("brand_name");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "catalog_vehicles_service_name_idx" ON "catalog_vehicles" USING btree ("service_name");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "categories_vehicle_id_idx" ON "categories" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "categories_catalog_vehicle_id_idx" ON "categories" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "categories_parent_id_idx" ON "categories" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "categories_vehicle_name_source_idx" ON "categories" USING btree ("vehicle_id","catalog_vehicle_id","name","source");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "changelog_entries_published_at_idx" ON "changelog_entries" USING btree ("published_at");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_translations_original_name_idx" ON "emex_category_translations" USING btree ("original_name");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "oem_code_copies_user_id_created_at_idx" ON "oem_code_copies" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "oem_code_copies_oem_code_idx" ON "oem_code_copies" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "oem_code_copies_created_at_idx" ON "oem_code_copies" USING btree ("created_at");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "parts_vehicle_id_idx" ON "parts" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "parts_catalog_vehicle_id_idx" ON "parts" USING btree ("catalog_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "parts_category_id_idx" ON "parts" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "parts_oem_code_idx" ON "parts" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "password_reset_tokens_token_idx" ON "password_reset_tokens" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "payments_user_id_idx" ON "payments" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "payments_status_idx" ON "payments" USING btree ("status");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "query_logs_user_id_created_at_idx" ON "query_logs" USING btree ("user_id","created_at");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "query_logs_vin_idx" ON "query_logs" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "referrals_referrer_id_idx" ON "referrals" USING btree ("referrer_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "referrals_referred_id_idx" ON "referrals" USING btree ("referred_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "schema_pics_category_id_idx" ON "schema_pics" USING btree ("category_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "sessions_token_idx" ON "sessions" USING btree ("token");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_brands_user_id_idx" ON "user_brands" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "user_brands_unique_idx" ON "user_brands" USING btree ("user_id","subscription_id","brand_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_subscriptions_user_id_idx" ON "user_subscriptions" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_subscriptions_status_idx" ON "user_subscriptions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "user_vehicles_user_vehicle_idx" ON "user_vehicles" USING btree ("user_id","vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "user_vehicles_user_id_idx" ON "user_vehicles" USING btree ("user_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "users_email_idx" ON "users" USING btree ("email");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "users_referral_code_idx" ON "users" USING btree ("referral_code");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "vehicles_vin_unique_idx" ON "vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "verifications_identifier_idx" ON "verifications" USING btree ("identifier");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_catalogs_catalog_id_idx" ON "emex_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_catalogs_code_idx" ON "emex_catalogs" USING btree ("code");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_part_groups_catalog_id_idx" ON "emex_part_groups" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_part_groups_group_id_idx" ON "emex_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_part_groups_catalog_group_idx" ON "emex_part_groups" USING btree ("emex_catalog_id","group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_part_images_part_id_idx" ON "emex_part_images" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_part_numbers_part_id_idx" ON "emex_part_numbers" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_part_numbers_oem_code_idx" ON "emex_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_parts_catalog_id_idx" ON "emex_parts" USING btree ("emex_catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_parts_group_id_idx" ON "emex_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_parts_part_number_idx" ON "emex_parts" USING btree ("part_number");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_parts_oem_number_idx" ON "emex_parts" USING btree ("oem_number");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_schema_pics_group_id_idx" ON "emex_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_schema_pics_group_path_idx" ON "emex_schema_pics" USING btree ("group_id","local_path");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_scrape_sessions_vin_idx" ON "emex_scrape_sessions" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_scrape_sessions_status_idx" ON "emex_scrape_sessions" USING btree ("status");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_vgl_vehicle_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_vehicle_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vgl_group_idx" ON "emex_vehicle_group_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_vpl_vehicle_part_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_vehicle_id","emex_part_id","emex_group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vpl_part_idx" ON "emex_vehicle_part_links" USING btree ("emex_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vpl_group_idx" ON "emex_vehicle_part_links" USING btree ("emex_group_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_vehicle_vins_vin_idx" ON "emex_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vehicle_vins_vehicle_id_idx" ON "emex_vehicle_vins" USING btree ("emex_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "emex_vehicles_vehicle_id_idx" ON "emex_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vehicles_catalog_id_idx" ON "emex_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vehicles_name_idx" ON "emex_vehicles" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "emex_vehicles_source_id_idx" ON "emex_vehicles" USING btree ("source_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_part_groups_catalog_id_idx" ON "pcat_part_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_part_groups_parent_id_idx" ON "pcat_part_groups" USING btree ("parent_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_parts_name_idx" ON "pcat_parts" USING btree ("name");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_schema_pics_group_car_idx" ON "pcat_schema_pics" USING btree ("group_id","car_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_vehicles_vin_idx" ON "pcat_vehicles" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pcat_vehicles_catalog_id_idx" ON "pcat_vehicles" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "pl24_catalogs_catalog_id_idx" ON "pl24_catalogs" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_part_groups_vehicle_id_idx" ON "pl24_part_groups" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_part_groups_group_id_idx" ON "pl24_part_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_part_images_part_id_idx" ON "pl24_part_images" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_part_numbers_part_id_idx" ON "pl24_part_numbers" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_part_numbers_oem_code_idx" ON "pl24_part_numbers" USING btree ("oem_code");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_parts_vehicle_id_idx" ON "pl24_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_parts_group_id_idx" ON "pl24_parts" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_schema_pics_group_id_idx" ON "pl24_schema_pics" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_groups_catalog_id_idx" ON "pl24_vehicle_groups" USING btree ("catalog_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_groups_group_id_idx" ON "pl24_vehicle_groups" USING btree ("group_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_parts_vehicle_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_parts_part_id_idx" ON "pl24_vehicle_parts" USING btree ("pl24_part_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_vins_vin_idx" ON "pl24_vehicle_vins" USING btree ("vin");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicle_vins_vehicle_id_idx" ON "pl24_vehicle_vins" USING btree ("pl24_vehicle_id");--> statement-breakpoint
|
||||
CREATE UNIQUE INDEX IF NOT EXISTS "pl24_vehicles_vehicle_id_idx" ON "pl24_vehicles" USING btree ("vehicle_id");--> statement-breakpoint
|
||||
CREATE INDEX IF NOT EXISTS "pl24_vehicles_catalog_id_idx" ON "pl24_vehicles" USING btree ("catalog_id");
|
||||
@@ -1,251 +1,287 @@
|
||||
import { createHash } from "node:crypto";
|
||||
import { readFileSync } from "node:fs";
|
||||
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
/**
|
||||
* Unit tests for the migration runner's bootstrap logic.
|
||||
* Unit tests for the hash-based migration runner (runMigrations).
|
||||
*
|
||||
* We test bootstrapExistingDb() directly with a mock postgres.Sql client
|
||||
* and mock the filesystem via vi.mock("node:fs").
|
||||
* We test runMigrations() with a mock postgres.Sql client and mock
|
||||
* the filesystem (readFileSync) to control journal and SQL content.
|
||||
*/
|
||||
|
||||
// ── Mock SQL content matching the baseline migration ──
|
||||
const MOCK_JOURNAL = {
|
||||
// ── Test data ──
|
||||
|
||||
const SQL_0000 = `CREATE TABLE IF NOT EXISTS "users" (id uuid PRIMARY KEY);\n--> statement-breakpoint\nCREATE TABLE IF NOT EXISTS "brands" (id uuid PRIMARY KEY);\n`;
|
||||
const SQL_0001 = `ALTER TABLE "users" ADD COLUMN "email" varchar(255);\n`;
|
||||
|
||||
const HASH_0000 = createHash("sha256").update(SQL_0000).digest("hex");
|
||||
const HASH_0001 = createHash("sha256").update(SQL_0001).digest("hex");
|
||||
|
||||
const JOURNAL_1_ENTRY = {
|
||||
version: "7",
|
||||
dialect: "postgresql",
|
||||
entries: [
|
||||
{ idx: 0, version: "7", when: 1778535325959, tag: "0000_brief_guardian", breakpoints: true },
|
||||
],
|
||||
};
|
||||
|
||||
const JOURNAL_2_ENTRIES = {
|
||||
version: "7",
|
||||
dialect: "postgresql",
|
||||
entries: [
|
||||
{ idx: 0, version: "7", when: 1778535325959, tag: "0000_brief_guardian", breakpoints: true },
|
||||
{
|
||||
idx: 0,
|
||||
idx: 1,
|
||||
version: "7",
|
||||
when: 1778535325959,
|
||||
tag: "0000_brief_guardian",
|
||||
when: 1747039600000,
|
||||
tag: "0001_charming_quicksand",
|
||||
breakpoints: true,
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
const MOCK_BASELINE_SQL = [
|
||||
'CREATE TABLE "users" (id uuid PRIMARY KEY);',
|
||||
'CREATE TABLE "changelog_entries" (id uuid PRIMARY KEY);',
|
||||
'CREATE TABLE "brands" (id uuid PRIMARY KEY);',
|
||||
].join("\n--> statement-breakpoint\n");
|
||||
|
||||
const MOCK_BASELINE_TABLES = ["users", "changelog_entries", "brands"];
|
||||
// Journal with non-monotonic `when` values — idx determines order, not `when`
|
||||
const JOURNAL_RANDOM_WHEN = {
|
||||
version: "7",
|
||||
dialect: "postgresql",
|
||||
entries: [
|
||||
{ idx: 2, version: "7", when: 1000, tag: "0002_late", breakpoints: true },
|
||||
{ idx: 0, version: "7", when: 9999999999999, tag: "0000_brief_guardian", breakpoints: true },
|
||||
{ idx: 1, version: "7", when: 500, tag: "0001_charming_quicksand", breakpoints: true },
|
||||
],
|
||||
};
|
||||
|
||||
// ── FS mock ──
|
||||
|
||||
// The mock file system is configured per-test. readFileSync throws by default
|
||||
// for any unmapped path.
|
||||
let mockFiles: Record<string, string> = {};
|
||||
|
||||
vi.mock("node:fs", async () => {
|
||||
const actual = await vi.importActual<typeof import("node:fs")>("node:fs");
|
||||
return {
|
||||
...actual,
|
||||
readFileSync: vi.fn((path: string, _encoding: string) => {
|
||||
throw new Error(`ENOENT: mock file not set up: ${path}`);
|
||||
// Match by path suffix — migrate.ts uses join(migrationsFolder, ...)
|
||||
// which produces absolute paths, while mockFiles uses relative keys.
|
||||
const normalizedPath = String(path).replace(/\\/g, "/");
|
||||
for (const [key, content] of Object.entries(mockFiles)) {
|
||||
if (normalizedPath.endsWith(key)) return content;
|
||||
}
|
||||
throw new Error(`ENOENT: mock file not set up: ${normalizedPath}`);
|
||||
}),
|
||||
};
|
||||
});
|
||||
|
||||
import { bootstrapExistingDb } from "../../database/migrate";
|
||||
// Import under test after mock is set up
|
||||
import { runMigrations } from "../../database/migrate";
|
||||
|
||||
// ── Helpers ──
|
||||
// ── Mock SQL client helpers ──
|
||||
|
||||
interface TrackingTx {
|
||||
unsafe: ReturnType<typeof vi.fn>;
|
||||
__unsafeCalls: string[];
|
||||
}
|
||||
|
||||
interface MockSqlClient {
|
||||
(queryParts: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
|
||||
(template: TemplateStringsArray, ...values: unknown[]): Promise<unknown[]>;
|
||||
unsafe: ReturnType<typeof vi.fn>;
|
||||
begin: ReturnType<typeof vi.fn>;
|
||||
end: ReturnType<typeof vi.fn>;
|
||||
__inserts: string[];
|
||||
__unsafeCalls: string[];
|
||||
_setQueryHandler: (handler: (template: string) => unknown[]) => void;
|
||||
__appliedHashes: string[];
|
||||
__beginCallback: ((tx: TrackingTx) => Promise<void>) | null;
|
||||
_setBeginError: (err: Error) => void;
|
||||
}
|
||||
|
||||
function createMockSqlClient(): MockSqlClient {
|
||||
let queryHandler: (template: string) => unknown[] = () => [];
|
||||
let beginError: Error | null = null;
|
||||
const beginCallback: ((tx: TrackingTx) => Promise<void>) | null = null;
|
||||
|
||||
const sql = vi.fn(async (queryParts: TemplateStringsArray, ..._values: unknown[]) => {
|
||||
const queryText = queryParts.join(" ").trim();
|
||||
// Capture INSERT queries directly
|
||||
if (queryText.includes("INSERT INTO")) {
|
||||
(sql as any).__inserts.push(queryText);
|
||||
return [];
|
||||
const sql = vi.fn(async (template: TemplateStringsArray, ..._values: unknown[]) => {
|
||||
const queryText = template.join(" ").trim();
|
||||
// Handle SELECT hash query
|
||||
if (queryText.includes("SELECT hash FROM")) {
|
||||
return (sql as any).__appliedHashes.map((h: string) => ({ hash: h }));
|
||||
}
|
||||
return queryHandler(queryText);
|
||||
return [];
|
||||
}) as unknown as MockSqlClient;
|
||||
|
||||
(sql as any).unsafe = vi.fn(async (q: string) => {
|
||||
((sql as any).__unsafeCalls as string[]).push(q);
|
||||
(sql as any).unsafe = vi.fn(async (_q: string) => {
|
||||
((sql as any).__unsafeCalls as string[]).push(_q);
|
||||
});
|
||||
(sql as any).begin = vi.fn(async (cb: (tx: TrackingTx) => Promise<void>) => {
|
||||
(sql as any).__beginCallback = cb;
|
||||
|
||||
const tx: TrackingTx = {
|
||||
unsafe: vi.fn(async (_q: string) => {
|
||||
tx.__unsafeCalls.push(_q);
|
||||
// Inject error if configured (for rollback test)
|
||||
if (beginError) throw beginError;
|
||||
}),
|
||||
__unsafeCalls: [],
|
||||
};
|
||||
|
||||
await cb(tx);
|
||||
});
|
||||
(sql as any).end = vi.fn().mockResolvedValue(undefined);
|
||||
(sql as any).__inserts = [];
|
||||
(sql as any).__unsafeCalls = [];
|
||||
|
||||
(sql as any)._setQueryHandler = (handler: typeof queryHandler) => {
|
||||
queryHandler = handler;
|
||||
(sql as any).__appliedHashes = [];
|
||||
(sql as any).__beginCallback = null;
|
||||
(sql as any)._setBeginError = (err: Error) => {
|
||||
beginError = err;
|
||||
};
|
||||
|
||||
return sql;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up the readFileSync mock to return journal + baseline SQL content.
|
||||
* Set up the mock file system for a journal and its SQL files.
|
||||
*/
|
||||
function mockFsForBootstrap() {
|
||||
const mockRFS = readFileSync as ReturnType<typeof vi.fn>;
|
||||
mockRFS.mockImplementation((path: string, _encoding: string) => {
|
||||
if (path.includes("_journal.json")) {
|
||||
return JSON.stringify(MOCK_JOURNAL);
|
||||
}
|
||||
if (path.includes("0000_brief_guardian.sql")) {
|
||||
return MOCK_BASELINE_SQL;
|
||||
}
|
||||
throw new Error(`Unexpected readFileSync: ${path}`);
|
||||
});
|
||||
function setMockFiles(journal: Record<string, unknown>, sqlFiles: Record<string, string>) {
|
||||
mockFiles = {};
|
||||
// Normalize paths
|
||||
mockFiles["drizzle/meta/_journal.json"] = JSON.stringify(journal);
|
||||
for (const [tag, content] of Object.entries(sqlFiles)) {
|
||||
mockFiles[`drizzle/${tag}.sql`] = content;
|
||||
}
|
||||
}
|
||||
|
||||
// ── Tests ──
|
||||
|
||||
describe("bootstrapExistingDb", () => {
|
||||
describe("runMigrations", () => {
|
||||
let sqlClient: MockSqlClient;
|
||||
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||
let exitMock: any;
|
||||
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
mockFiles = {};
|
||||
sqlClient = createMockSqlClient();
|
||||
exitMock = vi.spyOn(process, "exit").mockImplementation(() => undefined as never);
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
exitMock.mockRestore();
|
||||
});
|
||||
// ── Test a: Empty DB + 1 migration → applies it, inserts hash ──
|
||||
|
||||
// ── Group 1: __drizzle_migrations already exists ──
|
||||
describe("empty DB with 1 migration", () => {
|
||||
it("should apply the migration and record its hash", async () => {
|
||||
setMockFiles(JOURNAL_1_ENTRY, { "0000_brief_guardian": SQL_0000 });
|
||||
// No applied hashes (empty DB)
|
||||
(sqlClient as any).__appliedHashes = [];
|
||||
|
||||
describe("when __drizzle_migrations already exists", () => {
|
||||
it("should skip bootstrap (no CREATE, no INSERT)", async () => {
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return [{ table_name: "__drizzle_migrations" }];
|
||||
}
|
||||
return [];
|
||||
});
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
// Should create schema and tracking table
|
||||
const unsafeAll = (sqlClient as any).__unsafeCalls as string[];
|
||||
expect(unsafeAll.some((c: string) => c.includes("CREATE SCHEMA"))).toBe(true);
|
||||
expect(unsafeAll.some((c: string) => c.includes("CREATE TABLE IF NOT EXISTS"))).toBe(true);
|
||||
// Migration was applied (begin was called)
|
||||
expect((sqlClient as any).begin).toHaveBeenCalledTimes(1);
|
||||
|
||||
// Should NOT create schema or table
|
||||
expect((sqlClient as any).__unsafeCalls).toEqual([]);
|
||||
|
||||
// Should NOT insert any migration records
|
||||
expect((sqlClient as any).__inserts).toEqual([]);
|
||||
// The tx inside begin should have executed the 2 statements + INSERT
|
||||
const beginCallback = (sqlClient as any).__beginCallback;
|
||||
expect(beginCallback).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Group 2: All baseline tables present → bootstrap ──
|
||||
// ── Test b: Existing DB + matching hash → skips ──
|
||||
|
||||
describe("when all baseline tables exist (existing DB with no tracking)", () => {
|
||||
it("should create schema, create table, and mark baseline entries as applied", async () => {
|
||||
mockFsForBootstrap();
|
||||
describe("existing DB with matching hash", () => {
|
||||
it("should skip the migration (appliedCount=0, skippedCount=1)", async () => {
|
||||
setMockFiles(JOURNAL_1_ENTRY, { "0000_brief_guardian": SQL_0000 });
|
||||
(sqlClient as any).__appliedHashes = [HASH_0000];
|
||||
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return []; // __drizzle_migrations does NOT exist
|
||||
}
|
||||
// The baseline-tables check uses information_schema.tables with ANY
|
||||
if (queryText.includes("information_schema") && queryText.includes("ANY(")) {
|
||||
return MOCK_BASELINE_TABLES.map((t) => ({ table_name: t }));
|
||||
}
|
||||
return [];
|
||||
});
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
|
||||
// Verify CREATE SCHEMA was called
|
||||
expect((sqlClient as any).__unsafeCalls).toContain('CREATE SCHEMA IF NOT EXISTS "drizzle"');
|
||||
|
||||
// Verify CREATE TABLE was called
|
||||
const createTableCall = (sqlClient as any).__unsafeCalls.find((c: string) =>
|
||||
c.includes("CREATE TABLE IF NOT EXISTS"),
|
||||
);
|
||||
expect(createTableCall).toBeDefined();
|
||||
expect(createTableCall).toContain("__drizzle_migrations");
|
||||
|
||||
// Verify INSERT was called with a hash
|
||||
expect((sqlClient as any).__inserts.length).toBe(1);
|
||||
// begin should NOT have been called (no migration applied)
|
||||
expect((sqlClient as any).begin).not.toHaveBeenCalled();
|
||||
// Schema/table creation still happens
|
||||
const unsafeAll = (sqlClient as any).__unsafeCalls as string[];
|
||||
expect(unsafeAll.some((c: string) => c.includes("CREATE SCHEMA"))).toBe(true);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Group 3: No baseline tables exist → fresh DB ──
|
||||
// ── Test c: 2 migrations, 1 applied + 1 new → applies only the new one ──
|
||||
|
||||
describe("when no baseline tables exist (fresh DB)", () => {
|
||||
it("should skip bootstrap entirely", async () => {
|
||||
mockFsForBootstrap();
|
||||
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return []; // __drizzle_migrations does NOT exist
|
||||
}
|
||||
if (queryText.includes("information_schema") && queryText.includes("ANY(")) {
|
||||
return []; // no baseline tables exist
|
||||
}
|
||||
return [];
|
||||
describe("2 migrations, 1 already applied", () => {
|
||||
it("should apply only the new migration", async () => {
|
||||
setMockFiles(JOURNAL_2_ENTRIES, {
|
||||
"0000_brief_guardian": SQL_0000,
|
||||
"0001_charming_quicksand": SQL_0001,
|
||||
});
|
||||
// Only 0000's hash is already applied
|
||||
(sqlClient as any).__appliedHashes = [HASH_0000];
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
// No schema creation
|
||||
expect((sqlClient as any).__unsafeCalls).toEqual([]);
|
||||
// No inserts
|
||||
expect((sqlClient as any).__inserts).toEqual([]);
|
||||
// begin should be called exactly once (for 0001)
|
||||
expect((sqlClient as any).begin).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Group 4: Partial baseline state → error ──
|
||||
// ── Test d: Statement breakpoint parsing ──
|
||||
|
||||
describe("when some but not all baseline tables exist (partial state)", () => {
|
||||
it("should call process.exit(1) with a clear error message", async () => {
|
||||
mockFsForBootstrap();
|
||||
describe("statement breakpoint parsing", () => {
|
||||
it("should split on '--> statement-breakpoint' and execute each statement", async () => {
|
||||
setMockFiles(JOURNAL_1_ENTRY, { "0000_brief_guardian": SQL_0000 });
|
||||
(sqlClient as any).__appliedHashes = [];
|
||||
|
||||
(sqlClient as any)._setQueryHandler((queryText: string) => {
|
||||
if (
|
||||
queryText.includes("information_schema") &&
|
||||
queryText.includes("__drizzle_migrations")
|
||||
) {
|
||||
return []; // __drizzle_migrations does NOT exist
|
||||
}
|
||||
if (queryText.includes("information_schema") && queryText.includes("ANY(")) {
|
||||
// Only users and brands exist — changelog_entries is missing
|
||||
return [{ table_name: "users" }, { table_name: "brands" }];
|
||||
}
|
||||
return [];
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
expect((sqlClient as any).begin).toHaveBeenCalledTimes(1);
|
||||
|
||||
// The tx unsafe calls should include the 2 CREATE TABLE statements and 1 INSERT
|
||||
// We can verify by checking the begin callback's tx had exactly 3 unsafe calls
|
||||
const beginCallback = (sqlClient as any).__beginCallback;
|
||||
expect(beginCallback).not.toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
// ── Test e: Transaction rollback ──
|
||||
|
||||
describe("transaction rollback on error", () => {
|
||||
it("should not insert migration hash when a statement fails", async () => {
|
||||
setMockFiles(JOURNAL_2_ENTRIES, {
|
||||
"0000_brief_guardian": SQL_0000,
|
||||
"0001_charming_quicksand": SQL_0001,
|
||||
});
|
||||
// 0000 already applied, 0001 will be attempted
|
||||
(sqlClient as any).__appliedHashes = [HASH_0000];
|
||||
// Make the begin callback throw on first tx.unsafe call
|
||||
(sqlClient as any)._setBeginError(new Error("syntax error"));
|
||||
|
||||
await bootstrapExistingDb(sqlClient as any);
|
||||
await expect(runMigrations(sqlClient as any)).rejects.toThrow("syntax error");
|
||||
|
||||
// Should exit with code 1
|
||||
expect(exitMock).toHaveBeenCalledWith(1);
|
||||
// Note: __unsafeCalls / __inserts may be populated after process.exit
|
||||
// because the mock doesn't actually terminate execution.
|
||||
// begin WAS called, but the inner callback threw — no INSERT should have run
|
||||
expect((sqlClient as any).begin).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Test f: Journal ordering by idx, not when ──
|
||||
|
||||
describe("journal ordering", () => {
|
||||
it("should sort entries by idx regardless of 'when' values", async () => {
|
||||
const sqlFiles: Record<string, string> = {
|
||||
"0000_brief_guardian": SQL_0000,
|
||||
"0001_charming_quicksand": SQL_0001,
|
||||
"0002_late": 'CREATE INDEX IF NOT EXISTS "idx_test" ON "users" ("email");\n',
|
||||
};
|
||||
setMockFiles(JOURNAL_RANDOM_WHEN, sqlFiles);
|
||||
(sqlClient as any).__appliedHashes = [];
|
||||
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
// All 3 migrations should be applied (begin called 3 times)
|
||||
expect((sqlClient as any).begin).toHaveBeenCalledTimes(3);
|
||||
});
|
||||
});
|
||||
|
||||
// ── Test: Empty journal → no-op ──
|
||||
|
||||
describe("empty journal", () => {
|
||||
it("should exit early with no errors", async () => {
|
||||
setMockFiles({ version: "7", dialect: "postgresql", entries: [] }, {});
|
||||
|
||||
await runMigrations(sqlClient as any);
|
||||
|
||||
// begin should NOT have been called
|
||||
expect((sqlClient as any).begin).not.toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("hash computation", () => {
|
||||
it("should produce the expected SHA256 hash for a known input", () => {
|
||||
const input = "SELECT 1";
|
||||
const hash = createHash("sha256").update(input).digest("hex");
|
||||
expect(hash).toBe("e004ebd5b5532a4b85984a62f8ad48a81aa3460c1ca07701f386135d72cdecf5");
|
||||
});
|
||||
|
||||
it("should produce a different hash for different input", () => {
|
||||
const hashA = createHash("sha256").update("SELECT 1").digest("hex");
|
||||
const hashB = createHash("sha256").update("SELECT 2").digest("hex");
|
||||
expect(hashA).not.toBe(hashB);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -2,9 +2,6 @@ import { createHash } from "node:crypto";
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { join, resolve } from "node:path";
|
||||
import { config as loadEnv } from "dotenv";
|
||||
import { sql } from "drizzle-orm";
|
||||
import { drizzle } from "drizzle-orm/postgres-js";
|
||||
import { migrate } from "drizzle-orm/postgres-js/migrator";
|
||||
import postgres from "postgres";
|
||||
|
||||
// ── Dotenv bootstrap (load .env for standalone tsx execution) ──
|
||||
@@ -51,95 +48,29 @@ interface Journal {
|
||||
entries: JournalEntry[];
|
||||
}
|
||||
|
||||
export async function bootstrapExistingDb(sqlClient: postgres.Sql): Promise<void> {
|
||||
const tablesResult = await sqlClient`
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'drizzle'
|
||||
AND table_name = '__drizzle_migrations'
|
||||
`;
|
||||
|
||||
if (tablesResult.length > 0) {
|
||||
// __drizzle_migrations already exists — normal migration run
|
||||
console.log("[migrate] Migration tracking table exists — skipping bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Read journal to find baseline migration SQL files
|
||||
const journalPath = join(migrationsFolder, "meta", "_journal.json");
|
||||
let journal: Journal;
|
||||
|
||||
try {
|
||||
journal = JSON.parse(readFileSync(journalPath, "utf-8")) as Journal;
|
||||
} catch (err) {
|
||||
console.error("[migrate] Failed to read _journal.json:", (err as Error).message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!journal.entries || journal.entries.length === 0) {
|
||||
console.warn("[migrate] _journal.json has no entries — no migrations to bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Extract all CREATE TABLE table names from the baseline SQL file
|
||||
// We only match bare CREATE TABLE (not IF NOT EXISTS) so we can detect
|
||||
// which tables the baseline migration explicitly creates.
|
||||
const baselineEntry = journal.entries[0];
|
||||
const baselineSqlPath = join(migrationsFolder, `${baselineEntry.tag}.sql`);
|
||||
let baselineSql: string;
|
||||
try {
|
||||
baselineSql = readFileSync(baselineSqlPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(
|
||||
`[migrate] Failed to read baseline migration ${baselineEntry.tag}.sql:`,
|
||||
(err as Error).message,
|
||||
);
|
||||
throw err;
|
||||
}
|
||||
|
||||
const baselineTables = [...baselineSql.matchAll(/CREATE TABLE\s+"([^"]+)"/g)].map((m) => m[1]);
|
||||
|
||||
if (baselineTables.length === 0) {
|
||||
console.warn("[migrate] No CREATE TABLE statements found in baseline SQL — skipping bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check which baseline tables exist in the target database
|
||||
const existingResult = await sqlClient`
|
||||
SELECT table_name
|
||||
FROM information_schema.tables
|
||||
WHERE table_schema = 'public'
|
||||
AND table_name = ANY(${baselineTables})
|
||||
`;
|
||||
const existingTables = new Set(
|
||||
existingResult.map((r) => (r as Record<string, string>).table_name),
|
||||
);
|
||||
const existingCount = existingTables.size;
|
||||
const totalCount = baselineTables.length;
|
||||
|
||||
if (existingCount === 0) {
|
||||
// Fresh DB — migrate() will handle everything
|
||||
console.log("[migrate] Fresh database (no baseline tables) — skipping bootstrap");
|
||||
return;
|
||||
}
|
||||
|
||||
if (existingCount < totalCount) {
|
||||
// Partial-baseline state — error out with clear instructions
|
||||
const missingTables = baselineTables.filter((t) => !existingTables.has(t));
|
||||
console.error(
|
||||
`[migrate] DB is in partial-baseline state: ${existingCount}/${totalCount} tables present. Run \`pnpm db:push --force\` once to sync schema, then retry.`,
|
||||
);
|
||||
console.error(`[migrate] Missing tables (first 10): ${missingTables.slice(0, 10).join(", ")}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
// All baseline tables exist — bootstrap migration tracking
|
||||
console.log(`[migrate] Bootstrapping existing DB: all ${totalCount} baseline tables present`);
|
||||
|
||||
// Create the drizzle schema if it doesn't exist
|
||||
/**
|
||||
* Hash-based migration runner.
|
||||
*
|
||||
* Instead of Drizzle's built-in `migrate()` (which compares `folderMillis`
|
||||
* timestamps from `_journal.json` — susceptible to clock skew), this runner
|
||||
* tracks applied migrations by SHA256 of the SQL file content.
|
||||
*
|
||||
* How it works:
|
||||
* 1. Ensures the `drizzle` schema and `__drizzle_migrations` tracking table exist.
|
||||
* 2. Reads all previously-applied migration hashes from `__drizzle_migrations`.
|
||||
* 3. Reads `drizzle/meta/_journal.json` for the ordered list of migration files.
|
||||
* 4. For each migration, computes SHA256 of the SQL content.
|
||||
* 5. If the hash already exists → skip (already applied).
|
||||
* 6. If the hash is new → execute all statements in a single transaction,
|
||||
* then record the hash with the journal `when` timestamp.
|
||||
*
|
||||
* This is immune to the "new migration skipped" bug where Drizzle silently
|
||||
* ignores new migrations because `folderMillis` timestamps are stale or
|
||||
* skewed.
|
||||
*/
|
||||
export async function runMigrations(sqlClient: postgres.Sql): Promise<void> {
|
||||
// Ensure drizzle schema and tracking table exist
|
||||
await sqlClient.unsafe(`CREATE SCHEMA IF NOT EXISTS "drizzle"`);
|
||||
|
||||
// Create __drizzle_migrations table matching Drizzle's expected schema
|
||||
await sqlClient.unsafe(`
|
||||
CREATE TABLE IF NOT EXISTS "drizzle"."__drizzle_migrations" (
|
||||
id SERIAL PRIMARY KEY,
|
||||
@@ -148,25 +79,75 @@ export async function bootstrapExistingDb(sqlClient: postgres.Sql): Promise<void
|
||||
)
|
||||
`);
|
||||
|
||||
// Mark all journal entries as applied (hash the SQL content)
|
||||
const now = Date.now();
|
||||
for (const entry of journal.entries) {
|
||||
// Re-read baseline SQL (already loaded above for first entry)
|
||||
const sqlContent =
|
||||
entry.tag === baselineEntry.tag
|
||||
? baselineSql
|
||||
: readFileSync(join(migrationsFolder, `${entry.tag}.sql`), "utf-8");
|
||||
// Read all previously-applied hashes
|
||||
const applied = await sqlClient<{ hash: string }[]>`
|
||||
SELECT hash FROM "drizzle"."__drizzle_migrations"
|
||||
`;
|
||||
const appliedHashes = new Set(applied.map((r) => r.hash));
|
||||
|
||||
// Read journal
|
||||
const journalPath = join(migrationsFolder, "meta", "_journal.json");
|
||||
let journal: Journal;
|
||||
try {
|
||||
journal = JSON.parse(readFileSync(journalPath, "utf-8")) as Journal;
|
||||
} catch (err) {
|
||||
console.error("[migrate] Failed to read _journal.json:", (err as Error).message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
if (!journal.entries || journal.entries.length === 0) {
|
||||
console.warn("[migrate] _journal.json has no entries — no migrations to run");
|
||||
return;
|
||||
}
|
||||
|
||||
// Sort entries by idx (ascending) — applies migrations in correct order
|
||||
// regardless of `when` field monotonicity
|
||||
const sorted = [...journal.entries].sort((a, b) => a.idx - b.idx);
|
||||
|
||||
let appliedCount = 0;
|
||||
let skippedCount = 0;
|
||||
|
||||
for (const entry of sorted) {
|
||||
const sqlPath = join(migrationsFolder, `${entry.tag}.sql`);
|
||||
let sqlContent: string;
|
||||
try {
|
||||
sqlContent = readFileSync(sqlPath, "utf-8");
|
||||
} catch (err) {
|
||||
console.error(`[migrate] Failed to read migration ${entry.tag}.sql:`, (err as Error).message);
|
||||
throw err;
|
||||
}
|
||||
|
||||
const hash = createHash("sha256").update(sqlContent).digest("hex");
|
||||
await sqlClient`
|
||||
INSERT INTO "drizzle"."__drizzle_migrations" (hash, created_at)
|
||||
VALUES (${hash}, ${now})
|
||||
`;
|
||||
|
||||
if (appliedHashes.has(hash)) {
|
||||
skippedCount++;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Split on statement breakpoints and execute in a single transaction
|
||||
const statements = sqlContent
|
||||
.split("--> statement-breakpoint")
|
||||
.map((s) => s.trim())
|
||||
.filter((s) => s.length > 0);
|
||||
|
||||
await sqlClient.begin(async (tx) => {
|
||||
for (const stmt of statements) {
|
||||
await tx.unsafe(stmt);
|
||||
}
|
||||
|
||||
// Record hash with deterministic created_at from journal (not Date.now())
|
||||
const createdAt = Number.isFinite(entry.when) && entry.when > 0 ? entry.when : Date.now();
|
||||
|
||||
await tx.unsafe(
|
||||
`INSERT INTO "drizzle"."__drizzle_migrations" (hash, created_at) VALUES ('${hash}', ${createdAt})`,
|
||||
);
|
||||
});
|
||||
|
||||
appliedCount++;
|
||||
}
|
||||
|
||||
console.log(
|
||||
`[migrate] Bootstrapped existing DB: all ${totalCount} baseline tables present, ` +
|
||||
`marked ${journal.entries.length} migration(s) as applied`,
|
||||
`[migrate] Done: ${appliedCount} applied, ${skippedCount} already applied (total ${sorted.length})`,
|
||||
);
|
||||
}
|
||||
|
||||
@@ -183,13 +164,7 @@ async function run(): Promise<void> {
|
||||
const sqlClient = postgres(databaseUrl, { max: 1 });
|
||||
|
||||
try {
|
||||
// Bootstrap existing databases before running the migrator
|
||||
await bootstrapExistingDb(sqlClient);
|
||||
|
||||
// Run Drizzle migrator (idempotent — applies only unapplied migrations)
|
||||
const db = drizzle(sqlClient);
|
||||
await migrate(db, { migrationsFolder });
|
||||
console.log("[migrate] Migration complete");
|
||||
await runMigrations(sqlClient);
|
||||
|
||||
await sqlClient.end();
|
||||
process.exit(0);
|
||||
|
||||
Reference in New Issue
Block a user