Skip to content

مكتبة database — قواعد البيانات

مكتبة قواعد البيانات في لغة ص توفّر واجهة عربية شاملة للتعامل مع قواعد بيانات SQLite3 المدمجة. لا تحتاج إلى تثبيت خادم قواعد بيانات خارجي — كل شيء يعمل مباشرة من ملف واحد. تدعم المكتبة الاستعلامات المحضرة (Prepared Statements) للحماية من حقن SQL، والمعاملات (Transactions) لضمان سلامة البيانات، وبناء الاستعلامات البرمجي (Query Builder)، بالإضافة إلى أدوات إدارة المخطط والنسخ الاحتياطي.

الاستيراد

sad
استورد قاعدة_بيانات من "stdlib/database"

ملخص الدوال

الدالةالوصف
قاعدة_بيانات.sqlite(مسار)إنشاء اتصال بقاعدة بيانات SQLite
ق.نفّذ(sql، معاملات)تنفيذ استعلام SQL
ق.استعلم(sql، معاملات)تنفيذ استعلام وإرجاع النتائج
ق.صف_واحد(sql)إرجاع صف واحد فقط
ق.قيمة(sql)إرجاع قيمة مفردة
ق.حضّر(sql)إنشاء استعلام محضر
ق.معاملة(دالة)تنفيذ عمليات ضمن معاملة
ق.جداول()قائمة الجداول في قاعدة البيانات
ق.جدول_موجود(اسم)التحقق من وجود جدول
ق.أعمدة(جدول)معلومات أعمدة الجدول
ق.آخر_معرف()معرف آخر صف مُدرج
ق.التغييرات()عدد الصفوف المتأثرة
ق.نسخ_احتياطي(مسار)نسخ القاعدة إلى ملف آخر
ق.تحسين()تحسين أداء القاعدة (VACUUM)
ق.إصدار()إصدار محرك SQLite
ق.أغلق()إغلاق الاتصال

الاتصال وإدارة قاعدة البيانات

قاعدة_بيانات.sqlite(مسار)

تنشئ اتصالًا جديدًا بقاعدة بيانات SQLite. إذا لم يكن الملف موجودًا، يتم إنشاؤه تلقائيًا. يمكنك أيضًا استخدام قاعدة بيانات في الذاكرة عبر تمرير ":memory:" كمسار، وهي مفيدة للاختبارات السريعة والبيانات المؤقتة. يُعاد كائن قاعدة البيانات الذي من خلاله تُنفّذ جميع العمليات.

sad
استورد قاعدة_بيانات من "stdlib/database"

# اتصال بملف قاعدة بيانات
متغير ق = جديد قاعدة_بيانات.sqlite("مكتبة_المسجد.db")
اطبع_سطر("تم الاتصال بقاعدة البيانات بنجاح")

# قاعدة بيانات في الذاكرة (مؤقتة)
متغير ق_مؤقت = جديد قاعدة_بيانات.sqlite(":memory:")

ق.أغلق()

تُغلق الاتصال بقاعدة البيانات وتحرر جميع الموارد المرتبطة. يجب استدعاء هذه الدالة عند الانتهاء من استخدام القاعدة لتجنب تسرب الموارد. أي عمليات معلقة غير مؤكدة يتم التراجع عنها تلقائيًا.

sad
متغير ق = جديد قاعدة_بيانات.sqlite("بيانات.db")
# ... عمليات على القاعدة ...
ق.أغلق()
اطبع_سطر("تم إغلاق الاتصال")

تنفيذ الاستعلامات

ق.نفّذ(sql، معاملات)

تنفّذ استعلام SQL مثل CREATE TABLE أو INSERT أو UPDATE أو DELETE. تُرجع عدد الصفوف المتأثرة بالعملية. يمكنك تمرير معاملات كمصفوفة لاستخدامها مع عناصر النائبة ? في الاستعلام، مما يحمي من هجمات حقن SQL.

sad
متغير ق = جديد قاعدة_بيانات.sqlite("مكتبة.db")

# إنشاء جدول الكتب
ق.نفّذ("CREATE TABLE IF NOT EXISTS كتب (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    عنوان TEXT NOT NULL,
    مؤلف TEXT NOT NULL,
    سنة_النشر INTEGER,
    عدد_الصفحات INTEGER,
    التصنيف TEXT
)")

# إدراج كتاب باستخدام معاملات (حماية من حقن SQL)
ق.نفّذ("INSERT INTO كتب (عنوان، مؤلف، سنة_النشر، عدد_الصفحات، التصنيف) VALUES (?, ?, ?, ?, ?)"،
    ["صحيح البخاري"، "الإمام البخاري"، 846، 7563، "حديث"])

ق.نفّذ("INSERT INTO كتب (عنوان، مؤلف، سنة_النشر، عدد_الصفحات، التصنيف) VALUES (?, ?, ?, ?, ?)"،
    ["الموطأ"، "الإمام مالك"، 795، 1720، "فقه"])

ق.نفّذ("INSERT INTO كتب (عنوان، مؤلف، سنة_النشر، عدد_الصفحات، التصنيف) VALUES (?, ?, ?, ?, ?)"،
    ["إحياء علوم الدين"، "الإمام الغزالي"، 1106، 4500، "تزكية"])

ق.استعلم(sql، معاملات)

تنفّذ استعلام SQL وتُرجع النتائج كمصفوفة من الصفوف. كل صف عبارة عن خريطة (Map) حيث المفتاح هو اسم العمود والقيمة هي محتوى الخلية. تُستخدم مع استعلامات SELECT لجلب البيانات. تدعم المعاملات المسماة والموضعية.

sad
متغير ق = جديد قاعدة_بيانات.sqlite("مكتبة.db")

# جلب جميع الكتب
متغير كتب = ق.استعلم("SELECT * FROM كتب")
لكل كتاب في كتب
    اطبع_سطر(كتاب["عنوان"] + " — " + كتاب["مؤلف"])
نهاية

# جلب كتب تصنيف معين
متغير كتب_الحديث = ق.استعلم("SELECT عنوان، مؤلف FROM كتب WHERE التصنيف = ?"، ["حديث"])
لكل كتاب في كتب_الحديث
    اطبع_سطر("📖 " + كتاب["عنوان"])
نهاية

ق.صف_واحد(sql)

تنفّذ استعلام SQL وتُرجع الصف الأول فقط من النتائج، أو لاشيء إذا لم تُوجد نتائج. مفيدة عند البحث عن سجل محدد بالمفتاح الأساسي أو عند توقع نتيجة واحدة فقط.

sad
# البحث عن كتاب بعنوانه
متغير كتاب = ق.صف_واحد("SELECT * FROM كتب WHERE عنوان = ?"، ["صحيح البخاري"])
إذا (كتاب != لاشيء)
    اطبع_سطر("المؤلف: " + كتاب["مؤلف"])
    اطبع_سطر("الصفحات: " + لنص(كتاب["عدد_الصفحات"]))
وإلا
    اطبع_سطر("الكتاب غير موجود")
نهاية

ق.قيمة(sql)

تنفّذ استعلام SQL وتُرجع قيمة مفردة (العمود الأول من الصف الأول). مثالية لاستعلامات التجميع مثل COUNT وSUM وAVG التي تُرجع رقمًا واحدًا.

sad
# عدد الكتب في المكتبة
متغير العدد = ق.قيمة("SELECT COUNT(*) FROM كتب")
اطبع_سطر("عدد الكتب: " + لنص(العدد))

# متوسط عدد الصفحات
متغير المتوسط = ق.قيمة("SELECT AVG(عدد_الصفحات) FROM كتب")
اطبع_سطر("متوسط الصفحات: " + لنص(المتوسط))

# إجمالي الصفحات
متغير الإجمالي = ق.قيمة("SELECT SUM(عدد_الصفحات) FROM كتب")
اطبع_سطر("إجمالي الصفحات: " + لنص(الإجمالي))

الاستعلامات المحضرة

ق.حضّر(sql)

تُنشئ استعلامًا محضرًا (Prepared Statement) يمكن تنفيذه عدة مرات بمعاملات مختلفة. الاستعلامات المحضرة أسرع من تنفيذ SQL النصي المتكرر لأن قاعدة البيانات تُحلّل الاستعلام مرة واحدة فقط. كما أنها آمنة من حقن SQL بشكل افتراضي.

sad
متغير ق = جديد قاعدة_بيانات.sqlite("طلاب.db")

ق.نفّذ("CREATE TABLE IF NOT EXISTS طلاب (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    اسم TEXT NOT NULL,
    مستوى_الحفظ TEXT,
    عدد_الأجزاء INTEGER
)")

# استعلام محضر لإدراج طلاب حلقة التحفيظ
متغير إدراج = ق.حضّر("INSERT INTO طلاب (اسم، مستوى_الحفظ، عدد_الأجزاء) VALUES (?, ?, ?)")

# إدراج عدة طلاب بكفاءة
متغير طلاب = [
    ["عمر بن أحمد"، "متقدم"، 20]،
    ["فاطمة العلي"، "متوسط"، 10]،
    ["خالد الحربي"، "مبتدئ"، 3]،
    ["مريم السالم"، "متقدم"، 30]
]

لكل طالب في طلاب
    إدراج.اربط(1، طالب[0])
    إدراج.اربط(2، طالب[1])
    إدراج.اربط(3، طالب[2])
    إدراج.نفّذ()
    إدراج.أعد()
نهاية

اطبع_سطر("تم تسجيل " + لنص(حجم(طلاب)) + " طلاب")

المعاملات (Transactions)

ق.معاملة(دالة)

تنفّذ مجموعة من العمليات ضمن معاملة (Transaction) واحدة. إذا نجحت جميع العمليات، يتم تأكيدها (Commit). وإذا حدث خطأ في أي عملية، يتم التراجع عن الكل (Rollback)، مما يضمن سلامة البيانات وعدم وصولها إلى حالة غير متسقة. المعاملات أيضًا أسرع بكثير عند إدراج كمية كبيرة من البيانات.

sad
متغير ق = جديد قاعدة_بيانات.sqlite("وقف.db")

ق.نفّذ("CREATE TABLE IF NOT EXISTS حسابات (id INTEGER PRIMARY KEY، اسم TEXT، رصيد REAL)")
ق.نفّذ("INSERT INTO حسابات VALUES (1, 'صندوق الوقف', 100000.0)")
ق.نفّذ("INSERT INTO حسابات VALUES (2, 'مشروع بناء المسجد', 0.0)")

# تحويل مبلغ ضمن معاملة — إما يتم الكل أو لا شيء
ق.معاملة(دالة()
    متغير مبلغ = 25000.0

    # خصم من صندوق الوقف
    ق.نفّذ("UPDATE حسابات SET رصيد = رصيد - ? WHERE id = 1"، [مبلغ])

    # إضافة إلى مشروع المسجد
    ق.نفّذ("UPDATE حسابات SET رصيد = رصيد + ? WHERE id = 2"، [مبلغ])

    اطبع_سطر("تم تحويل " + لنص(مبلغ) + " ريال بنجاح")
نهاية)

# التحقق من الأرصدة
متغير حسابات = ق.استعلم("SELECT * FROM حسابات")
لكل حساب في حسابات
    اطبع_سطر(حساب["اسم"] + ": " + لنص(حساب["رصيد"]) + " ريال")
نهاية

معلومات المخطط

ق.جداول()

تُرجع مصفوفة بأسماء جميع الجداول الموجودة في قاعدة البيانات. مفيدة لاستكشاف بنية القاعدة أو للتحقق من حالة الترحيل (Migration).

sad
متغير القوائم = ق.جداول()
اطبع_سطر("الجداول الموجودة:")
لكل جدول في القوائم
    اطبع_سطر("  - " + جدول)
نهاية

ق.جدول_موجود(اسم)

تتحقق مما إذا كان الجدول المحدد موجودًا في قاعدة البيانات. تُرجع صحيح أو خطأ. مفيدة قبل إنشاء جداول جديدة أو عند التحقق من تطبيق الترحيلات.

sad
إذا (ق.جدول_موجود("طلاب"))
    اطبع_سطر("جدول الطلاب موجود")
وإلا
    اطبع_سطر("سيتم إنشاء جدول الطلاب...")
    ق.نفّذ("CREATE TABLE طلاب (id INTEGER PRIMARY KEY، اسم TEXT)")
نهاية

ق.أعمدة(جدول)

تُرجع معلومات تفصيلية عن أعمدة الجدول المحدد، بما في ذلك الاسم والنوع وما إذا كان مفتاحًا أساسيًا.

sad
متغير الأعمدة = ق.أعمدة("كتب")
لكل عمود في الأعمدة
    اطبع_سطر(عمود["اسم"] + " (" + عمود["نوع"] + ")")
نهاية

أدوات مساعدة

ق.آخر_معرف()

تُرجع معرف (ID) آخر صف تم إدراجه باستخدام AUTOINCREMENT. مفيدة بعد إدراج سجل جديد عندما تحتاج إلى معرفة المعرف المولّد تلقائيًا.

sad
ق.نفّذ("INSERT INTO كتب (عنوان، مؤلف) VALUES (?, ?)"، ["تفسير الطبري"، "ابن جرير الطبري"])
متغير المعرف = ق.آخر_معرف()
اطبع_سطر("تم إدراج الكتاب بمعرف: " + لنص(المعرف))

ق.التغييرات()

تُرجع عدد الصفوف التي تأثرت بآخر عملية INSERT أو UPDATE أو DELETE. مفيدة للتحقق من نجاح العمليات ومعرفة حجم التأثير.

sad
ق.نفّذ("UPDATE كتب SET التصنيف = 'تراث' WHERE سنة_النشر < 1000")
متغير المتأثرة = ق.التغييرات()
اطبع_سطر("تم تحديث " + لنص(المتأثرة) + " كتاب")

ق.نسخ_احتياطي(مسار)

تنسخ قاعدة البيانات الحالية بالكامل إلى ملف جديد. هذه عملية نسخ ذري — إما تنجح بالكامل أو لا تحدث. مفيدة لإنشاء نسخ احتياطية دورية قبل العمليات الحرجة.

sad
# نسخ احتياطي قبل عملية ترحيل كبيرة
ق.نسخ_احتياطي("نسخ_احتياطية/مكتبة_" + صيغة_تاريخ(الآن()، "%Y%m%d") + ".db")
اطبع_سطر("تم إنشاء النسخة الاحتياطية")

ق.تحسين()

تُنفّذ عملية VACUUM على قاعدة البيانات، مما يُعيد بناء الملف ويستعيد المساحة غير المستخدمة ويُحسّن الأداء. يُنصح بتنفيذها بعد حذف كمية كبيرة من البيانات.

sad
# حذف السجلات القديمة ثم تحسين القاعدة
ق.نفّذ("DELETE FROM سجلات WHERE التاريخ < '2024-01-01'")
ق.تحسين()
اطبع_سطر("تم التنظيف والتحسين")

ق.إصدار()

تُرجع رقم إصدار محرك SQLite المستخدم. مفيدة للتشخيص والتوافقية.

sad
اطبع_سطر("إصدار SQLite: " + ق.إصدار())

مثال شامل — نظام إدارة مكتبة المسجد

sad
استورد قاعدة_بيانات من "stdlib/database"

# إنشاء قاعدة بيانات مكتبة المسجد
متغير ق = جديد قاعدة_بيانات.sqlite("مكتبة_المسجد.db")

# إنشاء الجداول
ق.نفّذ("CREATE TABLE IF NOT EXISTS كتب (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    عنوان TEXT NOT NULL,
    مؤلف TEXT NOT NULL,
    التصنيف TEXT,
    عدد_النسخ INTEGER DEFAULT 1,
    متاح INTEGER DEFAULT 1
)")

ق.نفّذ("CREATE TABLE IF NOT EXISTS استعارات (
    id INTEGER PRIMARY KEY AUTOINCREMENT,
    كتاب_id INTEGER,
    اسم_المستعير TEXT NOT NULL,
    تاريخ_الاستعارة TEXT,
    تاريخ_الإرجاع TEXT,
    FOREIGN KEY (كتاب_id) REFERENCES كتب(id)
)")

# إضافة كتب ضمن معاملة
ق.معاملة(دالة()
    متغير كتب_جديدة = [
        ["رياض الصالحين"، "الإمام النووي"، "حديث"، 5]،
        ["زاد المعاد"، "ابن القيم"، "سيرة"، 3]،
        ["تفسير ابن كثير"، "ابن كثير"، "تفسير"، 2]،
        ["الأذكار"، "الإمام النووي"، "أذكار"، 7]،
        ["فتح الباري"، "ابن حجر العسقلاني"، "شروح"، 2]
    ]

    متغير إدراج = ق.حضّر("INSERT INTO كتب (عنوان، مؤلف، التصنيف، عدد_النسخ) VALUES (?, ?, ?, ?)")
    لكل كتاب في كتب_جديدة
        إدراج.اربط(1، كتاب[0])
        إدراج.اربط(2، كتاب[1])
        إدراج.اربط(3، كتاب[2])
        إدراج.اربط(4، كتاب[3])
        إدراج.نفّذ()
        إدراج.أعد()
    نهاية
نهاية)

# عرض الكتب المتاحة
اطبع_سطر("=== كتب مكتبة المسجد ===")
متغير جميع_الكتب = ق.استعلم("SELECT * FROM كتب WHERE متاح = 1 ORDER BY التصنيف")
لكل كتاب في جميع_الكتب
    اطبع_سطر(كتاب["عنوان"] + " | " + كتاب["مؤلف"] + " | نسخ: " + لنص(كتاب["عدد_النسخ"]))
نهاية

# إحصائيات
متغير إجمالي = ق.قيمة("SELECT COUNT(*) FROM كتب")
متغير إجمالي_نسخ = ق.قيمة("SELECT SUM(عدد_النسخ) FROM كتب")
اطبع_سطر("\nالإجمالي: " + لنص(إجمالي) + " عنوان — " + لنص(إجمالي_نسخ) + " نسخة")

# تسجيل استعارة
ق.معاملة(دالة()
    ق.نفّذ("INSERT INTO استعارات (كتاب_id، اسم_المستعير، تاريخ_الاستعارة) VALUES (?, ?, ?)"،
        [1، "أحمد المحمد"، "2025-01-15"])
    ق.نفّذ("UPDATE كتب SET عدد_النسخ = عدد_النسخ - 1 WHERE id = 1")
نهاية)

اطبع_سطر("تم تسجيل استعارة بنجاح")
ق.أغلق()

أمان الاستعلامات

استخدم دائمًا المعاملات (?) بدلاً من دمج النصوص مباشرة في الاستعلام. هذا يحمي من هجمات حقن SQL (SQL Injection).

sad
# ❌ خطأ وخطير — لا تفعل هذا
ق.نفّذ("SELECT * FROM كتب WHERE عنوان = '" + إدخال_المستخدم + "'")

# ✅ صحيح وآمن — استخدم المعاملات
ق.استعلم("SELECT * FROM كتب WHERE عنوان = ?"، [إدخال_المستخدم])

قاعدة بيانات في الذاكرة

لاختبار الكود بسرعة دون إنشاء ملفات، استخدم ":memory:" كمسار. البيانات تُفقد عند إغلاق البرنامج.

مُرخَّص بموجب رخصة MIT