1 /* Invisible Vector Library
2 * coded by Ketmar // Invisible Vector <ketmar@ketmar.no-ip.org>
3 * Understanding is not required. Only obedience.
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, version 3 of the License ONLY.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
18 module iv
.sq3_old
/*is aliced*/;
19 //pragma(lib, "sqlite3");
21 //version = sq3_debug_stmtlist;
25 //public import etc.c.sqlite3;
26 public import iv
.c
.sqlite3
;
28 import std
.range
.primitives
;
30 private import std
.internal
.cstring
: tempCString
;
34 //enum SQLITE_DETERMINISTIC = 0x800;
35 enum SQLITE_DIRECTONLY
= 0x000080000;
36 enum SQLITE_SUBTYPE
= 0x000100000;
37 enum SQLITE_INNOCUOUS
= 0x000200000;
39 enum SQLITE_PREPARE_PERSISTENT
= 0x01U
;
40 enum SQLITE_PREPARE_NORMALIZE
= 0x02U
;
41 enum SQLITE_PREPARE_NO_VTAB
= 0x04U
;
44 int sqlite3_prepare_v3(
45 sqlite3
*db, /** Database handle */
46 const(char)*zSql
, /** SQL statement, UTF-8 encoded */
47 int nByte
, /** Maximum length of zSql in bytes. */
48 uint prepFlags
, /* Zero or more SQLITE_PREPARE_ flags */
49 sqlite3_stmt
**ppStmt
, /** OUT: Statement handle */
50 const(char*)*pzTail
/** OUT: Pointer to unused portion of zSql */
57 ////////////////////////////////////////////////////////////////////////////////
58 mixin(NewExceptionClass
!("SQLiteException", "Exception"));
60 class SQLiteErr
: SQLiteException
{
63 this (string msg
, string file
=__FILE__
, usize line
=__LINE__
, Throwable next
=null) @trusted nothrow {
65 super("SQLite ERROR: "~msg
, file
, line
, next
);
68 this (sqlite3
* db, int rc
, string file
=__FILE__
, usize line
=__LINE__
, Throwable next
=null) @trusted nothrow {
69 //import core.stdc.stdio : stderr, fprintf;
70 //fprintf(stderr, "SQLITE ERROR: %s\n", sqlite3_errstr(rc));
72 if (rc
== SQLITE_OK
) {
73 super("SQLite ERROR: no error!", file
, line
, next
);
75 import std
.exception
: assumeUnique
;
76 import std
.string
: fromStringz
;
78 super(sqlite3_errstr(sqlite3_extended_errcode(db)).fromStringz
.assumeUnique
, file
, line
, next
);
80 super(sqlite3_errstr(rc
).fromStringz
.assumeUnique
, file
, line
, next
);
87 public void sq3check (sqlite3
* db, int rc
, string file
=__FILE__
, usize line
=__LINE__
) {
88 //pragma(inline, true);
89 if (rc
!= SQLITE_OK
) throw new SQLiteErr(db, rc
, file
, line
);
93 ////////////////////////////////////////////////////////////////////////////////
95 //k8: nope, don't do this, because users may want to call `sqlite3_config()` before it
96 shared static this () {
97 if (sqlite3_initialize() != SQLITE_OK) throw new Error("can't initialize SQLite");
100 // and this is not required at all
101 shared static ~this () {
107 // use this in `to` to avoid copying
108 public alias SQLStringc
= const(char)[];
109 // use this in `to` to avoid copying
110 public alias SQ3Blob
= const(char)[];
111 // use this in `to` to avoid copying
112 public alias SQ3Text
= const(char)[];
115 ////////////////////////////////////////////////////////////////////////////////
116 // WARNING! don't forget to finalize ALL prepared statements!
119 static struct DBInfo
{
122 usize onCloseSize
= 0;
123 char *onClose
= null; // 0-terminated
124 DBStatement
.Data
*stmthead
= null;
125 DBStatement
.Data
*stmttail
= null;
131 // caller should perform all necessary checks
132 void clearStatements () nothrow @trusted {
133 while (dbi
.stmthead
!is null) {
134 version(sq3_debug_stmtlist
) { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "clearStatements(%p): p=%p\n", dbi
, dbi
.stmthead
); }
135 DBStatement
.Data
*dd = dbi
.stmthead
;
136 dbi
.stmthead
= dd.next
;
141 if (dd.st
!is null) {
142 sqlite3_reset(dd.st
);
143 sqlite3_clear_bindings(dd.st
);
144 sqlite3_finalize(dd.st
);
159 // `null` schema means "open as R/O"
160 // non-null, but empty schema means "open as r/w"
161 // non-empty scheme means "create if absent"
162 this (const(char)[] name
, Mode mode
, const(char)[] pragmas
=null, const(char)[] schema
=null) { openEx(name
, mode
, pragmas
, schema
); }
163 this (const(char)[] name
, const(char)[] schema
) { openEx(name
, (schema
!is null ? Mode
.ReadWriteCreate
: Mode
.ReadOnly
), null, schema
); }
164 ~this () nothrow @trusted { close(); }
166 this (this) nothrow @trusted @nogc { if (dbi
!is null) ++dbi
.rc
; }
168 @property bool isOpen () const pure nothrow @safe @nogc { return (dbi
!is null && dbi
.db !is null); }
170 void close () nothrow @trusted {
173 import core
.stdc
.stdlib
: free
;
174 if (dbi
.db !is null) {
176 if (dbi
.onClose
!is null) {
177 auto rc
= sqlite3_exec(dbi
.db, dbi
.onClose
, null, null, null);
178 if (rc
!= SQLITE_OK
) {
179 import core
.stdc
.stdio
: stderr
, fprintf
;
180 fprintf(stderr
, "SQLITE ERROR ON CLOSE: %s\n", sqlite3_errstr(sqlite3_extended_errcode(dbi
.db)));
183 import core
.stdc
.stdio
: stderr
, fprintf
;
184 fprintf(stderr
, "exec:===\n%s\n===\n", dbi
.onClose
);
188 sqlite3_close_v2(dbi
.db);
196 void appendOnClose (const(char)[] stmts
) {
197 if (!isOpen
) throw new SQLiteException("database is not opened");
198 while (stmts
.length
&& stmts
[0] <= 32) stmts
= stmts
[1..$];
199 while (stmts
.length
&& stmts
[$-1] <= 32) stmts
= stmts
[0..$-1];
200 if (stmts
.length
== 0) return;
201 import core
.stdc
.stdlib
: realloc
;
202 //FIXME: overflow. don't do it.
203 usize nsz
= dbi
.onCloseSize
+stmts
.length
;
204 if (nsz
+1 <= dbi
.onCloseSize
) throw new SQLiteException("out of memory for OnClose");
205 char *np
= cast(char *)realloc(dbi
.onClose
, nsz
+1);
206 if (np
is null) throw new SQLiteException("out of memory for OnClose");
208 np
[dbi
.onCloseSize
..dbi
.onCloseSize
+stmts
.length
] = stmts
[];
209 dbi
.onCloseSize
+= stmts
.length
;
210 np
[dbi
.onCloseSize
] = 0;
213 void setOnClose (const(char)[] stmts
) {
214 if (!isOpen
) throw new SQLiteException("database is not opened");
215 if (dbi
.onClose
!is null) {
216 import core
.stdc
.stdlib
: free
;
221 appendOnClose(stmts
);
224 // `null` schema means "open as R/O"
225 // non-null, but empty schema means "open as r/w"
226 // non-empty scheme means "create is absend"
227 void openEx (const(char)[] name
, Mode mode
, const(char)[] pragmas
=null, const(char)[] schema
=null) {
229 import core
.stdc
.stdlib
: malloc
, free
;
230 import std
.internal
.cstring
;
231 immutable bool allowWrite
= (mode
== Mode
.ReadWrite || mode
== Mode
.ReadWriteCreate
);
232 bool allowCreate
= (mode
== Mode
.ReadWriteCreate
);
234 while (schema
.length
&& schema
[$-1] <= ' ') schema
= schema
[0..$-1];
235 if (schema
.length
== 0) allowCreate
= false;
237 dbi
= cast(DBInfo
*)malloc(DBInfo
.sizeof
);
238 if (!dbi
) throw new Error("out of memory");
241 immutable int rc
= sqlite3_open_v2(name
.tempCString
, &dbi
.db, (allowWrite ? SQLITE_OPEN_READWRITE
: SQLITE_OPEN_READONLY
)|
(allowCreate ? SQLITE_OPEN_CREATE
: 0), null);
242 if (rc
!= SQLITE_OK
) {
247 scope(failure
) { close(); }
249 if (allowCreate
&& schema
.length
) execute(schema
);
252 // `null` schema means "open as R/O"
253 void open (const(char)[] name
, const(char)[] schema
=null) {
254 Mode mode
= Mode
.ReadOnly
;
255 if (schema
!= null) {
256 while (schema
.length
&& schema
[$-1] <= ' ') schema
= schema
[0..$-1];
257 mode
= (schema
.length ? Mode
.ReadWriteCreate
: Mode
.ReadWrite
);
259 return openEx(name
, mode
, null, schema
);
262 ulong lastRowId () nothrow @trusted { return (isOpen ?
sqlite3_last_insert_rowid(dbi
.db) : 0); }
264 void setBusyTimeout (int msecs
) nothrow @trusted { if (isOpen
) sqlite3_busy_timeout(dbi
.db, msecs
); }
266 // execute one or more SQL statements
267 // SQLite will take care of splitting
268 void execute (const(char)[] ops
) {
269 if (!isOpen
) throw new SQLiteException("database is not opened");
270 import std
.internal
.cstring
;
272 immutable int rc
= sqlite3_exec(dbi
.db, ops
.tempCString
, null, null, null/*&errmsg*/);
273 if (rc
!= SQLITE_OK
) {
274 //import core.stdc.stdio : stderr, fprintf;
275 //fprintf(stderr, "SQLITE ERROR: %s\n", errmsg);
276 //sqlite3_free(errmsg);
277 sq3check(dbi
.db, rc
);
281 // create prepared SQL statement
282 DBStatement
statement (const(char)[] stmtstr
, bool persistent
=false) {
283 if (!isOpen
) throw new SQLiteException("database is not opened");
284 return DBStatement(dbi
, stmtstr
, persistent
);
287 DBStatement
persistentStatement (const(char)[] stmtstr
) {
288 return statement(stmtstr
, persistent
:true);
291 sqlite3
* getHandle () nothrow @nogc @trusted { return (isOpen ? dbi
.db : null); }
294 alias UserFn
= void function (sqlite3_context
*ctx
, int argc
, sqlite3_value
**argv
);
297 void createFunction (const(char)[] name
, int argc
, UserFn xFunc
, bool deterministic
=true, int moreflags
=0) {
298 import std
.internal
.cstring
: tempCString
;
299 if (!isOpen
) throw new SQLiteException("database is not opened");
300 immutable int rc
= sqlite3_create_function(dbi
.db, name
.tempCString
, argc
, SQLITE_UTF8|
(deterministic ? SQLITE_DETERMINISTIC
: 0)|moreflags
, null, xFunc
, null, null);
301 sq3check(dbi
.db, rc
);
306 // ////////////////////////////////////////////////////////////////////////// //
307 public static bool isUTF8ValidSQ3 (const(char)[] str) pure nothrow @trusted @nogc {
308 usize len
= str.length
;
309 immutable(ubyte)* p
= cast(immutable(ubyte)*)str.ptr
;
311 immutable ubyte b
= *p
++;
312 if (b
== 0) return false;
313 if (b
< 128) continue;
315 (b
&0xe0) == 0xc0 ?
1 :
316 (b
&0xf0) == 0xe0 ?
2 :
317 (b
&0xf8) == 0xe8 ?
3 :
319 if (!blen
) return false;
320 if (len
< blen
) return false;
323 immutable ubyte b1
= *p
++;
324 if ((b1
&0xc0) != 0x80) return false;
331 // ////////////////////////////////////////////////////////////////////////// //
332 struct DBFieldIndex
{
347 string
toString () const pure nothrow @trusted @nogc {
349 case Unknown
: return "Unknown";
350 case Integer
: return "Integer";
351 case Float
: return "Float";
352 case Text
: return "Text";
353 case Blob
: return "Blob";
354 case Null
: return "Null";
363 enum FieldIndexMixin
= `
364 if (!valid) throw new SQLiteException("cannot bind in invalid statement");
365 if (data.stepIndex != 0) throw new SQLiteException("can't bind on busy statement");
366 char[257] fldname = 0;
367 if (name.length > 255) throw new SQLiteException("field name too long");
368 if (name[0] == ':' || name[0] == '?' || name[0] == '@') {
369 fldname[0..name.length] = name[];
372 fldname[1..name.length+1] = name[];
374 immutable idx = sqlite3_bind_parameter_index(data.st, fldname.ptr);
375 if (idx < 1) throw new SQLiteException("invalid field name: '"~name.idup~"'");
379 this (this) nothrow @trusted @nogc { this.incref(data
); }
380 ~this () nothrow @trusted { this.decref(data
); data
= null; }
382 private this (Database
.DBInfo
*dbi
, const(char)[] stmtstr
, bool persistent
=false) {
383 if (dbi
is null || dbi
.db is null) throw new SQLiteException("database is not opened");
384 if (stmtstr
.length
> int.max
/4) throw new SQLiteException("statement too big");
385 import core
.stdc
.stdlib
: malloc
;
386 data
= cast(Data
*)malloc(Data
.sizeof
);
388 import core
.exception
: onOutOfMemoryErrorNoGC
;
389 onOutOfMemoryErrorNoGC();
393 // register in the owner list
394 version(sq3_debug_stmtlist
) { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "new stmt(%p): p=%p\n", dbi
, data
); }
396 data
.prev
= dbi
.stmttail
;
397 if (dbi
.stmttail
!is null) dbi
.stmttail
.next
= data
; else dbi
.stmthead
= data
;
400 scope(failure
) { data
.st
= null; DBStatement
.decref(data
); }
403 sq3check(dbi
.db, sqlite3_prepare_v3(dbi
.db, stmtstr
.ptr
, cast(int)stmtstr
.length
, SQLITE_PREPARE_PERSISTENT
, &data
.st
, &e
));
405 sq3check(dbi
.db, sqlite3_prepare_v2(dbi
.db, stmtstr
.ptr
, cast(int)stmtstr
.length
, &data
.st
, &e
));
407 // check for extra code
409 usize left
= stmtstr
.length
-cast(usize
)(e
-stmtstr
.ptr
);
410 //{ import core.stdc.stdio : printf; printf("%u: <%.*s>\n", cast(uint)left, cast(uint)left, e); }
413 //{ import core.stdc.stdio : printf; printf("left=%u: ch=%u\n", cast(uint)left, cast(uint)ch); }
414 if (ch
<= 32 || ch
== ';') continue;
416 if (left
< 1 ||
*e
!= '-') throw new SQLiteErr("extra code in SQL statement (--)");
417 while (left
&& *e
!= '\n') { --left
; ++e
; }
421 if (left
< 1 ||
*e
!= '*') throw new SQLiteErr("extra code in SQL statement (mlc)");
425 if (*e
== '*' && e
[1] == '/') {
434 throw new SQLiteErr("extra code in SQL statement (text): "~e
[0..left
].idup
);
439 @property bool valid () nothrow @trusted @nogc { return (data
!is null && data
.st
!is null); }
441 void close () nothrow @trusted { if (data
!is null) this.decref(data
); data
= null; }
443 @property auto range () {
444 if (!valid
) throw new SQLiteException("cannot get range from invalid statement");
445 //if (st is null) throw new SQLiteException("statement is not prepared");
446 if (data
.stepIndex
!= 0) throw new SQLiteException("can't get range from busy statement");
447 return DBRowRange(this);
450 void reset () nothrow @trusted {
451 //if (data.stepIndex != 0) throw new SQLiteException("can't reset busy statement");
454 sqlite3_reset(data
.st
);
455 sqlite3_clear_bindings(data
.st
);
460 if (!valid
) throw new SQLiteException("cannot execute invalid statement");
461 if (data
.stepIndex
!= 0) throw new SQLiteException("can't doAll on busy statement");
464 auto rc
= sqlite3_step(data
.st
);
465 if (rc
== SQLITE_DONE
) break;
466 if (rc
!= SQLITE_ROW
) sq3check(data
.owner
.db, rc
);
470 ref DBStatement
bind(T
) (uint idx
, T value
) if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T
) {
471 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
472 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
473 if (idx
< 1 || idx
> sqlite3_bind_parameter_count(data
.st
)) {
474 import std
.conv
: to
;
475 throw new SQLiteException("invalid field index: "~to
!string(idx
));
478 static if (is(T
== typeof(null))) {
479 rc
= sqlite3_bind_null(data
.st
, idx
);
480 } else static if (isNarrowString
!T
) {
481 if (value
.length
> cast(usize
)int.max
) throw new SQLiteException("value too big");
482 static if (is(ElementEncodingType
!T
== immutable(char))) {
484 if (isUTF8ValidSQ3(value
[])) {
485 rc
= sqlite3_bind_text(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, /*SQLITE_STATIC*/SQLITE_TRANSIENT
);
487 rc
= sqlite3_bind_blob(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, /*SQLITE_STATIC*/SQLITE_TRANSIENT
);
490 rc
= sqlite3_bind_text(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, /*SQLITE_STATIC*/SQLITE_TRANSIENT
);
494 if (isUTF8ValidSQ3(value
[])) {
495 rc
= sqlite3_bind_text(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, SQLITE_TRANSIENT
);
497 rc
= sqlite3_bind_blob(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, SQLITE_TRANSIENT
);
500 rc
= sqlite3_bind_text(data
.st
, idx
, value
.ptr
, cast(int)value
.length
, SQLITE_TRANSIENT
);
503 } else static if (isIntegral
!T
) {
504 static if (isSigned
!T
) {
506 static if (T
.sizeof
<= 4) {
507 rc
= sqlite3_bind_int(data
.st
, idx
, cast(int)value
);
509 rc
= sqlite3_bind_int64(data
.st
, idx
, cast(long)value
);
513 static if (T
.sizeof
< 4) {
514 rc
= sqlite3_bind_int(data
.st
, idx
, cast(int)cast(uint)value
);
516 rc
= sqlite3_bind_int64(data
.st
, idx
, cast(ulong)value
);
520 static assert(0, "WTF?!");
522 sq3check(data
.owner
.db, rc
);
526 ref DBStatement
bind(T
) (const(char)[] name
, T value
) if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T
) {
527 mixin(FieldIndexMixin
);
528 return bind
!T(idx
, value
);
532 ref DBStatement
bindText (uint idx
, const(void)[] text
, bool transient
=true, bool allowNull
=false) {
533 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
534 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
536 import std
.conv
: to
;
537 throw new SQLiteException("invalid field index: "~to
!string(idx
));
542 rc
= sqlite3_bind_null(data
.st
, idx
);
544 rc
= sqlite3_bind_text(data
.st
, idx
, "".ptr
, 0, SQLITE_STATIC
);
547 rc
= sqlite3_bind_text(data
.st
, idx
, cast(const(char)*)text
.ptr
, cast(int)text
.length
, (transient ? SQLITE_TRANSIENT
: SQLITE_STATIC
));
549 sq3check(data
.owner
.db, rc
);
553 ref DBStatement
bindText (const(char)[] name
, const(void)[] text
, bool transient
=true, bool allowNull
=false) {
554 mixin(FieldIndexMixin
);
555 return bindText(cast(uint)idx
, text
, transient
, allowNull
);
559 ref DBStatement
bindBlob (uint idx
, const(void)[] blob
, bool transient
=true, bool allowNull
=false) {
560 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
561 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
563 import std
.conv
: to
;
564 throw new SQLiteException("invalid field index: "~to
!string(idx
));
569 rc
= sqlite3_bind_null(data
.st
, idx
);
571 rc
= sqlite3_bind_blob(data
.st
, idx
, "".ptr
, 0, SQLITE_STATIC
);
574 rc
= sqlite3_bind_blob(data
.st
, idx
, blob
.ptr
, cast(int)blob
.length
, (transient ? SQLITE_TRANSIENT
: SQLITE_STATIC
));
576 sq3check(data
.owner
.db, rc
);
580 ref DBStatement
bindBlob (const(char)[] name
, const(void)[] blob
, bool transient
=true, bool allowNull
=false) {
581 mixin(FieldIndexMixin
);
582 return bindBlob(cast(uint)idx
, blob
, transient
, allowNull
);
586 ref DBStatement
bindNull (uint idx
) {
587 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
588 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
590 import std
.conv
: to
;
591 throw new SQLiteException("invalid field index: "~to
!string(idx
));
593 immutable int rc
= sqlite3_bind_null(data
.st
, idx
);
594 sq3check(data
.owner
.db, rc
);
598 ref DBStatement
bindNull (const(char)[] name
) {
599 mixin(FieldIndexMixin
);
600 return bindNull(cast(uint)idx
);
604 ref DBStatement
bindInt(T
) (uint idx
, T v
) if (isIntegral
!T
) {
605 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
606 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
608 import std
.conv
: to
;
609 throw new SQLiteException("invalid field index: "~to
!string(idx
));
611 static if (isSigned
!T
) {
612 immutable int rc
= sqlite3_bind_int64(data
.st
, idx
, cast(long)v
);
614 immutable int rc
= sqlite3_bind_int64(data
.st
, idx
, cast(ulong)v
);
616 sq3check(data
.owner
.db, rc
);
620 ref DBStatement
bindInt(T
) (const(char)[] name
, T v
) if (isIntegral
!T
) {
621 mixin(FieldIndexMixin
);
622 return bindInt(cast(uint)idx
, v
);
626 ref DBStatement
bindFloat(T
) (uint idx
, T v
) if (is(T
== float) ||
is(T
== double)) {
627 if (!valid
) throw new SQLiteException("cannot bind in invalid statement");
628 if (data
.stepIndex
!= 0) throw new SQLiteException("can't bind on busy statement");
630 import std
.conv
: to
;
631 throw new SQLiteException("invalid field index: "~to
!string(idx
));
633 rc
= sqlite3_bind_double(data
.st
, idx
, cast(double)v
);
634 sq3check(data
.owner
.db, rc
);
638 ref DBStatement
bindFloat(T
) (const(char)[] name
, T v
) if (is(T
== float) ||
is(T
== double)) {
639 mixin(FieldIndexMixin
);
640 return bindFloat(cast(uint)idx
, v
);
643 int colcount () nothrow @trusted {
644 if (!valid
) return 0;
645 return sqlite3_column_count(data
.st
);
648 const(char)[] colname (int idx
) nothrow @trusted {
649 if (!valid || idx
< 0) return null;
650 if (idx
>= sqlite3_column_count(data
.st
)) return null;
651 const(char)* cname
= sqlite3_column_name(data
.st
, idx
);
652 if (cname
is null) return null;
654 while (cname
[ep
]) ++ep
;
660 private this (DBStatement
.Data
* adata
) nothrow @trusted @nogc {
662 DBStatement
.incref(data____
);
666 this (this) nothrow @trusted @nogc { DBStatement
.incref(data____
); ++data____
.rowcount
; }
668 ~this () nothrow @trusted {
669 DBStatement
.decrowref(data____
);
670 DBStatement
.decref(data____
);
673 bool valid_ () pure nothrow @trusted @nogc {
674 pragma(inline
, true);
675 return (data____
.stepIndex
> 0 && data____
.st
!is null);
678 int colcount_ () nothrow @trusted {
679 if (data____
.st
is null) return 0;
680 return sqlite3_column_count(data____
.st
);
683 const(char)[] colname_ (int idx
) nothrow @trusted {
684 if (idx
< 0 || data____
.st
is null) return null;
685 if (idx
>= sqlite3_column_count(data____
.st
)) return null;
686 const(char)* cname
= sqlite3_column_name(data____
.st
, idx
);
687 if (cname
is null) return null;
689 while (cname
[ep
]) ++ep
;
693 int fieldIndex____ (const(char)[] name
) {
694 if (name
.length
> 0) {
695 foreach (immutable int idx
; 0..sqlite3_data_count(data____
.st
)) {
696 import core
.stdc
.string
: memcmp
, strlen
;
697 auto n
= sqlite3_column_name(data____
.st
, idx
);
699 auto len
= strlen(n
);
700 if (len
== name
.length
&& memcmp(n
, name
.ptr
, len
) == 0) return idx
;
704 throw new SQLiteException("invalid field name: '"~name
.idup
~"'");
708 if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T ||
is(T
: DBFieldIndex
) ||
is(T
: DBFieldType
) ||
709 is(T
== float) ||
is(T
== double))
711 if (!valid_
) throw new SQLiteException("can't get row field of completed statement");
712 if (idx
>= sqlite3_data_count(data____
.st
)) throw new SQLiteException("invalid result index");
713 static if (is(T
: DBFieldIndex
)) {
714 return DBFieldIndex(idx
);
715 } else static if (is(T
: DBFieldType
)) {
716 switch (sqlite3_column_type(data____
.st
, idx
)) {
717 case SQLITE_INTEGER
: return DBFieldType(DBFieldType
.Integer
);
718 case SQLITE_FLOAT
: return DBFieldType(DBFieldType
.Float
);
719 case SQLITE3_TEXT
: return DBFieldType(DBFieldType
.Text
);
720 case SQLITE_BLOB
: return DBFieldType(DBFieldType
.Blob
);
721 case SQLITE_NULL
: return DBFieldType(DBFieldType
.Null
);
724 return DBFieldType(DBFieldType
.Unknown
);
725 } else static if (isIntegral
!T
) {
726 auto res
= sqlite3_column_int64(data____
.st
, idx
);
727 if (res
< T
.min || res
> T
.max
) throw new SQLiteException("integral overflow");
729 } else static if (is(T
== double)) {
730 auto res
= sqlite3_column_double(data____
.st
, idx
);
731 return cast(double)res
;
732 } else static if (is(T
== float)) {
733 auto res
= sqlite3_column_double(data____
.st
, idx
);
734 return cast(float)res
;
736 auto len
= sqlite3_column_bytes(data____
.st
, idx
);
737 if (len
< 0) throw new SQLiteException("invalid result");
738 const(char)* res
= cast(const(char)*)sqlite3_column_blob(data____
.st
, idx
);
739 if (len
== 0) res
= ""; else if (res
is null) throw new SQLiteException("invalid result");
740 static if (is(ElementEncodingType
!T
== const(char))) {
742 } else static if (is(ElementEncodingType
!T
== immutable(char))) {
743 return res
[0..len
].idup
;
745 return res
[0..len
].dup
;
749 T
to(T
) (const(char)[] name
) { return this.to
!T(fieldIndex____(name
)); }
751 const(ubyte)[] blob_ (const(char)[] name
) {
752 immutable int idx
= fieldIndex____(name
);
753 auto len
= sqlite3_column_bytes(data____
.st
, idx
);
754 if (len
< 0) throw new SQLiteException("invalid result");
755 const(ubyte)* res
= cast(const(ubyte)*)sqlite3_column_blob(data____
.st
, idx
);
760 T
opIndexImpl(T
) (uint idx
) if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T ||
is(T
: DBFieldIndex
) ||
is(T
: DBFieldType
)) { return this.to
!T(idx
); }
761 T
opIndexImpl(T
) (const(char)[] name
) if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T ||
is(T
: DBFieldIndex
) ||
is(T
: DBFieldType
)) { return this.to
!T(name
); }
762 alias opIndex
= opIndexImpl
;
765 template opDispatch(string name
) {
766 T
opDispatchImpl(T
=const(char)[]) () if ((isNarrowString
!T
&& is(ElementEncodingType
!T
: char)) || isIntegral
!T ||
is(T
: DBFieldIndex
) ||
is(T
: DBFieldType
)) { return this.to
!T(name
); }
767 alias opDispatch
= opDispatchImpl
;
770 auto index_ () pure const nothrow @nogc { return (data____
.stepIndex
> 0 ? data____
.stepIndex
-1 : 0); }
772 private DBStatement
.Data
* data____
;
777 private this (ref DBStatement astat
) {
779 DBStatement
.incref(data
);
781 assert(data
.stepIndex
== 0);
786 this (this) nothrow @trusted @nogc { DBStatement
.incref(data
); ++data
.rowcount
; }
788 ~this () nothrow @trusted {
789 DBStatement
.decrowref(data
);
790 DBStatement
.decref(data
);
793 @property bool empty () const pure nothrow @nogc { return (data
.stepIndex
== 0); }
795 @property auto front () {
796 if (data
.stepIndex
== 0) throw new SQLiteException("can't get front element of completed statement");
801 if (data
.stepIndex
== 0) throw new SQLiteException("can't pop element of completed statement");
802 auto rc
= sqlite3_step(data
.st
);
803 if (rc
== SQLITE_DONE
) {
807 if (rc
!= SQLITE_ROW
) {
809 sq3check(data
.owner
.db, rc
);
814 auto index_ () pure const nothrow @nogc { return (data
.stepIndex
> 0 ? data
.stepIndex
-1 : 0); }
816 private DBStatement
.Data
* data
;
817 } // end of DBRowRange
820 static void incref (Data
* data
) nothrow @nogc @trusted {
821 if (data
!is null) ++data
.refcount
;
824 static void decref (Data
* data
) nothrow @trusted {
826 if (--data
.refcount
== 0) {
827 import core
.stdc
.stdlib
: free
;
828 if (data
.st
!is null) {
829 sqlite3_reset(data
.st
);
830 sqlite3_clear_bindings(data
.st
);
831 sqlite3_finalize(data
.st
);
833 // unregister from the owner list
835 version(sq3_debug_stmtlist
) { import core
.stdc
.stdio
: stderr
, fprintf
; fprintf(stderr
, "removing stmt(%p): p=%p\n", data
.owner
, data
); }
836 if (data
.prev
) data
.prev
.next
= data
.next
; else data
.owner
.stmthead
= data
.next
;
837 if (data
.next
) data
.next
.prev
= data
.prev
; else data
.owner
.stmttail
= data
.prev
;
846 static void decrowref (Data
* data
) nothrow @trusted {
848 if (--data
.rowcount
== 0) {
850 if (data
.st
!is null) {
851 sqlite3_reset(data
.st
);
852 sqlite3_clear_bindings(data
.st
);
861 uint rowcount
= 0; // number of row structs using this statement
863 sqlite3_stmt
* st
= null;
866 Database
.DBInfo
* owner
= null;