Rumah >pembangunan bahagian belakang >Golang >Bangunan Pertanyaan SQL yang boleh dikekalkan dengan Golang

Bangunan Pertanyaan SQL yang boleh dikekalkan dengan Golang

WBOY
WBOYasal
2024-08-16 07:04:32474semak imbas

Maintainable SQL Query Building with Golang

Sebarang aplikasi yang berfungsi dengan pertanyaan SQL boleh mendapat manfaat daripada menggunakan pembina pertanyaan untuk meningkatkan kebolehbacaan kod, kebolehselenggaraan dan keselamatan. Malah, terdapat banyak perpustakaan berbeza yang melakukan perkara itu di Golang. Di Vaunt, kami mencuba pelbagai pilihan sebelum akhirnya memutuskan untuk menciptanya sendiri. Akhirnya, kami mahukan sesuatu yang selamat dan menyediakan penggantian berubah-ubah untuk menghalang suntikan SQL sambil masih boleh dibaca dan boleh mempunyai pernyataan bersyarat. Jadi kami mencipta perpustakaan baharu yang dipanggil tqla, dikeluarkan dan diumumkan lewat tahun lepas. Anda boleh membaca lebih lanjut mengenainya dalam artikel ini.

Sebelum kami membina tqla, kami terutamanya menggunakan Tupai untuk logik pembinaan pertanyaan SQL kami—dan kami sangat mengesyorkannya. Kami masih menggunakan Squirrel di beberapa kawasan tetapi secara beransur-ansur mula menggantikan dan melaksanakan logik pembinaan pertanyaan baharu dengan tqla. Kami telah menemui banyak keadaan di mana tqla telah meningkatkan keupayaan kami untuk mengekalkan kod kami dan menyelesaikan masalah yang kami hadapi semasa menggunakan pembina pernyataan lain.

Kes Penggunaan Dunia Nyata

Di Vaunt, kami baru-baru ini menjalani pemindahan pangkalan data daripada CockroachDB ke TiDB. Walaupun CockroachDB berprestasi dan boleh dipercayai, kami akhirnya menghadapi keputusan untuk menambah techstack kami untuk menyokong pangkalan data OLAP. Keperluan untuk ini adalah untuk menyokong beban kerja analisis kami pada produk cerapan komuniti sumber terbuka kami. Untuk memastikan jejak teknologi kami kecil, kami memutuskan untuk bergerak ke hadapan dengan TiDB dan memanfaatkan seni bina HTAP pangkalan data. 

CockroachDB sebahagian besarnya serasi dengan PostgreSQL, dan kami menggunakan sintaks PostgreSQL untuk kebanyakan pertanyaan SQL kami. Untuk bertukar kepada TiDB, kami terpaksa menukar beberapa jadual kami dan mengemas kini pertanyaan untuk menggunakan sintaks MySQL. Di beberapa lokasi semasa penghijrahan, kami mendapati bahawa kami menggunakan pernyataan binaan pertanyaan bersyarat secara tidak wajar dan tidak mempunyai ujian yang sesuai untuk mengetahui bahawa pernyataan tersebut dijana secara tidak betul.

Demonstrasi

Dalam README Squirrel, terdapat contoh cara anda boleh menggunakan binaan pertanyaan bersyarat untuk mengemas kini pernyataan dengan penapis pilihan:

if len(q) > 0 {
    users = users.Where("name LIKE ?", fmt.Sprint("%", q, "%"))
}

Berikut ialah contoh sebenar tetapi ringkas tentang cara kami mengemas kini salah satu pertanyaan kami untuk menyertai jadual secara bersyarat dan menambah penapis pilihan:

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)
}

Bolehkah anda melihat isu dengan kod itu? Jika tidak, jangan risau—ia adalah sesuatu yang terlepas daripada ulasan kod kami sendiri sehingga kami menjalankan ujian kami. 

Isunya di sini ialah kami terlupa untuk mengemas kini pembangun penyata dengan hasil fungsi pembina. Sebagai contoh, penapis keadaan pembekal sebaliknya harus membaca:

if len(provider) > 0 {
    statementBuilder = statementBuilder.Where(`provider = ?`, provider)
}

Ini adalah kesilapan yang agak mudah untuk dilakukan dan mudah ditangkap dengan kes ujian yang mencukupi, tetapi kerana ia bukan kod teknikal yang tidak sah, ia mungkin mengambil sedikit masa untuk menyedari perkara yang berlaku dengan segera.

Satu lagi isu kebolehbacaan dengan persediaan ini ialah gabungan bersyarat dipisahkan daripada pernyataan pilih awal. Kami boleh menyusun semula pembina untuk meletakkan setiap bahagian di tempat yang sepatutnya, tetapi ia memerlukan berbilang semakan pernyataan bersyarat pendua dan masih mengalami beberapa isu kebolehbacaan.

Menggunakan tqla

Demonstrasi di atas menggunakan Tupai sejak itu telah ditulis semula, dan persamaan dalam tqla kelihatan seperti ini:

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
}

Seperti yang anda lihat, sintaks templat untuk tqla menjadikan menggabungkan klausa bersyarat sangat mudah. Tqla secara automatik menggantikan pembolehubah yang kami tetapkan dengan ruang letak kami yang ditentukan dan menyediakan hujah yang boleh kami gunakan dengan pemacu sql kami untuk melaksanakan pernyataan.

Serupa dengan Squirrel, pendekatan membina pernyataan ini mudah diuji, kerana kami boleh mencipta set objek data yang berbeza untuk dihantar kepada pembina templat dan mengesahkan output.

Anda dapat melihat bahawa kami boleh menambah soalan bersyarat dengan mudah di tempat yang paling sesuai. Sebagai contoh, di sini kami mempunyai SERTAI bersyarat terus selepas pernyataan FROM—dan walaupun kami masih mempunyai beberapa semakan syarat, ia tidak terlalu merumitkan templat.

Fungsi Tersuai

Ciri tqla lain yang bagus yang membantu meningkatkan kebolehselenggaraan pembangun sql kami ialah keupayaan untuk menentukan fungsi tersuai yang boleh kami gunakan dalam templat untuk mengabstrak beberapa logik transformasi.

Berikut ialah contoh cara kami menggunakan fungsi untuk menukar masa Golang. Nilai masa menjadi sql.NullTime untuk membolehkan kami melakukan sisipan dengan objek data kami tanpa perlu menukarnya terlebih dahulu:

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
}

Dengan fungsi ini yang ditakrifkan dalam peta funcs tqla kami, kami kini boleh menggunakannya secara bebas dalam templat pertanyaan kami dengan memberikannya parameter daripada objek data iaitu medan masa.Masa. Kita juga boleh memanggil fungsi ini beberapa kali dalam templat yang sama dengan medan yang berbeza.

Here is a simplified example:

statement, args, err := t.Compile(`
    INSERT INTO events
        (name, created_at, merged_at, closed_at)
    VALUES ( 
        {{ .Name }},
        {{ time .CreatedAt }},
        {{ time .MergedAt }},
        {{ time .ClosedAt }}
    )`, eventData)

Conclusion

In conclusion, we believe that using tqla can help improve the maintainability of query building logic while offering some powerful utility for creating dynamic queries. The simplicity of the template structure allows for clean code readability and can make it faster to debug any potential errors.

We made tqla open source to share this library in hopes that it provides a good option for other users wanting a simple, maintainable, and secure way to build sql queries in many different types of applications.

If you are interested, please check out the repository and give it a star if it helps you in any way. Feel free to make any feature requests or bug reports!

We are always open to receiving feedback and contributions.

To stay in the loop on future development, follow us on X or join our Discord!

Atas ialah kandungan terperinci Bangunan Pertanyaan SQL yang boleh dikekalkan dengan Golang. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Kandungan artikel ini disumbangkan secara sukarela oleh netizen, dan hak cipta adalah milik pengarang asal. Laman web ini tidak memikul tanggungjawab undang-undang yang sepadan. Jika anda menemui sebarang kandungan yang disyaki plagiarisme atau pelanggaran, sila hubungi admin@php.cn