Как связать базу данных SQLite в двоичный файл Go?

Я пытаюсь использовать go-bindata и packr, но эти пакеты не показывают, как упаковать файл базы данных SQLite в двоичный файл.

Мне не нужно обновлять базу данных, я просто хочу прочитать данные при запуске.

Как я могу встроить файл базы данных SQLite в двоичный файл Go?

1 ответ

Решение

Драйвер SQLite не может прочитать файл базы данных из памяти (например, из байтового среза). Но вы можете записать данные во временный файл и открыть его:

//go:generate go run gen.go

package main

import (
    "database/sql"
    "fmt"
    "io/ioutil"
    "log"
    "os"

    _ "github.com/mattn/go-sqlite3"
)

func main() {
    // Create temporary file for database.
    tmpDB, err := ioutil.TempFile("", "db*.sqlite3")
    if err != nil {
        log.Fatal(err)
    }
    // Remove this file after on exit.
    defer func() {
        err := os.Remove(tmpDB.Name())
        if err != nil {
            log.Print(err)
        }
    }()

    // Write database to file.
    _, err = tmpDB.Write(sqlDB)
    if err != nil {
        log.Print(err)
    }
    err = tmpDB.Close()
    if err != nil {
        log.Print(err)
    }

    // Open DB.
    db, err := sql.Open("sqlite3", tmpDB.Name()+"?mode=ro")
    if err != nil {
        log.Fatal(err)
    }

    // Make sure it's loaded correct.
    rows, err := db.Query("select * from test")
    if err != nil {
        log.Fatal(err)
    }

    for rows.Next() {
        var c string
        err := rows.Scan(&c)
        if err != nil {
            log.Fatal(err)
        }
        fmt.Println(c)
    }
}

И вы можете написать базу данных db.go с чем-то вроде:

// +build generate

package main

import (
    "fmt"
    "io/ioutil"
    "log"
    "os"
    "strings"
)

func main() {
    // Read source database file.
    d, err := ioutil.ReadFile("source.sqlite3")
    if err != nil {
        log.Fatal(err)
    }

    fp, err := os.Create("db.go")
    if err != nil {
        log.Fatal(err)
    }

    _, err = fmt.Fprintf(fp, "// Code generated by gen.go; DO NOT EDIT.\n\n"+
        "package main\n\n"+
        "var sqlDB = %s\n", asbyte(d))
    if err != nil {
        log.Fatal(err)
    }
}

// Write any data as byte array.
func asbyte(s []byte) string {
    var b strings.Builder
    for i, c := range s {
        if i%19 == 0 {
            b.WriteString("\n\t\t")
        }
        b.WriteString(fmt.Sprintf("%#x, ", c))
    }
    return "[]byte{" + b.String() + "}"
}

Вы также можете использовать go-bindata или packr для этого, если хотите, но я не вижу преимущества.


Альтернативный способ - использовать базу данных памяти, которая может быть быстрее в зависимости от того, что вы хотите сделать.

  1. Внедрите схему SQL и строки, которые вы хотите, в ваш двоичный файл Go как строки.
  2. Откройте новую базу данных памяти при запуске вашей программы (sql.Open("sqlite3",: memory: `) и создайте схему и вставьте строки.

При использовании этого метода нет доступа к диску, поэтому запрос к нему, вероятно, будет немного быстрее при больших затратах времени на запуск (эталонный тест, чтобы быть уверенным!)

Другие вопросы по тегам