db_sqlite.nim 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. ## A higher level `SQLite`:idx: database wrapper. This interface
  10. ## is implemented for other databases too.
  11. ##
  12. ## Basic usage
  13. ## ===========
  14. ##
  15. ## The basic flow of using this module is:
  16. ##
  17. ## 1. Open database connection
  18. ## 2. Execute SQL query
  19. ## 3. Close database connection
  20. ##
  21. ## Parameter substitution
  22. ## ----------------------
  23. ##
  24. ## All `db_*` modules support the same form of parameter substitution.
  25. ## That is, using the `?` (question mark) to signify the place where a
  26. ## value should be placed. For example:
  27. ##
  28. ## .. code-block:: Nim
  29. ##
  30. ## sql"INSERT INTO my_table (colA, colB, colC) VALUES (?, ?, ?)"
  31. ##
  32. ## Opening a connection to a database
  33. ## ----------------------------------
  34. ##
  35. ## .. code-block:: Nim
  36. ##
  37. ## import std/db_sqlite
  38. ##
  39. ## # user, password, database name can be empty.
  40. ## # These params are not used on db_sqlite module.
  41. ## let db = open("mytest.db", "", "", "")
  42. ## db.close()
  43. ##
  44. ## Creating a table
  45. ## ----------------
  46. ##
  47. ## .. code-block:: Nim
  48. ##
  49. ## db.exec(sql"DROP TABLE IF EXISTS my_table")
  50. ## db.exec(sql"""CREATE TABLE my_table (
  51. ## id INTEGER,
  52. ## name VARCHAR(50) NOT NULL
  53. ## )""")
  54. ##
  55. ## Inserting data
  56. ## --------------
  57. ##
  58. ## .. code-block:: Nim
  59. ##
  60. ## db.exec(sql"INSERT INTO my_table (id, name) VALUES (0, ?)",
  61. ## "Jack")
  62. ##
  63. ## Larger example
  64. ## --------------
  65. ##
  66. ## .. code-block:: nim
  67. ##
  68. ## import std/[db_sqlite, math]
  69. ##
  70. ## let db = open("mytest.db", "", "", "")
  71. ##
  72. ## db.exec(sql"DROP TABLE IF EXISTS my_table")
  73. ## db.exec(sql"""CREATE TABLE my_table (
  74. ## id INTEGER PRIMARY KEY,
  75. ## name VARCHAR(50) NOT NULL,
  76. ## i INT(11),
  77. ## f DECIMAL(18, 10)
  78. ## )""")
  79. ##
  80. ## db.exec(sql"BEGIN")
  81. ## for i in 1..1000:
  82. ## db.exec(sql"INSERT INTO my_table (name, i, f) VALUES (?, ?, ?)",
  83. ## "Item#" & $i, i, sqrt(i.float))
  84. ## db.exec(sql"COMMIT")
  85. ##
  86. ## for x in db.fastRows(sql"SELECT * FROM my_table"):
  87. ## echo x
  88. ##
  89. ## let id = db.tryInsertId(sql"""INSERT INTO my_table (name, i, f)
  90. ## VALUES (?, ?, ?)""",
  91. ## "Item#1001", 1001, sqrt(1001.0))
  92. ## echo "Inserted item: ", db.getValue(sql"SELECT name FROM my_table WHERE id=?", id)
  93. ##
  94. ## db.close()
  95. ##
  96. ## Storing binary data example
  97. ##----------------------------
  98. ##
  99. ## .. code-block:: nim
  100. ##
  101. ## import std/random
  102. ##
  103. ## ## Generate random float datas
  104. ## var orig = newSeq[float64](150)
  105. ## randomize()
  106. ## for x in orig.mitems:
  107. ## x = rand(1.0)/10.0
  108. ##
  109. ## let db = open("mysqlite.db", "", "", "")
  110. ## block: ## Create database
  111. ## ## Binary datas needs to be of type BLOB in SQLite
  112. ## let createTableStr = sql"""CREATE TABLE test(
  113. ## id INTEGER NOT NULL PRIMARY KEY,
  114. ## data BLOB
  115. ## )
  116. ## """
  117. ## db.exec(createTableStr)
  118. ##
  119. ## block: ## Insert data
  120. ## var id = 1
  121. ## ## Data needs to be converted to seq[byte] to be interpreted as binary by bindParams
  122. ## var dbuf = newSeq[byte](orig.len*sizeof(float64))
  123. ## copyMem(unsafeAddr(dbuf[0]), unsafeAddr(orig[0]), dbuf.len)
  124. ##
  125. ## ## Use prepared statement to insert binary data into database
  126. ## var insertStmt = db.prepare("INSERT INTO test (id, data) VALUES (?, ?)")
  127. ## insertStmt.bindParams(id, dbuf)
  128. ## let bres = db.tryExec(insertStmt)
  129. ## ## Check insert
  130. ## doAssert(bres)
  131. ## # Destroy statement
  132. ## finalize(insertStmt)
  133. ##
  134. ## block: ## Use getValue to select data
  135. ## var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
  136. ## ## Calculate sequence size from buffer size
  137. ## let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
  138. ## ## Copy binary string data in dataTest into a seq
  139. ## var res: seq[float64] = newSeq[float64](seqSize)
  140. ## copyMem(unsafeAddr(res[0]), addr(dataTest[0]), dataTest.len)
  141. ##
  142. ## ## Check datas obtained is identical
  143. ## doAssert res == orig
  144. ##
  145. ## db.close()
  146. ##
  147. ##
  148. ## Note
  149. ## ====
  150. ## This module does not implement any ORM features such as mapping the types from the schema.
  151. ## Instead, a `seq[string]` is returned for each row.
  152. ##
  153. ## The reasoning is as follows:
  154. ## 1. it's close to what many DBs offer natively (`char**`:c:)
  155. ## 2. it hides the number of types that the DB supports
  156. ## (int? int64? decimal up to 10 places? geo coords?)
  157. ## 3. it's convenient when all you do is to forward the data to somewhere else (echo, log, put the data into a new query)
  158. ##
  159. ## See also
  160. ## ========
  161. ##
  162. ## * `db_odbc module <db_odbc.html>`_ for ODBC database wrapper
  163. ## * `db_mysql module <db_mysql.html>`_ for MySQL database wrapper
  164. ## * `db_postgres module <db_postgres.html>`_ for PostgreSQL database wrapper
  165. {.experimental: "codeReordering".}
  166. import sqlite3, macros
  167. import db_common
  168. export db_common
  169. import std/private/[since, dbutils]
  170. when defined(nimPreviewSlimSystem):
  171. import std/assertions
  172. type
  173. DbConn* = PSqlite3 ## Encapsulates a database connection.
  174. Row* = seq[string] ## A row of a dataset. `NULL` database values will be
  175. ## converted to an empty string.
  176. InstantRow* = PStmt ## A handle that can be used to get a row's column
  177. ## text on demand.
  178. SqlPrepared* = distinct PStmt ## a identifier for the prepared queries
  179. proc dbError*(db: DbConn) {.noreturn.} =
  180. ## Raises a `DbError` exception.
  181. ##
  182. ## **Examples:**
  183. ##
  184. ## .. code-block:: Nim
  185. ##
  186. ## let db = open("mytest.db", "", "", "")
  187. ## if not db.tryExec(sql"SELECT * FROM not_exist_table"):
  188. ## dbError(db)
  189. ## db.close()
  190. var e: ref DbError
  191. new(e)
  192. e.msg = $sqlite3.errmsg(db)
  193. raise e
  194. proc dbQuote*(s: string): string =
  195. ## Escapes the `'` (single quote) char to `''`.
  196. ## Because single quote is used for defining `VARCHAR` in SQL.
  197. runnableExamples:
  198. doAssert dbQuote("'") == "''''"
  199. doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'"
  200. result = "'"
  201. for c in items(s):
  202. if c == '\'': add(result, "''")
  203. else: add(result, c)
  204. add(result, '\'')
  205. proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
  206. dbFormatImpl(formatstr, dbQuote, args)
  207. proc prepare*(db: DbConn; q: string): SqlPrepared {.since: (1, 3).} =
  208. ## Creates a new `SqlPrepared` statement.
  209. if prepare_v2(db, q, q.len.cint,result.PStmt, nil) != SQLITE_OK:
  210. discard finalize(result.PStmt)
  211. dbError(db)
  212. proc tryExec*(db: DbConn, query: SqlQuery,
  213. args: varargs[string, `$`]): bool {.
  214. tags: [ReadDbEffect, WriteDbEffect].} =
  215. ## Tries to execute the query and returns `true` if successful, `false` otherwise.
  216. ##
  217. ## **Examples:**
  218. ##
  219. ## .. code-block:: Nim
  220. ##
  221. ## let db = open("mytest.db", "", "", "")
  222. ## if not db.tryExec(sql"SELECT * FROM my_table"):
  223. ## dbError(db)
  224. ## db.close()
  225. assert(not db.isNil, "Database not connected.")
  226. var q = dbFormat(query, args)
  227. var stmt: sqlite3.PStmt
  228. if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
  229. let x = step(stmt)
  230. if x in {SQLITE_DONE, SQLITE_ROW}:
  231. result = finalize(stmt) == SQLITE_OK
  232. else:
  233. discard finalize(stmt)
  234. result = false
  235. proc tryExec*(db: DbConn, stmtName: SqlPrepared): bool {.
  236. tags: [ReadDbEffect, WriteDbEffect].} =
  237. let x = step(stmtName.PStmt)
  238. if x in {SQLITE_DONE, SQLITE_ROW}:
  239. result = true
  240. else:
  241. discard finalize(stmtName.PStmt)
  242. result = false
  243. proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
  244. tags: [ReadDbEffect, WriteDbEffect].} =
  245. ## Executes the query and raises a `DbError` exception if not successful.
  246. ##
  247. ## **Examples:**
  248. ##
  249. ## .. code-block:: Nim
  250. ##
  251. ## let db = open("mytest.db", "", "", "")
  252. ## try:
  253. ## db.exec(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
  254. ## 1, "item#1")
  255. ## except:
  256. ## stderr.writeLine(getCurrentExceptionMsg())
  257. ## finally:
  258. ## db.close()
  259. if not tryExec(db, query, args): dbError(db)
  260. proc newRow(L: int): Row =
  261. newSeq(result, L)
  262. for i in 0..L-1: result[i] = ""
  263. proc setupQuery(db: DbConn, query: SqlQuery,
  264. args: varargs[string]): PStmt =
  265. assert(not db.isNil, "Database not connected.")
  266. var q = dbFormat(query, args)
  267. if prepare_v2(db, q.cstring, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
  268. proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3).} =
  269. assert(not db.isNil, "Database not connected.")
  270. result = stmtName
  271. proc setRow(stmt: PStmt, r: var Row, cols: cint) =
  272. for col in 0'i32..cols-1:
  273. let cb = column_bytes(stmt, col)
  274. setLen(r[col], cb) # set capacity
  275. if column_type(stmt, col) == SQLITE_BLOB:
  276. copyMem(addr(r[col][0]), column_blob(stmt, col), cb)
  277. else:
  278. setLen(r[col], 0)
  279. let x = column_text(stmt, col)
  280. if not isNil(x): add(r[col], x)
  281. iterator fastRows*(db: DbConn, query: SqlQuery,
  282. args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
  283. ## Executes the query and iterates over the result dataset.
  284. ##
  285. ## This is very fast, but potentially dangerous. Use this iterator only
  286. ## if you require **ALL** the rows.
  287. ##
  288. ## **Note:** Breaking the `fastRows()` iterator during a loop will cause the
  289. ## next database query to raise a `DbError` exception `unable to close due
  290. ## to ...`.
  291. ##
  292. ## **Examples:**
  293. ##
  294. ## .. code-block:: Nim
  295. ##
  296. ## let db = open("mytest.db", "", "", "")
  297. ##
  298. ## # Records of my_table:
  299. ## # | id | name |
  300. ## # |----|----------|
  301. ## # | 1 | item#1 |
  302. ## # | 2 | item#2 |
  303. ##
  304. ## for row in db.fastRows(sql"SELECT id, name FROM my_table"):
  305. ## echo row
  306. ##
  307. ## # Output:
  308. ## # @["1", "item#1"]
  309. ## # @["2", "item#2"]
  310. ##
  311. ## db.close()
  312. var stmt = setupQuery(db, query, args)
  313. var L = (column_count(stmt))
  314. var result = newRow(L)
  315. try:
  316. while step(stmt) == SQLITE_ROW:
  317. setRow(stmt, result, L)
  318. yield result
  319. finally:
  320. if finalize(stmt) != SQLITE_OK: dbError(db)
  321. iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row
  322. {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
  323. discard setupQuery(db, stmtName)
  324. var L = (column_count(stmtName.PStmt))
  325. var result = newRow(L)
  326. try:
  327. while step(stmtName.PStmt) == SQLITE_ROW:
  328. setRow(stmtName.PStmt, result, L)
  329. yield result
  330. except:
  331. dbError(db)
  332. iterator instantRows*(db: DbConn, query: SqlQuery,
  333. args: varargs[string, `$`]): InstantRow
  334. {.tags: [ReadDbEffect].} =
  335. ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_
  336. ## but returns a handle that can be used to get column text
  337. ## on demand using `[]`. Returned handle is valid only within the iterator body.
  338. ##
  339. ## **Examples:**
  340. ##
  341. ## .. code-block:: Nim
  342. ##
  343. ## let db = open("mytest.db", "", "", "")
  344. ##
  345. ## # Records of my_table:
  346. ## # | id | name |
  347. ## # |----|----------|
  348. ## # | 1 | item#1 |
  349. ## # | 2 | item#2 |
  350. ##
  351. ## for row in db.instantRows(sql"SELECT * FROM my_table"):
  352. ## echo "id:" & row[0]
  353. ## echo "name:" & row[1]
  354. ## echo "length:" & $len(row)
  355. ##
  356. ## # Output:
  357. ## # id:1
  358. ## # name:item#1
  359. ## # length:2
  360. ## # id:2
  361. ## # name:item#2
  362. ## # length:2
  363. ##
  364. ## db.close()
  365. var stmt = setupQuery(db, query, args)
  366. try:
  367. while step(stmt) == SQLITE_ROW:
  368. yield stmt
  369. finally:
  370. if finalize(stmt) != SQLITE_OK: dbError(db)
  371. iterator instantRows*(db: DbConn, stmtName: SqlPrepared): InstantRow
  372. {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
  373. var stmt = setupQuery(db, stmtName).PStmt
  374. try:
  375. while step(stmt) == SQLITE_ROW:
  376. yield stmt
  377. except:
  378. dbError(db)
  379. proc toTypeKind(t: var DbType; x: int32) =
  380. case x
  381. of SQLITE_INTEGER:
  382. t.kind = dbInt
  383. t.size = 8
  384. of SQLITE_FLOAT:
  385. t.kind = dbFloat
  386. t.size = 8
  387. of SQLITE_BLOB: t.kind = dbBlob
  388. of SQLITE_NULL: t.kind = dbNull
  389. of SQLITE_TEXT: t.kind = dbVarchar
  390. else: t.kind = dbUnknown
  391. proc setColumns(columns: var DbColumns; x: PStmt) =
  392. let L = column_count(x)
  393. setLen(columns, L)
  394. for i in 0'i32 ..< L:
  395. columns[i].name = $column_name(x, i)
  396. columns[i].typ.name = $column_decltype(x, i)
  397. toTypeKind(columns[i].typ, column_type(x, i))
  398. columns[i].tableName = $column_table_name(x, i)
  399. iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
  400. args: varargs[string, `$`]): InstantRow
  401. {.tags: [ReadDbEffect].} =
  402. ## Similar to `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_,
  403. ## but sets information about columns to `columns`.
  404. ##
  405. ## **Examples:**
  406. ##
  407. ## .. code-block:: Nim
  408. ##
  409. ## let db = open("mytest.db", "", "", "")
  410. ##
  411. ## # Records of my_table:
  412. ## # | id | name |
  413. ## # |----|----------|
  414. ## # | 1 | item#1 |
  415. ## # | 2 | item#2 |
  416. ##
  417. ## var columns: DbColumns
  418. ## for row in db.instantRows(columns, sql"SELECT * FROM my_table"):
  419. ## discard
  420. ## echo columns[0]
  421. ##
  422. ## # Output:
  423. ## # (name: "id", tableName: "my_table", typ: (kind: dbNull,
  424. ## # notNull: false, name: "INTEGER", size: 0, maxReprLen: 0, precision: 0,
  425. ## # scale: 0, min: 0, max: 0, validValues: @[]), primaryKey: false,
  426. ## # foreignKey: false)
  427. ##
  428. ## db.close()
  429. var stmt = setupQuery(db, query, args)
  430. setColumns(columns, stmt)
  431. try:
  432. while step(stmt) == SQLITE_ROW:
  433. yield stmt
  434. finally:
  435. if finalize(stmt) != SQLITE_OK: dbError(db)
  436. proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
  437. ## Returns text for given column of the row.
  438. ##
  439. ## See also:
  440. ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
  441. ## example code
  442. $column_text(row, col)
  443. proc unsafeColumnAt*(row: InstantRow, index: int32): cstring {.inline.} =
  444. ## Returns cstring for given column of the row.
  445. ##
  446. ## See also:
  447. ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
  448. ## example code
  449. column_text(row, index)
  450. proc len*(row: InstantRow): int32 {.inline.} =
  451. ## Returns number of columns in a row.
  452. ##
  453. ## See also:
  454. ## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
  455. ## example code
  456. column_count(row)
  457. proc getRow*(db: DbConn, query: SqlQuery,
  458. args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
  459. ## Retrieves a single row. If the query doesn't return any rows, this proc
  460. ## will return a `Row` with empty strings for each column.
  461. ##
  462. ## **Examples:**
  463. ##
  464. ## .. code-block:: Nim
  465. ##
  466. ## let db = open("mytest.db", "", "", "")
  467. ##
  468. ## # Records of my_table:
  469. ## # | id | name |
  470. ## # |----|----------|
  471. ## # | 1 | item#1 |
  472. ## # | 2 | item#2 |
  473. ##
  474. ## doAssert db.getRow(sql"SELECT id, name FROM my_table"
  475. ## ) == Row(@["1", "item#1"])
  476. ## doAssert db.getRow(sql"SELECT id, name FROM my_table WHERE id = ?",
  477. ## 2) == Row(@["2", "item#2"])
  478. ##
  479. ## # Returns empty.
  480. ## doAssert db.getRow(sql"INSERT INTO my_table (id, name) VALUES (?, ?)",
  481. ## 3, "item#3") == @[]
  482. ## doAssert db.getRow(sql"DELETE FROM my_table WHERE id = ?", 3) == @[]
  483. ## doAssert db.getRow(sql"UPDATE my_table SET name = 'ITEM#1' WHERE id = ?",
  484. ## 1) == @[]
  485. ## db.close()
  486. var stmt = setupQuery(db, query, args)
  487. var L = (column_count(stmt))
  488. result = newRow(L)
  489. if step(stmt) == SQLITE_ROW:
  490. setRow(stmt, result, L)
  491. if finalize(stmt) != SQLITE_OK: dbError(db)
  492. proc getAllRows*(db: DbConn, query: SqlQuery,
  493. args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
  494. ## Executes the query and returns the whole result dataset.
  495. ##
  496. ## **Examples:**
  497. ##
  498. ## .. code-block:: Nim
  499. ##
  500. ## let db = open("mytest.db", "", "", "")
  501. ##
  502. ## # Records of my_table:
  503. ## # | id | name |
  504. ## # |----|----------|
  505. ## # | 1 | item#1 |
  506. ## # | 2 | item#2 |
  507. ##
  508. ## doAssert db.getAllRows(sql"SELECT id, name FROM my_table") == @[Row(@["1", "item#1"]), Row(@["2", "item#2"])]
  509. ## db.close()
  510. result = @[]
  511. for r in fastRows(db, query, args):
  512. result.add(r)
  513. proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row]
  514. {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
  515. result = @[]
  516. for r in fastRows(db, stmtName):
  517. result.add(r)
  518. iterator rows*(db: DbConn, query: SqlQuery,
  519. args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
  520. ## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_,
  521. ## but slower and safe.
  522. ##
  523. ## **Examples:**
  524. ##
  525. ## .. code-block:: Nim
  526. ##
  527. ## let db = open("mytest.db", "", "", "")
  528. ##
  529. ## # Records of my_table:
  530. ## # | id | name |
  531. ## # |----|----------|
  532. ## # | 1 | item#1 |
  533. ## # | 2 | item#2 |
  534. ##
  535. ## for row in db.rows(sql"SELECT id, name FROM my_table"):
  536. ## echo row
  537. ##
  538. ## ## Output:
  539. ## ## @["1", "item#1"]
  540. ## ## @["2", "item#2"]
  541. ##
  542. ## db.close()
  543. for r in fastRows(db, query, args): yield r
  544. iterator rows*(db: DbConn, stmtName: SqlPrepared): Row
  545. {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
  546. for r in fastRows(db, stmtName): yield r
  547. proc getValue*(db: DbConn, query: SqlQuery,
  548. args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
  549. ## Executes the query and returns the first column of the first row of the
  550. ## result dataset. Returns `""` if the dataset contains no rows or the database
  551. ## value is `NULL`.
  552. ##
  553. ## **Examples:**
  554. ##
  555. ## .. code-block:: Nim
  556. ##
  557. ## let db = open("mytest.db", "", "", "")
  558. ##
  559. ## # Records of my_table:
  560. ## # | id | name |
  561. ## # |----|----------|
  562. ## # | 1 | item#1 |
  563. ## # | 2 | item#2 |
  564. ##
  565. ## doAssert db.getValue(sql"SELECT name FROM my_table WHERE id = ?",
  566. ## 2) == "item#2"
  567. ## doAssert db.getValue(sql"SELECT id, name FROM my_table") == "1"
  568. ## doAssert db.getValue(sql"SELECT name, id FROM my_table") == "item#1"
  569. ##
  570. ## db.close()
  571. var stmt = setupQuery(db, query, args)
  572. if step(stmt) == SQLITE_ROW:
  573. let cb = column_bytes(stmt, 0)
  574. if cb == 0:
  575. result = ""
  576. else:
  577. if column_type(stmt, 0) == SQLITE_BLOB:
  578. result.setLen(cb)
  579. copyMem(addr(result[0]), column_blob(stmt, 0), cb)
  580. else:
  581. result = newStringOfCap(cb)
  582. add(result, column_text(stmt, 0))
  583. else:
  584. result = ""
  585. if finalize(stmt) != SQLITE_OK: dbError(db)
  586. proc getValue*(db: DbConn, stmtName: SqlPrepared): string
  587. {.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
  588. var stmt = setupQuery(db, stmtName).PStmt
  589. if step(stmt) == SQLITE_ROW:
  590. let cb = column_bytes(stmt, 0)
  591. if cb == 0:
  592. result = ""
  593. else:
  594. if column_type(stmt, 0) == SQLITE_BLOB:
  595. result.setLen(cb)
  596. copyMem(addr(result[0]), column_blob(stmt, 0), cb)
  597. else:
  598. result = newStringOfCap(cb)
  599. add(result, column_text(stmt, 0))
  600. else:
  601. result = ""
  602. proc tryInsertID*(db: DbConn, query: SqlQuery,
  603. args: varargs[string, `$`]): int64
  604. {.tags: [WriteDbEffect], raises: [DbError].} =
  605. ## Executes the query (typically "INSERT") and returns the
  606. ## generated ID for the row or -1 in case of an error.
  607. ##
  608. ## **Examples:**
  609. ##
  610. ## .. code-block:: Nim
  611. ##
  612. ## let db = open("mytest.db", "", "", "")
  613. ## db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")
  614. ##
  615. ## doAssert db.tryInsertID(sql"INSERT INTO not_exist_table (id, name) VALUES (?, ?)",
  616. ## 1, "item#1") == -1
  617. ## db.close()
  618. assert(not db.isNil, "Database not connected.")
  619. var q = dbFormat(query, args)
  620. var stmt: sqlite3.PStmt
  621. result = -1
  622. if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
  623. if step(stmt) == SQLITE_DONE:
  624. result = last_insert_rowid(db)
  625. if finalize(stmt) != SQLITE_OK:
  626. result = -1
  627. else:
  628. discard finalize(stmt)
  629. proc insertID*(db: DbConn, query: SqlQuery,
  630. args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
  631. ## Executes the query (typically "INSERT") and returns the
  632. ## generated ID for the row.
  633. ##
  634. ## Raises a `DbError` exception when failed to insert row.
  635. ## For Postgre this adds `RETURNING id` to the query, so it only works
  636. ## if your primary key is named `id`.
  637. ##
  638. ## **Examples:**
  639. ##
  640. ## .. code-block:: Nim
  641. ##
  642. ## let db = open("mytest.db", "", "", "")
  643. ## db.exec(sql"CREATE TABLE my_table (id INTEGER, name VARCHAR(50) NOT NULL)")
  644. ##
  645. ## for i in 0..2:
  646. ## let id = db.insertID(sql"INSERT INTO my_table (id, name) VALUES (?, ?)", i, "item#" & $i)
  647. ## echo "LoopIndex = ", i, ", InsertID = ", id
  648. ##
  649. ## # Output:
  650. ## # LoopIndex = 0, InsertID = 1
  651. ## # LoopIndex = 1, InsertID = 2
  652. ## # LoopIndex = 2, InsertID = 3
  653. ##
  654. ## db.close()
  655. result = tryInsertID(db, query, args)
  656. if result < 0: dbError(db)
  657. proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
  658. args: varargs[string, `$`]): int64
  659. {.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} =
  660. ## same as tryInsertID
  661. tryInsertID(db, query, args)
  662. proc insert*(db: DbConn, query: SqlQuery, pkName: string,
  663. args: varargs[string, `$`]): int64
  664. {.tags: [WriteDbEffect], since: (1, 3).} =
  665. ## same as insertId
  666. result = tryInsert(db, query, pkName, args)
  667. if result < 0: dbError(db)
  668. proc execAffectedRows*(db: DbConn, query: SqlQuery,
  669. args: varargs[string, `$`]): int64 {.
  670. tags: [ReadDbEffect, WriteDbEffect].} =
  671. ## Executes the query (typically "UPDATE") and returns the
  672. ## number of affected rows.
  673. ##
  674. ## **Examples:**
  675. ##
  676. ## .. code-block:: Nim
  677. ##
  678. ## let db = open("mytest.db", "", "", "")
  679. ##
  680. ## # Records of my_table:
  681. ## # | id | name |
  682. ## # |----|----------|
  683. ## # | 1 | item#1 |
  684. ## # | 2 | item#2 |
  685. ##
  686. ## doAssert db.execAffectedRows(sql"UPDATE my_table SET name = 'TEST'") == 2
  687. ##
  688. ## db.close()
  689. exec(db, query, args)
  690. result = changes(db)
  691. proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64
  692. {.tags: [ReadDbEffect, WriteDbEffect],since: (1, 3).} =
  693. exec(db, stmtName)
  694. result = changes(db)
  695. proc close*(db: DbConn) {.tags: [DbEffect].} =
  696. ## Closes the database connection.
  697. ##
  698. ## **Examples:**
  699. ##
  700. ## .. code-block:: Nim
  701. ##
  702. ## let db = open("mytest.db", "", "", "")
  703. ## db.close()
  704. if sqlite3.close(db) != SQLITE_OK: dbError(db)
  705. proc open*(connection, user, password, database: string): DbConn {.
  706. tags: [DbEffect].} =
  707. ## Opens a database connection. Raises a `DbError` exception if the connection
  708. ## could not be established.
  709. ##
  710. ## **Note:** Only the `connection` parameter is used for `sqlite`.
  711. ##
  712. ## **Examples:**
  713. ##
  714. ## .. code-block:: Nim
  715. ##
  716. ## try:
  717. ## let db = open("mytest.db", "", "", "")
  718. ## ## do something...
  719. ## ## db.getAllRows(sql"SELECT * FROM my_table")
  720. ## db.close()
  721. ## except:
  722. ## stderr.writeLine(getCurrentExceptionMsg())
  723. var db: DbConn
  724. if sqlite3.open(connection, db) == SQLITE_OK:
  725. result = db
  726. else:
  727. dbError(db)
  728. proc setEncoding*(connection: DbConn, encoding: string): bool {.
  729. tags: [DbEffect].} =
  730. ## Sets the encoding of a database connection, returns `true` for
  731. ## success, `false` for failure.
  732. ##
  733. ## **Note:** The encoding cannot be changed once it's been set.
  734. ## According to SQLite3 documentation, any attempt to change
  735. ## the encoding after the database is created will be silently
  736. ## ignored.
  737. exec(connection, sql"PRAGMA encoding = ?", [encoding])
  738. result = connection.getValue(sql"PRAGMA encoding") == encoding
  739. proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} =
  740. discard finalize(sqlPrepared.PStmt)
  741. template dbBindParamError*(paramIdx: int, val: varargs[untyped]) =
  742. ## Raises a `DbError` exception.
  743. var e: ref DbError
  744. new(e)
  745. e.msg = "error binding param in position " & $paramIdx
  746. raise e
  747. proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int32) {.since: (1, 3).} =
  748. ## Binds a int32 to the specified paramIndex.
  749. if bind_int(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
  750. dbBindParamError(paramIdx, val)
  751. proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int64) {.since: (1, 3).} =
  752. ## Binds a int64 to the specified paramIndex.
  753. if bind_int64(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
  754. dbBindParamError(paramIdx, val)
  755. proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int) {.since: (1, 3).} =
  756. ## Binds a int to the specified paramIndex.
  757. when sizeof(int) == 8:
  758. bindParam(ps, paramIdx, val.int64)
  759. else:
  760. bindParam(ps, paramIdx, val.int32)
  761. proc bindParam*(ps: SqlPrepared, paramIdx: int, val: float64) {.since: (1, 3).} =
  762. ## Binds a 64bit float to the specified paramIndex.
  763. if bind_double(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
  764. dbBindParamError(paramIdx, val)
  765. proc bindNull*(ps: SqlPrepared, paramIdx: int) {.since: (1, 3).} =
  766. ## Sets the bindparam at the specified paramIndex to null
  767. ## (default behaviour by sqlite).
  768. if bind_null(ps.PStmt, paramIdx.int32) != SQLITE_OK:
  769. dbBindParamError(paramIdx)
  770. proc bindParam*(ps: SqlPrepared, paramIdx: int, val: string, copy = true) {.since: (1, 3).} =
  771. ## Binds a string to the specified paramIndex.
  772. ## if copy is true then SQLite makes its own private copy of the data immediately
  773. if bind_text(ps.PStmt, paramIdx.int32, val.cstring, val.len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
  774. dbBindParamError(paramIdx, val)
  775. proc bindParam*(ps: SqlPrepared, paramIdx: int,val: openArray[byte], copy = true) {.since: (1, 3).} =
  776. ## binds a blob to the specified paramIndex.
  777. ## if copy is true then SQLite makes its own private copy of the data immediately
  778. let len = val.len
  779. if bind_blob(ps.PStmt, paramIdx.int32, val[0].unsafeAddr, len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
  780. dbBindParamError(paramIdx, val)
  781. macro bindParams*(ps: SqlPrepared, params: varargs[untyped]): untyped {.since: (1, 3).} =
  782. let bindParam = bindSym("bindParam", brOpen)
  783. let bindNull = bindSym("bindNull")
  784. let preparedStatement = genSym()
  785. result = newStmtList()
  786. # Store `ps` in a temporary variable. This prevents `ps` from being evaluated every call.
  787. result.add newNimNode(nnkLetSection).add(newIdentDefs(preparedStatement, newEmptyNode(), ps))
  788. for idx, param in params:
  789. if param.kind != nnkNilLit:
  790. result.add newCall(bindParam, preparedStatement, newIntLitNode idx + 1, param)
  791. else:
  792. result.add newCall(bindNull, preparedStatement, newIntLitNode idx + 1)
  793. macro untypedLen(args: varargs[untyped]): int =
  794. newLit(args.len)
  795. template exec*(db: DbConn, stmtName: SqlPrepared,
  796. args: varargs[typed]): untyped =
  797. when untypedLen(args) > 0:
  798. if reset(stmtName.PStmt) != SQLITE_OK:
  799. dbError(db)
  800. if clear_bindings(stmtName.PStmt) != SQLITE_OK:
  801. dbError(db)
  802. stmtName.bindParams(args)
  803. if not tryExec(db, stmtName): dbError(db)
  804. when not defined(testing) and isMainModule:
  805. var db = open(":memory:", "", "", "")
  806. exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
  807. exec(db, sql"insert into tbl1 values('hello!',10)", [])
  808. exec(db, sql"insert into tbl1 values('goodbye', 20)", [])
  809. var p1 = db.prepare "create table tbl2(one varchar(10), two smallint)"
  810. exec(db, p1)
  811. finalize(p1)
  812. var p2 = db.prepare "insert into tbl2 values('hello!',10)"
  813. exec(db, p2)
  814. finalize(p2)
  815. var p3 = db.prepare "insert into tbl2 values('goodbye', 20)"
  816. exec(db, p3)
  817. finalize(p3)
  818. #db.query("create table tbl1(one varchar(10), two smallint)")
  819. #db.query("insert into tbl1 values('hello!',10)")
  820. #db.query("insert into tbl1 values('goodbye', 20)")
  821. for r in db.rows(sql"select * from tbl1", []):
  822. echo(r[0], r[1])
  823. for r in db.instantRows(sql"select * from tbl1", []):
  824. echo(r[0], r[1])
  825. var p4 = db.prepare "select * from tbl2"
  826. for r in db.rows(p4):
  827. echo(r[0], r[1])
  828. finalize(p4)
  829. var i5 = 0
  830. var p5 = db.prepare "select * from tbl2"
  831. for r in db.instantRows(p5):
  832. inc i5
  833. echo(r[0], r[1])
  834. assert i5 == 2
  835. finalize(p5)
  836. for r in db.rows(sql"select * from tbl2", []):
  837. echo(r[0], r[1])
  838. for r in db.instantRows(sql"select * from tbl2", []):
  839. echo(r[0], r[1])
  840. var p6 = db.prepare "select * from tbl2 where one = ? "
  841. p6.bindParams("goodbye")
  842. var rowsP3 = 0
  843. for r in db.rows(p6):
  844. rowsP3 = 1
  845. echo(r[0], r[1])
  846. assert rowsP3 == 1
  847. finalize(p6)
  848. var p7 = db.prepare "select * from tbl2 where two=?"
  849. p7.bindParams(20'i32)
  850. when sizeof(int) == 4:
  851. p7.bindParams(20)
  852. var rowsP = 0
  853. for r in db.rows(p7):
  854. rowsP = 1
  855. echo(r[0], r[1])
  856. assert rowsP == 1
  857. finalize(p7)
  858. exec(db, sql"CREATE TABLE photos(ID INTEGER PRIMARY KEY AUTOINCREMENT, photo BLOB)")
  859. var p8 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
  860. var d = "abcdefghijklmnopqrstuvwxyz"
  861. p8.bindParams(1'i32, "abcdefghijklmnopqrstuvwxyz")
  862. exec(db, p8)
  863. finalize(p8)
  864. var p10 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
  865. p10.bindParams(2'i32,nil)
  866. exec(db, p10)
  867. exec( db, p10, 3, nil)
  868. finalize(p10)
  869. for r in db.rows(sql"select * from photos where ID = 1", []):
  870. assert r[1].len == d.len
  871. assert r[1] == d
  872. var i6 = 0
  873. for r in db.rows(sql"select * from photos where ID = 3", []):
  874. i6 = 1
  875. assert i6 == 1
  876. var p9 = db.prepare("select * from photos where PHOTO is ?")
  877. p9.bindParams(nil)
  878. var rowsP2 = 0
  879. for r in db.rows(p9):
  880. rowsP2 = 1
  881. echo(r[0], repr r[1])
  882. assert rowsP2 == 1
  883. finalize(p9)
  884. db_sqlite.close(db)