Spec-Zone.ru › Nim

src/db_connector/db_sqlite

Примечание: Для использования этого модуля, выполните nimble install db_connector.

Обёртка уровня выше для базы данных SQLite. Этот интерфейс реализован и для других баз данных.

Основные примеры использования

Основные действия при использовании этого модуля:

  1. Открытие подключения к базе данных
  2. Выполнение SQL запроса
  3. Закрытие подключения к базе данных

Подстановка параметров

Все модули db_* поддерживают одинаковую форму подстановки параметров. То есть, используется ? (вопросительный знак) для обозначения места, куда нужно поместить значение. Например:

sql"INSERT INTO my_table (colA, colB, colC) VALUES (?, ?, ?)"

Открытие подключения к базе данных

import db_connector/db_sqlite

# user, password, database name can be empty.
# These params are not used on db_sqlite module.
let db = open("mytest.db", "", "", "")
db.close()

Создание таблицы

db.exec(sql"DROP TABLE IF EXISTS my_table")
db.exec(sql"""CREATE TABLE my_table (
                 id   INTEGER,
                 name VARCHAR(50) NOT NULL
              )""")

Вставка данных

db.exec(sql"INSERT INTO my_table (id, name) VALUES (0, ?)",
        "Jack")

Более сложный пример

import db_connector/db_sqlite
import std/math

let db = open("mytest.db", "", "", "")

db.exec(sql"DROP TABLE IF EXISTS my_table")
db.exec(sql"""CREATE TABLE my_table (
                 id    INTEGER PRIMARY KEY,
                 name  VARCHAR(50) NOT NULL,
                 i     INT(11),
                 f     DECIMAL(18, 10)
              )""")

db.exec(sql"BEGIN")
for i in 1..1000:
  db.exec(sql"INSERT INTO my_table (name, i, f) VALUES (?, ?, ?)",
          "Item#" & $i, i, sqrt(i.float))
db.exec(sql"COMMIT")

for x in db.fastRows(sql"SELECT * FROM my_table"):
  echo x

let id = db.tryInsertId(sql"""INSERT INTO my_table (name, i, f)
                              VALUES (?, ?, ?)""",
                        "Item#1001", 1001, sqrt(1001.0))
echo "Inserted item: ", db.getValue(sql"SELECT name FROM my_table WHERE id=?", id)

db.close()

Пример хранения двоичных данных

import std/random

## Generate random float datas
var orig = newSeq[float64](150)
randomize()
for x in orig.mitems:
  x = rand(1.0)/10.0

let db = open("mysqlite.db", "", "", "")
block: ## Create database
  ## Binary datas needs to be of type BLOB in SQLite
  let createTableStr = sql"""CREATE TABLE test(
    id INTEGER NOT NULL PRIMARY KEY,
    data BLOB
  )
  """
  db.exec(createTableStr)

block: ## Insert data
  var id = 1
  ## Data needs to be converted to seq[byte] to be interpreted as binary by bindParams
  var dbuf = newSeq[byte](orig.len*sizeof(float64))
  copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len)
  
  ## Use prepared statement to insert binary data into database
  var insertStmt = db.prepare("INSERT INTO test (id, data) VALUES (?, ?)")
  insertStmt.bindParams(id, dbuf)
  let bres = db.tryExec(insertStmt)
  ## Check insert
  doAssert(bres)
  # Destroy statement
  finalize(insertStmt)

block: ## Use getValue to select data
  var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
  ## Calculate sequence size from buffer size
  let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
  ## Copy binary string data in dataTest into a seq
  var res: seq[float64] = newSeq[float64](seqSize)
  copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len)
  
  ## Check datas obtained is identical
  doAssert res == orig

db.close()

Примечание

Этот модуль не реализует никаких функций ORM, таких как отображение типов из схемы. Вместо этого, для каждой строки возвращается seq[string].

Обоснование:

  1. Это близко к тому, что многие СУБД предлагают напрямую (char**)
  2. Это скрывает количество типов, которые поддерживает СУБД (int? int64? decimal до 10 знаков? геокоординаты?)
  3. Это удобно, когда всё, что вам нужно сделать, это передать данные куда-нибудь ещё (вывести, записать в журнал, поместить данные в новый запрос)

См. также

  • Модуль db_odbc для обёртки базы данных ODBC
  • Модуль db_mysql для обёртки базы данных MySQL
  • Модуль db_postgres для обёртки базы данных PostgreSQL

Импорты

sqlite3, db_common, dbutils

Типы

DbConn = PSqlite3
Оборачивает соединение с базой данных.
InstantRow = PStmt
Ручка, которая может использоваться для получения текстового представления столбца строки по требованию.
Row = seq[string]
Строка набора данных. NULL значения базы данных будут преобразованы в пустую строку.
SqlPrepared = distinct PStmt
идентификатор подготовленных запросов

Процедуры

proc `[]`(row: InstantRow; col: int32): string {.inline, ...raises: [], tags: [],
    forbids: [].}

Возвращает текст для заданного столбца строки.

См. также:

  • Итератор instantRows пример кода
proc bindNull(ps: SqlPrepared; paramIdx: int) {....raises: [DbError], tags: [],
    forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: float64) {.
    ...raises: [DbError], tags: [], forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: int) {....raises: [DbError],
    tags: [], forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: int32) {....raises: [DbError],
    tags: [], forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: int64) {....raises: [DbError],
    tags: [], forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: openArray[byte]; copy = true) {.
    ...raises: [DbError], tags: [], forbids: [].}
proc bindParam(ps: SqlPrepared; paramIdx: int; val: string; copy = true) {.
    ...raises: [DbError], tags: [], forbids: [].}
proc close(db: DbConn) {....tags: [DbEffect], raises: [DbError], forbids: [].}

Закрывает подключение к базе данных.

Примеры:

let db = open("mytest.db", "", "", "")
db.close()
proc dbError(db: DbConn) {.noreturn, ...raises: [DbError], tags: [], forbids: [].}

Выбрасывает исключение DbError.

Примеры:

let db = open("mytest.db", "", "", "")
if not db.tryExec(sql"SELECT * FROM not_exist_table"):
  dbError(db)
db.close()
proc dbQuote(s: string): string {....raises: [], tags: [], forbids: [].}
Экранирует символ ' (одинарная кавычка) в ''. Так как одинарная кавычка используется для определения VARCHAR в SQL.

Пример:

doAssert dbQuote("'") == "''''"
doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'"
proc exec(db: DbConn; query: SqlQuery; args: varargs[string, `$`]) {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос и выбрасывает исключение DbError при неудаче.

Примеры:

let db = open("mytest.db", "", "", "")
try:
  db.exec(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
          1, "item#1")
except:
  stderr.writeLine(getCurrentExceptionMsg())
finally:
  db.close()
proc execAffectedRows(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): int64 {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос (обычно "UPDATE") и возвращает количество затронутых строк.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

doAssert db.execAffectedRows(sql"UPDATE my_table SET name = 'TEST'") == 2

db.close()
proc execAffectedRows(db: DbConn; stmtName: SqlPrepared): int64 {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}
proc finalize(sqlPrepared: SqlPrepared) {.discardable, ...raises: [], tags: [],
    forbids: [].}
proc getAllRows(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): seq[
    Row] {....tags: [ReadDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос и возвращает весь набор результатов.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

doAssert db.getAllRows(sql"SELECT id, name FROM my_table") == @[Row(@["1", "item#1"]), Row(@["2", "item#2"])]
db.close()
proc getAllRows(db: DbConn; stmtName: SqlPrepared): seq[Row] {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}
proc getRow(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): Row {.
    ...tags: [ReadDbEffect], raises: [DbError], forbids: [].}

Извлекает одну строку. Если запрос не возвращает строк, эта процедура вернёт Row с пустыми строками для каждого столбца.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

doAssert db.getRow(sql"SELECT id, name FROM my_table"
                   ) == Row(@["1", "item#1"])
doAssert db.getRow(sql"SELECT id, name FROM my_table WHERE id = ?",
                   2) == Row(@["2", "item#2"])

# Returns empty.
doAssert db.getRow(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
                   3, "item#3") == @[]
doAssert db.getRow(sql"DELETE FROM my_table WHERE id = ?", 3) == @[]
doAssert db.getRow(sql"UPDATE my_table SET name = 'ITEM#1' WHERE id = ?",
                   1) == @[]
db.close()
proc getValue(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): string {.
    ...tags: [ReadDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос и возвращает первый столбец первой строки набора результатов. Возвращает "" если набор данных не содержит строк или значение базы данных NULL.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

doAssert db.getValue(sql"SELECT name FROM my_table WHERE id = ?",
                     2) == "item#2"
doAssert db.getValue(sql"SELECT id, name FROM my_table") == "1"
doAssert db.getValue(sql"SELECT name, id FROM my_table") == "item#1"

db.close()
proc getValue(db: DbConn; stmtName: SqlPrepared): string {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [], forbids: [].}
proc insert(db: DbConn; query: SqlQuery; pkName: string;
            args: varargs[string, `$`]): int64 {....tags: [WriteDbEffect],
    raises: [DbError], forbids: [].}
то же самое, что и insertId
proc insertID(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): int64 {.
    ...tags: [WriteDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос (обычно "INSERT") и возвращает сгенерированный ID для строки.

Выбрасывает исключение DbError при неудачной вставке строки. Для Postgre это добавляет RETURNING id к запросу, поэтому работает только если ваш первичный ключ называется id.

Примеры:

let db = open("mytest.db", "", "", "")
db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")

for i in 0..2:
  let id = db.insertID(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", i, "item#" & $i)
  echo "LoopIndex = ", i, ", InsertID = ", id

# Output:
# LoopIndex = 0, InsertID = 1
# LoopIndex = 1, InsertID = 2
# LoopIndex = 2, InsertID = 3

db.close()
proc len(row: InstantRow): int32 {.inline, ...raises: [], tags: [], forbids: [].}

Возвращает количество столбцов в строке.

См. также:

  • Итератор instantRows пример кода
proc open(connection, user, password, database: string): DbConn {.
    ...tags: [DbEffect], raises: [DbError], forbids: [].}

Открывает подключение к базе данных. Выбрасывает исключение DbError если подключение не удалось установить.

Примечание: Только параметр connection используется для sqlite.

Примеры:

try:
  let db = open("mytest.db", "", "", "")
  ## do something...
  ## db.getAllRows(sql"SELECT * FROM my_table")
  db.close()
except:
  stderr.writeLine(getCurrentExceptionMsg())
proc prepare(db: DbConn; q: string): SqlPrepared {....raises: [DbError], tags: [],
    forbids: [].}
Создаёт новый объект SqlPrepared.
proc setEncoding(connection: DbConn; encoding: string): bool {....tags: [DbEffect],
    raises: [DbError], forbids: [].}

Устанавливает кодировку подключения к базе данных, возвращает true при успехе, false при неудаче.

Примечание: Кодировку нельзя изменить после её установки. Согласно документации SQLite3, любая попытка изменить кодировку после создания базы данных будет проигнорирована.

proc tryExec(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): bool {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}

Пытается выполнить запрос и возвращает true при успехе, false в противном случае.

Примеры:

let db = open("mytest.db", "", "", "")
if not db.tryExec(sql"SELECT * FROM my_table"):
  dbError(db)
db.close()
proc tryExec(db: DbConn; stmtName: SqlPrepared): bool {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [], forbids: [].}
proc tryInsert(db: DbConn; query: SqlQuery; pkName: string;
               args: varargs[string, `$`]): int64 {....tags: [WriteDbEffect],
    raises: [DbError], forbids: [].}
то же самое, что и tryInsertID
proc tryInsertID(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): int64 {.
    ...tags: [WriteDbEffect], raises: [DbError], forbids: [].}

Выполняет запрос (обычно "INSERT") и возвращает сгенерированный ID для строки или -1 в случае ошибки.

Примеры:

let db = open("mytest.db", "", "", "")
db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")

doAssert db.tryInsertID(sql"INSERT INTO not_exist_table (id, name) VALUES (?, ?)",
                        1, "item#1") == -1
db.close()
proc unsafeColumnAt(row: InstantRow; index: int32): cstring {.inline,
    ...raises: [], tags: [], forbids: [].}

Возвращает cstring для заданного столбца строки.

См. также:

  • Итератор instantRows пример кода

Итераторы

iterator fastRows(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): Row {.
    ...tags: [ReadDbEffect], raises: [DbError, DbError], forbids: [].}

Выполняет запрос и перебирает набор результатов.

Это очень быстро, но потенциально опасно. Используйте этот итератор только если вам нужны ВСЕ строки.

Примечание: Прерывание fastRows() итератора во время цикла приведёт к тому, что следующий запрос к базе данных вызовет исключение DbError unable to close due to ....

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

for row in db.fastRows(sql"SELECT id, name FROM my_table"):
  echo row

# Output:
# @["1", "item#1"]
# @["2", "item#2"]

db.close()
iterator fastRows(db: DbConn; stmtName: SqlPrepared): Row {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}
iterator instantRows(db: DbConn; columns: var DbColumns; query: SqlQuery;
                     args: varargs[string, `$`]): InstantRow {.
    ...tags: [ReadDbEffect], raises: [DbError, DbError], forbids: [].}

Аналогичен итератору instantRows, но устанавливает информацию о столбцах в columns.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

var columns: DbColumns
for row in db.instantRows(columns, sql"SELECT * FROM my_table"):
  discard
echo columns[0]

# Output:
# (name: "id", tableName: "my_table", typ: (kind: dbNull,
# notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0,
# scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false,
# foreignKey: false)

db.close()
iterator instantRows(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): InstantRow {.
    ...tags: [ReadDbEffect], raises: [DbError, DbError], forbids: [].}

Аналогичен итератору fastRows, но возвращает ручку, которую можно использовать для получения текста столбца по требованию с помощью []. Возвращаемая ручка валидна только внутри тела итератора.

Примеры:

let db = open("mytest.db", "", "", "")
 
 # Records of my_table:
 # | id | name     |
 # |----|----------|
 # |  1 | item#1   |
 # |  2 | item#2   |
 
 for row in db.instantRows(sql"SELECT * FROM my_table"):
   echo "id:" & row[0]
   echo "name:" & row[1]
   echo "length:" & $len(row)
 
 # Output:
 # id:1
 # name:item#1
 # length:2
 # id:2
 # name:item#2
 # length:2
 
 db.close()
iterator instantRows(db: DbConn; stmtName: SqlPrepared): InstantRow {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}
iterator rows(db: DbConn; query: SqlQuery; args: varargs[string, `$`]): Row {.
    ...tags: [ReadDbEffect], raises: [DbError], forbids: [].}

Аналогичен итератору fastRows, но медленнее и безопаснее.

Примеры:

let db = open("mytest.db", "", "", "")

# Records of my_table:
# | id | name     |
# |----|----------|
# |  1 | item#1   |
# |  2 | item#2   |

for row in db.rows(sql"SELECT id, name FROM my_table"):
  echo row

## Output:
## @["1", "item#1"]
## @["2", "item#2"]

db.close()
iterator rows(db: DbConn; stmtName: SqlPrepared): Row {.
    ...tags: [ReadDbEffect, WriteDbEffect], raises: [DbError], forbids: [].}

Макросы

macro bindParams(ps: SqlPrepared; params: varargs[untyped]): untyped

Шаблоны

template dbBindParamError(paramIdx: int; val: varargs[untyped])
Вызывает исключение DbError.
template exec(db: DbConn; stmtName: SqlPrepared; args: varargs[typed]): untyped

© 2006–2024 Andreas Rumpf
Licensed under the MIT License.
https://nim-lang.org/docs/db_sqlite.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API