يمكن لأي تطبيق يعمل مع استعلامات SQL الاستفادة من استخدام منشئ الاستعلامات لتحسين إمكانية قراءة التعليمات البرمجية وقابلية الصيانة والأمان. في الواقع، هناك العديد من المكتبات المختلفة التي تفعل ذلك في جولانج. هنا في Vaunt، جربنا العديد من الخيارات المختلفة قبل أن نقرر أخيرًا إنشاء واحدة بأنفسنا. في النهاية، أردنا شيئًا آمنًا ونوفر بديلاً متغيرًا لمنع حقن SQL مع الاستمرار في قراءته والحصول على عبارات شرطية. لذلك أنشأنا مكتبة جديدة تسمى "تقلا"، وتم إصدارها والإعلان عنها في أواخر العام الماضي. يمكنك قراءة المزيد عنها في هذه المقالة.
قبل أن نبني tqla، استخدمنا Squirrel بشكل أساسي لمنطق بناء استعلام SQL الخاص بنا - ونحن نوصي به بشدة. ما زلنا نستخدم Squirrel في بعض المناطق ولكننا بدأنا تدريجيًا في استبدال وتنفيذ منطق بناء الاستعلام الجديد بـ tqla. لقد وجدنا العديد من الحالات التي قامت فيها tqla بتحسين قدرتنا على الحفاظ على التعليمات البرمجية الخاصة بنا وإصلاح المشكلات التي واجهناها عند استخدام منشئي البيانات الآخرين.
في Vaunt، خضعنا مؤخرًا لعملية ترحيل قاعدة البيانات من CockroachDB إلى TiDB. على الرغم من أن CockroachDB كان فعالاً وموثوقًا، إلا أننا واجهنا في النهاية قرار الإضافة إلى techstack الخاص بنا لدعم قاعدة بيانات OLAP. كانت الحاجة إلى ذلك هي دعم عبء العمل التحليلي لدينا على منتج رؤية المجتمع مفتوح المصدر الخاص بنا. لإبقاء بصمتنا التكنولوجية صغيرة، قررنا المضي قدمًا في استخدام TiDB والاستفادة من بنية HTAP لقاعدة البيانات.
CockroachDB متوافق إلى حد كبير مع PostgreSQL، وقد استخدمنا صيغة PostgreSQL للعديد من استعلامات SQL الخاصة بنا. للتبديل إلى TiDB، كان علينا تغيير عدد قليل من جداولنا وتحديث الاستعلامات لاستخدام بناء جملة MySQL. في عدد قليل من المواقع أثناء الترحيل، اكتشفنا أننا كنا نستخدم بشكل غير صحيح عبارات بناء الاستعلام الشرطي ونفتقر إلى الاختبارات المناسبة لاكتشاف أنه تم إنشاء العبارات بشكل غير صحيح.
في ملف README الخاص بـ Squirrel، يوجد مثال لكيفية استخدام بناء الاستعلام الشرطي لتحديث البيانات باستخدام المرشحات الاختيارية:
if len(q) > 0 { users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%")) }
إليك مثال حقيقي، ولكن مبسط، لكيفية تحديث أحد استعلاماتنا لربط الجداول بشكل مشروط وإضافة مرشح اختياري:
psql := squirrel.StatementBuilder.PlaceholderFormat(squirrel.Question) statementBuilder := psql.Select(`i.id`). From("vaunt.installations i"). Where(`entity_name = ?`, name) if len(provider) > 0 { statementBuilder.Where(`provider = ?`, provider) } if len(repo) > 0 { statementBuilder.Join(`repositories as r on JSON_CONTAINS(i.repositories, CONCAT('["', r.id, '"]'))`) statementBuilder.Where(`r.name = ?`, repo) }
هل يمكنك اكتشاف مشكلة الكود؟ إذا لم يكن الأمر كذلك، فلا تقلق، فهذا شيء تسلل من خلال مراجعات التعليمات البرمجية الخاصة بنا أيضًا حتى أجرينا اختباراتنا.
المشكلة هنا هي أننا نسينا تحديث منشئ البيانات بنتيجة وظائف المنشئ. على سبيل المثال، يجب بدلاً من ذلك قراءة عامل تصفية شرط الموفر:
if len(provider) > 0 { statementBuilder = statementBuilder.Where(`provider = ?`, provider) }
يعد هذا خطأً بسيطًا نسبيًا ويمكن اكتشافه بسهولة من خلال حالات اختبار كافية، ولكن نظرًا لأنه ليس رمزًا غير صالح من الناحية الفنية، فقد يستغرق الأمر بعض الوقت لإدراك ما يحدث على الفور.
هناك مشكلة أخرى تتعلق بقابلية القراءة في هذا الإعداد وهي أن الصلة الشرطية منفصلة عن عبارة التحديد الأولية. يمكننا إعادة تنظيم المُنشئ لوضع كل قطعة في مكانها الصحيح، لكن ذلك سيتطلب عدة عمليات فحص مكررة للبيانات الشرطية وما زال يعاني من بعض مشكلات قابلية القراءة.
تمت إعادة كتابة العرض التوضيحي أعلاه باستخدام Squirrel منذ ذلك الحين، ويبدو المعادل في tqla كما يلي:
t, err := tqla.New(tqla.WithPlaceHolder(tqla.Question)) if err != nil { return nil, err } query, args, err := t.Compile(` SELECT i.id FROM vaunt.installations as i {{ if .Repo }} JOIN vaunt.repositories as r on JSON_CONTAINS(i.repositories, CONCAT('["', r.id, '"]'), '$') {{ end }} WHERE entity_name = {{ .Name}} {{ if .Provider }} AND i.provider = {{ .Provider }} {{ end }} {{ if .Repo }} AND r.name = {{ .Repo }} {{ end }} `, data) if err != nil { return nil, err }
كما ترون، فإن بناء جملة القالب الخاص بـ tqla يجعل دمج الجمل الشرطية أمرًا بسيطًا للغاية. يقوم Tqla تلقائيًا باستبدال المتغيرات التي نقوم بإعدادها بالعناصر النائبة المحددة لدينا ويوفر الوسائط التي يمكننا استخدامها مع برنامج تشغيل SQL الخاص بنا لتنفيذ العبارة.
على غرار Squirrel، من السهل اختبار أسلوب بناء البيانات هذا، حيث يمكننا إنشاء مجموعات مختلفة من كائنات البيانات لتمريرها إلى منشئ القوالب والتحقق من صحة المخرجات.
يمكنك أن ترى أننا قادرون بسهولة على إضافة أجزاء شرطية من الاستعلام في المكان المناسب لها. على سبيل المثال، لدينا هنا صلة مشروطة مباشرة بعد عبارة FROM - وعلى الرغم من أنه لا يزال لدينا عمليات تحقق متعددة من الحالة، إلا أن ذلك لا يزيد من تعقيد القالب.
ميزة tqla لطيفة أخرى تساعد على تحسين قابلية الصيانة لمنشئي SQL لدينا وهي القدرة على تحديد الوظائف المخصصة التي يمكننا استخدامها في القوالب لتجريد بعض منطق التحويل.
إليك مثال على كيفية استخدامنا لوظيفة لتحويل قيمة time.Time الخاصة بـ Golang إلى sql.NullTime للسماح لنا بإجراء إدراج باستخدام كائنات البيانات الخاصة بنا دون الحاجة إلى تحويلها مسبقًا:
funcs := template.FuncMap{ "time": func(t time.Time) sql.NullTime { if t.IsZero() { return sql.NullTime{Valid: false} } return sql.NullTime{Time: t, Valid: true} }, } t, err := tqla.New(tqla.WithPlaceHolder(tqla.Question), tqla.WithFuncMap(funcs)) if err != nil { return err }
مع تحديد هذه الوظيفة في خريطة tqla funcs، يمكننا الآن استخدامها بحرية في قوالب الاستعلام الخاصة بنا من خلال تزويدها بمعلمة من كائن البيانات الذي يمثل حقل الوقت. يمكننا أيضًا استدعاء هذه الوظيفة عدة مرات في نفس القالب مع حقول مختلفة.
إليك مثال مبسط:
statement, args, err := t.Compile(` INSERT INTO events (name, created_at, merged_at, closed_at) VALUES ( {{ .Name }}, {{ time .CreatedAt }}, {{ time .MergedAt }}, {{ time .ClosedAt }} )`, eventData)
في الختام، نعتقد أن استخدام tqla يمكن أن يساعد في تحسين إمكانية صيانة منطق بناء الاستعلام مع تقديم بعض الأدوات المساعدة القوية لإنشاء استعلامات ديناميكية. تتيح بساطة بنية القالب سهولة قراءة التعليمات البرمجية بشكل واضح ويمكن أن تجعل تصحيح أي أخطاء محتملة أسرع.
لقد جعلنا tqla مفتوح المصدر لمشاركة هذه المكتبة على أمل أن توفر خيارًا جيدًا للمستخدمين الآخرين الذين يريدون طريقة بسيطة وقابلة للصيانة وآمنة لإنشاء استعلامات SQL في العديد من أنواع التطبيقات المختلفة.
إذا كنت مهتمًا، يرجى مراجعة المستودع وإعطائه نجمة إذا كان يساعدك بأي شكل من الأشكال. لا تتردد في تقديم أي طلبات ميزات أو تقارير الأخطاء!
نحن منفتحون دائمًا لتلقي التعليقات والمساهمات.
للبقاء على اطلاع بالتطورات المستقبلية، تابعنا على X أو انضم إلى Discord!
تنصل: جميع الموارد المقدمة هي جزئيًا من الإنترنت. إذا كان هناك أي انتهاك لحقوق الطبع والنشر الخاصة بك أو الحقوق والمصالح الأخرى، فيرجى توضيح الأسباب التفصيلية وتقديم دليل على حقوق الطبع والنشر أو الحقوق والمصالح ثم إرسالها إلى البريد الإلكتروني: [email protected]. سوف نتعامل مع الأمر لك في أقرب وقت ممكن.
Copyright© 2022 湘ICP备2022001581号-3