إنتقل إلى المحتوى الرئيسي

ترحيلات قاعدة البيانات

نظرة عامة

نستخدم نظام ترحيلات متسلسل لإدارة تغييرات قاعدة البيانات. جميع الترحيلات موجودة في:

infrastructure/supabase/migrations/

تسمية الملفات

00XXX_description.sql
  • 00XXX - رقم تسلسلي (5 أرقام)
  • description - وصف مختصر بالإنجليزية

أمثلة:

00001_complete_schema.sql
00023_create_seats_table.sql
00054_bus_templates.sql
00065_add_delayed_trip_status.sql

الترحيلات الرئيسية

الرقمالملفالوصف
00001complete_schema.sqlالمخطط الكامل الأولي
00010fix_audit_log_function.sqlإصلاح دالة سجل المراجعة
00015add_missing_columns.sqlأعمدة مفقودة
00016add_technical_issue_ticket_type.sqlنوع تذكرة جديد
00023create_seats_table.sqlجدول المقاعد
00028fix_schema_issues.sqlإصلاحات المخطط
00029fix_passenger_rls.sqlسياسات RLS للركاب
00030fix_bookings_rls.sqlسياسات RLS للحجوزات
00035fix_null_seat_numbers.sqlإصلاح أرقام المقاعد
00038maintenance_logs.sqlسجلات الصيانة
00043email_verification.sqlالتحقق من البريد
00045atomic_booking.sqlالحجز الذري
00047fix_critical_issues.sqlإصلاحات حرجة
00048cascade_trip_cancellation.sqlإلغاء متسلسل
00049fix_seat_lock_race_condition.sqlإصلاح تنافس القفل
00054bus_templates.sqlقوالب الباصات
00055layout_enhancements.sqlتحسينات التخطيط
00056seat_configuration.sqlتكوين المقاعد
00058fix_schema_mismatches.sqlإصلاح عدم التطابق
00064fix_seat_numbers_type.sqlإصلاح نوع أرقام المقاعد
00065add_delayed_trip_status.sqlحالة التأخير
00068seed_bus_layouts.sqlبذر تخطيطات الباصات
00116ensure_waitlist_table.sqlضمان وجود جدول قائمة الانتظار وسياساته في البيئات التي بُدئت من baseline قديم
00137restore_resource_contract_compatibility.sqlإعادة أعمدة وجداول ودوال توافقية تعتمد عليها واجهات الويب والموبايل وPostgREST
00138fix_remaining_resource_contract_data.sqlاستكمال بيانات التوافق: أسماء المدن والسائقين، أسعار المسارات، أكواد الحجز، ودالة get_booking_by_code
00191public_trip_tracking.sqlعقد تتبع عام محدود بكود الحجز يعيد المسار والحالة وGPS ضمن نافذة زمنية من دون بيانات الراكب أو الدفع
00194repair_seat_lock_booking_contract.sqlإصلاح قفل المقاعد المتسلسل، التحقق من ملكية جلسة القفل، الحجز الأحادي والذهاب والعودة الذري، وتسعير الخادم
00195fix_alphanumeric_booking_cancellation.sqlإلغاء ذري آمن لأسماء المقاعد الحرفية مثل 1D مع إعادة السعة من trigger واحد
00196atomic_company_booking_contract.sqlتوحيد الأرقام الترتيبية والأسماء الفعلية وإضافة مخزون مقاعد وحجز مكتبي ذريين ومقيدين بالشركة
00197enable_dispatcher_booking_cancellation.sqlمواءمة الإلغاء مع أدوار إدارة الحجوزات: السماح لمسؤول التشغيل مع إبقاء السائق مرفوضاً
00198security_runtime_hardening.sqlإزالة سياسات anon التاريخية للحجوزات والركاب، جعل RPCs افتراضياً مغلقة، وإعادة تقييد دوال الإدارة والخدمة
00199realtime_runtime_schema.sqlتثبيت مخطط Realtime التشغيلي وصلاحياته المطلوبة في بيئة الإنتاج الذاتية
00200dashboard_analytics_snapshot.sqlتجميع تحليلات الشركة في RPC مفوض واحد ومنع روابط PostgREST الضخمة والنتائج المبتورة
00201dashboard_advanced_analytics.sqlتجميع التحليلات المتقدمة في RPC واحد وتصحيح الإشغال والاحتفاظ ومنع تنزيل الحجوزات الخام والمكررة
00202atomic_customer_booking_modification.sqlتعديل حجز العميل ذرياً بأسماء المقاعد الفعلية وتسعير الخادم وتحديث السعة وتدوير رموز الصعود
00203secure_customer_trip_reviews.sqlربط مراجعة العميل بحجزه ورحلته المكتملة ومنع تغيير الهوية أو حالة الإشراف من العميل
00204atomic_loyalty_reward_redemption.sqlاستبدال مكافآت الولاء ذرياً مع قفل الرصيد والمكافأة وحفظ لقطة شروط القسيمة
00205apply_loyalty_rewards_to_customer_bookings.sqlمعاينة قسيمة الولاء وتطبيقها مرة واحدة داخل حجز فردي أو ذهاب وعودة مع حفظ تفصيل السعر
00206reassert_seat_capacity_ownership.sqlإعادة تثبيت trigger واحد لملكية سعة الرحلة ومصالحة السعة وتطبيع checksum لمسودة 00166 المعروفة فقط
00207restrict_booking_price_trigger_function.sqlحجب تنفيذ دالة trigger الداخلية لتفصيل سعر الحجز عن anon وauthenticated
00208reassert_offline_qr_token_contract.sqlإعادة تثبيت عقد رموز QR غير المتصلة وتطبيع checksum لمسودة 00181 التاريخية المعروفة فقط
00209reassert_trip_message_driver_identity.sqlإعادة تثبيت سياسات هوية سائق محادثة الرحلة وتطبيع checksum لمسودة 00182 التاريخية المعروفة فقط
00210unified_company_payment_ledger.sqlتوحيد حقيقة دفع الحجز ودفتر تحصيل الشركة ومستخدمي قرارات التأكيد والاسترداد
00211company_cash_reconciliation.sqlصندوق نقدي وتسلسل قرارات دفع ذرية مع إلغاء غير المكتمل واسترداد كامل وملاحظات نهائية ثابتة
00212recurring_trip_series.sqlمعاينة وإنشاء ذري لسلاسل الرحلات مع تعارضات الباص والسائق والصيانة وربط الرحلة بالسلسلة
00213company_trip_dispatch.sqlدورة تشغيل موحدة وحضور وإلغاء ذري وانتقالات سائق أمامية وسحب الكتابة المباشرة للحجوزات والتكليفات
00214company_operating_day_trip_list.sqlقائمة يوم تشغيل بتوقيت دمشق وسجل حديث أولاً مع حالات التشغيل وسياق الباص والسائق
00215reassert_checked_in_reporting.sqlإعادة تثبيت احتساب الحجوزات المسجلة حضوراً ضمن قوائم الحجوزات والإيراد والتحليلات
00216reassert_secure_customer_trip_reviews.sqlإعادة تثبيت ملكية مراجعة الرحلة واكتمالها وثبات هويتها وتطبيع checksum لمسودة 00203 المعروفة فقط
00217enterprise_notification_delivery.sqlصندوق إرسال معاملات متعدد القنوات مع قوالب وإعادة محاولة وcallbacks قابلة للتكرار
00218enterprise_document_registry.sqlسجل مستندات مالية immutable وأحداث تصيير مدققة للتذاكر والإيصالات والتقارير
00219global_product_foundation.sqlأساس المعايير العامة: أنواع المقاعد، ملفات الشركات العامة، صور الرحلات، المشاهد المحفوظة، والفهارس
00220public_company_profiles_and_trip_media.sqlعقود القراءة/التحرير الآمنة لملفات الشركات العامة ووسائط الرحلات
00221traveler_identity_and_booking_continuity.sqlهويات دخول متعددة للمسافر وحفظ سياق الحجز عبر المصادقة
00222global_product_api_contracts.sqlRPCs موحدة للكيانات المرتبطة والمرشحات والمشاهد وتهيئة أنواع المقاعد
00223traveler_phone_completion.sqlاستكمال رقم هاتف المسافر والتحقق منه بصيغة E.164 بعد تسجيل الدخول
00224connected_transport_core.sqlStops, Journey Legs, quote snapshots, and Trip-specific seat/extra inventory
00225connected_operations_control.sqlCaller-bound telemetry, Stop Calls, check-in, disruptions, alerts, announcements, and public tracking
00226partner_quality_settlements.sqlVersioned quality, SLA evidence, commercial terms, settlements, documents, payments, and monthly scheduling
00227atomic_journey_booking_inventory.sqlJourney Booking, Passengers, signed Tickets, and atomic cross-leg seat/extra quote-hold-confirm flow
00228journey_discovery.sqlبحث رحلات مترابطة مباشر/متعدد المقاطع مع نوافذ تحويل وتسعير وسعة خادمية
00229journey_checkout_read_model.sqlنموذج قراءة checkout آمن يعيد العرض والمخزون والركاب والمقاطع من الحقيقة الأساسية
00230atomic_journey_orders.sqlطلب Journey ذري يدعم الذهاب والعودة ويثبت كل الحجوزات أو يتراجع بالكامل
00231journey_order_hold_lifecycle.sqlتحرير وانتهاء حجوزات موارد الطلب بأوامر idempotent وصلاحيات مرتبطة بالمالك
00232traveler_phone_completion_privileges.sqlتضييق صلاحيات RPC استكمال الهاتف إلى الأدوار المطلوبة فقط
00233default_city_stop_lifecycle.sqlإنشاء وصيانة محطة افتراضية لكل مدينة من دون hard-code لتطوير البحث متعدد المحطات
00234cash_shift_null_payment_guard.sqlإصلاح شرط الصندوق النقدي كي لا يعامل provider الفارغ كتحصيل مكتبي
00235booking_reference_trigger_order.sqlضمان إنشاء مرجع الحجز قبل bridge الخاص بـJourney aggregate
00236seed_bus_layout_provisioning.sqlتهيئة تخطيط مقاعد صالح لباصات QA عبر دالة مالك قاعدة البيانات غير مكشوفة للتطبيقات
00237connected_operations_rls_predicates.sqlpredicates مفوضة ضيقة لقراءة التنبيهات والتعطلات والإعلانات عبر tenant
00238live_operations_rls_consolidation.sqlإزالة سياسات GPS الواسعة/التكرارية وتوحيد قراءة الأحداث والمحطات والموقع الحي حسب الدور والعلاقة
00239operations_exception_priority.sqlترتيب مركز العمليات حسب الخطر الفعلي وإعطاء الأولوية للرحلات النشطة والتأخير وGPS والتنبيهات
00240journey_order_column_resolution.sqlإزالة التباس أسماء أعمدة/مخرجات تأكيد Journey Order داخل PL/pgSQL
00241cash_reconciliation_rls_predicate.sqlpredicate آمن وعزل tenant لقراءة ورديات وتسويات الصندوق النقدي
00242cash_reconciliation_rpc_privileges.sqlحصر أوامر التسوية المالية بالمستخدمين المصرح لهم وwebhook مزود الدفع بـservice role
00243traveler_journey_history.sqlسجل Journey موحّد للذهاب والعودة والرحلات المفردة، مقيّد بالمالك ومقسّم بمؤشر ثابت
00244traveler_journey_tracking.sqlتتبع حي مقيّد بملكية المسافر يعيد المرحلة التشغيلية الصحيحة لكل Journey
00245preserve_booked_trip_stop_calls.sqlتحديث Stop Calls المحجوزة في مكانها عند إعادة توقيت Trip بدلاً من كسر مراجع Journey
00246traveler_journey_self_service.sqlإلغاء Journey ذاتي بسياسة شركة، quote مسبق، idempotency، وتحرير ذري للمقاعد والموارد
00247canonical_journey_ticket_boarding.sqlتسجيل صعود ذري لكل Passenger × Journey Leg عبر Ticket موقعة مع مزامنة offline
00248reconcile_enterprise_notification_migration_history.sqlتحقق مغلق من بنية الإشعارات قبل تسوية checksum تاريخ migration 00217
00249connected_journey_rls_predicates.sqlpredicates غير متكررة تربط قراءة Journey بالمالك أو الشركة أو الدور التشغيلي
00250connected_demo_tenant_lifecycle.sqlحذف وإعادة بناء عالم العرض المترابط بترتيب الاعتماديات مع حصر bypass بالـdemo فقط
00251indexed_dashboard_trip_page.sqlحدود يوم تشغيل قابلة للفهرسة، صفحة الرحلات قبل joins، وفهارس الشركة/الحالة/وقت الانطلاق
00252driver_trip_execution_read_model.sqlنموذج تنفيذ رحلة السائق من Journey Tickets مع manifest وحضور وأحداث تشغيل مترابطة
00253enterprise_booking_workspace_and_documents.sqlمساحة عمل حجز شركة موحدة وسجل تأكيد/فاتورة/إلغاء immutable ومراجع وأحداث تصيير
00254company_ticket_attendance.sqlكشف شركة موحد على Journey Ticket وحضور/غياب/تراجع ذري لكل مسافر مع fallback للحجوزات القديمة
00255company_owned_loyalty.sqlبرنامج ولاء مستقل لكل شركة مع عزل الأرصدة والمكافآت والإحالات وعقود الكسب والاستبدال الذرية
00256connected_transport_directory_rls.sqlفصل قراءة دليل المحطات وأنماط المسار والمخزون العام عن جداول الهوية الخاصة بواسطة RLS predicates ضيقة
00257runtime_security_posture_reassertion.sqlسحب تنفيذ trigger functions من أدوار العميل وحصر SECURITY DEFINER المجهولة بقائمة تواقيع علنية دقيقة
00258driver_directory_rls_boundary.sqlعزل دليل السائقين حسب الشركة والدور من دون كشف سجلات شركات أخرى
00259driver_attendance_and_incident_corrections.sqlتصحيح هوية السائق وحضور المسافر والحوادث مع دعم تراجع عدم الحضور
00260canonical_seat_inventory_compatibility.sqlتوحيد مخزون المقاعد الأساسي مع عدادات التوافق ومنع الانحراف بين قنوات البيع
00261crypto_function_search_path.sqlتثبيت search_path لدوال التشفير داخل المعاملات لمنع فشل قفل المقاعد وعمليات الحجز
00262retire_platform_loyalty_rpc_acl.sqlسحب تنفيذ عقود الولاء العامة المتقاعدة من كل أدوار API وservice_role
00263canonical_booking_trip_reassignment.sqlنقل Booking Segment بين الرحلات مع مزامنة Journey Leg والمقاعد والتذاكر والإضافات والتسعير والمخزون
00264notification_worker_acl_reassertion.sqlسحب عقود التذكير المباشر المتقاعدة وإعادة تثبيت قائمة أوامر عامل الإشعارات الدائم
00265complete_pgcrypto_function_search_paths.sqlإكمال search_path الموثوق لدوال حجز Journey والجودة والتسويات التي تستخدم pgcrypto
00266reconcile_converted_seat_locks_once.sqlمنع احتساب قفل المقعد والحجز الناتج عنه مرتين أثناء التحويل وإعادة مصالحة مخزون المقاعد
00267google_traveler_auth_candidate.sqlحل هوية المسافر الموثقة للخادم فقط ومنع إعادة استخدام حسابات الموظفين في دخول Google على الويب والموبايل
00268preserve_checked_in_seat_occupancy.sqlإبقاء المقعد مشغولاً بعد تسجيل الصعود أو إكمال الرحلة وتحريره فقط عند الإلغاء أو عدم الحضور
00269safe_google_traveler_identity_linking.sqlربط Google بحساب المسافر الحالي ذرياً مع إثبات الملكية ومنع التعارض مع مسافر أو موظف آخر
00270booking_confirmation_boarding_qr.sqlإضافة اعتماد صعود موقّع لكل مسافر إلى snapshot تأكيد الحجز وتوحيد رابط التذكرة في قوالب الاتصال
00271booking_guest_access_sessions.sqlsnapshot منظم لاسم العائلة وجلسة ضيف opaque محددة بالحجز مع مهلة خمول 15 دقيقة وحد مطلق 30 دقيقة
00272legacy_booking_canonical_ticket_sync.sqlإسقاط حجوزات التوافق من قنوات العميل والمكتب إلى المقاعد وتذاكر Journey الموقّعة ذرياً وبصورة idempotent
00273preserve_expired_legacy_booking_holds.sqlحماية حجوزات التوافق المنتهية من إعادة التفعيل وإغلاق إسقاطات مقاعدها بناءً على دليل الجلسة الدائم
00274isolate_native_journey_from_legacy_ticket_sync.sqlعزل Journey checkout الأصلي صراحةً عن trigger التوافق ومنع تفسير source الفارغ على أنه حجز legacy
00275service_only_identity_aware_journey_confirmation.sqlحصر أوامر تأكيد Journey التي تحفظ اسم العائلة في خدمة الخادم ومنع استدعاء SECURITY DEFINER مباشرة من anon أو authenticated
00276traveler_booking_billing_address.sqlعنوان فوترة اختياري محفوظ ضمن حقيقة حجز المسافر
00277card_payment_billing_details.sqlاشتراط بيانات الفوترة المهنية عند تفعيل الدفع بالبطاقة فقط
00278online_payments_development_only.sqlإبقاء محولات الدفع الإلكتروني في وضع التطوير مع النقد كخيار إنتاج افتراضي
00279trip_pickup_locations.sqlنقاط صعود مملوكة للشركة مع إحداثيات وتعليمات ولقطة ثابتة لكل رحلة
00280booking_pickup_location_documents.sqlتمرير لقطة موقع الصعود إلى مستندات الحجز والتذكرة
00281cash_only_payment_trigger_grants.sqlإعادة تثبيت صلاحيات أوامر الدفع النقدي بعد إضافة حواجز طرق الدفع
00282notification_pickup_context.sqlإضافة سياق موقع الصعود إلى قوالب الإشعارات المعاملاتية
00283trip_dispatch_pickup_projection.sqlإظهار موقع الصعود في نموذج قراءة التشغيل والإرسال
00284notification_pgcrypto_search_path.sqlتثبيت مسار pgcrypto الموثوق لدوال الإشعارات
00285booking_confirmation_ticket_copy.sqlتوحيد نص رابط التذكرة ورمز الصعود في تأكيدات الحجز
00286public_trip_pickup_projection.sqlإعادة بناء عرض الرحلات العام ليشمل لقطة موقع الصعود
00287journey_search_stop_call_identity.sqlفصل هوية Stop عن Trip Stop Call في البحث مع توافق العملاء وربط آمن بعرض السعر
00288trip_account_scope_isolation.sqlعزل مخزون الإنتاج وQA والعروض وقصر اكتشاف المسافر على الشركات النشطة المنشورة غير الموقوفة
00289backfill_legacy_internal_tenant_scope.sqlتصنيف شركة QA الإنتاجية القديمة وترقية الهويات المرتبطة بها إلى نطاق داخلي موثوق
00290journey_quote_postgres_uuid_compatibility.sqlقبول كل UUID قانوني في PostgreSQL عند إنشاء عرض Journey، بما فيه معرفات QA الحتمية القديمة
00291authenticated_journey_confirmation_actor.sqlتمرير هوية المسافر المتحقق منها عبر جسر خدمة محدود عند تأكيد Journey
00292legacy_booking_durable_journey_sessions.sqlإنشاء جلسة Journey دائمة للحجوزات المتوافقة كي يعيد المالك فتح التأكيد والتذاكر
00293demo_passenger_bookable_launch.sqlاختيار مسار Demo مستقبلي قابل للبيع فعلياً حسب Stop Calls والمركبة والمخزون وتوقيت دمشق
00294publish_isolated_demo_profiles.sqlنشر ملف شركة Demo المعزولة آلياً مع إبقاء رؤيتها محكومة بنطاق الحساب
00295runtime_schema_client_grant_hardening.sqlإعادة تثبيت العقود النهائية لـ00292–00294 وتسوية checksums محددة وإغلاق جداول تجاوزت RLS بسبب grants قديمة
00296security_definer_search_path_and_service_table_grants.sqlتثبيت search_path لكل SECURITY DEFINER قديمة وسحب ACL عميل متبقٍ من حالة ترحيل PII
00297company_cash_payment_settings_provisioning.sqlضمان سياسة دفع نقدية افتراضية لكل شركة قائمة وجديدة مع قيد uniqueness وعقد onboarding
00298secure_document_share_grants.sqlمنح مشاركة مستند واحد، جلسات recipient محدودة، وأحداث تدقيق immutable مع أسرار مخزنة كـhash فقط
00299document_share_issuer_authorization.sqlحل مالك الحجز أو السائق المعيّن من هوية الخادم ومنع actor scope يختاره العميل
00300two_phase_document_share_delivery.sqlفصل تفويض المستند عن تسجيل التسليم حتى لا ينشئ فشل renderer حدث عرض أو تنزيل كاذب
00301complete_document_share_issuer_roles.sqlاستكمال مصدري المشاركة بمشغلي الشركة والإدارة النشطين وإلغاء المنحة بهوية المصدر الموثقة
00302database_authoritative_document_share_expiry.sqlحساب آجال المنحة والجلسة من ساعة PostgreSQL الموثوقة ومنع اختلاف ساعة التطبيق من تمديدها أو رفضها خطأً
00303document_share_demo_lifecycle.sqlربط سجلات مشاركة المستند بدورة حياة مستندات العروض التجريبية كي لا تمنع إعادة ضبط المستأجر التجريبي مع إبقاء السجل immutable خارج teardown
00304document_share_finance_role_scope.sqlإرجاع دور مصدر المستند من الهوية الموثقة، حصر مستندات مالية الشركة بالمالك/المدير، واستبعاد دعم المنصة من قناة الإصدار

تشغيل الترحيلات

التطوير المحلي

# التحقق من سلسلة الترحيلات أولاً
make db-validate-migrations

# تشغيل الترحيلات الجديدة فقط
make db-migrate

# تشغيل جميع عقود قاعدة البيانات داخل معاملات قابلة للتراجع
pnpm test:db-contracts

# مطابقة المخطط الحي وسجل الترحيلات وRLS والفهارس والصلاحيات
pnpm audit:db-schema

الإنتاج

# تحقق من أن الأرقام فريدة
./infrastructure/scripts/validate-migrations.sh

# طبّق الترحيلات بالتسلسل مع تسجيلها
# هذا الأمر يطبّق أيضاً seed مرجعياً دائماً من
# infrastructure/supabase/seeds/reference-data.sql
./infrastructure/scripts/run-migrations.sh

# إذا احتجت demo seed الشامل فاستخدمه فقط خارج الإنتاج
./infrastructure/scripts/run-migrations.sh --seed

# لحسابات الإنتاج الداخلية فقط
./infrastructure/scripts/run-migrations.sh --internal-seed

بعد ترحيل يضيف جدولاً أو دالةً تستخدمها PostgREST مباشرة، أعد تحميل schema cache إذا لم تفعل أداة التشغيل ذلك تلقائياً:

docker compose exec -T db psql -U postgres -d postgres -c "NOTIFY pgrst, 'reload schema';"

تعمل سكربتات الترحيل على Bash الافتراضي في macOS أيضاً، لذلك لا تتطلب Bash 4 أو mapfile.

الملف infrastructure/supabase/seeds/reference-data.sql يُطبَّق في كل تشغيل لأنه يحتوي على بيانات مرجعية مطلوبة في جميع البيئات مثل صلاحيات الإدارة والمدن السورية.

--seed يشير إلى infrastructure/supabase/seed.sql وهو seed واسع للديمو/التطوير. لا تستخدمه في الإنتاج الحقيقي.
أما --internal-seed فيستدعي seed محدوداً وموجهاً للحسابات التشغيلية الداخلية فقط.


كتابة ترحيل جديد

1. إنشاء الملف

# الحصول على آخر رقم
ls infrastructure/supabase/migrations/ | tail -1

# إنشاء ملف جديد
touch infrastructure/supabase/migrations/00069_my_migration.sql

2. هيكل الترحيل

-- Migration: 00069_my_migration.sql
-- Description: وصف التغيير
-- Author: اسم المطور
-- Date: YYYY-MM-DD

-- ============================================
-- التغييرات
-- ============================================

-- 1. إضافة جدول جديد
CREATE TABLE IF NOT EXISTS new_table (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
name TEXT NOT NULL,
created_at TIMESTAMPTZ DEFAULT NOW()
);

-- 2. إضافة فهرس
CREATE INDEX IF NOT EXISTS idx_new_table_name ON new_table(name);

-- 3. تفعيل RLS
ALTER TABLE new_table ENABLE ROW LEVEL SECURITY;

-- 4. إضافة سياسة
CREATE POLICY "new_table_select" ON new_table
FOR SELECT USING (true);

-- 5. منح الصلاحيات
GRANT ALL ON new_table TO authenticated;
GRANT SELECT ON new_table TO anon;

-- 6. التعليقات
COMMENT ON TABLE new_table IS 'وصف الجدول';

3. أفضل الممارسات

لا تستخدم رقماً موجوداً مسبقاً

كل ترحيل يجب أن يملك prefix رقمي فريد من 5 خانات. مثال:

00109_admin_platform_operations.sql
00110_next_change.sql

أي تكرار في الرقم سيُعتبر خطأ نشر ويمنع التطبيق.

استخدام IF NOT EXISTS / IF EXISTS

-- جيد
CREATE TABLE IF NOT EXISTS my_table (...);
DROP INDEX IF EXISTS idx_my_index;
DROP POLICY IF EXISTS "my_policy" ON my_table;

-- سيء - قد يفشل
CREATE TABLE my_table (...);
CREATE POLICY "my_policy" ON my_table FOR SELECT USING (true);

التحقق قبل التعديل

-- التحقق من وجود العمود قبل إضافته
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.columns
WHERE table_name = 'my_table' AND column_name = 'new_column'
) THEN
ALTER TABLE my_table ADD COLUMN new_column TEXT;
END IF;
END $$;

استخدام المعاملات

BEGIN;

-- التغييرات هنا
ALTER TABLE my_table ADD COLUMN col1 TEXT;
ALTER TABLE my_table ADD COLUMN col2 INTEGER;

COMMIT;

أنماط شائعة

إضافة عمود جديد

-- إضافة عمود مع قيمة افتراضية
ALTER TABLE trips ADD COLUMN IF NOT EXISTS
delay_minutes INTEGER DEFAULT 0;

-- إضافة عمود JSONB
ALTER TABLE buses ADD COLUMN IF NOT EXISTS
metadata JSONB DEFAULT '{}'::JSONB;

-- تعليق على العمود
COMMENT ON COLUMN trips.delay_minutes IS 'دقائق التأخير';

إضافة نوع ENUM جديد

-- إضافة قيمة لـ ENUM موجود
ALTER TYPE trip_status ADD VALUE IF NOT EXISTS 'DELAYED';

-- إنشاء ENUM جديد
DO $$
BEGIN
IF NOT EXISTS (SELECT 1 FROM pg_type WHERE typname = 'payment_status') THEN
CREATE TYPE payment_status AS ENUM ('PENDING', 'PAID', 'REFUNDED');
END IF;
END $$;

إنشاء دالة Trigger

-- دالة تحديث updated_at
CREATE OR REPLACE FUNCTION update_updated_at()
RETURNS TRIGGER AS $$
BEGIN
NEW.updated_at = NOW();
RETURN NEW;
END;
$$ LANGUAGE plpgsql;

-- إنشاء الـ Trigger
DROP TRIGGER IF EXISTS my_table_updated_at ON my_table;
CREATE TRIGGER my_table_updated_at
BEFORE UPDATE ON my_table
FOR EACH ROW
EXECUTE FUNCTION update_updated_at();

تحديث البيانات الموجودة

-- تحديث بسيط
UPDATE bookings SET status = 'CONFIRMED'
WHERE status = 'PENDING' AND created_at < NOW() - INTERVAL '1 hour';

-- تحديث مع JOIN
UPDATE seats s
SET status = 'BOOKED', booking_id = b.id
FROM bookings b
WHERE s.trip_id = b.trip_id
AND s.seat_number = b.seat_number
AND b.status = 'CONFIRMED';

التراجع عن الترحيلات

إنشاء ملف تراجع

-- Rollback: 00069_my_migration.sql

-- حذف السياسات
DROP POLICY IF EXISTS "new_table_select" ON new_table;

-- إلغاء RLS
ALTER TABLE new_table DISABLE ROW LEVEL SECURITY;

-- حذف الجدول
DROP TABLE IF EXISTS new_table;

تنفيذ التراجع

psql $DATABASE_URL -f rollback/00069_rollback.sql

استكشاف الأخطاء

خطأ: "relation already exists"

-- الحل: استخدام IF NOT EXISTS
CREATE TABLE IF NOT EXISTS my_table (...);

خطأ: "column does not exist"

-- التحقق من وجود العمود
SELECT column_name FROM information_schema.columns
WHERE table_name = 'my_table';

خطأ: "cannot drop type because other objects depend on it"

-- عرض التبعيات
SELECT * FROM pg_depend WHERE refobjid = 'my_type'::regtype::oid;

-- الحذف المتسلسل (خطير!)
DROP TYPE my_type CASCADE;

خطأ: "permission denied"

-- منح الصلاحيات
GRANT ALL ON my_table TO authenticated;
GRANT USAGE ON SCHEMA public TO anon;

التحقق من الترحيل

قبل التطبيق

-- التحقق من الصياغة
\i infrastructure/supabase/migrations/00069_my_migration.sql

-- في معاملة (لا يُطبق)
BEGIN;
\i infrastructure/supabase/migrations/00069_my_migration.sql
ROLLBACK;

بعد التطبيق

-- التحقق من الجدول
\d my_table

-- التحقق من السياسات
SELECT * FROM pg_policies WHERE tablename = 'my_table';

-- التحقق من الفهارس
SELECT * FROM pg_indexes WHERE tablename = 'my_table';

سجل الترحيلات

نستخدم جدول لتتبع الترحيلات المطبقة:

CREATE TABLE IF NOT EXISTS _migrations (
id SERIAL PRIMARY KEY,
name TEXT UNIQUE NOT NULL,
applied_at TIMESTAMPTZ DEFAULT NOW()
);

-- تسجيل ترحيل
INSERT INTO _migrations (name) VALUES ('00069_my_migration');

أوامر مفيدة

# عرض حالة الترحيلات
supabase db status

# إنشاء ترحيل من التغييرات
supabase db diff -f my_migration

# تطبيق الترحيلات
supabase db push

# إعادة تعيين قاعدة البيانات
supabase db reset

In-place migration edits & checksum drift

The runner records a sha256 checksum of every applied migration in the applied_migrations table and compares the complete applied history in one database query. Editing a file after it has been applied to any environment now stops the runner before new migrations execute. The runner never re-applies an edited migration because that could repeat production side effects.

When in-place edits are allowed

Only when the edit is a no-op against the current state of every environment the migration has already been applied to. Examples:

  • Adding a comment / RAISE NOTICE
  • Reordering idempotent IF NOT EXISTS / IF EXISTS guards in a way that doesn't change which statements run
  • Fixing a bug in a section guarded by IF that was a no-op on prod

If the edit changes what the migration did on a previously-applied environment, write a new migration instead. The new migration should re-run the corrective work; the old migration's checksum stays whatever prod recorded.

Updating applied_migrations.checksum after a deliberate in-place edit

After confirming the on-prod state already reflects the new content, update the checksum so future drift checks pass:

UPDATE applied_migrations
SET checksum = '<new_sha256>'
WHERE filename = '<migration_filename>';

The new checksum can be recomputed locally with shasum -a 256 infrastructure/supabase/migrations/<file>.sql on macOS or sha256sum infrastructure/supabase/migrations/<file>.sql on Linux.

Tooling

infrastructure/scripts/validate-migrations.sh validates filename format and sequencing. run-migrations.sh then validates each recorded filename and checksum before applying anything. The single historical pre-release draft of 00166 is accepted only for its exact old and current hashes; 00206 reasserts the intended schema/data state and normalizes that record. Every other checksum transition fails closed and requires a corrective migration plus explicit operational review.