Compare commits

...

309 Commits

Author SHA1 Message Date
ringabout
3eddba8b5e stdlib final cleanup 2022-12-01 22:54:04 +08:00
ringabout
911e302d37 'lock levels' are deprecated, now a noop (#20539)
* 'lock levels' are deprecated, now a noop

* fixes tests
2022-10-11 15:17:09 +08:00
Yuriy Glukhov
6987408dae Compile sqlite3 with -O3 flag (#16697) 2021-01-12 14:26:52 +02:00
Juan Carlos
eebe3b59af Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
pyloor
8f6fee9098 adding sqlite3 backup functions (#13346)
* adding sqlite3 backup functions

* changing sleep to sqlite3_sleep to prevent clashes
2020-02-07 07:11:05 +01:00
Federico Ceratto
2efcbd55c3 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Andreas Rumpf
dd89ef9605 prepare sqlite3 wrapper for static linking 2019-09-03 18:43:23 +02:00
Andreas Rumpf
4f602e475c makes the -d:nimIncremental compiler mode compile again 2019-07-21 16:41:46 +02:00
Araq
56cf60b446 minor style changes 2019-07-11 00:28:20 +02:00
Araq
6dbeb3cf46 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
cc923b3422 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
553d7c72b4 improvements to memtracking 2017-01-26 11:25:55 +01:00
Araq
8cbb0a3452 Merge branch 'devel' into sighashes 2016-11-28 20:59:30 +01:00
Araq
d67317ce5c Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Araq
ba218f8a80 fixes DLL hell on Windows 2015-09-05 12:03:53 +02:00
Andreas Rumpf
40aa0294f3 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
a4a8cd86ce Merge pull request #3029 from rbehrends/fix-sqlite3-osx
Fix SQLite3 dynlib on OS X.
2015-07-01 00:20:37 +02:00
pdw
4a883c5c5f src/wrappers/s-z - Dropped 'T' from types 2015-05-24 22:50:19 -05:00
Araq
21df5beb3d resolved conflict 2014-09-13 17:59:37 +02:00
Araq
347b0fa0ba updated db_sqlite.nim 2014-08-29 00:16:12 +02:00
Araq
42d017a95e Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Araq
7dd1d096da Merge branch 'devel' of https://github.com/Araq/Nimrod into devel 2014-04-25 19:55:06 +02:00
Araq
e905df2135 Merge branch 'devel' of https://github.com/Araq/Nimrod into devel 2014-04-20 01:19:10 +02:00
Araq
d606c6bf81 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
5c0a036152 fixes #213 2012-10-04 09:49:04 +02:00
Araq
8511346cfb bugfix: optimization of complex constant string concatenations 2012-02-17 02:16:33 +01:00
Araq
edc8f6497c added some missing wrappers 2010-08-24 00:22:36 +02:00
Andreas Rumpf
0bab6fe0b6 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
9d95883024 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
38fd3a28f2 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
0d9c28205b continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
2d4cd386c0 added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
54bbc9837f version0.7.10 2009-06-08 08:06:25 +02:00
ringabout
b3c7ddaea0 remove useless defines (#20867) 2022-11-17 23:04:43 +08:00
ringabout
e846f40758 fixes postgres with nimPreviewSlimSystem (#20455) 2022-09-29 23:54:45 +08:00
Artem Klevtsov
2408bf4a33 postgres module fixes (followup #17030) (#17053)
* Fix pqSetSingleRowMode case. Add links to the docs

* Add missing PGContextVisibility enum

* Remove unused PGContextVisibility enum
2021-02-17 01:15:58 +06:00
Artem Klevtsov
b13bfa05a2 Add pqsetsinglerowmode to postgres module (#17030)
* Add pqsetsinglerowmode to postgres module

Very useful to reduce memory usage when fetch a large amount of data.
Documentation: https://www.postgresql.org/docs/current/libpq-single-row-mode.html

* Add missing enum to the ExecStatusType

* Revert entry

* Update ConnStatusType enum
2021-02-14 20:27:44 +06:00
Juan Carlos
df42b85c5c Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Bung
dd343a9a6d add pqserverVersion,pqconnectionNeedsPassword,pqconnectionUsedPassword (#13060) 2020-01-07 17:52:02 +08:00
Araq
a7719009f3 minor style changes 2019-07-11 00:28:20 +02:00
Hitesh Jasani
c6055c0d86 Expose api to disable autoinitializing OpenSSL. (#11247) 2019-05-14 11:08:53 -04:00
Araq
c06bfeb37f removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
5e1faeae31 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Araq
5358bf12c0 preparations for more Nimble packages; clear licensing; fixes #2949 2015-06-20 23:24:21 +02:00
pdw
54199d93d5 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Araq
ae74f4cd2c Merge branch 'devel' of https://github.com/Araq/Nimrod into bigbreak
Conflicts:
	lib/impure/db_postgres.nim
	lib/pure/os.nim
	src/wrappers/postgres.nim
2014-10-04 19:40:23 +02:00
Araq
5834a5dac5 cleaned up various modules 2014-09-19 00:54:01 +02:00
Araq
dba3546bbc resolved conflict 2014-09-13 17:59:37 +02:00
Araq
2cece88e0d Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Andreas Rumpf
f76e59a8bf bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
0ab1f41412 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
28ca6e11ca fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
e0ceaba773 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
ad119a2188 more enhancements for the lib 2010-02-08 22:07:45 +01:00
Andreas Rumpf
fcffc84765 bug concerning constant evaluation fixed 2009-11-26 00:32:07 +01:00
Andreas Rumpf
8fd8905382 added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
99653a0b8b version0.7.10 2009-06-08 08:06:25 +02:00
metagn
e5aa3924fb move widestrs out of system (#20462)
* move widestrs out of system

* fix osproc
2022-10-01 23:35:09 +03:00
cooldome
d94c9dccd7 fix sqlgetdata regression in odbc (#15161)
* fix sqlgetdata import

* fix db_odbc

* more fixes

* fix style
2020-08-07 21:37:54 +01:00
cooldome
76f299e2c0 fix odbc regressions (#15009) [backport]
* fix odbc regressions

* make only necessary changes
2020-07-18 09:21:09 +01:00
Bung
8af6e92606 fix #9771 (#14357)
* fix #9771
* map SQLLEN SQLULEN
* fix proc params take TSqlLen

Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 03:00:06 +08:00
Juan Carlos
96920dace2 Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Araq
8070ba46e0 minor style changes 2019-07-11 00:28:20 +02:00
Andreas Rumpf
45b4eb49ca Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
efbdda55d1 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Jacek Sieka
829b9aad05 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Araq
d0fd779b4c Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Andreas Rumpf
245763c29f Merge pull request #3265 from coffeepots/patch-2
Add widestring versions of SQLPrepare and SQLExecDirect
2015-08-28 18:27:26 +02:00
Araq
deba8d4c18 preparations for more Nimble packages; clear licensing; fixes #2949 2015-06-20 23:24:21 +02:00
pdw
b48e21db34 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Araq
76ca4298f1 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
rumpf_a@web.de
79413cf74c added missing library files 2010-03-07 23:17:37 +01:00
Andreas Rumpf
1a0949c9df examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
e6f0b0b016 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
18bd38d4da continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
7f7f90abbb more enhancements for the lib 2010-02-08 22:07:45 +01:00
Andreas Rumpf
c9d5a1081d added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
13429077a0 version0.7.10 2009-06-08 08:06:25 +02:00
flywind
5e768b34ec use lowercase --define switches (#17283) 2021-03-07 19:08:41 +08:00
Juan Carlos
5673f8fdb6 Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Nindaleth
189f8f8dd1 fix several typos in documentation and comments (#12553) 2019-10-30 09:08:45 +01:00
Araq
13d081aa5d minor style changes 2019-07-11 00:28:20 +02:00
Jacek Sieka
feb550f0b4 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
59d4f801ef mysql.nim: fixes how the DLL version loading works 2018-03-23 19:18:49 +01:00
Dennis Felsing
b5cbae1726 Enable mariadb shared libs for mysql as well (#7199) 2018-02-10 16:42:13 +01:00
Andreas Rumpf
2720c2f818 make MySQL client work with MariaDB; prefer newer versions over older 2018-01-18 11:32:43 +01:00
Dominik Picheta
1bc4196603 Merge pull request #4822 from alphashuro/patch-1
Update mysql.nim
2016-09-24 10:46:11 +02:00
Araq
0f9554fbbc Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Andreas Rumpf
956bcc4715 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
pdw
484ef29df1 src/wrappers/c-r - Dropped 'T' from types 2015-05-24 22:43:13 -05:00
Andreas Rumpf
f4312b4c82 Merge pull request #2140 from FedericoCeratto/devel
Fix some typos
2015-02-16 20:32:24 +01:00
Andreas Rumpf
a511b9f918 Merge pull request #2108 from oderwat/patch-1
Fixing dylib name for OSX
2015-02-12 10:02:53 +01:00
Andreas Rumpf
9034bc173f Merge pull request #1926 from ekarlso/mysql-osx
Support MySQL on OSX
2015-01-11 01:07:56 +01:00
Araq
faadf3eb85 Merge branch 'devel' of https://github.com/Araq/Nim into devel
Conflicts:
	compiler/semexprs.nim
2015-01-07 02:44:22 +01:00
Araq
f6cd14bc61 resolved conflict 2014-09-13 17:59:37 +02:00
Araq
f155fd122d Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
50426e3f23 resolved conflict 2014-04-10 01:47:20 +02:00
Araq
b0624c8eff Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
6f9876dc04 preparations for making 'closure' the default calling convention for proc types 2012-07-16 23:00:57 +02:00
Araq
9528b9d853 added some missing wrappers 2010-08-24 00:22:36 +02:00
Andreas Rumpf
b7adea8e18 examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
d4f66891e6 fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
rumpf_a@web.de
20b5cc6985 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
b4e02ea65c bugfix: inconsequent tuple usage 2009-12-09 00:41:55 +01:00
Andreas Rumpf
33935a547c added tools and web dirs 2009-09-15 23:22:22 +02:00
Andreas Rumpf
08fafdc2c1 overload resolution for proc vars 2009-06-24 17:13:22 +02:00
Andreas Rumpf
16fee27851 version0.7.10 2009-06-08 08:06:25 +02:00
jfilby
58d6166fac Fix several memory leaks in the Postgres wrapper. (#20940) 2022-11-27 20:28:38 +02:00
Andrey Makarov
c12b6a894a Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
e102f4fb86 refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
fb463f545f Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
ee7
95b4524705 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Artem Klevtsov
ef92d6ec76 Improve db_postgres iterators (#18144)
* Fix pqSetSingleRowMode case. Add links to the docs

* Add missing PGContextVisibility enum

* Remove unused PGContextVisibility enum

* Improve db_postgres iterators

* Fix instantRows with DbColumns. Cosmetics.

* Reduce copy&paste in db_postgres

* Move pqclear inside loop
2021-06-03 19:41:57 +07:00
Thomas T. Jarløv
9f35bb3968 Escape %00 / \0 in dbQuote (#18015) [backport:1.4]
Fix https://github.com/nim-lang/Nim/issues/17925
2021-05-15 21:26:15 +02:00
Fröhlich A
2b6e1d4836 Fix insert calling wrong function (#17856)
The `insert` method is calling `tryInsertID`, which ignores the `pkName` parameter.
Calling `tryInsert` instead should be correct.
2021-04-29 14:11:05 +02:00
Danil Yarantsev
196a801c3f Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
60e92168f9 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
Juan Carlos
35dadac3fb db_postgres document how to use it with unix socket (#15187) 2020-08-17 03:39:58 -03:00
Bung
6f7c749ea5 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Chris Heller
988a4ef900 Check pqntuples > 0 in getValue. Fixes #12973 (#12974) 2019-12-29 08:41:18 -08:00
Federico Ceratto
6c6a7e34e9 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Chris Heller
60f707fdad Skip db_postgres.setRow if getRow returns 0 rows (#11859)
Fixes nim-lang/Nim#11821
2019-08-04 10:28:24 -07:00
Huy
c2daf0055e add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
aa4184b375 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Araq
83b5a2bc57 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Andreas Rumpf
dcfe341955 fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
5bc1ac4a51 make tests green again 2017-11-07 12:57:32 +01:00
Andreas Rumpf
16976718da Merge branch 'devel' into araq 2017-11-02 10:46:30 +01:00
Andreas Rumpf
196aad2c57 Merge branch 'devel' into araq 2017-09-15 21:12:59 +02:00
Andreas Rumpf
a202556ac7 Merge branch 'devel' into faster-nimsuggest 2017-02-17 10:48:01 +01:00
Araq
209b4a171e Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
0dcdfa0ab5 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
7407d00052 Merge pull request #3514 from vegansk/3513_pgsql_prepstat
Fixed issue 3513: wrong setupQuery realization
2015-11-11 16:09:18 +00:00
Dominik Picheta
043d77d2bc Improved postgres docs and added untestable tests. 2015-10-26 21:10:21 +01:00
JamesP
24c0887f54 fix doco by removing extra ` chars, change a word and line up multi-line text 2015-10-13 07:34:32 +10:00
JamesP
8fb1a0fa9e add doco outlining the two SQL parameter substitution mechanisms for the db_postgres module
adjust doco note: indent by one space

shorten doco example lines, by splitting across multiple lines

shorten doco line widths by splitting long lines into multi-lines

fix to prepare() example in doco "Note:" section
2015-09-17 14:15:33 +10:00
JamesP
a69826615a setupQuery() with SqlQuery take parameter substitution with "?"
add instantRows() with SqlPrepared parameter

fix setupQuery() for SqlQuery to produce a unique identiying query name

add rows() iterator with SqlPrepared parameter

add execAffectedRows for SqlPrepared
2015-09-17 13:08:24 +10:00
JamesP
f9f73aed79 tryExec() with SqlQuery now takes "?" substitution parameters
add tryExec() with SqlPrepared parameter

exec() with SqlQuery now expects "?" parameter substitution
2015-09-17 09:46:41 +10:00
JamesP
2179f45be2 add check to dbFormat() to verify parameter substitution has "?" identifier
add check to prepare() that parameter substitution has "$1" identifier
2015-09-17 09:42:40 +10:00
Andreas Rumpf
2157ebc536 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
8023c2c851 Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
7240056537 lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
88791d9a9e Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Andreas Rumpf
01648fead0 Merge pull request #1889 from ramnes/devel
Happy new year!
2015-01-07 14:14:43 +01:00
Araq
c71afbcd53 Merge branch 'devel' into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/json.nim
	lib/pure/math.nim
	lib/system/atomics.nim
2014-11-03 11:42:36 +01:00
Araq
b984992511 docgen should work again 2014-10-05 03:06:19 +02:00
Araq
b612c42e1c Merge branch 'devel' of https://github.com/Araq/Nimrod into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/os.nim
	lib/wrappers/postgres.nim
2014-10-04 19:40:23 +02:00
Araq
1bb90ad3ae cleaned up various modules 2014-09-19 00:54:01 +02:00
Araq
0e5874a93a resolved conflict 2014-09-13 17:59:37 +02:00
Araq
00e4d53100 Nimrod renamed to Nim 2014-08-28 09:59:26 +02:00
Araq
76ae6168d8 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
4eb3dda5e4 resolved conflict 2014-04-10 01:47:20 +02:00
Zahary Karadjov
f882499ca2 Merge branch 'master' of github.com:Araq/Nimrod into upstream 2013-12-09 00:26:04 +02:00
Araq
657f3bdfd0 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
3e4072c6ea Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
f1a64fdb1c fixes #250 2012-11-19 00:16:25 +01:00
Araq
4ade87d840 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
593b731002 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
9a99aeac00 openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
d34c093f8d changed integer promotion rules; breaks bootstrapping and lots of code 2012-07-08 21:03:47 +02:00
Araq
32c87721a7 small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
512d1d0bb2 added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
910dd6bcc9 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Andreas Rumpf
c49e9ff84b fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
8ce203abe0 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
9743e567ae continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
1c8496a3ad cleanup of library docs 2010-02-04 00:47:59 +01:00
Andreas Rumpf
af073ad381 bugfixes for the high-level postgreSQL wrapper 2010-01-29 01:50:39 +01:00
Andreas Rumpf
9e21f3aa24 bugfix: fd_set mustn't be prefixed with struct 2010-01-14 00:05:02 +01:00
Andreas Rumpf
1c2b176d62 bugfix: inconsequent tuple usage 2009-12-09 00:41:55 +01:00
Andreas Rumpf
7c01f52e92 bug concerning constant evaluation fixed 2009-11-26 00:32:07 +01:00
rumpf_a@web.de
20cd512457 version 0.8.2 2009-10-21 10:20:15 +02:00
ringabout
a6997fb8eb fixes ptr to cstring warnings[backport] (#20848)
* fix =#13790 ptr char (+friends) should not implicitly convert to cstring

* Apply suggestions from code review

* first round; compiles on windows

* nimPreviewSlimSystem

* conversion is unsafe, cast needed

* fixes more tests

* fixes asyncnet

* another try another error

* last one

* true

* one more

* why bugs didn't show at once

* add `nimPreviewCstringConversion` switch

* typo

* fixes ptr to cstring warnings[backport]

* add fixes

Co-authored-by: xflywind <43030857+xflywind@users.noreply.github.com>
2022-11-16 17:22:51 +08:00
Andrey Makarov
d0cc21e322 Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
769869c699 refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
344d861247 Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
ee7
a3eb2381f2 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Danil Yarantsev
2834588250 Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
05f1ddc34c use single backtick (#17100) 2021-02-19 00:47:21 -06:00
shirleyquirk
234ecc42fa odbc regression from #14357 (#15417)
#14357 changed from sending 'val', an int, to a pointer to int, which is understandable, but not how SQLSetEnvAttr works.
"Depending on the value of Attribute, ValuePtr will be a 32-bit integer value or point to a null-terminated character string"
2020-09-29 16:08:24 +01:00
cooldome
36a191c9e6 fix sqlgetdata regression in odbc (#15161)
* fix sqlgetdata import

* fix db_odbc

* more fixes

* fix style
2020-08-07 21:37:54 +01:00
Bung
246b6e3ae0 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Bung
53836903d3 fix #9771 (#14357)
* fix #9771
* map SQLLEN SQLULEN
* fix proc params take TSqlLen

Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 03:00:06 +08:00
Andrew Owen
900d65ae91 Fix typo in docs (#12356) [ci skip] 2019-10-04 00:40:15 -06:00
Federico Ceratto
a17c1f85c3 Fix spellings (#12277) [backport] 2019-09-27 06:02:54 +01:00
Huy
31c1148eb0 add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
2702689fe7 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Andreas Rumpf
c337a17fb0 Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
40e8c2d744 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Araq
541d93bd90 docs: remove most stale links; refs #9109 2018-09-29 11:23:29 +02:00
Andreas Rumpf
16c646c816 Merge branch 'jlp765-db_odbc2' into devel 2016-05-28 20:12:29 +02:00
Araq
aaf7187fe5 resolved merge conflicts 2016-03-29 15:30:44 +02:00
Araq
255c7670c9 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
1cc74251ee Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
ringabout
0ed7f08bed refactor dbFormat (#19746)
* refactor dbFormat

* add simple tests
2022-09-29 18:16:42 +08:00
Andrey Makarov
9b7320d352 Implement Pandoc Markdown concise link extension (#20304)
* Implement Pandoc Markdown concise link extension

This implements https://github.com/nim-lang/Nim/issues/20127.
Besides reference to headings we also support doing references
to Nim symbols inside Nim modules.

Markdown:
```
Some heading
------------

Ref. [Some heading].
```

Nim:
```
proc someFunction*() ...

... ## Ref. [someFunction]
```

This is substitution for RST syntax like `` `target`_ ``.
All 3 syntax variants of extension from Pandoc Markdown are supported:
`[target]`, `[target][]`, `[description][target]`.

This PR also fixes clashes in existing files, particularly
conflicts with RST footnote feature, which does not work with
this PR (but there is a plan to adopt a popular [Markdown footnote
extension](https://pandoc.org/MANUAL.html#footnotes) to make footnotes work).

Also the PR fixes a bug that Markdown links did not work when `[...]`
section had a line break.

The implementation is straightforward since link resolution did not
change w.r.t. RST implementation, it's almost only about new syntax
addition. The only essential difference is a possibility to add a custom
link description: form `[description][target]` which does not have an
RST equivalent.

* fix nim 1.0 gotcha
2022-09-04 21:52:21 +03:00
Andrey Makarov
03a0fda1bd Markdown code blocks part 6 (#20292) 2022-09-01 02:39:02 +03:00
ee7
730580382c make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
ringabout
ea15babc09 fixes #20153; do not escape _ for mysql [backport] (#20164)
* fixes #20153; do not escape `_` for mysql

* add a test

* Update db_mysql.nim

* Update tdb_mysql.nim

Co-authored-by: Clay Sweetser <Varriount@users.noreply.github.com>
2022-08-06 05:15:58 +08:00
flywind
eb1a2176e7 enable style:usages for stdlib tests [backport: 1.6] (#19715)
* enable style:usages for stdlib tests

* freeAddrInfo

* more tests

* importc

* bufSize

* fix more

* => parseSql and renderSql
2022-04-13 17:53:02 +08:00
Danil Yarantsev
ce00e9284f Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
942694fd46 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
Andreas Rumpf
abee78c3b0 fixes db_mysql broken quoting; refs c16ee37a71 (r44209990) [backport:1.4] (#16035) 2020-11-18 23:06:16 +01:00
Andreas Rumpf
4ea64743cb fixes #15560 (#15587) 2020-10-15 15:49:00 +02:00
Bung
d0bc5875b0 Fix #15219 SQL escape in db_mysql is not enough (#15234) 2020-09-04 17:04:27 +08:00
Bung
51b0bd3121 add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Andreas Rumpf
e5a46907ab use system.move instead of system.shallowCopy if the GC mode requires it 2019-10-03 16:46:09 +02:00
Huy
4f663a104e add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
Zed
ec08afd847 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Araq
021fe523b1 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Andreas Rumpf
26afc0703a fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
8c93fc2229 fixed merge conflict 2018-06-04 16:16:50 +02:00
Andreas Rumpf
e8b734dde1 fixes #7200 2018-02-10 16:41:38 +01:00
pgkos
4b4613fc57 Fix segfault in db_mysql fastRows (#5605) 2017-03-26 09:42:15 +02:00
Araq
14c0c64c59 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
68eab87dd2 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
a9c1a94037 Merge branch 'jlp765-db_mysqlExamples' into devel 2015-09-05 21:15:25 +01:00
Andreas Rumpf
4be74a1d20 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
bed7bc325f Merge pull request #3264 from jlp765/db_mysql
fixes #3220 src/db_mysql.nim getValue()
2015-09-03 14:09:43 +02:00
Andreas Rumpf
ce8659ac36 Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
d02789dd6e lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
e745c73dea Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Andreas Rumpf
0a735a6f12 Merge pull request #2108 from oderwat/patch-1
Fixing dylib name for OSX
2015-02-12 10:02:53 +01:00
Araq
9e3d2d725e changed formating 2014-12-08 19:49:20 +01:00
Araq
ea72ccbaf8 Merge branch 'devel' into bigbreak
Conflicts:
	src/db_postgres.nim
	lib/pure/json.nim
	lib/pure/math.nim
	lib/system/atomics.nim
2014-11-03 11:42:36 +01:00
Araq
ad8ac587e0 resolved conflict 2014-09-13 17:59:37 +02:00
Araq
747245aa82 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Simon Hafner
a9d421e40f Merge pull request #1358 from def-/db_mysql-sql
Add missing sql proc for db_mysql
2014-07-14 14:03:10 -05:00
Andreas Rumpf
a271a07b64 resolved conflict 2014-04-10 01:47:20 +02:00
Araq
81dcfba125 Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
cc4073fa60 Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
5e801014fa fixes #250 2012-11-19 00:16:25 +01:00
Araq
0c583a38b4 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
e922895883 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
1b4db44660 openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
acadc5f5eb small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
bc84a6e7ce added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
e8442eaf11 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Andreas Rumpf
5bd3fbb2d6 bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
20de1a342e examples/lib use the new wrappers 2010-02-28 23:04:18 +01:00
Andreas Rumpf
8f0e58819e fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
1b686605d1 SQLite wrapper 2010-02-24 01:04:39 +01:00
rumpf_a@web.de
1a05787855 continued work on html/xmlparser 2010-02-14 00:29:35 +01:00
Andreas Rumpf
a04fc1c0f3 httpserver supports a fixed port number 2010-02-06 12:24:54 +01:00
Andreas Rumpf
1518052813 cleanup of library docs 2010-02-04 00:47:59 +01:00
Andreas Rumpf
b8296988d0 db_mysql.nim added 2010-01-29 08:09:23 +01:00
Andrey Makarov
1b5c7a6941 Markdown code blocks migration part 7 (#20547) 2022-10-12 17:13:43 +03:00
ringabout
70a19c8db8 make more standard libraries work with nimPreviewSlimSystem (#20343)
* make more standard libraries work with `nimPreviewSlimSystem`

* typo

* part two

* Delete specutils.nim

* fixes more tests

* more fixes

* fixes tests

* fixes three more tests

* add formatfloat import

* fix

* last
2022-09-28 02:06:23 +08:00
ee7
4010ad7229 make implicit cstring conversions explicit (#19488)
The Nim manual says that an implicit conversion to cstring will
eventually not be allowed [1]:

    A Nim `string` is implicitly convertible to `cstring` for convenience.

    [...]

    Even though the conversion is implicit, it is not *safe*: The garbage collector
    does not consider a `cstring` to be a root and may collect the underlying
    memory. For this reason, the implicit conversion will be removed in future
    releases of the Nim compiler. Certain idioms like conversion of a `const` string
    to `cstring` are safe and will remain to be allowed.

And from Nim 1.6.0, such a conversion triggers a warning [2]:

    A dangerous implicit conversion to `cstring` now triggers a `[CStringConv]` warning.
    This warning will become an error in future versions! Use an explicit conversion
    like `cstring(x)` in order to silence the warning.

However, some files in this repo produced such a warning. For example,
before this commit, compiling `parsejson.nim` would produce:

    /foo/Nim/lib/pure/parsejson.nim(221, 37) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]
    /foo/Nim/lib/pure/parsejson.nim(231, 39) Warning: implicit conversion to 'cstring' from a non-const location: my.buf; this will become a compile time error in the future [CStringConv]

This commit resolves the most visible `CStringConv` warnings, making the
cstring conversions explicit.

[1] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/doc/manual.md#cstring-type
[2] https://github.com/nim-lang/Nim/blob/d2318d9ccfe6/changelogs/changelog_1_6_0.md#type-system
2022-08-19 21:40:53 +02:00
Andreas Rumpf
14c6c1c695 Revert "fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744)" (#19745)
This reverts commit b10f0e7bca.
2022-04-25 11:59:37 +02:00
flywind
33f668caaf fix db_sqlite.tryInsertID does raise exceptions in 1.6.0 #19743 (#19744) 2022-04-25 02:20:18 +08:00
Araq
c699f4b5ab db_sqlite: added a space 2022-03-18 14:17:54 +01:00
flywind
eaad754508 fix a sqlite bug (#18669) 2021-08-13 00:21:01 +08:00
Andrey Makarov
f3c0728405 docs: make inline markup more compatible with Markdown (#18053)
fixes https://github.com/timotheecour/Nim/issues/739
2021-05-21 07:54:20 +03:00
Andrey Makarov
b2d26fa9ca fix RST parsing when no indent after enum.item (fix #17249) (#17257) 2021-03-12 10:33:21 +03:00
Danil Yarantsev
89fe268007 Change stdlib imports to use std prefix in most examples (#17202) 2021-03-01 00:17:19 +03:00
flywind
fbb62a6c32 use single backtick (#17100) 2021-02-19 00:47:21 -06:00
RokkuCode
5b8ae3cbf4 fixes #16080 (#16091) [backport:1.2]
* fixes #16080

db_sqlite: Error: undeclared field: 'untypedLen'

* redacting fix
2020-11-23 15:15:55 +01:00
Regis Caillaud
42a503b069 Handle BLOB column type in SQLite as binary data (#15681)
* Fixed not handling blob correctly in sqlite
* Fixed setLen commented by mistake
* Added binary example as db_sqlite doc
* Added tests for sqlite binary data
2020-11-02 13:02:55 +01:00
Bung
d078a65a4d add bindParams to db_sqlite (#14408)
* add bindParams to db_sqlite
* no need typeinfo
* remove extro spaces
* reduce bindParams to two branches,raise DbError
* Update src/db_sqlite.nim
* change bindParams to macro,accept varargs[untyped] as params
* change bind blob val to openArray[byte]
* remove unused err type
* explicitly using i32 param
* using import std/private/since
* SQLITE_OK to right hand

* bindParam val int using bindParam overload
* copy data by default
* change exec to template
* remove SqlPrepared procs unused varargs
* fix setupquery for prepared,reset first for exec prepared,add bindNull for literal nil

Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-26 13:44:47 +08:00
Bung
69b6a2dc3a add insert,tryInsert unify for postgres that need pk name (#14416)
* add insert,tryInsert unify for postgres that need pk name
* add ReadDbEffect to new procs
* add .since and changelog
* change since to 1.3
* Update src/db_postgres.nim

Co-authored-by: bung87 <crc32@qq.com>
Co-authored-by: Andreas Rumpf <rumpf_a@web.de>
Co-authored-by: alaviss <leorize+oss@disroot.org>
2020-05-23 01:24:52 +08:00
Bung
2bfe76908d add SqlPrepared api fix #13559 (#14365)
Co-authored-by: bung87 <crc32@qq.com>
2020-05-16 21:50:04 +08:00
itsumura-h
0ac0f2e2c4 fix #7241 (#13779)
finalize() should run in insert()
2020-04-03 16:38:05 +09:00
Juan Carlos
90fe5e03af Deprecate DCE:on (#13839) 2020-04-01 21:58:43 -03:00
Ray Imber
8a40b19273 Documentation improvements around the db interface (#12362)
Added more details about the limits and reasoning behind the API.
Came about from this discussion on IRC: https://irclogs.nim-lang.org/04-10-2019.html#16:58:04
2019-10-08 02:24:06 -07:00
Andreas Rumpf
f20f3269fc removed unused imports [refactoring] 2019-08-08 16:51:41 +02:00
Andreas Rumpf
d11230ea13 makes the -d:nimIncremental compiler mode compile again 2019-07-21 16:41:46 +02:00
Huy
79654a8191 add unsafeColumnAt procs, that return unsafe cstring from InstantRow (#11647) 2019-07-10 19:56:09 +07:00
jiro
e02ee2c195 db_sqlite: Update documentation (#10330) (#11266) 2019-05-20 03:27:12 +09:00
Zed
fb481c0df8 Fix header inconsistencies in documentation (#11071) 2019-04-23 08:40:20 +02:00
Andreas Rumpf
ad45dcd223 Merge branch 'devel' into araq-quirky-exceptions 2019-02-08 17:18:17 +01:00
Araq
8c15451400 removes deprecated T/P types 2018-11-16 13:27:46 +01:00
Federico Ceratto
ed588265ca Update example (#9395) 2018-10-16 23:17:37 +01:00
Andreas Rumpf
ebdbdb0ca5 fixes merge conflict 2018-08-19 15:14:03 +02:00
Andreas Rumpf
f503ef5fd9 fixed merge conflict 2018-06-04 16:16:50 +02:00
Araq
e1b3dc208a fixes testament compilation 2018-05-14 17:45:44 +02:00
Jacek Sieka
870869d081 remove dead code elimination option (#7669) 2018-04-23 17:02:38 +08:00
Andreas Rumpf
a9a982c5e9 make tests green again 2017-11-07 12:57:32 +01:00
Andreas Rumpf
d409e7f4ba Merge branch 'devel' into araq 2017-10-02 08:31:38 +02:00
Araq
fdf460d131 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 14:54:50 +01:00
Araq
a6664bf8e3 Merge branch 'devel' of https://github.com/nim-lang/Nim into devel 2016-01-18 10:55:23 +01:00
Dominik Picheta
e6e5e86f5c Merge pull request #3294 from jlp765/db_sqliteDoco
Db sqlite doco update (similar to db_mysql)
2015-09-06 23:08:06 +01:00
Andreas Rumpf
46eb616a37 Merge pull request #3289 from nanoant/patch/fix-whitespace
Patch/fix whitespace
2015-09-04 23:37:59 +02:00
Andreas Rumpf
457ef2855b Merge pull request #2866 from nanoant/patch/db-deffered-rows
db: InstantRow and instantRows
2015-06-10 11:06:36 +02:00
pdw
118e4667b7 lib/impure - Dropped 'T' from types 2015-05-24 22:24:07 -05:00
Andreas Rumpf
5da8e91358 Merge pull request #2583 from BlaXpirit/test-stdlib
Test stdlib
2015-04-21 15:36:37 +02:00
Andreas Rumpf
4998c99d05 Merge pull request #2337 from kemege/db_charset
Add character set options when opening DB connection, default to UTF-8
2015-03-17 01:32:26 +01:00
Araq
0a9ca2633f fixes #1866 2015-01-10 23:52:05 +01:00
Araq
c7703ec24f updated db_sqlite.nim 2014-08-29 00:16:12 +02:00
Araq
01c6a074c1 Nimrod renamed to Nim 2014-08-28 09:50:51 +02:00
Andreas Rumpf
bb3f67be0e resolved conflict 2014-04-10 01:47:20 +02:00
Araq
c90a02503b Merge branch 'master' of github.com:Araq/Nimrod 2013-03-17 16:44:31 +01:00
Araq
6201c0ba56 Merge branch 'master' of github.com:Araq/Nimrod 2012-11-25 11:03:27 +01:00
Araq
832dc48f89 fixes #250 2012-11-19 00:16:25 +01:00
Araq
d05879916d 'assert' hides EAssertionFailsure; stdlib makes use of 'tags' 2012-11-18 01:36:20 +01:00
Araq
79ced725f0 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-20 18:48:49 +02:00
Araq
714341b1e3 bugfix: leak in db_sqlite.GetValue 2012-10-10 01:05:03 +02:00
Araq
bb03ef88b3 Merge branch 'master' of github.com:Araq/Nimrod 2012-10-10 01:03:04 +02:00
Araq
faefcaf47e openarray/varargs split; breaks bootstrapping 2012-08-16 08:34:33 +02:00
Araq
0e7254f2eb changed integer promotion rules; breaks bootstrapping and lots of code 2012-07-08 21:03:47 +02:00
Araq
9f6219d5cf small bugfixes; documentation generator supports smilies for the forum 2012-05-01 11:14:29 +02:00
Araq
f17a5c0c6b some code formating 2012-04-26 01:16:50 +02:00
Araq
1df6a386a7 added db_*.getRow 2012-04-24 08:44:36 +02:00
Araq
701187b6d7 added system.setControlCHook, system.writeStackTrace 2012-02-19 23:37:37 +01:00
Araq
790515ff98 year 2012 for most copyright headers 2012-01-02 23:07:35 +01:00
Araq
19896f6a42 much more efficient rod file generation 2011-10-18 17:21:51 +02:00
Andreas Rumpf
7c1e294e23 bugfix: return type of gtk2.check_menu_item_new* 2010-03-17 18:58:34 +01:00
Andreas Rumpf
b021a7c91b fixed pango/pangoutils new wrappers 2010-02-26 01:26:16 +01:00
Andreas Rumpf
4cba67edd7 SQLite wrapper 2010-02-24 01:04:39 +01:00
14 changed files with 1 additions and 3190 deletions

View File

@@ -1,418 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## A higher level `mySQL`:idx: database wrapper. The same interface is
## implemented for other databases too.
##
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_postgres <db_postgres.html>`_.
##
## Parameter substitution
## ======================
##
## All `db_*` modules support the same form of parameter substitution.
## That is, using the `?` (question mark) to signify the place where a
## value should be placed. For example:
## ```
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
## ```
##
## Examples
## ========
##
## Opening a connection to a database
## ----------------------------------
##
## ```
## import std/db_mysql
## let db = open("localhost", "user", "password", "dbname")
## db.close()
## ```
##
## Creating a table
## ----------------
##
## ```
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
## ```
##
## Inserting data
## --------------
##
## ```
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Dominik")
## ```
##
## Larger example
## --------------
##
## ```
## import std/[db_mysql, math]
##
## let theDb = open("localhost", "nim", "nim", "test")
##
## theDb.exec(sql"Drop table if exists myTestTbl")
## theDb.exec(sql("create table myTestTbl (" &
## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
## " Name VARCHAR(50) NOT NULL, " &
## " i INT(11), " &
## " f DECIMAL(18,10))"))
##
## theDb.exec(sql"START TRANSACTION")
## for i in 1..1000:
## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#" & $i, i, sqrt(i.float))
## theDb.exec(sql"COMMIT")
##
## for x in theDb.fastRows(sql"select * from myTestTbl"):
## echo x
##
## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#1001", 1001, sqrt(1001.0))
## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
##
## theDb.close()
## ```
import strutils, mysql
import db_common
export db_common
import std/private/[since, dbutils]
type
DbConn* = distinct PMySQL ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## converted to nil.
InstantRow* = object ## a handle that can be used to get a row's
## column text on demand
row: cstringArray
len: int
proc dbError*(db: DbConn) {.noreturn.} =
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $mysql.error(PMySQL db)
raise e
when false:
proc dbQueryOpt*(db: DbConn, query: string, args: varargs[string, `$`]) =
var stmt = mysql_stmt_init(db)
if stmt == nil: dbError(db)
if mysql_stmt_prepare(stmt, query, len(query)) != 0:
dbError(db)
var
binding: seq[MYSQL_BIND]
discard mysql_stmt_close(stmt)
proc dbQuote*(s: string): string =
## DB quotes the string. Note that this doesn't escape `%` and `_`.
result = newStringOfCap(s.len + 2)
result.add "'"
for c in items(s):
# see https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html#mysql-escaping
case c
of '\0': result.add "\\0"
of '\b': result.add "\\b"
of '\t': result.add "\\t"
of '\l': result.add "\\n"
of '\r': result.add "\\r"
of '\x1a': result.add "\\Z"
of '"': result.add "\\\""
of '\'': result.add "\\'"
of '\\': result.add "\\\\"
else: result.add c
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
dbFormatImpl(formatstr, dbQuote, args)
proc tryExec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var q = dbFormat(query, args)
return mysql.real_query(PMySQL db, q.cstring, q.len) == 0'i32
proc rawExec(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) =
var q = dbFormat(query, args)
if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db)
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query and raises EDB if not successful.
var q = dbFormat(query, args)
if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32: dbError(db)
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc properFreeResult(sqlres: mysql.PRES, row: cstringArray) =
if row != nil:
while mysql.fetch_row(sqlres) != nil: discard
mysql.freeResult(sqlres)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
## if you require **ALL** the rows.
##
## Breaking the fastRows() iterator during a loop will cause the next
## database query to raise an `EDb` exception `Commands out of sync`.
rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil:
var
L = int(mysql.numFields(sqlres))
row: cstringArray
result: Row
backup: Row
newSeq(result, L)
while true:
row = mysql.fetch_row(sqlres)
if row == nil: break
for i in 0..L-1:
setLen(result[i], 0)
result[i].add row[i]
yield result
properFreeResult(sqlres, row)
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## Same as fastRows but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within the iterator body.
rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil:
let L = int(mysql.numFields(sqlres))
var row: cstringArray
while true:
row = mysql.fetch_row(sqlres)
if row == nil: break
yield InstantRow(row: row, len: L)
properFreeResult(sqlres, row)
proc setTypeName(t: var DbType; f: PFIELD) =
t.name = $f.name
t.maxReprLen = Natural(f.max_length)
if (NOT_NULL_FLAG and f.flags) != 0: t.notNull = true
case f.ftype
of TYPE_DECIMAL:
t.kind = dbDecimal
of TYPE_TINY:
t.kind = dbInt
t.size = 1
of TYPE_SHORT:
t.kind = dbInt
t.size = 2
of TYPE_LONG:
t.kind = dbInt
t.size = 4
of TYPE_FLOAT:
t.kind = dbFloat
t.size = 4
of TYPE_DOUBLE:
t.kind = dbFloat
t.size = 8
of TYPE_NULL:
t.kind = dbNull
of TYPE_TIMESTAMP:
t.kind = dbTimestamp
of TYPE_LONGLONG:
t.kind = dbInt
t.size = 8
of TYPE_INT24:
t.kind = dbInt
t.size = 3
of TYPE_DATE:
t.kind = dbDate
of TYPE_TIME:
t.kind = dbTime
of TYPE_DATETIME:
t.kind = dbDatetime
of TYPE_YEAR:
t.kind = dbDate
of TYPE_NEWDATE:
t.kind = dbDate
of TYPE_VARCHAR, TYPE_VAR_STRING, TYPE_STRING:
t.kind = dbVarchar
of TYPE_BIT:
t.kind = dbBit
of TYPE_NEWDECIMAL:
t.kind = dbDecimal
of TYPE_ENUM: t.kind = dbEnum
of TYPE_SET: t.kind = dbSet
of TYPE_TINY_BLOB, TYPE_MEDIUM_BLOB, TYPE_LONG_BLOB,
TYPE_BLOB: t.kind = dbBlob
of TYPE_GEOMETRY:
t.kind = dbGeometry
proc setColumnInfo(columns: var DbColumns; res: PRES; L: int) =
setLen(columns, L)
for i in 0..<L:
let fp = mysql.fetch_field_direct(res, cint(i))
setTypeName(columns[i].typ, fp)
columns[i].name = $fp.name
columns[i].tableName = $fp.table
columns[i].primaryKey = (fp.flags and PRI_KEY_FLAG) != 0
#columns[i].foreignKey = there is no such thing in mysql
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
args: varargs[string, `$`]): InstantRow =
## Same as fastRows but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within the iterator body.
rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil:
let L = int(mysql.numFields(sqlres))
setColumnInfo(columns, sqlres, L)
var row: cstringArray
while true:
row = mysql.fetch_row(sqlres)
if row == nil: break
yield InstantRow(row: row, len: L)
properFreeResult(sqlres, row)
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
## Returns text for given column of the row.
$row.row[col]
proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
## Return cstring of given column of the row
row.row[index]
proc len*(row: InstantRow): int {.inline.} =
## Returns number of columns in the row.
row.len
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil:
var L = int(mysql.numFields(sqlres))
result = newRow(L)
var row = mysql.fetch_row(sqlres)
if row != nil:
for i in 0..L-1:
setLen(result[i], 0)
add(result[i], row[i])
properFreeResult(sqlres, row)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
## executes the query and returns the whole result dataset.
result = @[]
rawExec(db, query, args)
var sqlres = mysql.useResult(PMySQL db)
if sqlres != nil:
var L = int(mysql.numFields(sqlres))
var row: cstringArray
var j = 0
while true:
row = mysql.fetch_row(sqlres)
if row == nil: break
setLen(result, j+1)
newSeq(result[j], L)
for i in 0..L-1:
result[j][i] = $row[i]
inc(j)
mysql.freeResult(sqlres)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
result = getRow(db, query, args)[0]
proc tryInsertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect], raises: [DbError].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
var q = dbFormat(query, args)
if mysql.real_query(PMySQL db, q.cstring, q.len) != 0'i32:
result = -1'i64
else:
result = mysql.insertId(PMySQL db)
proc insertId*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} =
## same as tryInsertID
tryInsertID(db, query, args)
proc insert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], since: (1, 3).} =
## same as insertId
result = tryInsert(db, query,pkName, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect].} =
## runs the query (typically "UPDATE") and returns the
## number of affected rows
rawExec(db, query, args)
result = mysql.affectedRows(PMySQL db)
proc close*(db: DbConn) {.tags: [DbEffect].} =
## closes the database connection.
if PMySQL(db) != nil: mysql.close(PMySQL db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [DbEffect].} =
## opens a database connection. Raises `EDb` if the connection could not
## be established.
var res = mysql.init(nil)
if res == nil: dbError("could not open database connection")
let
colonPos = connection.find(':')
host = if colonPos < 0: connection
else: substr(connection, 0, colonPos-1)
port: int32 = if colonPos < 0: 0'i32
else: substr(connection, colonPos+1).parseInt.int32
if mysql.realConnect(res, host.cstring, user, password, database,
port, nil, 0) == nil:
var errmsg = $mysql.error(res)
mysql.close(res)
dbError(errmsg)
result = DbConn(res)
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [DbEffect].} =
## sets the encoding of a database connection, returns true for
## success, false for failure.
result = mysql.set_character_set(PMySQL connection, encoding) == 0

View File

@@ -1,529 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Nim Contributors
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## A higher level `ODBC` database wrapper.
##
## This is the same interface that is implemented for other databases.
##
## This has NOT yet been (extensively) tested against ODBC drivers for
## Teradata, Oracle, Sybase, MSSqlvSvr, et. al. databases.
##
## Currently all queries are ANSI calls, not Unicode.
##
## See also: `db_postgres <db_postgres.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_mysql <db_mysql.html>`_.
##
## Parameter substitution
## ======================
##
## All `db_*` modules support the same form of parameter substitution.
## That is, using the `?` (question mark) to signify the place where a
## value should be placed. For example:
##
## ```Nim
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
## ```
##
##
## Examples
## ========
##
## Opening a connection to a database
## ----------------------------------
##
## ```Nim
## import std/db_odbc
## var db = open("localhost", "user", "password", "dbname")
## db.close()
## ```
##
## Creating a table
## ----------------
##
## ```Nim
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
## ```
##
## Inserting data
## --------------
##
## ```Nim
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Andreas")
## ```
##
## Large example
## -------------
##
## ```Nim
## import std/[db_odbc, math]
##
## var theDb = open("localhost", "nim", "nim", "test")
##
## theDb.exec(sql"Drop table if exists myTestTbl")
## theDb.exec(sql("create table myTestTbl (" &
## " Id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY, " &
## " Name VARCHAR(50) NOT NULL, " &
## " i INT(11), " &
## " f DECIMAL(18,10))"))
##
## theDb.exec(sql"START TRANSACTION")
## for i in 1..1000:
## theDb.exec(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#" & $i, i, sqrt(i.float))
## theDb.exec(sql"COMMIT")
##
## for x in theDb.fastRows(sql"select * from myTestTbl"):
## echo x
##
## let id = theDb.tryInsertId(sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)",
## "Item#1001", 1001, sqrt(1001.0))
## echo "Inserted item: ", theDb.getValue(sql"SELECT name FROM myTestTbl WHERE id=?", id)
##
## theDb.close()
## ```
import strutils, odbcsql
import db_common
export db_common
import std/private/[since, dbutils]
type
OdbcConnTyp = tuple[hDb: SqlHDBC, env: SqlHEnv, stmt: SqlHStmt]
DbConn* = OdbcConnTyp ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## converted to nil.
InstantRow* = tuple[row: seq[string], len: int] ## a handle that can be
## used to get a row's
## column text on demand
var
buf: array[0..4096, char]
proc properFreeResult(hType: int, sqlres: var SqlHandle) {.
tags: [WriteDbEffect], raises: [].} =
try:
discard SQLFreeHandle(hType.TSqlSmallInt, sqlres)
sqlres = nil
except: discard
proc getErrInfo(db: var DbConn): tuple[res: int, ss, ne, msg: string] {.
tags: [ReadDbEffect], raises: [].} =
## Returns ODBC error information
var
sqlState: array[0..512, char]
nativeErr: array[0..512, char]
errMsg: array[0..512, char]
retSz: TSqlSmallInt = 0
res: TSqlSmallInt = 0
try:
sqlState[0] = '\0'
nativeErr[0] = '\0'
errMsg[0] = '\0'
res = SQLErr(db.env, db.hDb, db.stmt,
cast[PSQLCHAR](sqlState.addr),
cast[PSQLCHAR](nativeErr.addr),
cast[PSQLCHAR](errMsg.addr),
511.TSqlSmallInt, retSz.addr)
except:
discard
return (res.int, $(cast[cstring](addr sqlState)), $cast[cstring](addr nativeErr), $cast[cstring](addr errMsg))
proc dbError*(db: var DbConn) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
## Raises an `[DbError]` exception with ODBC error information
var
e: ref DbError
ss, ne, msg: string = ""
isAnError = false
res: int = 0
prevSs = ""
while true:
prevSs = ss
(res, ss, ne, msg) = db.getErrInfo()
if prevSs == ss:
break
# sqlState of 00000 is not an error
elif ss == "00000":
break
elif ss == "01000":
echo "\nWarning: ", ss, " ", msg
continue
else:
isAnError = true
echo "\nError: ", ss, " ", msg
if isAnError:
new(e)
e.msg = "ODBC Error"
if db.stmt != nil:
properFreeResult(SQL_HANDLE_STMT, db.stmt)
properFreeResult(SQL_HANDLE_DBC, db.hDb)
properFreeResult(SQL_HANDLE_ENV, db.env)
raise e
proc sqlCheck(db: var DbConn, resVal: TSqlSmallInt) {.raises: [DbError]} =
## Wrapper that raises `EDb` if `resVal` is neither SQL_SUCCESS or SQL_NO_DATA
if resVal notIn [SQL_SUCCESS, SQL_NO_DATA]: dbError(db)
proc sqlGetDBMS(db: var DbConn): string {.
tags: [ReadDbEffect, WriteDbEffect], raises: [] .} =
## Returns the ODBC SQL_DBMS_NAME string
const
SQL_DBMS_NAME = 17.SqlUSmallInt
var
sz: TSqlSmallInt = 0
buf[0] = '\0'
try:
db.sqlCheck(SQLGetInfo(db.hDb, SQL_DBMS_NAME, cast[SqlPointer](buf.addr),
4095.TSqlSmallInt, sz.addr))
except: discard
return $(cast[cstring](addr buf))
proc dbQuote*(s: string): string {.noSideEffect.} =
## DB quotes the string.
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
else: add(result, c)
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string {.
noSideEffect.} =
## Replace any `?` placeholders with `args`,
## and quotes the arguments
dbFormatImpl(formatstr, dbQuote, args)
proc prepareFetch(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): TSqlSmallInt {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
# Prepare a statement, execute it and fetch the data to the driver
# ready for retrieval of the data
# Used internally by iterators and retrieval procs
# requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished
db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args)
db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.sqlCheck(SQLExecute(db.stmt))
result = SQLFetch(db.stmt)
db.sqlCheck(result)
proc prepareFetchDirect(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
# Prepare a statement, execute it and fetch the data to the driver
# ready for retrieval of the data
# Used internally by iterators and retrieval procs
# requires calling
# properFreeResult(SQL_HANDLE_STMT, db.stmt)
# when finished
db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt))
var q = dbFormat(query, args)
db.sqlCheck(SQLExecDirect(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
db.sqlCheck(SQLFetch(db.stmt))
proc tryExec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Tries to execute the query and returns true if successful, false otherwise.
var
res:TSqlSmallInt = -1
try:
db.prepareFetchDirect(query, args)
var
rCnt:TSqlLen = -1
res = SQLRowCount(db.stmt, rCnt)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
if res != SQL_SUCCESS: dbError(db)
except: discard
return res == SQL_SUCCESS
proc rawExec(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
db.prepareFetchDirect(query, args)
proc exec*(db: var DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query and raises EDB if not successful.
db.prepareFetchDirect(query, args)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
proc newRow(L: int): Row {.noSideEFfect.} =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
iterator fastRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
## if you require **ALL** the rows.
##
## Breaking the fastRows() iterator during a loop may cause a driver error
## for subsequent queries
##
## Rows are retrieved from the server at each iteration.
var
rowRes: Row
sz: TSqlLen = 0
cCnt: TSqlSmallInt = 0
res: TSqlSmallInt = 0
res = db.prepareFetch(query, args)
if res == SQL_NO_DATA:
discard
elif res == SQL_SUCCESS:
res = SQLNumResultCols(db.stmt, cCnt)
rowRes = newRow(cCnt)
rowRes.setLen(max(cCnt,0))
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095, sz.addr))
rowRes[colId-1] = $cast[cstring]((addr buf))
yield rowRes
res = SQLFetch(db.stmt)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
db.sqlCheck(res)
iterator instantRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect, WriteDbEffect].} =
## Same as fastRows but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within the iterator body.
var
rowRes: Row = @[]
sz: TSqlLen = 0
cCnt: TSqlSmallInt = 0
res: TSqlSmallInt = 0
res = db.prepareFetch(query, args)
if res == SQL_NO_DATA:
discard
elif res == SQL_SUCCESS:
res = SQLNumResultCols(db.stmt, cCnt)
rowRes = newRow(cCnt)
rowRes.setLen(max(cCnt,0))
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095, sz.addr))
rowRes[colId-1] = $cast[cstring](addr buf)
yield (row: rowRes, len: cCnt.int)
res = SQLFetch(db.stmt)
properFreeResult(SQL_HANDLE_STMT, db.stmt)
db.sqlCheck(res)
proc `[]`*(row: InstantRow, col: int): string {.inline.} =
## Returns text for given column of the row
$row.row[col]
proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
## Return cstring of given column of the row
row.row[index].cstring
proc len*(row: InstantRow): int {.inline.} =
## Returns number of columns in the row
row.len
proc getRow*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
var
rowRes: Row
sz: TSqlLen = 0
cCnt: TSqlSmallInt = 0
res: TSqlSmallInt = 0
res = db.prepareFetch(query, args)
if res == SQL_NO_DATA:
result = @[]
elif res == SQL_SUCCESS:
res = SQLNumResultCols(db.stmt, cCnt)
rowRes = newRow(cCnt)
rowRes.setLen(max(cCnt,0))
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095, sz.addr))
rowRes[colId-1] = $cast[cstring](addr buf)
res = SQLFetch(db.stmt)
result = rowRes
properFreeResult(SQL_HANDLE_STMT, db.stmt)
db.sqlCheck(res)
proc getAllRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError] .} =
## Executes the query and returns the whole result dataset.
var
rows: seq[Row] = @[]
rowRes: Row
sz: TSqlLen = 0
cCnt: TSqlSmallInt = 0
res: TSqlSmallInt = 0
res = db.prepareFetch(query, args)
if res == SQL_NO_DATA:
result = @[]
elif res == SQL_SUCCESS:
res = SQLNumResultCols(db.stmt, cCnt)
rowRes = newRow(cCnt)
rowRes.setLen(max(cCnt,0))
while res == SQL_SUCCESS:
for colId in 1..cCnt:
buf[0] = '\0'
db.sqlCheck(SQLGetData(db.stmt, colId.SqlUSmallInt, SQL_C_CHAR,
cast[cstring](buf.addr), 4095, sz.addr))
rowRes[colId-1] = $cast[cstring](addr buf)
rows.add(rowRes)
res = SQLFetch(db.stmt)
result = rows
properFreeResult(SQL_HANDLE_STMT, db.stmt)
db.sqlCheck(res)
iterator rows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Same as `fastRows`, but slower and safe.
##
## This retrieves ALL rows into memory before
## iterating through the rows.
## Large dataset queries will impact on memory usage.
for r in items(getAllRows(db, query, args)): yield r
proc getValue*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
result = ""
try:
result = getRow(db, query, args)[0]
except: discard
proc tryInsertId*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
if not tryExec(db, query, args):
result = -1'i64
else:
result = -1'i64
try:
case sqlGetDBMS(db).toLower():
of "postgresql":
result = getValue(db, sql"SELECT LASTVAL();", []).parseInt
of "mysql":
result = getValue(db, sql"SELECT LAST_INSERT_ID();", []).parseInt
of "sqlite":
result = getValue(db, sql"SELECT LAST_INSERT_ROWID();", []).parseInt
of "microsoft sql server":
result = getValue(db, sql"SELECT SCOPE_IDENTITY();", []).parseInt
of "oracle":
result = getValue(db, sql"SELECT id.currval FROM DUAL;", []).parseInt
else: result = -1'i64
except: discard
proc insertId*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc tryInsert*(db: var DbConn, query: SqlQuery,pkName: string,
args: varargs[string, `$`]): int64
{.tags: [ReadDbEffect, WriteDbEffect], raises: [], since: (1, 3).} =
## same as tryInsertID
tryInsertID(db, query, args)
proc insert*(db: var DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [ReadDbEffect, WriteDbEffect], since: (1, 3).} =
## same as insertId
result = tryInsert(db, query,pkName, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: var DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Runs the query (typically "UPDATE") and returns the
## number of affected rows
result = -1
db.sqlCheck(SQLAllocHandle(SQL_HANDLE_STMT, db.hDb, db.stmt.SqlHandle))
var q = dbFormat(query, args)
db.sqlCheck(SQLPrepare(db.stmt, q.PSQLCHAR, q.len.TSqlSmallInt))
rawExec(db, query, args)
var rCnt:TSqlLen = -1
db.sqlCheck(SQLRowCount(db.hDb, rCnt))
properFreeResult(SQL_HANDLE_STMT, db.stmt)
result = rCnt.int64
proc close*(db: var DbConn) {.
tags: [WriteDbEffect], raises: [].} =
## Closes the database connection.
if db.hDb != nil:
try:
var res = SQLDisconnect(db.hDb)
if db.stmt != nil:
res = SQLFreeHandle(SQL_HANDLE_STMT, db.stmt)
res = SQLFreeHandle(SQL_HANDLE_DBC, db.hDb)
res = SQLFreeHandle(SQL_HANDLE_ENV, db.env)
db = (hDb: nil, env: nil, stmt: nil)
except:
discard
proc open*(connection, user, password, database: string): DbConn {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Opens a database connection.
##
## Raises `EDb` if the connection could not be established.
##
## Currently the database parameter is ignored,
## but included to match `open()` in the other db_xxxxx library modules.
var
val = SQL_OV_ODBC3
resLen = 0
result = (hDb: nil, env: nil, stmt: nil)
# allocate environment handle
var res = SQLAllocHandle(SQL_HANDLE_ENV, result.env, result.env)
if res != SQL_SUCCESS: dbError("Error: unable to initialise ODBC environment.")
res = SQLSetEnvAttr(result.env,
SQL_ATTR_ODBC_VERSION.TSqlInteger,
cast[SqlPointer](val), resLen.TSqlInteger)
if res != SQL_SUCCESS: dbError("Error: unable to set ODBC driver version.")
# allocate hDb handle
res = SQLAllocHandle(SQL_HANDLE_DBC, result.env, result.hDb)
if res != SQL_SUCCESS: dbError("Error: unable to allocate connection handle.")
# Connect: connection = dsn str,
res = SQLConnect(result.hDb,
connection.PSQLCHAR , connection.len.TSqlSmallInt,
user.PSQLCHAR, user.len.TSqlSmallInt,
password.PSQLCHAR, password.len.TSqlSmallInt)
if res != SQL_SUCCESS:
result.dbError()
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [ReadDbEffect, WriteDbEffect], raises: [DbError].} =
## Currently not implemented for ODBC.
##
## Sets the encoding of a database connection, returns true for
## success, false for failure.
##result = set_character_set(connection, encoding) == 0
dbError("setEncoding() is currently not implemented by the db_odbc module")

View File

@@ -1,647 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## A higher level `PostgreSQL`:idx: database wrapper. This interface
## is implemented for other databases also.
##
## See also: `db_odbc <db_odbc.html>`_, `db_sqlite <db_sqlite.html>`_,
## `db_mysql <db_mysql.html>`_.
##
## Parameter substitution
## ======================
##
## All `db_*` modules support the same form of parameter substitution.
## That is, using the `?` (question mark) to signify the place where a
## value should be placed. For example:
##
## ```Nim
## sql"INSERT INTO myTable (colA, colB, colC) VALUES (?, ?, ?)"
## ```
##
## **Note**: There are two approaches to parameter substitution support by
## this module.
##
## 1. `SqlQuery` using `?, ?, ?, ...` (same as all the `db_*` modules)
##
## 2. `SqlPrepared` using `$1, $2, $3, ...`
##
## ```Nim
## prepare(db, "myExampleInsert",
## sql"""INSERT INTO myTable
## (colA, colB, colC)
## VALUES ($1, $2, $3)""",
## 3)
## ```
##
##
## Unix Socket
## ===========
##
## Using Unix sockets instead of TCP connection can
## `improve performance up to 30% ~ 175% for some operations <https://momjian.us/main/blogs/pgblog/2012.html#June_6_2012>`_.
##
## To use Unix sockets with `db_postgres`, change the server address to the socket file path:
##
## ```Nim
## import std/db_postgres ## Change "localhost" or "127.0.0.1" to the socket file path
## let db = db_postgres.open("/run/postgresql", "user", "password", "database")
## echo db.getAllRows(sql"SELECT version();")
## db.close()
## ```
##
## The socket file path is operating system specific and distribution specific,
## additional configuration may or may not be needed on your `postgresql.conf`.
## The Postgres server must be on the same computer and only works for Unix-like operating systems.
##
##
## Examples
## ========
##
## Opening a connection to a database
## ----------------------------------
##
## ```Nim
## import std/db_postgres
## let db = open("localhost", "user", "password", "dbname")
## db.close()
## ```
##
## Creating a table
## ----------------
##
## ```Nim
## db.exec(sql"DROP TABLE IF EXISTS myTable")
## db.exec(sql("""CREATE TABLE myTable (
## id integer,
## name varchar(50) not null)"""))
## ```
##
## Inserting data
## --------------
##
## ```Nim
## db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
## "Dominik")
## ```
import strutils, postgres
import db_common
export db_common
import std/private/[since, dbutils]
type
DbConn* = PPGconn ## encapsulates a database connection
Row* = seq[string] ## a row of a dataset. NULL database values will be
## converted to nil.
InstantRow* = object ## a handle that can be
res: PPGresult ## used to get a row's
SqlPrepared* = distinct string ## a identifier for the prepared queries
proc dbError*(db: DbConn) {.noreturn.} =
## raises a DbError exception.
var e: ref DbError
new(e)
e.msg = $pqErrorMessage(db)
raise e
proc dbQuote*(s: string): string =
## DB quotes the string.
result = "'"
for c in items(s):
case c
of '\'': add(result, "''")
of '\0': add(result, "\\0")
else: add(result, c)
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
dbFormatImpl(formatstr, dbQuote, args)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.tags: [ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil,
nil, nil, 0)
result = pqresultStatus(res) == PGRES_COMMAND_OK
pqclear(res)
proc tryExec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): bool {.tags: [
ReadDbEffect, WriteDbEffect].} =
## tries to execute the query and returns true if successful, false otherwise.
var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
nil, nil, 0)
deallocCStringArray(arr)
result = pqresultStatus(res) == PGRES_COMMAND_OK
pqclear(res)
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect].} =
## executes the query and raises EDB if not successful.
var res = pqexecParams(db, dbFormat(query, args).cstring, 0, nil, nil,
nil, nil, 0)
if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
pqclear(res)
proc exec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]) {.tags: [ReadDbEffect, WriteDbEffect].} =
var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
nil, nil, 0)
deallocCStringArray(arr)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
pqclear(res)
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PPGresult =
result = pqexec(db, dbFormat(query, args).cstring)
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
proc setupQuery(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]): PPGresult =
var arr = allocCStringArray(args)
result = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
nil, nil, 0)
deallocCStringArray(arr)
if pqResultStatus(result) != PGRES_TUPLES_OK: dbError(db)
proc setupSingeRowQuery(db: DbConn, query: SqlQuery,
args: varargs[string]) =
if pqsendquery(db, dbFormat(query, args).cstring) != 1:
dbError(db)
if pqSetSingleRowMode(db) != 1:
dbError(db)
proc setupSingeRowQuery(db: DbConn, stmtName: SqlPrepared,
args: varargs[string]) =
var arr = allocCStringArray(args)
if pqsendqueryprepared(db, stmtName.cstring, int32(args.len), arr, nil, nil, 0) != 1:
dbError(db)
if pqSetSingleRowMode(db) != 1:
dbError(db)
deallocCStringArray(arr)
proc prepare*(db: DbConn; stmtName: string, query: SqlQuery;
nParams: int): SqlPrepared =
## Creates a new `SqlPrepared` statement. Parameter substitution is done
## via `$1`, `$2`, `$3`, etc.
if nParams > 0 and not string(query).contains("$1"):
dbError("parameter substitution expects \"$1\"")
var res = pqprepare(db, stmtName, query.cstring, int32(nParams), nil)
if pqResultStatus(res) != PGRES_COMMAND_OK: dbError(db)
result = SqlPrepared(stmtName)
pqclear(res)
proc setRow(res: PPGresult, r: var Row, line, cols: int32) =
for col in 0'i32..cols-1:
setLen(r[col], 0)
let x = pqgetvalue(res, line, col)
if x.isNil:
r[col] = ""
else:
add(r[col], x)
template fetchRows(db: DbConn): untyped =
var res: PPGresult = nil
while true:
res = pqgetresult(db)
if res == nil:
break
let status = pqresultStatus(res)
if status == PGRES_TUPLES_OK:
discard
elif status != PGRES_SINGLE_TUPLE:
dbError(db)
else:
let L = pqNfields(res)
var result = newRow(L)
setRow(res, result, 0, L)
yield result
pqclear(res)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the query and iterates over the result dataset. This is very
## fast, but potentially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though.
setupSingeRowQuery(db, query, args)
fetchRows(db)
iterator fastRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## executes the query and iterates over the result dataset. This is very
## fast, but potentially dangerous: If the for-loop-body executes another
## query, the results can be undefined. For Postgres it is safe though.
setupSingeRowQuery(db, stmtName, args)
fetchRows(db)
template fetchinstantRows(db: DbConn): untyped =
var res: PPGresult = nil
while true:
res = pqgetresult(db)
if res == nil:
break
let status = pqresultStatus(res)
if status == PGRES_TUPLES_OK:
discard
elif status != PGRES_SINGLE_TUPLE:
dbError(db)
else:
yield InstantRow(res: res)
pqclear(res)
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within iterator body.
setupSingeRowQuery(db, query, args)
fetchinstantRows(db)
iterator instantRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## same as fastRows but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within iterator body.
setupSingeRowQuery(db, stmtName, args)
fetchinstantRows(db)
proc getColumnType(res: PPGresult, col: int) : DbType =
## returns DbType for given column in the row
## defined in pg_type.h file in the postgres source code
## Wire representation for types: http://www.npgsql.org/dev/types.html
var oid = pqftype(res, int32(col))
## The integer returned is the internal OID number of the type
case oid
of 16: return DbType(kind: DbTypeKind.dbBool, name: "bool")
of 17: return DbType(kind: DbTypeKind.dbBlob, name: "bytea")
of 21: return DbType(kind: DbTypeKind.dbInt, name: "int2", size: 2)
of 23: return DbType(kind: DbTypeKind.dbInt, name: "int4", size: 4)
of 20: return DbType(kind: DbTypeKind.dbInt, name: "int8", size: 8)
of 1560: return DbType(kind: DbTypeKind.dbBit, name: "bit")
of 1562: return DbType(kind: DbTypeKind.dbInt, name: "varbit")
of 18: return DbType(kind: DbTypeKind.dbFixedChar, name: "char")
of 19: return DbType(kind: DbTypeKind.dbFixedChar, name: "name")
of 1042: return DbType(kind: DbTypeKind.dbFixedChar, name: "bpchar")
of 25: return DbType(kind: DbTypeKind.dbVarchar, name: "text")
of 1043: return DbType(kind: DbTypeKind.dbVarChar, name: "varchar")
of 2275: return DbType(kind: DbTypeKind.dbVarchar, name: "cstring")
of 700: return DbType(kind: DbTypeKind.dbFloat, name: "float4")
of 701: return DbType(kind: DbTypeKind.dbFloat, name: "float8")
of 790: return DbType(kind: DbTypeKind.dbDecimal, name: "money")
of 1700: return DbType(kind: DbTypeKind.dbDecimal, name: "numeric")
of 704: return DbType(kind: DbTypeKind.dbTimeInterval, name: "tinterval")
of 702: return DbType(kind: DbTypeKind.dbTimestamp, name: "abstime")
of 703: return DbType(kind: DbTypeKind.dbTimeInterval, name: "reltime")
of 1082: return DbType(kind: DbTypeKind.dbDate, name: "date")
of 1083: return DbType(kind: DbTypeKind.dbTime, name: "time")
of 1114: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamp")
of 1184: return DbType(kind: DbTypeKind.dbTimestamp, name: "timestamptz")
of 1186: return DbType(kind: DbTypeKind.dbTimeInterval, name: "interval")
of 1266: return DbType(kind: DbTypeKind.dbTime, name: "timetz")
of 114: return DbType(kind: DbTypeKind.dbJson, name: "json")
of 142: return DbType(kind: DbTypeKind.dbXml, name: "xml")
of 3802: return DbType(kind: DbTypeKind.dbJson, name: "jsonb")
of 600: return DbType(kind: DbTypeKind.dbPoint, name: "point")
of 601: return DbType(kind: DbTypeKind.dbLseg, name: "lseg")
of 602: return DbType(kind: DbTypeKind.dbPath, name: "path")
of 603: return DbType(kind: DbTypeKind.dbBox, name: "box")
of 604: return DbType(kind: DbTypeKind.dbPolygon, name: "polygon")
of 628: return DbType(kind: DbTypeKind.dbLine, name: "line")
of 718: return DbType(kind: DbTypeKind.dbCircle, name: "circle")
of 650: return DbType(kind: DbTypeKind.dbInet, name: "cidr")
of 829: return DbType(kind: DbTypeKind.dbMacAddress, name: "macaddr")
of 869: return DbType(kind: DbTypeKind.dbInet, name: "inet")
of 2950: return DbType(kind: DbTypeKind.dbVarchar, name: "uuid")
of 3614: return DbType(kind: DbTypeKind.dbVarchar, name: "tsvector")
of 3615: return DbType(kind: DbTypeKind.dbVarchar, name: "tsquery")
of 2970: return DbType(kind: DbTypeKind.dbVarchar, name: "txid_snapshot")
of 27: return DbType(kind: DbTypeKind.dbComposite, name: "tid")
of 1790: return DbType(kind: DbTypeKind.dbComposite, name: "refcursor")
of 2249: return DbType(kind: DbTypeKind.dbComposite, name: "record")
of 3904: return DbType(kind: DbTypeKind.dbComposite, name: "int4range")
of 3906: return DbType(kind: DbTypeKind.dbComposite, name: "numrange")
of 3908: return DbType(kind: DbTypeKind.dbComposite, name: "tsrange")
of 3910: return DbType(kind: DbTypeKind.dbComposite, name: "tstzrange")
of 3912: return DbType(kind: DbTypeKind.dbComposite, name: "daterange")
of 3926: return DbType(kind: DbTypeKind.dbComposite, name: "int8range")
of 22: return DbType(kind: DbTypeKind.dbArray, name: "int2vector")
of 30: return DbType(kind: DbTypeKind.dbArray, name: "oidvector")
of 143: return DbType(kind: DbTypeKind.dbArray, name: "xml[]")
of 199: return DbType(kind: DbTypeKind.dbArray, name: "json[]")
of 629: return DbType(kind: DbTypeKind.dbArray, name: "line[]")
of 651: return DbType(kind: DbTypeKind.dbArray, name: "cidr[]")
of 719: return DbType(kind: DbTypeKind.dbArray, name: "circle[]")
of 791: return DbType(kind: DbTypeKind.dbArray, name: "money[]")
of 1000: return DbType(kind: DbTypeKind.dbArray, name: "bool[]")
of 1001: return DbType(kind: DbTypeKind.dbArray, name: "bytea[]")
of 1002: return DbType(kind: DbTypeKind.dbArray, name: "char[]")
of 1003: return DbType(kind: DbTypeKind.dbArray, name: "name[]")
of 1005: return DbType(kind: DbTypeKind.dbArray, name: "int2[]")
of 1006: return DbType(kind: DbTypeKind.dbArray, name: "int2vector[]")
of 1007: return DbType(kind: DbTypeKind.dbArray, name: "int4[]")
of 1008: return DbType(kind: DbTypeKind.dbArray, name: "regproc[]")
of 1009: return DbType(kind: DbTypeKind.dbArray, name: "text[]")
of 1028: return DbType(kind: DbTypeKind.dbArray, name: "oid[]")
of 1010: return DbType(kind: DbTypeKind.dbArray, name: "tid[]")
of 1011: return DbType(kind: DbTypeKind.dbArray, name: "xid[]")
of 1012: return DbType(kind: DbTypeKind.dbArray, name: "cid[]")
of 1013: return DbType(kind: DbTypeKind.dbArray, name: "oidvector[]")
of 1014: return DbType(kind: DbTypeKind.dbArray, name: "bpchar[]")
of 1015: return DbType(kind: DbTypeKind.dbArray, name: "varchar[]")
of 1016: return DbType(kind: DbTypeKind.dbArray, name: "int8[]")
of 1017: return DbType(kind: DbTypeKind.dbArray, name: "point[]")
of 1018: return DbType(kind: DbTypeKind.dbArray, name: "lseg[]")
of 1019: return DbType(kind: DbTypeKind.dbArray, name: "path[]")
of 1020: return DbType(kind: DbTypeKind.dbArray, name: "box[]")
of 1021: return DbType(kind: DbTypeKind.dbArray, name: "float4[]")
of 1022: return DbType(kind: DbTypeKind.dbArray, name: "float8[]")
of 1023: return DbType(kind: DbTypeKind.dbArray, name: "abstime[]")
of 1024: return DbType(kind: DbTypeKind.dbArray, name: "reltime[]")
of 1025: return DbType(kind: DbTypeKind.dbArray, name: "tinterval[]")
of 1027: return DbType(kind: DbTypeKind.dbArray, name: "polygon[]")
of 1040: return DbType(kind: DbTypeKind.dbArray, name: "macaddr[]")
of 1041: return DbType(kind: DbTypeKind.dbArray, name: "inet[]")
of 1263: return DbType(kind: DbTypeKind.dbArray, name: "cstring[]")
of 1115: return DbType(kind: DbTypeKind.dbArray, name: "timestamp[]")
of 1182: return DbType(kind: DbTypeKind.dbArray, name: "date[]")
of 1183: return DbType(kind: DbTypeKind.dbArray, name: "time[]")
of 1185: return DbType(kind: DbTypeKind.dbArray, name: "timestamptz[]")
of 1187: return DbType(kind: DbTypeKind.dbArray, name: "interval[]")
of 1231: return DbType(kind: DbTypeKind.dbArray, name: "numeric[]")
of 1270: return DbType(kind: DbTypeKind.dbArray, name: "timetz[]")
of 1561: return DbType(kind: DbTypeKind.dbArray, name: "bit[]")
of 1563: return DbType(kind: DbTypeKind.dbArray, name: "varbit[]")
of 2201: return DbType(kind: DbTypeKind.dbArray, name: "refcursor[]")
of 2951: return DbType(kind: DbTypeKind.dbArray, name: "uuid[]")
of 3643: return DbType(kind: DbTypeKind.dbArray, name: "tsvector[]")
of 3645: return DbType(kind: DbTypeKind.dbArray, name: "tsquery[]")
of 3807: return DbType(kind: DbTypeKind.dbArray, name: "jsonb[]")
of 2949: return DbType(kind: DbTypeKind.dbArray, name: "txid_snapshot[]")
of 3905: return DbType(kind: DbTypeKind.dbArray, name: "int4range[]")
of 3907: return DbType(kind: DbTypeKind.dbArray, name: "numrange[]")
of 3909: return DbType(kind: DbTypeKind.dbArray, name: "tsrange[]")
of 3911: return DbType(kind: DbTypeKind.dbArray, name: "tstzrange[]")
of 3913: return DbType(kind: DbTypeKind.dbArray, name: "daterange[]")
of 3927: return DbType(kind: DbTypeKind.dbArray, name: "int8range[]")
of 2287: return DbType(kind: DbTypeKind.dbArray, name: "record[]")
of 705: return DbType(kind: DbTypeKind.dbUnknown, name: "unknown")
else: return DbType(kind: DbTypeKind.dbUnknown, name: $oid) ## Query the system table pg_type to determine exactly which type is referenced.
proc setColumnInfo(columns: var DbColumns; res: PPGresult, L: int32) =
setLen(columns, L)
for i in 0'i32..<L:
columns[i].name = $pqfname(res, i)
columns[i].typ = getColumnType(res, i)
columns[i].tableName = $(pqftable(res, i)) ## Returns the OID of the table from which the given column was fetched.
## Query the system table pg_class to determine exactly which table is referenced.
#columns[i].primaryKey = libpq does not have a function for that
#columns[i].foreignKey = libpq does not have a function for that
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery;
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
setupSingeRowQuery(db, query, args)
var res: PPGresult = nil
var colsObtained = false
while true:
res = pqgetresult(db)
if not colsObtained:
setColumnInfo(columns, res, pqnfields(res))
colsObtained = true
if res == nil:
break
let status = pqresultStatus(res)
if status == PGRES_TUPLES_OK:
discard
elif status != PGRES_SINGLE_TUPLE:
dbError(db)
else:
yield InstantRow(res: res)
pqclear(res)
proc `[]`*(row: InstantRow; col: int): string {.inline.} =
## returns text for given column of the row
$pqgetvalue(row.res, int32(0), int32(col))
proc unsafeColumnAt*(row: InstantRow, index: int): cstring {.inline.} =
## Return cstring of given column of the row
pqgetvalue(row.res, int32(0), int32(index))
proc len*(row: InstantRow): int {.inline.} =
## returns number of columns in the row
int(pqNfields(row.res))
proc getRow(res: PPGresult): Row =
let L = pqnfields(res)
result = newRow(L)
if pqntuples(res) > 0:
setRow(res, result, 0, L)
pqclear(res)
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## retrieves a single row. If the query doesn't return any rows, this proc
## will return a Row with empty strings for each column.
let res = setupQuery(db, query, args)
result = getRow(res)
proc getRow*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
let res = setupQuery(db, stmtName, args)
result = getRow(res)
proc getAllRows(res: PPGresult): seq[Row] =
let N = pqntuples(res)
let L = pqnfields(res)
result = newSeqOfCap[Row](N)
var row = newRow(L)
for i in 0'i32..N-1:
setRow(res, row, i, L)
result.add(row)
pqclear(res)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.
tags: [ReadDbEffect].} =
## executes the query and returns the whole result dataset.
let res = setupQuery(db, query, args)
result = getAllRows(res)
proc getAllRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): seq[Row] {.tags:
[ReadDbEffect].} =
## executes the prepared query and returns the whole result dataset.
let res = setupQuery(db, stmtName, args)
result = getAllRows(res)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, query, args)): yield r
iterator rows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## same as `fastRows`, but slower and safe.
for r in items(getAllRows(db, stmtName, args)): yield r
proc getValue(res: PPGresult): string =
if pqntuples(res) > 0:
var x = pqgetvalue(res, 0, 0)
result = if isNil(x): "" else: $x
else:
result = ""
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
let res = setupQuery(db, query, args)
result = getValue(res)
pqclear(res)
proc getValue*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): string {.
tags: [ReadDbEffect].} =
## executes the query and returns the first column of the first row of the
## result dataset. Returns "" if the dataset contains no rows or the database
## value is NULL.
let res = setupQuery(db, stmtName, args)
result = getValue(res)
pqclear(res)
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [WriteDbEffect].}=
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error. For Postgre this adds
## `RETURNING id` to the query, so it only works if your primary key is
## named `id`.
let res = setupQuery(db, SqlQuery(string(query) & " RETURNING id"),
args)
var x = pqgetvalue(res, 0, 0)
if not isNil(x):
result = parseBiggestInt($x)
else:
result = -1
pqclear(res)
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [WriteDbEffect].} =
## executes the query (typically "INSERT") and returns the
## generated ID for the row. For Postgre this adds
## `RETURNING id` to the query, so it only works if your primary key is
## named `id`.
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc tryInsert*(db: DbConn, query: SqlQuery,pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], since: (1, 3).}=
## executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
let res = setupQuery(db, SqlQuery(string(query) & " RETURNING " & pkName),
args)
var x = pqgetvalue(res, 0, 0)
if not isNil(x):
result = parseBiggestInt($x)
else:
result = -1
pqclear(res)
proc insert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], since: (1, 3).} =
## executes the query (typically "INSERT") and returns the
## generated ID
result = tryInsert(db, query, pkName, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [
ReadDbEffect, WriteDbEffect].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
var q = dbFormat(query, args)
var res = pqExec(db, q.cstring)
if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
result = parseBiggestInt($pqcmdTuples(res))
pqclear(res)
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared,
args: varargs[string, `$`]): int64 {.tags: [
ReadDbEffect, WriteDbEffect].} =
## executes the query (typically "UPDATE") and returns the
## number of affected rows.
var arr = allocCStringArray(args)
var res = pqexecPrepared(db, stmtName.cstring, int32(args.len), arr,
nil, nil, 0)
deallocCStringArray(arr)
if pqresultStatus(res) != PGRES_COMMAND_OK: dbError(db)
result = parseBiggestInt($pqcmdTuples(res))
pqclear(res)
proc close*(db: DbConn) {.tags: [DbEffect].} =
## closes the database connection.
if db != nil: pqfinish(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [DbEffect].} =
## opens a database connection. Raises `EDb` if the connection could not
## be established.
##
## Clients can also use Postgres keyword/value connection strings to
## connect.
##
## Example:
## ```nim
## con = open("", "", "", "host=localhost port=5432 dbname=mydb")
## ```
##
## See http://www.postgresql.org/docs/current/static/libpq-connect.html#LIBPQ-CONNSTRING
## for more information.
let
colonPos = connection.find(':')
host = if colonPos < 0: connection
else: substr(connection, 0, colonPos-1)
port = if colonPos < 0: ""
else: substr(connection, colonPos+1)
result = pqsetdbLogin(host.cstring, port.cstring, nil, nil, database, user, password)
if pqStatus(result) != CONNECTION_OK: dbError(result) # result = nil
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [DbEffect].} =
## sets the encoding of a database connection, returns true for
## success, false for failure.
return pqsetClientEncoding(connection, encoding) == 0
# Tests are in ../../tests/untestable/tpostgres.

View File

@@ -1,940 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## A higher level `SQLite`:idx: database wrapper. This interface
## is implemented for other databases too.
##
## Basic usage
## ===========
##
## The basic flow of using this module is:
##
## 1. Open database connection
## 2. Execute SQL query
## 3. Close database connection
##
## Parameter substitution
## ----------------------
##
## All `db_*` modules support the same form of parameter substitution.
## That is, using the `?` (question mark) to signify the place where a
## value should be placed. For example:
##
## ```Nim
## sql"INSERT INTO my_table (colA, colB, colC) VALUES (?, ?, ?)"
## ```
##
## Opening a connection to a database
## ----------------------------------
##
## ```Nim
## import std/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()
## ```
##
## Creating a table
## ----------------
##
## ```Nim
## db.exec(sql"DROP TABLE IF EXISTS my_table")
## db.exec(sql"""CREATE TABLE my_table (
## id INTEGER,
## name VARCHAR(50) NOT NULL
## )""")
## ```
##
## Inserting data
## --------------
##
## ```Nim
## db.exec(sql"INSERT INTO my_table (id, name) VALUES (0, ?)",
## "Jack")
## ```
##
## Larger example
## --------------
##
## ```Nim
## import std/[db_sqlite, 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()
## ```
##
## Storing binary data example
##----------------------------
##
## ```nim
## 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()
## ```
##
##
## Note
## ====
## This module does not implement any ORM features such as mapping the types from the schema.
## Instead, a `seq[string]` is returned for each row.
##
## The reasoning is as follows:
## 1. it's close to what many DBs offer natively (`char**`:c:)
## 2. it hides the number of types that the DB supports
## (int? int64? decimal up to 10 places? geo coords?)
## 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)
##
## See also
## ========
##
## * `db_odbc module <db_odbc.html>`_ for ODBC database wrapper
## * `db_mysql module <db_mysql.html>`_ for MySQL database wrapper
## * `db_postgres module <db_postgres.html>`_ for PostgreSQL database wrapper
{.experimental: "codeReordering".}
import sqlite3, macros
import db_common
export db_common
import std/private/[since, dbutils]
when defined(nimPreviewSlimSystem):
import std/assertions
type
DbConn* = PSqlite3 ## Encapsulates a database connection.
Row* = seq[string] ## A row of a dataset. `NULL` database values will be
## converted to an empty string.
InstantRow* = PStmt ## A handle that can be used to get a row's column
## text on demand.
SqlPrepared* = distinct PStmt ## a identifier for the prepared queries
proc dbError*(db: DbConn) {.noreturn.} =
## Raises a `DbError` exception.
##
## **Examples:**
## ```Nim
## let db = open("mytest.db", "", "", "")
## if not db.tryExec(sql"SELECT * FROM not_exist_table"):
## dbError(db)
## db.close()
## ```
var e: ref DbError
new(e)
e.msg = $sqlite3.errmsg(db)
raise e
proc dbQuote*(s: string): string =
## Escapes the `'` (single quote) char to `''`.
## Because single quote is used for defining `VARCHAR` in SQL.
runnableExamples:
doAssert dbQuote("'") == "''''"
doAssert dbQuote("A Foobar's pen.") == "'A Foobar''s pen.'"
result = "'"
for c in items(s):
if c == '\'': add(result, "''")
else: add(result, c)
add(result, '\'')
proc dbFormat(formatstr: SqlQuery, args: varargs[string]): string =
dbFormatImpl(formatstr, dbQuote, args)
proc prepare*(db: DbConn; q: string): SqlPrepared {.since: (1, 3).} =
## Creates a new `SqlPrepared` statement.
if prepare_v2(db, q, q.len.cint,result.PStmt, nil) != SQLITE_OK:
discard finalize(result.PStmt)
dbError(db)
proc tryExec*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
## Tries to execute the query and returns `true` if successful, `false` otherwise.
##
## **Examples:**
## ```Nim
## let db = open("mytest.db", "", "", "")
## if not db.tryExec(sql"SELECT * FROM my_table"):
## dbError(db)
## db.close()
## ```
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.PStmt
if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
let x = step(stmt)
if x in {SQLITE_DONE, SQLITE_ROW}:
result = finalize(stmt) == SQLITE_OK
else:
discard finalize(stmt)
result = false
proc tryExec*(db: DbConn, stmtName: SqlPrepared): bool {.
tags: [ReadDbEffect, WriteDbEffect].} =
let x = step(stmtName.PStmt)
if x in {SQLITE_DONE, SQLITE_ROW}:
result = true
else:
discard finalize(stmtName.PStmt)
result = false
proc exec*(db: DbConn, query: SqlQuery, args: varargs[string, `$`]) {.
tags: [ReadDbEffect, WriteDbEffect].} =
## Executes the query and raises a `DbError` exception if not successful.
##
## **Examples:**
## ```Nim
## 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()
## ```
if not tryExec(db, query, args): dbError(db)
proc newRow(L: int): Row =
newSeq(result, L)
for i in 0..L-1: result[i] = ""
proc setupQuery(db: DbConn, query: SqlQuery,
args: varargs[string]): PStmt =
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
if prepare_v2(db, q.cstring, q.len.cint, result, nil) != SQLITE_OK: dbError(db)
proc setupQuery(db: DbConn, stmtName: SqlPrepared): SqlPrepared {.since: (1, 3).} =
assert(not db.isNil, "Database not connected.")
result = stmtName
proc setRow(stmt: PStmt, r: var Row, cols: cint) =
for col in 0'i32..cols-1:
let cb = column_bytes(stmt, col)
setLen(r[col], cb) # set capacity
if column_type(stmt, col) == SQLITE_BLOB:
copyMem(addr(r[col][0]), column_blob(stmt, col), cb)
else:
setLen(r[col], 0)
let x = column_text(stmt, col)
if not isNil(x): add(r[col], x)
iterator fastRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Executes the query and iterates over the result dataset.
##
## This is very fast, but potentially dangerous. Use this iterator only
## if you require **ALL** the rows.
##
## **Note:** Breaking the `fastRows()` iterator during a loop will cause the
## next database query to raise a `DbError` exception `unable to close due
## to ...`.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
var result = newRow(L)
try:
while step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
yield result
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
iterator fastRows*(db: DbConn, stmtName: SqlPrepared): Row
{.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
discard setupQuery(db, stmtName)
var L = (column_count(stmtName.PStmt))
var result = newRow(L)
try:
while step(stmtName.PStmt) == SQLITE_ROW:
setRow(stmtName.PStmt, result, L)
yield result
except:
dbError(db)
iterator instantRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_
## but returns a handle that can be used to get column text
## on demand using `[]`. Returned handle is valid only within the iterator body.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
var stmt = setupQuery(db, query, args)
try:
while step(stmt) == SQLITE_ROW:
yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
iterator instantRows*(db: DbConn, stmtName: SqlPrepared): InstantRow
{.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
var stmt = setupQuery(db, stmtName).PStmt
try:
while step(stmt) == SQLITE_ROW:
yield stmt
except:
dbError(db)
proc toTypeKind(t: var DbType; x: int32) =
case x
of SQLITE_INTEGER:
t.kind = dbInt
t.size = 8
of SQLITE_FLOAT:
t.kind = dbFloat
t.size = 8
of SQLITE_BLOB: t.kind = dbBlob
of SQLITE_NULL: t.kind = dbNull
of SQLITE_TEXT: t.kind = dbVarchar
else: t.kind = dbUnknown
proc setColumns(columns: var DbColumns; x: PStmt) =
let L = column_count(x)
setLen(columns, L)
for i in 0'i32 ..< L:
columns[i].name = $column_name(x, i)
columns[i].typ.name = $column_decltype(x, i)
toTypeKind(columns[i].typ, column_type(x, i))
columns[i].tableName = $column_table_name(x, i)
iterator instantRows*(db: DbConn; columns: var DbColumns; query: SqlQuery,
args: varargs[string, `$`]): InstantRow
{.tags: [ReadDbEffect].} =
## Similar to `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_,
## but sets information about columns to `columns`.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
var stmt = setupQuery(db, query, args)
setColumns(columns, stmt)
try:
while step(stmt) == SQLITE_ROW:
yield stmt
finally:
if finalize(stmt) != SQLITE_OK: dbError(db)
proc `[]`*(row: InstantRow, col: int32): string {.inline.} =
## Returns text for given column of the row.
##
## See also:
## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
## example code
$column_text(row, col)
proc unsafeColumnAt*(row: InstantRow, index: int32): cstring {.inline.} =
## Returns cstring for given column of the row.
##
## See also:
## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
## example code
column_text(row, index)
proc len*(row: InstantRow): int32 {.inline.} =
## Returns number of columns in a row.
##
## See also:
## * `instantRows iterator <#instantRows.i,DbConn,SqlQuery,varargs[string,]>`_
## example code
column_count(row)
proc getRow*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Retrieves a single row. If the query doesn't return any rows, this proc
## will return a `Row` with empty strings for each column.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
var stmt = setupQuery(db, query, args)
var L = (column_count(stmt))
result = newRow(L)
if step(stmt) == SQLITE_ROW:
setRow(stmt, result, L)
if finalize(stmt) != SQLITE_OK: dbError(db)
proc getAllRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): seq[Row] {.tags: [ReadDbEffect].} =
## Executes the query and returns the whole result dataset.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
result = @[]
for r in fastRows(db, query, args):
result.add(r)
proc getAllRows*(db: DbConn, stmtName: SqlPrepared): seq[Row]
{.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
result = @[]
for r in fastRows(db, stmtName):
result.add(r)
iterator rows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): Row {.tags: [ReadDbEffect].} =
## Similar to `fastRows iterator <#fastRows.i,DbConn,SqlQuery,varargs[string,]>`_,
## but slower and safe.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
for r in fastRows(db, query, args): yield r
iterator rows*(db: DbConn, stmtName: SqlPrepared): Row
{.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
for r in fastRows(db, stmtName): yield r
proc getValue*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): string {.tags: [ReadDbEffect].} =
## Executes the query and returns the first column of the first row of the
## result dataset. Returns `""` if the dataset contains no rows or the database
## value is `NULL`.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
var stmt = setupQuery(db, query, args)
if step(stmt) == SQLITE_ROW:
let cb = column_bytes(stmt, 0)
if cb == 0:
result = ""
else:
if column_type(stmt, 0) == SQLITE_BLOB:
result.setLen(cb)
copyMem(addr(result[0]), column_blob(stmt, 0), cb)
else:
result = newStringOfCap(cb)
add(result, column_text(stmt, 0))
else:
result = ""
if finalize(stmt) != SQLITE_OK: dbError(db)
proc getValue*(db: DbConn, stmtName: SqlPrepared): string
{.tags: [ReadDbEffect,WriteDbEffect], since: (1, 3).} =
var stmt = setupQuery(db, stmtName).PStmt
if step(stmt) == SQLITE_ROW:
let cb = column_bytes(stmt, 0)
if cb == 0:
result = ""
else:
if column_type(stmt, 0) == SQLITE_BLOB:
result.setLen(cb)
copyMem(addr(result[0]), column_blob(stmt, 0), cb)
else:
result = newStringOfCap(cb)
add(result, column_text(stmt, 0))
else:
result = ""
proc tryInsertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], raises: [DbError].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row or -1 in case of an error.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
assert(not db.isNil, "Database not connected.")
var q = dbFormat(query, args)
var stmt: sqlite3.PStmt
result = -1
if prepare_v2(db, q.cstring, q.len.cint, stmt, nil) == SQLITE_OK:
if step(stmt) == SQLITE_DONE:
result = last_insert_rowid(db)
if finalize(stmt) != SQLITE_OK:
result = -1
else:
discard finalize(stmt)
proc insertID*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.tags: [WriteDbEffect].} =
## Executes the query (typically "INSERT") and returns the
## generated ID for the row.
##
## Raises a `DbError` exception when failed to insert row.
## For Postgre this adds `RETURNING id` to the query, so it only works
## if your primary key is named `id`.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
result = tryInsertID(db, query, args)
if result < 0: dbError(db)
proc tryInsert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], raises: [DbError], since: (1, 3).} =
## same as tryInsertID
tryInsertID(db, query, args)
proc insert*(db: DbConn, query: SqlQuery, pkName: string,
args: varargs[string, `$`]): int64
{.tags: [WriteDbEffect], since: (1, 3).} =
## same as insertId
result = tryInsert(db, query, pkName, args)
if result < 0: dbError(db)
proc execAffectedRows*(db: DbConn, query: SqlQuery,
args: varargs[string, `$`]): int64 {.
tags: [ReadDbEffect, WriteDbEffect].} =
## Executes the query (typically "UPDATE") and returns the
## number of affected rows.
##
## **Examples:**
##
## ```Nim
## 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()
## ```
exec(db, query, args)
result = changes(db)
proc execAffectedRows*(db: DbConn, stmtName: SqlPrepared): int64
{.tags: [ReadDbEffect, WriteDbEffect],since: (1, 3).} =
exec(db, stmtName)
result = changes(db)
proc close*(db: DbConn) {.tags: [DbEffect].} =
## Closes the database connection.
##
## **Examples:**
## ```Nim
## let db = open("mytest.db", "", "", "")
## db.close()
## ```
if sqlite3.close(db) != SQLITE_OK: dbError(db)
proc open*(connection, user, password, database: string): DbConn {.
tags: [DbEffect].} =
## Opens a database connection. Raises a `DbError` exception if the connection
## could not be established.
##
## **Note:** Only the `connection` parameter is used for `sqlite`.
##
## **Examples:**
## ```Nim
## try:
## let db = open("mytest.db", "", "", "")
## ## do something...
## ## db.getAllRows(sql"SELECT * FROM my_table")
## db.close()
## except:
## stderr.writeLine(getCurrentExceptionMsg())
## ```
var db: DbConn
if sqlite3.open(connection, db) == SQLITE_OK:
result = db
else:
dbError(db)
proc setEncoding*(connection: DbConn, encoding: string): bool {.
tags: [DbEffect].} =
## Sets the encoding of a database connection, returns `true` for
## success, `false` for failure.
##
## **Note:** The encoding cannot be changed once it's been set.
## According to SQLite3 documentation, any attempt to change
## the encoding after the database is created will be silently
## ignored.
exec(connection, sql"PRAGMA encoding = ?", [encoding])
result = connection.getValue(sql"PRAGMA encoding") == encoding
proc finalize*(sqlPrepared:SqlPrepared) {.discardable, since: (1, 3).} =
discard finalize(sqlPrepared.PStmt)
template dbBindParamError*(paramIdx: int, val: varargs[untyped]) =
## Raises a `DbError` exception.
var e: ref DbError
new(e)
e.msg = "error binding param in position " & $paramIdx
raise e
proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int32) {.since: (1, 3).} =
## Binds a int32 to the specified paramIndex.
if bind_int(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
dbBindParamError(paramIdx, val)
proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int64) {.since: (1, 3).} =
## Binds a int64 to the specified paramIndex.
if bind_int64(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
dbBindParamError(paramIdx, val)
proc bindParam*(ps: SqlPrepared, paramIdx: int, val: int) {.since: (1, 3).} =
## Binds a int to the specified paramIndex.
when sizeof(int) == 8:
bindParam(ps, paramIdx, val.int64)
else:
bindParam(ps, paramIdx, val.int32)
proc bindParam*(ps: SqlPrepared, paramIdx: int, val: float64) {.since: (1, 3).} =
## Binds a 64bit float to the specified paramIndex.
if bind_double(ps.PStmt, paramIdx.int32, val) != SQLITE_OK:
dbBindParamError(paramIdx, val)
proc bindNull*(ps: SqlPrepared, paramIdx: int) {.since: (1, 3).} =
## Sets the bindparam at the specified paramIndex to null
## (default behaviour by sqlite).
if bind_null(ps.PStmt, paramIdx.int32) != SQLITE_OK:
dbBindParamError(paramIdx)
proc bindParam*(ps: SqlPrepared, paramIdx: int, val: string, copy = true) {.since: (1, 3).} =
## Binds a string to the specified paramIndex.
## if copy is true then SQLite makes its own private copy of the data immediately
if bind_text(ps.PStmt, paramIdx.int32, val.cstring, val.len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
dbBindParamError(paramIdx, val)
proc bindParam*(ps: SqlPrepared, paramIdx: int,val: openArray[byte], copy = true) {.since: (1, 3).} =
## binds a blob to the specified paramIndex.
## if copy is true then SQLite makes its own private copy of the data immediately
let len = val.len
if bind_blob(ps.PStmt, paramIdx.int32, val[0].unsafeAddr, len.int32, if copy: SQLITE_TRANSIENT else: SQLITE_STATIC) != SQLITE_OK:
dbBindParamError(paramIdx, val)
macro bindParams*(ps: SqlPrepared, params: varargs[untyped]): untyped {.since: (1, 3).} =
let bindParam = bindSym("bindParam", brOpen)
let bindNull = bindSym("bindNull")
let preparedStatement = genSym()
result = newStmtList()
# Store `ps` in a temporary variable. This prevents `ps` from being evaluated every call.
result.add newNimNode(nnkLetSection).add(newIdentDefs(preparedStatement, newEmptyNode(), ps))
for idx, param in params:
if param.kind != nnkNilLit:
result.add newCall(bindParam, preparedStatement, newIntLitNode idx + 1, param)
else:
result.add newCall(bindNull, preparedStatement, newIntLitNode idx + 1)
macro untypedLen(args: varargs[untyped]): int =
newLit(args.len)
template exec*(db: DbConn, stmtName: SqlPrepared,
args: varargs[typed]): untyped =
when untypedLen(args) > 0:
if reset(stmtName.PStmt) != SQLITE_OK:
dbError(db)
if clear_bindings(stmtName.PStmt) != SQLITE_OK:
dbError(db)
stmtName.bindParams(args)
if not tryExec(db, stmtName): dbError(db)
when not defined(testing) and isMainModule:
var db = open(":memory:", "", "", "")
exec(db, sql"create table tbl1(one varchar(10), two smallint)", [])
exec(db, sql"insert into tbl1 values('hello!',10)", [])
exec(db, sql"insert into tbl1 values('goodbye', 20)", [])
var p1 = db.prepare "create table tbl2(one varchar(10), two smallint)"
exec(db, p1)
finalize(p1)
var p2 = db.prepare "insert into tbl2 values('hello!',10)"
exec(db, p2)
finalize(p2)
var p3 = db.prepare "insert into tbl2 values('goodbye', 20)"
exec(db, p3)
finalize(p3)
#db.query("create table tbl1(one varchar(10), two smallint)")
#db.query("insert into tbl1 values('hello!',10)")
#db.query("insert into tbl1 values('goodbye', 20)")
for r in db.rows(sql"select * from tbl1", []):
echo(r[0], r[1])
for r in db.instantRows(sql"select * from tbl1", []):
echo(r[0], r[1])
var p4 = db.prepare "select * from tbl2"
for r in db.rows(p4):
echo(r[0], r[1])
finalize(p4)
var i5 = 0
var p5 = db.prepare "select * from tbl2"
for r in db.instantRows(p5):
inc i5
echo(r[0], r[1])
assert i5 == 2
finalize(p5)
for r in db.rows(sql"select * from tbl2", []):
echo(r[0], r[1])
for r in db.instantRows(sql"select * from tbl2", []):
echo(r[0], r[1])
var p6 = db.prepare "select * from tbl2 where one = ? "
p6.bindParams("goodbye")
var rowsP3 = 0
for r in db.rows(p6):
rowsP3 = 1
echo(r[0], r[1])
assert rowsP3 == 1
finalize(p6)
var p7 = db.prepare "select * from tbl2 where two=?"
p7.bindParams(20'i32)
when sizeof(int) == 4:
p7.bindParams(20)
var rowsP = 0
for r in db.rows(p7):
rowsP = 1
echo(r[0], r[1])
assert rowsP == 1
finalize(p7)
exec(db, sql"CREATE TABLE photos(ID INTEGER PRIMARY KEY AUTOINCREMENT, photo BLOB)")
var p8 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
var d = "abcdefghijklmnopqrstuvwxyz"
p8.bindParams(1'i32, "abcdefghijklmnopqrstuvwxyz")
exec(db, p8)
finalize(p8)
var p10 = db.prepare "INSERT INTO photos (ID,PHOTO) VALUES (?,?)"
p10.bindParams(2'i32,nil)
exec(db, p10)
exec( db, p10, 3, nil)
finalize(p10)
for r in db.rows(sql"select * from photos where ID = 1", []):
assert r[1].len == d.len
assert r[1] == d
var i6 = 0
for r in db.rows(sql"select * from photos where ID = 3", []):
i6 = 1
assert i6 == 1
var p9 = db.prepare("select * from photos where PHOTO is ?")
p9.bindParams(nil)
var rowsP2 = 0
for r in db.rows(p9):
rowsP2 = 1
echo(r[0], repr r[1])
assert rowsP2 == 1
finalize(p9)
db_sqlite.close(db)

View File

@@ -1,100 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2015 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Common datatypes and definitions for all `db_*.nim` (
## `db_mysql <db_mysql.html>`_, `db_postgres <db_postgres.html>`_,
## and `db_sqlite <db_sqlite.html>`_) modules.
type
DbError* = object of IOError ## exception that is raised if a database error occurs
SqlQuery* = distinct string ## an SQL query string
DbEffect* = object of IOEffect ## effect that denotes a database operation
ReadDbEffect* = object of DbEffect ## effect that denotes a read operation
WriteDbEffect* = object of DbEffect ## effect that denotes a write operation
DbTypeKind* = enum ## a superset of datatypes that might be supported.
dbUnknown, ## unknown datatype
dbSerial, ## datatype used for primary auto-increment keys
dbNull, ## datatype used for the NULL value
dbBit, ## bit datatype
dbBool, ## boolean datatype
dbBlob, ## blob datatype
dbFixedChar, ## string of fixed length
dbVarchar, ## string datatype
dbJson, ## JSON datatype
dbXml, ## XML datatype
dbInt, ## some integer type
dbUInt, ## some unsigned integer type
dbDecimal, ## decimal numbers (fixed-point number)
dbFloat, ## some floating point type
dbDate, ## a year-month-day description
dbTime, ## HH:MM:SS information
dbDatetime, ## year-month-day and HH:MM:SS information,
## plus optional time or timezone information
dbTimestamp, ## Timestamp values are stored as the number of seconds
## since the epoch ('1970-01-01 00:00:00' UTC).
dbTimeInterval, ## an interval [a,b] of times
dbEnum, ## some enum
dbSet, ## set of enum values
dbArray, ## an array of values
dbComposite, ## composite type (record, struct, etc)
dbUrl, ## a URL
dbUuid, ## a UUID
dbInet, ## an IP address
dbMacAddress, ## a MAC address
dbGeometry, ## some geometric type
dbPoint, ## Point on a plane (x,y)
dbLine, ## Infinite line ((x1,y1),(x2,y2))
dbLseg, ## Finite line segment ((x1,y1),(x2,y2))
dbBox, ## Rectangular box ((x1,y1),(x2,y2))
dbPath, ## Closed or open path (similar to polygon) ((x1,y1),...)
dbPolygon, ## Polygon (similar to closed path) ((x1,y1),...)
dbCircle, ## Circle <(x,y),r> (center point and radius)
dbUser1, ## user definable datatype 1 (for unknown extensions)
dbUser2, ## user definable datatype 2 (for unknown extensions)
dbUser3, ## user definable datatype 3 (for unknown extensions)
dbUser4, ## user definable datatype 4 (for unknown extensions)
dbUser5 ## user definable datatype 5 (for unknown extensions)
DbType* = object ## describes a database type
kind*: DbTypeKind ## the kind of the described type
notNull*: bool ## does the type contain NULL?
name*: string ## the name of the type
size*: Natural ## the size of the datatype; 0 if of variable size
maxReprLen*: Natural ## maximal length required for the representation
precision*, scale*: Natural ## precision and scale of the number
min*, max*: BiggestInt ## the minimum and maximum of allowed values
validValues*: seq[string] ## valid values of an enum or a set
DbColumn* = object ## information about a database column
name*: string ## name of the column
tableName*: string ## name of the table the column belongs to (optional)
typ*: DbType ## type of the column
primaryKey*: bool ## is this a primary key?
foreignKey*: bool ## is this a foreign key?
DbColumns* = seq[DbColumn]
template sql*(query: string): SqlQuery =
## constructs a SqlQuery from the string `query`. This is supposed to be
## used as a raw-string-literal modifier:
## `sql"update user set counter = counter + 1"`
##
## If assertions are turned off, it does nothing. If assertions are turned
## on, later versions will check the string for valid syntax.
SqlQuery(query)
proc dbError*(msg: string) {.noreturn, noinline.} =
## raises an DbError exception with message `msg`.
var e: ref DbError
new(e)
e.msg = msg
raise e

View File

@@ -1,88 +0,0 @@
#
#
# Nim's Runtime Library
# (c) Copyright 2016 Andreas Rumpf
#
# See the file "copying.txt", included in this
# distribution, for details about the copyright.
#
## Memory tracking support for Nim.
when not defined(memTracker) and not isMainModule:
{.error: "Memory tracking support is turned off!".}
{.push memtracker: off.}
# we import the low level wrapper and are careful not to use Nim's
# memory manager for anything here.
import sqlite3
var
dbHandle: PSqlite3
insertStmt {.threadvar.}: Pstmt
const insertQuery = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
template sbind(x: int; value) =
when value is cstring:
let ret = insertStmt.bindText(x, value, value.len.int32, SQLITE_TRANSIENT)
if ret != SQLITE_OK:
quit "could not bind value"
else:
let ret = insertStmt.bindInt64(x, value)
if ret != SQLITE_OK:
quit "could not bind value"
when defined(memTracker):
proc logEntries(log: TrackLog) {.nimcall, tags: [], gcsafe.} =
if insertStmt.isNil:
if prepare_v2(dbHandle, insertQuery,
insertQuery.len, insertStmt, nil) != SQLITE_OK:
quit "could not bind query to insertStmt " & $sqlite3.errmsg(dbHandle)
for i in 0..log.count-1:
var success = false
let e = log.data[i]
discard sqlite3.reset(insertStmt)
discard clearBindings(insertStmt)
sbind 1, e.op
sbind(2, cast[int](e.address))
sbind 3, e.size
sbind 4, e.file
sbind 5, e.line
if step(insertStmt) == SQLITE_DONE:
success = true
if not success:
quit "could not write to database! " & $sqlite3.errmsg(dbHandle)
proc execQuery(q: string) =
var s: Pstmt
if prepare_v2(dbHandle, q, q.len.int32, s, nil) == SQLITE_OK:
discard step(s)
if finalize(s) != SQLITE_OK:
quit "could not finalize " & $sqlite3.errmsg(dbHandle)
else:
quit "could not prepare statement " & $sqlite3.errmsg(dbHandle)
proc setupDb() =
execQuery """create table if not exists tracking(
id integer primary key,
op varchar not null,
address integer not null,
size integer not null,
file varchar not null,
line integer not null
)"""
execQuery "delete from tracking"
if sqlite3.open("memtrack.db", dbHandle) == SQLITE_OK:
setupDb()
const query = "INSERT INTO tracking(op, address, size, file, line) values (?, ?, ?, ?, ?)"
if prepare_v2(dbHandle, insertQuery,
insertQuery.len, insertStmt, nil) == SQLITE_OK:
when defined(memTracker):
setTrackLogger logEntries
else:
quit "could not prepare statement B " & $sqlite3.errmsg(dbHandle)
else:
quit "could not setup sqlite " & $sqlite3.errmsg(dbHandle)
{.pop.}

View File

@@ -1,15 +0,0 @@
import db_common
template dbFormatImpl*(formatstr: SqlQuery, dbQuote: proc (s: string): string, args: varargs[string]): string =
var res = ""
var a = 0
for c in items(string(formatstr)):
if c == '?':
if a == args.len:
dbError("""The number of "?" given exceeds the number of parameters present in the query.""")
add(res, dbQuote(args[a]))
inc(a)
else:
add(res, c)
res

View File

@@ -39,11 +39,6 @@ import
cpuload,
critbits,
cstrutils,
db_common,
db_mysql,
db_odbc,
db_postgres,
db_sqlite,
deques,
distros,
dynlib,

View File

@@ -1,4 +1,5 @@
discard """
disabled: true
action: compile
matrix: "--threads:off"
"""

View File

@@ -1,25 +0,0 @@
discard """
action: "compile"
"""
import db_mysql, db_odbc, db_postgres
import os
from stdtest/specialpaths import buildDir
block:
block:
const dbName = buildDir / "db.sqlite3"
var db = db_mysql.open(dbName, "", "", "")
discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t")
block:
const dbName = buildDir / "db.odbc"
var db = db_odbc.open(dbName, "", "", "")
discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t")
block:
const dbName = buildDir / "db.postgres"
var db = db_postgres.open(dbName, "", "", "")
discard tryInsertId(db, sql"INSERT INTO myTestTbl (name,i,f) VALUES (?,?,?)", "t")

View File

@@ -1,5 +0,0 @@
import std/db_mysql
import std/assertions
doAssert dbQuote("SELECT * FROM foo WHERE col1 = 'bar_baz'") == "'SELECT * FROM foo WHERE col1 = \\'bar_baz\\''"
doAssert dbQuote("SELECT * FROM foo WHERE col1 LIKE '%bar_baz%'") == "'SELECT * FROM foo WHERE col1 LIKE \\'%bar_baz%\\''"

View File

@@ -1,79 +0,0 @@
discard """
action: "run"
exitcode: 0
"""
import db_sqlite
import random
import os
from stdtest/specialpaths import buildDir
import std/assertions
block tsqlitebindatas: ## db_sqlite binary data
const dbName = buildDir / "tsqlitebindatas.db"
let origName = "Bobby"
var orig = newSeq[float64](150)
randomize()
for x in orig.mitems:
x = rand(1.0)/10.0
discard tryRemoveFile(dbName)
let db = open(dbName, "", "", "")
let createTableStr = sql"""CREATE TABLE test(
id INTEGER NOT NULL PRIMARY KEY,
name TEXT,
data BLOB
)
"""
db.exec(createTableStr)
var dbuf = newSeq[byte](orig.len*sizeof(float64))
copyMem(addr(dbuf[0]), addr(orig[0]), dbuf.len)
var insertStmt = db.prepare("INSERT INTO test (id, name, data) VALUES (?, ?, ?)")
insertStmt.bindParams(1, origName, dbuf)
let bres = db.tryExec(insertStmt)
doAssert(bres)
finalize(insertStmt)
var nameTest = db.getValue(sql"SELECT name FROM test WHERE id = ?", 1)
doAssert nameTest == origName
var dataTest = db.getValue(sql"SELECT data FROM test WHERE id = ?", 1)
let seqSize = int(dataTest.len*sizeof(byte)/sizeof(float64))
var res: seq[float64] = newSeq[float64](seqSize)
copyMem(addr(res[0]), addr(dataTest[0]), dataTest.len)
doAssert res.len == orig.len
doAssert res == orig
db.close()
doAssert tryRemoveFile(dbName)
block:
block:
const dbName = buildDir / "db.sqlite3"
var db = db_sqlite.open(dbName, "", "", "")
var witness = false
try:
db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);"))
db.exec(sql("REPLACE INTO table (url, another_field) VALUES (?, '123');"))
except DbError as e:
witness = true
doAssert e.msg == "The number of \"?\" given exceeds the number of parameters present in the query."
finally:
db.close()
removeFile(dbName)
doAssert witness
block:
const dbName = buildDir / "db.sqlite3"
var db = db_sqlite.open(dbName, "", "", "")
try:
db.exec(sql("CREATE TABLE table1 (url TEXT, other_field INT);"))
db.exec(sql("INSERT INTO table1 (url, other_field) VALUES (?, ?);"), "http://domain.com/test?param=1", 123)
finally:
db.close()
removeFile(dbName)

View File

@@ -1,8 +0,0 @@
discard """
action: compile
"""
import db_sqlite
var db: DbConn
exec(db, sql"create table blabla()")

View File

@@ -1,332 +1 @@
import db_postgres, strutils
let db = open("localhost", "dom", "", "test")
db.exec(sql"DROP TABLE IF EXISTS myTable")
db.exec(sql("""CREATE TABLE myTable (
id integer PRIMARY KEY,
name varchar(50) not null)"""))
let name = "Dom"
db.exec(sql"INSERT INTO myTable (id, name) VALUES (0, ?)",
name)
doAssert db.getValue(sql"SELECT name FROM myTable") == name
# Check issue #3513
doAssert db.getValue(sql"SELECT name FROM myTable") == name
# issue #3560
proc addToDb(conn: DbConn, fileId: int, fileName: string): int64 =
result = conn.insertId(sql("INSERT into files (id, filename) VALUES (?, ?)"), fileId, fileName)
db.exec(sql"DROP TABLE IF EXISTS files")
db.exec(sql"DROP TABLE IF EXISTS fileobjects")
db.exec(sql("""CREATE TABLE FILEOBJECTS(
ID SERIAL PRIMARY KEY,
FILE_SIZE INT,
MD5 CHAR(32) NOT NULL UNIQUE
);"""))
db.exec(sql("""CREATE TABLE FILES(
ID SERIAL PRIMARY KEY,
OBJECT_ID INT,
FILENAME TEXT NOT NULL,
URI TEXT,
SCHEME CHAR(10),
PUBLIC BOOLEAN DEFAULT FALSE,
CONSTRAINT fk1_fileobjs FOREIGN KEY (object_id)
REFERENCES fileobjects (id) MATCH SIMPLE
ON DELETE CASCADE
);"""))
let f1 = db.addToDb(1, "hello.tmp")
doAssert f1 == 1
let f2 = db.addToDb(2, "hello2.tmp")
doAssert f2 == 2
# PreparedStmt vs. normal query
try:
echo db.getValue(sql("select * from files where id = $1"), 1)
doAssert false, "Exception expected"
except DbError:
let msg = getCurrentExceptionMsg().normalize
info "DbError",
msg = $msg
doAssert "no parameter" in msg
doAssert "$1" in msg
doAssert db.getValue(sql("select filename from files where id = ?"), 1) == "hello.tmp"
var first = prepare(db, "one", sql"select filename from files where id = $1", 1)
doAssert db.getValue(first, 1) == "hello.tmp"
try:
var second = prepare(db, "two", sql"select filename from files where id = ?", 1)
doAssert false, "Exception expected"
except:
let msg = getCurrentExceptionMsg().normalize
doAssert "expects" in msg
doAssert "$1" in msg
doAssert "parameter substitution" in msg
# issue #3569
db.exec(SqlQuery("DROP TABLE IF EXISTS tags"))
db.exec(SqlQuery("CREATE TABLE tags(id serial UNIQUE, name varchar(255))"))
for i in 1..10:
var name = "t" & $i
echo(name)
discard db.getRow(
SqlQuery("INSERT INTO tags(name) VALUES(\'$1\') RETURNING id" % [name]))
# get column details
db.exec(SqlQuery("DROP TABLE IF EXISTS dbtypes;"))
db.exec(SqlQuery("DROP TYPE IF EXISTS custom_enum;"))
db.exec(SqlQuery("CREATE TYPE custom_enum AS ENUM ('1', '2', '3');"))
db.exec(SqlQuery("DROP TYPE IF EXISTS custom_composite;"))
db.exec(SqlQuery("CREATE TYPE custom_composite AS (r double precision, i double precision);"))
db.exec(SqlQuery("""CREATE TABLE dbtypes(
id serial UNIQUE,
bytea_col bytea,
smallint_col smallint,
integer_col integer,
bigint_col bigint,
decimal_col decimal,
numeric_col numeric,
real_col real,
double_precision_col double precision,
smallserial_col smallserial,
serial_col serial,
bigserial_col bigserial,
money_col money,
varchar_col varchar(10),
character_col character(1),
text_col text,
timestamp_col timestamp,
date_col date,
time_col time,
interval_col interval,
bool_col boolean,
custom_enum_col custom_enum,
point_col point,
line_col line,
lseg_col lseg,
box_col box,
path_col path,
polygon_col polygon,
circle_col circle,
cidr_col cidr,
inet_col inet,
macaddr_col macaddr,
bit_col bit,
varbit_col bit(3),
tsvector_col tsvector,
tsquery_col tsquery,
uuid_col uuid,
xml_col xml,
json_col json,
array_col integer[],
custom_composite_col custom_composite,
range_col int4range
);"""))
db.exec(SqlQuery("INSERT INTO dbtypes (id) VALUES(0);"))
var dbCols : DbColumns = @[]
for row in db.instantRows(dbCols, sql"SELECT * FROM dbtypes"):
doAssert len(dbCols) == 42
doAssert dbCols[0].name == "id"
doAssert dbCols[0].typ.kind == DbTypeKind.dbInt
doAssert dbCols[0].typ.name == "int4"
doAssert dbCols[0].typ.size == 4
doAssert dbCols[1].name == "bytea_col"
doAssert dbCols[1].typ.kind == DbTypeKind.dbBlob
doAssert dbCols[1].typ.name == "bytea"
doAssert dbCols[2].name == "smallint_col"
doAssert dbCols[2].typ.kind == DbTypeKind.dbInt
doAssert dbCols[2].typ.name == "int2"
doAssert dbCols[2].typ.size == 2
doAssert dbCols[3].name == "integer_col"
doAssert dbCols[3].typ.kind == DbTypeKind.dbInt
doAssert dbCols[3].typ.name == "int4"
doAssert dbCols[3].typ.size == 4
doAssert dbCols[4].name == "bigint_col"
doAssert dbCols[4].typ.kind == DbTypeKind.dbInt
doAssert dbCols[4].typ.name == "int8"
doAssert dbCols[4].typ.size == 8
doAssert dbCols[5].name == "decimal_col"
doAssert dbCols[5].typ.kind == DbTypeKind.dbDecimal
doAssert dbCols[5].typ.name == "numeric"
doAssert dbCols[6].name == "numeric_col"
doAssert dbCols[6].typ.kind == DbTypeKind.dbDecimal
doAssert dbCols[6].typ.name == "numeric"
doAssert dbCols[7].name == "real_col"
doAssert dbCols[7].typ.kind == DbTypeKind.dbFloat
doAssert dbCols[7].typ.name == "float4"
doAssert dbCols[8].name == "double_precision_col"
doAssert dbCols[8].typ.kind == DbTypeKind.dbFloat
doAssert dbCols[8].typ.name == "float8"
doAssert dbCols[9].name == "smallserial_col"
doAssert dbCols[9].typ.kind == DbTypeKind.dbInt
doAssert dbCols[9].typ.name == "int2"
doAssert dbCols[10].name == "serial_col"
doAssert dbCols[10].typ.kind == DbTypeKind.dbInt
doAssert dbCols[10].typ.name == "int4"
doAssert dbCols[11].name == "bigserial_col"
doAssert dbCols[11].typ.kind == DbTypeKind.dbInt
doAssert dbCols[11].typ.name == "int8"
doAssert dbCols[12].name == "money_col"
doAssert dbCols[12].typ.kind == DbTypeKind.dbDecimal
doAssert dbCols[12].typ.name == "money"
doAssert dbCols[13].name == "varchar_col"
doAssert dbCols[13].typ.kind == DbTypeKind.dbVarchar
doAssert dbCols[13].typ.name == "varchar"
doAssert dbCols[14].name == "character_col"
doAssert dbCols[14].typ.kind == DbTypeKind.dbFixedChar
doAssert dbCols[14].typ.name == "bpchar"
doAssert dbCols[15].name == "text_col"
doAssert dbCols[15].typ.kind == DbTypeKind.dbVarchar
doAssert dbCols[15].typ.name == "text"
doAssert dbCols[16].name == "timestamp_col"
doAssert dbCols[16].typ.kind == DbTypeKind.dbTimestamp
doAssert dbCols[16].typ.name == "timestamp"
doAssert dbCols[17].name == "date_col"
doAssert dbCols[17].typ.kind == DbTypeKind.dbDate
doAssert dbCols[17].typ.name == "date"
doAssert dbCols[18].name == "time_col"
doAssert dbCols[18].typ.kind == DbTypeKind.dbTime
doAssert dbCols[18].typ.name == "time"
doAssert dbCols[19].name == "interval_col"
doAssert dbCols[19].typ.kind == DbTypeKind.dbTimeInterval
doAssert dbCols[19].typ.name == "interval"
doAssert dbCols[20].name == "bool_col"
doAssert dbCols[20].typ.kind == DbTypeKind.dbBool
doAssert dbCols[20].typ.name == "bool"
doAssert dbCols[21].name == "custom_enum_col"
doAssert dbCols[21].typ.kind == DbTypeKind.dbUnknown
doAssert parseInt(dbCols[21].typ.name) > 0
doAssert dbCols[22].name == "point_col"
doAssert dbCols[22].typ.kind == DbTypeKind.dbPoint
doAssert dbCols[22].typ.name == "point"
doAssert dbCols[23].name == "line_col"
doAssert dbCols[23].typ.kind == DbTypeKind.dbLine
doAssert dbCols[23].typ.name == "line"
doAssert dbCols[24].name == "lseg_col"
doAssert dbCols[24].typ.kind == DbTypeKind.dbLseg
doAssert dbCols[24].typ.name == "lseg"
doAssert dbCols[25].name == "box_col"
doAssert dbCols[25].typ.kind == DbTypeKind.dbBox
doAssert dbCols[25].typ.name == "box"
doAssert dbCols[26].name == "path_col"
doAssert dbCols[26].typ.kind == DbTypeKind.dbPath
doAssert dbCols[26].typ.name == "path"
doAssert dbCols[27].name == "polygon_col"
doAssert dbCols[27].typ.kind == DbTypeKind.dbPolygon
doAssert dbCols[27].typ.name == "polygon"
doAssert dbCols[28].name == "circle_col"
doAssert dbCols[28].typ.kind == DbTypeKind.dbCircle
doAssert dbCols[28].typ.name == "circle"
doAssert dbCols[29].name == "cidr_col"
doAssert dbCols[29].typ.kind == DbTypeKind.dbInet
doAssert dbCols[29].typ.name == "cidr"
doAssert dbCols[30].name == "inet_col"
doAssert dbCols[30].typ.kind == DbTypeKind.dbInet
doAssert dbCols[30].typ.name == "inet"
doAssert dbCols[31].name == "macaddr_col"
doAssert dbCols[31].typ.kind == DbTypeKind.dbMacAddress
doAssert dbCols[31].typ.name == "macaddr"
doAssert dbCols[32].name == "bit_col"
doAssert dbCols[32].typ.kind == DbTypeKind.dbBit
doAssert dbCols[32].typ.name == "bit"
doAssert dbCols[33].name == "varbit_col"
doAssert dbCols[33].typ.kind == DbTypeKind.dbBit
doAssert dbCols[33].typ.name == "bit"
doAssert dbCols[34].name == "tsvector_col"
doAssert dbCols[34].typ.kind == DbTypeKind.dbVarchar
doAssert dbCols[34].typ.name == "tsvector"
doAssert dbCols[35].name == "tsquery_col"
doAssert dbCols[35].typ.kind == DbTypeKind.dbVarchar
doAssert dbCols[35].typ.name == "tsquery"
doAssert dbCols[36].name == "uuid_col"
doAssert dbCols[36].typ.kind == DbTypeKind.dbVarchar
doAssert dbCols[36].typ.name == "uuid"
doAssert dbCols[37].name == "xml_col"
doAssert dbCols[37].typ.kind == DbTypeKind.dbXml
doAssert dbCols[37].typ.name == "xml"
doAssert dbCols[38].name == "json_col"
doAssert dbCols[38].typ.kind == DbTypeKind.dbJson
doAssert dbCols[38].typ.name == "json"
doAssert dbCols[39].name == "array_col"
doAssert dbCols[39].typ.kind == DbTypeKind.dbArray
doAssert dbCols[39].typ.name == "int4[]"
doAssert dbCols[40].name == "custom_composite_col"
doAssert dbCols[40].typ.kind == DbTypeKind.dbUnknown
doAssert parseInt(dbCols[40].typ.name) > 0
doAssert dbCols[41].name == "range_col"
doAssert dbCols[41].typ.kind == DbTypeKind.dbComposite
doAssert dbCols[41].typ.name == "int4range"
# issue 6571
db.exec(sql"DROP TABLE IF EXISTS DICTIONARY")
db.exec(sql("""CREATE TABLE DICTIONARY(
id SERIAL PRIMARY KEY,
entry VARCHAR(1000) NOT NULL,
definition VARCHAR(4000) NOT NULL
);"""))
var entry = "あっそ"
var definition = "(int) (See ああそうそう) oh, really (uninterested)/oh yeah?/hmmmmm"
discard db.getRow(
sql("INSERT INTO DICTIONARY(entry, definition) VALUES(?, ?) RETURNING id"), entry, definition)
doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
entry = "Format string entry"
definition = "Format string definition"
db.exec(SqlQuery("INSERT INTO DICTIONARY(entry, definition) VALUES (?, ?)"), entry, definition)
doAssert db.getValue(sql"SELECT definition FROM DICTIONARY WHERE entry = ?", entry) == definition
echo("All tests succeeded!")
db.close()