db_sqlite.nim 28 KB

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