1 /**
2  * DDBC - D DataBase Connector - abstraction layer for RDBMS access, with interface similar to JDBC. 
3  * 
4  * Source file ddbc/drivers/pgsqlddbc.d.
5  *
6  * DDBC library attempts to provide implementation independent interface to different databases.
7  * 
8  * Set of supported RDBMSs can be extended by writing Drivers for particular DBs.
9  * 
10  * JDBC documentation can be found here:
11  * $(LINK http://docs.oracle.com/javase/1.5.0/docs/api/java/sql/package-summary.html)$(BR)
12  *
13  * This module contains implementation of SQLite Driver
14  * 
15  * You can find usage examples in unittest{} sections.
16  *
17  * Copyright: Copyright 2013
18  * License:   $(LINK www.boost.org/LICENSE_1_0.txt, Boost License 1.0).
19  * Author:   Vadim Lopatin
20  */
21 module ddbc.drivers.sqliteddbc;
22 
23 
24 version(USE_SQLITE) {
25 
26     import std.algorithm;
27     import std.conv;
28     import std.datetime;
29     import std.exception;
30     import std.stdio;
31     import std.string;
32     import std.variant;
33     import core.sync.mutex;
34     import ddbc.common;
35     import ddbc.core;
36     //import ddbc.drivers.sqlite;
37     import ddbc.drivers.utils;
38     import etc.c.sqlite3;
39     import std.traits : isSomeString;
40 
41 
42     version (Windows) {
43         pragma (lib, "sqlite3");
44     } else version (linux) {
45         pragma (lib, "sqlite3");
46     } else version (OSX) {
47         pragma (lib, "sqlite3");
48     } else version (Posix) {
49         pragma (lib, "libsqlite3");
50     } else {
51         pragma (msg, "You will need to manually link in the SQLite library.");
52     } 
53 
54     version(unittest) {
55         /*
56             To allow unit tests using PostgreSQL server,
57          */
58         /// change to false to disable tests on real PostgreSQL server
59         immutable bool SQLITE_TESTS_ENABLED = true;
60         /// change parameters if necessary
61         const string SQLITE_UNITTEST_FILENAME = "ddbctest.sqlite";
62 
63         static if (SQLITE_TESTS_ENABLED) {
64             /// use this data source for tests
65             DataSource createUnitTestSQLITEDataSource() {
66                 return createConnectionPool("sqlite:" ~ SQLITE_UNITTEST_FILENAME);
67             }
68         }
69     }
70 
71     /// Converts from a selection of the standard SQLite time formats into a DateTime object.
72     private DateTime fromResultSet(S)(in S sqliteString) @safe pure
73         if (isSomeString!S) {
74 
75         try {
76             switch (sqliteString.length) {
77                 case 5:
78                     if (sqliteString[2] == ':') {
79                         // HH:MM
80                         int hours = cast(int) to!uint(sqliteString[0..2]);
81                         int minutes = cast(int) to!uint(sqliteString[3..5]);
82                         return DateTime(0, 1, 1, hours, minutes);
83                     }
84                     break;
85                 case 8:
86                     if (sqliteString[2] == ':' && sqliteString[5] == ':') {
87                         // HH:MM:SS
88                         auto time = TimeOfDay.fromISOExtString(sqliteString);
89                         return DateTime(Date(), time);
90                     }
91                     break;
92                 case 10:
93                     if (sqliteString[4] == '-' && sqliteString[7] == '-') {
94                         // YYYY-MM-DD
95                         auto date = Date.fromISOExtString(sqliteString);
96                         return DateTime(date, TimeOfDay());
97                     }
98                     break;
99                 case 12:
100                     if (sqliteString[2] == ':' && sqliteString[5] == ':') {
101                         // HH:MM:SS.SSS
102                         auto time = TimeOfDay.fromISOExtString(sqliteString[0..8]);
103                         int hours = cast(int) to!uint(sqliteString[0 .. 2]);
104                         return DateTime(Date(), time);
105                     }
106                     break;
107                 case 16:
108                      // YYYY-MM-DD HH:MM
109                      // YYYY-MM-DDTHH:MM
110 
111                     auto date = Date.fromISOExtString(sqliteString[0..10]);
112 
113                     int hours = cast(int) to!uint(sqliteString[11 .. 13]);
114                     int minutes = cast(int) to!uint(sqliteString[14 .. 16]);
115                     auto time = TimeOfDay(hours, minutes);
116                     return DateTime(date, time);
117                 case 19:
118                 case 23:
119                      // YYYY-MM-DD HH:MM:SS
120                      // YYYY-MM-DD HH:MM:SS.SSS
121                      // YYYY-MM-DDTHH:MM:SS
122                      // YYYY-MM-DDTHH:MM:SS.SSS
123 
124                     auto date = Date.fromISOExtString(sqliteString[0..10]);
125                     auto time = TimeOfDay.fromISOExtString(sqliteString[11..19]);
126                     return DateTime(date, time);
127                 default:
128                     // Fall through to the throw statement below
129                     break;
130             }
131         } catch (ConvException) {
132             // Let the exception fall to the throw statement below
133         }
134         throw new DateTimeException(format("Unknown SQLite date string: %s", sqliteString));
135     }
136 
137     class SQLITEConnection : ddbc.core.Connection {
138     private:
139         string filename;
140 
141         sqlite3 * conn;
142 
143         bool closed;
144         bool autocommit;
145         Mutex mutex;
146         
147         
148         SQLITEStatement [] activeStatements;
149         
150         void closeUnclosedStatements() {
151             SQLITEStatement [] list = activeStatements.dup;
152             foreach(stmt; list) {
153                 stmt.close();
154             }
155         }
156         
157         void checkClosed() {
158             if (closed)
159                 throw new SQLException("Connection is already closed");
160         }
161         
162     public:
163 
164         private string getError() {
165             return copyCString(sqlite3_errmsg(conn));
166         }
167 
168         void lock() {
169             mutex.lock();
170         }
171         
172         void unlock() {
173             mutex.unlock();
174         }
175         
176         sqlite3 * getConnection() { return conn; }
177         
178         
179         void onStatementClosed(SQLITEStatement stmt) {
180             myRemove(activeStatements, stmt);
181         }
182         
183         this(string url, string[string] params) {
184             mutex = new Mutex();
185             extractParamsFromURL(url, params);
186             if (url.startsWith("sqlite:"))
187                 url = url[7 .. $];
188             this.filename = url;
189             //writeln("trying to connect");
190             int res = sqlite3_open(toStringz(filename), &conn);
191             if(res != SQLITE_OK)
192                 throw new SQLException("SQLITE Error " ~ to!string(res) ~ " while trying to open DB " ~ filename ~ " : " ~ getError());
193             assert(conn !is null);
194             closed = false;
195             setAutoCommit(true);
196         }
197         
198         override void close() {
199             checkClosed();
200             
201             lock();
202             scope(exit) unlock();
203             
204             closeUnclosedStatements();
205             int res = sqlite3_close(conn);
206             if (res != SQLITE_OK)
207                 throw new SQLException("SQLITE Error " ~ to!string(res) ~ " while trying to close DB " ~ filename ~ " : " ~ getError());
208             closed = true;
209         }
210         
211         override void commit() {
212             checkClosed();
213             
214             lock();
215             scope(exit) unlock();
216             
217             Statement stmt = createStatement();
218             scope(exit) stmt.close();
219             stmt.executeUpdate("COMMIT");
220         }
221         
222         override Statement createStatement() {
223             checkClosed();
224             
225             lock();
226             scope(exit) unlock();
227             
228             SQLITEStatement stmt = new SQLITEStatement(this);
229             activeStatements ~= stmt;
230             return stmt;
231         }
232         
233         PreparedStatement prepareStatement(string sql) {
234             checkClosed();
235             
236             lock();
237             scope(exit) unlock();
238             
239             SQLITEPreparedStatement stmt = new SQLITEPreparedStatement(this, sql);
240             activeStatements ~= cast(SQLITEStatement)stmt;
241             return stmt;
242         }
243         
244         override string getCatalog() {
245             return "default";
246         }
247         
248         /// Sets the given catalog name in order to select a subspace of this Connection object's database in which to work.
249         override void setCatalog(string catalog) {
250             checkClosed();
251             throw new SQLException("Not implemented");
252         }
253         
254         override bool isClosed() {
255             return closed;
256         }
257         
258         override void rollback() {
259             checkClosed();
260             
261             lock();
262             scope(exit) unlock();
263             
264             Statement stmt = createStatement();
265             scope(exit) stmt.close();
266             //TODO:
267             //stmt.executeUpdate("ROLLBACK");
268         }
269         override bool getAutoCommit() {
270             return autocommit;
271         }
272         override void setAutoCommit(bool autoCommit) {
273             checkClosed();
274             if (this.autocommit == autoCommit)
275                 return;
276             lock();
277             scope(exit) unlock();
278             
279             Statement stmt = createStatement();
280             scope(exit) stmt.close();
281             //TODO:
282             //stmt.executeUpdate("SET autocommit = " ~ (autoCommit ? "ON" : "OFF"));
283             this.autocommit = autoCommit;
284         }
285     }
286 
287     class SQLITEStatement : Statement {
288     private:
289         SQLITEConnection conn;
290         //  Command * cmd;
291         //  ddbc.drivers.mysql.ResultSet rs;
292         SQLITEResultSet resultSet;
293         
294         bool closed;
295         
296     public:
297         void checkClosed() {
298             enforce!SQLException(!closed, "Statement is already closed");
299         }
300         
301         void lock() {
302             conn.lock();
303         }
304         
305         void unlock() {
306             conn.unlock();
307         }
308         
309         this(SQLITEConnection conn) {
310             this.conn = conn;
311         }
312         
313     public:
314         SQLITEConnection getConnection() {
315             checkClosed();
316             return conn;
317         }
318 
319         private PreparedStatement _currentStatement;
320         private ResultSet _currentResultSet;
321 
322         private void closePreparedStatement() {
323             if (_currentResultSet !is null) {
324                 _currentResultSet.close();
325                 _currentResultSet = null;
326             }
327             if (_currentStatement !is null) {
328                 _currentStatement.close();
329                 _currentStatement = null;
330             }
331         }
332 
333         override ddbc.core.ResultSet executeQuery(string query) {
334             closePreparedStatement();
335             _currentStatement = conn.prepareStatement(query);
336             _currentResultSet = _currentStatement.executeQuery();
337             return _currentResultSet;
338         }
339         
340     //    string getError() {
341     //        return copyCString(PQerrorMessage(conn.getConnection()));
342     //    }
343         
344         override int executeUpdate(string query) {
345             Variant dummy;
346             return executeUpdate(query, dummy);
347         }
348         
349         override int executeUpdate(string query, out Variant insertId) {
350             closePreparedStatement();
351             _currentStatement = conn.prepareStatement(query);
352             return _currentStatement.executeUpdate(insertId);
353         }
354         
355         override void close() {
356             checkClosed();
357             lock();
358             scope(exit) unlock();
359             closePreparedStatement();
360             closed = true;
361             conn.onStatementClosed(this);
362         }
363         
364         void closeResultSet() {
365         }
366     }
367 
368     class SQLITEPreparedStatement : SQLITEStatement, PreparedStatement {
369         string query;
370         int paramCount;
371 
372         sqlite3_stmt * stmt;
373 
374         bool done;
375         bool preparing;
376 
377         ResultSetMetaData metadata;
378         ParameterMetaData paramMetadata;
379         this(SQLITEConnection conn, string query) {
380             super(conn);
381             this.query = query;
382 
383             int res = sqlite3_prepare_v2(
384                 conn.getConnection(),            /* Database handle */
385                 toStringz(query),       /* SQL statement, UTF-8 encoded */
386                 cast(int)query.length,              /* Maximum length of zSql in bytes. */
387                 &stmt,  /* OUT: Statement handle */
388                 null     /* OUT: Pointer to unused portion of zSql */
389                 );
390             enforce!SQLException(res == SQLITE_OK, "Error #" ~ to!string(res) ~ " while preparing statement " ~ query ~ " : " ~ conn.getError());
391             paramMetadata = createParamMetadata();
392             paramCount = paramMetadata.getParameterCount();
393             metadata = createMetadata();
394             resetParams();
395             preparing = true;
396         }
397         bool[] paramIsSet;
398         void resetParams() {
399             paramIsSet = new bool[paramCount];
400         }
401         // before execution of query
402         private void allParamsSet() {
403             for(int i = 0; i < paramCount; i++) {
404                 enforce!SQLException(paramIsSet[i], "Parameter " ~ to!string(i + 1) ~ " is not set");
405             }
406             if (preparing) {
407                 preparing = false;
408             } else {
409                 closeResultSet();
410                 sqlite3_reset(stmt);
411             }
412         }
413         // before setting any parameter
414         private void checkIndex(int index) {
415             if (index < 1 || index > paramCount)
416                 throw new SQLException("Parameter index " ~ to!string(index) ~ " is out of range");
417             if (!preparing) {
418                 closeResultSet();
419                 sqlite3_reset(stmt);
420                 preparing = true;
421             }
422         }
423         ref Variant getParam(int index) {
424             throw new SQLException("Not implemented");
425             //      checkIndex(index);
426             //      return cmd.param(cast(ushort)(index - 1));
427         }
428     public:
429         SqlType sqliteToSqlType(int t) {
430             switch(t) {
431                 case SQLITE_INTEGER: return SqlType.BIGINT;
432                 case SQLITE_FLOAT: return SqlType.DOUBLE;
433                 case SQLITE3_TEXT: return SqlType.VARCHAR;
434                 case SQLITE_BLOB: return SqlType.BLOB;
435                 case SQLITE_NULL: return SqlType.NULL;
436                 default:
437                     return SqlType.BLOB;
438             }
439         }
440 
441         ResultSetMetaData createMetadata() {
442             int fieldCount = sqlite3_column_count(stmt);
443             ColumnMetadataItem[] list = new ColumnMetadataItem[fieldCount];
444             for(int i = 0; i < fieldCount; i++) {
445                 ColumnMetadataItem item = new ColumnMetadataItem();
446                 item.label = copyCString(sqlite3_column_origin_name(stmt, i));
447                 item.name = copyCString(sqlite3_column_name(stmt, i));
448                 item.schemaName = copyCString(sqlite3_column_database_name(stmt, i));
449                 item.tableName = copyCString(sqlite3_column_table_name(stmt, i));
450                 item.type = sqliteToSqlType(sqlite3_column_type(stmt, i));
451                 list[i] = item;
452             }
453             return new ResultSetMetaDataImpl(list);
454         }
455 
456         ParameterMetaData createParamMetadata() {
457             int fieldCount = sqlite3_bind_parameter_count(stmt);
458             ParameterMetaDataItem[] res = new ParameterMetaDataItem[fieldCount];
459             for(int i = 0; i < fieldCount; i++) {
460                 ParameterMetaDataItem item = new ParameterMetaDataItem();
461                 item.type = SqlType.VARCHAR;
462                 res[i] = item;
463             }
464             paramCount = fieldCount;
465             return new ParameterMetaDataImpl(res);
466         }
467 
468         override void close() {
469             if (closed)
470                 return;
471             checkClosed();
472             lock();
473             scope(exit) unlock();
474 
475             closeResultSet();
476             int res = sqlite3_finalize(stmt);
477             enforce!SQLException(res == SQLITE_OK, "Error #" ~ to!string(res) ~ " while closing prepared statement " ~ query ~ " : " ~ conn.getError());
478             closed = true;
479             conn.onStatementClosed(this);
480         }
481 
482         
483         /// Retrieves a ResultSetMetaData object that contains information about the columns of the ResultSet object that will be returned when this PreparedStatement object is executed.
484         override ResultSetMetaData getMetaData() {
485             checkClosed();
486             lock();
487             scope(exit) unlock();
488             return metadata;
489         }
490         
491         /// Retrieves the number, types and properties of this PreparedStatement object's parameters.
492         override ParameterMetaData getParameterMetaData() {
493             checkClosed();
494             lock();
495             scope(exit) unlock();
496             return paramMetadata;
497         }
498 
499         override int executeUpdate(out Variant insertId) {
500             //throw new SQLException("Not implemented");
501             checkClosed();
502             lock();
503             scope(exit) unlock();
504             allParamsSet();
505 
506             int rowsAffected = 0;
507             int res = sqlite3_step(stmt);
508             if (res == SQLITE_DONE) {
509                 insertId = Variant(sqlite3_last_insert_rowid(conn.getConnection()));
510                 rowsAffected = sqlite3_changes(conn.getConnection());
511                 done = true;
512             } else if (res == SQLITE_ROW) {
513                 // row is available
514                 rowsAffected = -1;
515             } else {
516                 enforce!SQLException(false, "Error #" ~ to!string(res) ~ " while trying to execute prepared statement: "  ~ " : " ~ conn.getError());
517             }
518             return rowsAffected;
519         }
520         
521         override int executeUpdate() {
522             Variant insertId;
523             return executeUpdate(insertId);
524         }
525         
526         override ddbc.core.ResultSet executeQuery() {
527             checkClosed();
528             lock();
529             scope(exit) unlock();
530             allParamsSet();
531             enforce!SQLException(metadata.getColumnCount() > 0, "Query doesn't return result set");
532             resultSet = new SQLITEResultSet(this, stmt, getMetaData());
533             return resultSet;
534         }
535         
536         override void clearParameters() {
537             throw new SQLException("Not implemented");
538             //      checkClosed();
539             //      lock();
540             //      scope(exit) unlock();
541             //      for (int i = 1; i <= paramCount; i++)
542             //          setNull(i);
543         }
544         
545         override void setFloat(int parameterIndex, float x) {
546             setDouble(parameterIndex, x);
547         }
548         override void setDouble(int parameterIndex, double x){
549             checkClosed();
550             lock();
551             scope(exit) unlock();
552             checkIndex(parameterIndex);
553             sqlite3_bind_double(stmt, parameterIndex, x);
554             paramIsSet[parameterIndex - 1] = true;
555         }
556         override void setBoolean(int parameterIndex, bool x) {
557             setLong(parameterIndex, x ? 1 : 0);
558         }
559         override void setLong(int parameterIndex, long x) {
560             checkClosed();
561             lock();
562             scope(exit) unlock();
563             checkIndex(parameterIndex);
564             sqlite3_bind_int64(stmt, parameterIndex, x);
565             paramIsSet[parameterIndex - 1] = true;
566         }
567         override void setUlong(int parameterIndex, ulong x) {
568             setLong(parameterIndex, cast(long)x);
569         }
570         override void setInt(int parameterIndex, int x) {
571             setLong(parameterIndex, cast(long)x);
572         }
573         override void setUint(int parameterIndex, uint x) {
574             setLong(parameterIndex, cast(long)x);
575         }
576         override void setShort(int parameterIndex, short x) {
577             setLong(parameterIndex, cast(long)x);
578         }
579         override void setUshort(int parameterIndex, ushort x) {
580             setLong(parameterIndex, cast(long)x);
581         }
582         override void setByte(int parameterIndex, byte x) {
583             setLong(parameterIndex, cast(long)x);
584         }
585         override void setUbyte(int parameterIndex, ubyte x) {
586             setLong(parameterIndex, cast(long)x);
587         }
588         override void setBytes(int parameterIndex, byte[] x) {
589             checkClosed();
590             lock();
591             scope(exit) unlock();
592             checkIndex(parameterIndex);
593             if (x.ptr is null) {
594                 setNull(parameterIndex);
595                 return;
596             }
597             sqlite3_bind_blob(stmt, parameterIndex, cast(const (void *))x.ptr, cast(int)x.length, SQLITE_TRANSIENT);
598             paramIsSet[parameterIndex - 1] = true;
599         }
600         override void setUbytes(int parameterIndex, ubyte[] x) {
601             checkClosed();
602             lock();
603             scope(exit) unlock();
604             checkIndex(parameterIndex);
605             if (x.ptr is null) {
606                 setNull(parameterIndex);
607                 return;
608             }
609             sqlite3_bind_blob(stmt, parameterIndex, cast(const char *)x.ptr, cast(int)x.length, SQLITE_TRANSIENT);
610             paramIsSet[parameterIndex - 1] = true;
611         }
612         override void setString(int parameterIndex, string x) {
613             checkClosed();
614             lock();
615             scope(exit) unlock();
616             checkIndex(parameterIndex);
617             if (x.ptr is null) {
618                 setNull(parameterIndex);
619                 return;
620             }
621             sqlite3_bind_text(stmt, parameterIndex, cast(const char *)x.ptr, cast(int)x.length, SQLITE_TRANSIENT);
622             paramIsSet[parameterIndex - 1] = true;
623         }
624         override void setDateTime(int parameterIndex, DateTime x) {
625             setString(parameterIndex, x.toISOString());
626         }
627         override void setDate(int parameterIndex, Date x) {
628             setString(parameterIndex, x.toISOString());
629         }
630         override void setTime(int parameterIndex, TimeOfDay x) {
631             setString(parameterIndex, x.toISOString());
632         }
633         override void setVariant(int parameterIndex, Variant x) {
634             if (x == null)
635                 setNull(parameterIndex);
636             else if (x.convertsTo!long)
637                 setLong(parameterIndex, x.get!long);
638             else if (x.convertsTo!ulong)
639                 setLong(parameterIndex, x.get!ulong);
640             else if (x.convertsTo!double)
641                 setDouble(parameterIndex, x.get!double);
642             else if (x.convertsTo!(byte[]))
643                 setBytes(parameterIndex, x.get!(byte[]));
644             else if (x.convertsTo!(ubyte[]))
645                 setUbytes(parameterIndex, x.get!(ubyte[]));
646             else if (x.convertsTo!DateTime)
647                 setDateTime(parameterIndex, x.get!DateTime);
648             else if (x.convertsTo!Date)
649                 setDate(parameterIndex, x.get!Date);
650             else if (x.convertsTo!TimeOfDay)
651                 setTime(parameterIndex, x.get!TimeOfDay);
652             else
653                 setString(parameterIndex, x.toString());
654         }
655         override void setNull(int parameterIndex) {
656             checkClosed();
657             lock();
658             scope(exit) unlock();
659             checkIndex(parameterIndex);
660             sqlite3_bind_null(stmt, parameterIndex);
661             paramIsSet[parameterIndex - 1] = true;
662         }
663         override void setNull(int parameterIndex, int sqlType) {
664             setNull(parameterIndex);
665         }
666     }
667 
668     class SQLITEResultSet : ResultSetImpl {
669         private SQLITEStatement stmt;
670         private sqlite3_stmt * rs;
671         ResultSetMetaData metadata;
672         private bool closed;
673         private int currentRowIndex;
674 //        private int rowCount;
675         private int[string] columnMap;
676         private bool lastIsNull;
677         private int columnCount;
678 
679         private bool _last;
680         private bool _first;
681 
682         // checks index, updates lastIsNull, returns column type
683         int checkIndex(int columnIndex) {
684             enforce!SQLException(columnIndex >= 1 && columnIndex <= columnCount, "Column index out of bounds: " ~ to!string(columnIndex));
685             int res = sqlite3_column_type(rs, columnIndex - 1);
686             lastIsNull = (res == SQLITE_NULL);
687             return res;
688         }
689         
690         void checkClosed() {
691             if (closed)
692                 throw new SQLException("Result set is already closed");
693         }
694         
695     public:
696         
697         void lock() {
698             stmt.lock();
699         }
700         
701         void unlock() {
702             stmt.unlock();
703         }
704         
705         this(SQLITEStatement stmt, sqlite3_stmt * rs, ResultSetMetaData metadata) {
706             this.stmt = stmt;
707             this.rs = rs;
708             this.metadata = metadata;
709             closed = false;
710             // The column count cannot use sqlite3_data_count, because sqlite3_step has not yet been used with this result set.
711             // Because there are not results ready to return, sqlite3_data_count will return 0 causing no columns to be mapped.
712             this.columnCount = metadata.getColumnCount();
713             for (int i=0; i<columnCount; i++) {
714                 columnMap[metadata.getColumnName(i + 1)] = i;
715             }
716             currentRowIndex = -1;
717             _first = true;
718         }
719         
720         void onStatementClosed() {
721             closed = true;
722         }
723         string decodeTextBlob(ubyte[] data) {
724             char[] res = new char[data.length];
725             foreach (i, ch; data) {
726                 res[i] = cast(char)ch;
727             }
728             return to!string(res);
729         }
730         
731         // ResultSet interface implementation
732         
733         //Retrieves the number, types and properties of this ResultSet object's columns
734         override ResultSetMetaData getMetaData() {
735             checkClosed();
736             lock();
737             scope(exit) unlock();
738             return metadata;
739         }
740         
741         override void close() {
742             if (closed)
743                 return;
744             checkClosed();
745             lock();
746             scope(exit) unlock();
747             stmt.closeResultSet();
748             closed = true;
749         }
750         override bool first() {
751             checkClosed();
752             lock();
753             scope(exit) unlock();
754             throw new SQLException("Not implemented");
755         }
756         override bool isFirst() {
757             checkClosed();
758             lock();
759             scope(exit) unlock();
760             return _first;
761         }
762         override bool isLast() {
763             checkClosed();
764             lock();
765             scope(exit) unlock();
766             return _last;
767         }
768 
769         override bool next() {
770             checkClosed();
771             lock();
772             scope(exit) unlock();
773 
774             if (_first) {
775                 _first = false;
776                 //writeln("next() first time invocation, columnCount=" ~ to!string(columnCount));
777                 //return columnCount > 0;
778             }
779 
780             int res = sqlite3_step(rs);
781             if (res == SQLITE_DONE) {
782                 _last = true;
783                 columnCount = sqlite3_data_count(rs);
784                 //writeln("sqlite3_step = SQLITE_DONE columnCount=" ~ to!string(columnCount));
785                 // end of data
786                 return columnCount > 0;
787             } else if (res == SQLITE_ROW) {
788                 //writeln("sqlite3_step = SQLITE_ROW");
789                 // have a row
790                 currentRowIndex++;
791                 columnCount = sqlite3_data_count(rs);
792                 return true;
793             } else {
794                 enforce!SQLException(false, "Error #" ~ to!string(res) ~ " while reading query result: " ~ copyCString(sqlite3_errmsg(stmt.conn.getConnection())));
795                 return false;
796             }
797         }
798         
799         override int findColumn(string columnName) {
800             checkClosed();
801             lock();
802             scope(exit) unlock();
803             int * p = (columnName in columnMap);
804             if (!p)
805                 throw new SQLException("Column " ~ columnName ~ " not found");
806             return *p + 1;
807         }
808         
809         override bool getBoolean(int columnIndex) {
810             return getLong(columnIndex) != 0;
811         }
812         override ubyte getUbyte(int columnIndex) {
813             return cast(ubyte)getLong(columnIndex);
814         }
815         override byte getByte(int columnIndex) {
816             return cast(byte)getLong(columnIndex);
817         }
818         override short getShort(int columnIndex) {
819             return cast(short)getLong(columnIndex);
820         }
821         override ushort getUshort(int columnIndex) {
822             return cast(ushort)getLong(columnIndex);
823         }
824         override int getInt(int columnIndex) {
825             return cast(int)getLong(columnIndex);
826         }
827         override uint getUint(int columnIndex) {
828             return cast(uint)getLong(columnIndex);
829         }
830         override long getLong(int columnIndex) {
831             checkClosed();
832             checkIndex(columnIndex);
833             lock();
834             scope(exit) unlock();
835             auto v = sqlite3_column_int64(rs, columnIndex - 1);
836             return v;
837         }
838         override ulong getUlong(int columnIndex) {
839             return cast(ulong)getLong(columnIndex);
840         }
841         override double getDouble(int columnIndex) {
842             checkClosed();
843             checkIndex(columnIndex);
844             lock();
845             scope(exit) unlock();
846             auto v = sqlite3_column_double(rs, columnIndex - 1);
847             return v;
848         }
849         override float getFloat(int columnIndex) {
850             return cast(float)getDouble(columnIndex);
851         }
852         override byte[] getBytes(int columnIndex) {
853             checkClosed();
854             checkIndex(columnIndex);
855             lock();
856             scope(exit) unlock();
857             const byte * bytes = cast(const byte *)sqlite3_column_blob(rs, columnIndex - 1);
858             int len = sqlite3_column_bytes(rs, columnIndex - 1);
859             byte[] res = new byte[len];
860             for (int i=0; i<len; i++)
861                 res[i] = bytes[i];
862             return res;
863         }
864         override ubyte[] getUbytes(int columnIndex) {
865             checkClosed();
866             checkIndex(columnIndex);
867             lock();
868             scope(exit) unlock();
869             const ubyte * bytes = cast(const ubyte *)sqlite3_column_blob(rs, columnIndex - 1);
870             int len = sqlite3_column_bytes(rs, columnIndex - 1);
871             ubyte[] res = new ubyte[len];
872             for (int i=0; i<len; i++)
873                 res[i] = bytes[i];
874             return res;
875         }
876         override string getString(int columnIndex) {
877             checkClosed();
878             checkIndex(columnIndex);
879             lock();
880             scope(exit) unlock();
881             const char * bytes = cast(const char *)sqlite3_column_text(rs, columnIndex - 1);
882             int len = sqlite3_column_bytes(rs, columnIndex - 1);
883             char[] res = new char[len];
884             for (int i=0; i<len; i++)
885                 res[i] = bytes[i];
886             return cast(string)res;
887         }
888         override DateTime getDateTime(int columnIndex) {
889             string s = getString(columnIndex);
890             DateTime dt;
891             if (s is null)
892                 return dt;
893             try {
894                 return fromResultSet(s);
895             } catch (Throwable e) {
896                 throw new SQLException("Cannot convert string to DateTime - " ~ s);
897             }
898         }
899         override Date getDate(int columnIndex) {
900             string s = getString(columnIndex);
901             Date dt;
902             if (s is null)
903                 return dt;
904             try {
905                 return fromResultSet(s).date;
906             } catch (Throwable e) {
907                 throw new SQLException("Cannot convert string to DateTime - " ~ s);
908             }
909         }
910         override TimeOfDay getTime(int columnIndex) {
911             string s = getString(columnIndex);
912             TimeOfDay dt;
913             if (s is null)
914                 return dt;
915             try {
916                 return fromResultSet(s).timeOfDay;
917             } catch (Throwable e) {
918                 throw new SQLException("Cannot convert string to DateTime - " ~ s);
919             }
920         }
921         
922         override Variant getVariant(int columnIndex) {
923             checkClosed();
924             int type = checkIndex(columnIndex);
925             lock();
926             scope(exit) unlock();
927             Variant v = null;
928             if (lastIsNull)
929                 return v;
930             switch (type) {
931                 case SQLITE_INTEGER:
932                     v = getLong(columnIndex);
933                     break;
934                 case SQLITE_FLOAT:
935                     v = getDouble(columnIndex);
936                     break;
937                 case SQLITE3_TEXT:
938                     v = getString(columnIndex);
939                     break;
940                 case SQLITE_BLOB:
941                     v = getUbytes(columnIndex);
942                     break;
943                 default:
944                     break;
945             }
946             return v;
947         }
948         override bool wasNull() {
949             checkClosed();
950             lock();
951             scope(exit) unlock();
952             return lastIsNull;
953         }
954         override bool isNull(int columnIndex) {
955             checkClosed();
956             lock();
957             scope(exit) unlock();
958             checkIndex(columnIndex);
959             return lastIsNull;
960         }
961         
962         //Retrieves the Statement object that produced this ResultSet object.
963         override Statement getStatement() {
964             checkClosed();
965             lock();
966             scope(exit) unlock();
967             return stmt;
968         }
969         
970         //Retrieves the current row number
971         override int getRow() {
972             checkClosed();
973             lock();
974             scope(exit) unlock();
975             if (currentRowIndex <0)
976                 return 0;
977             return currentRowIndex + 1;
978         }
979         
980         //Retrieves the fetch size for this ResultSet object.
981         override ulong getFetchSize() {
982             checkClosed();
983             lock();
984             scope(exit) unlock();
985             return -1;
986         }
987     }
988 
989 
990     // sample URL:
991     // mysql://localhost:3306/DatabaseName
992 
993     //String url = "jdbc:postgresql://localhost/test";
994     //Properties props = new Properties();
995     //props.setProperty("user","fred");
996     //props.setProperty("password","secret");
997     //Connection conn = DriverManager.getConnection(url, props);
998     class SQLITEDriver : Driver {
999         // helper function
1000         public static string generateUrl(string host, ushort port, string dbname) {
1001             return "sqlite://" ~ host ~ ":" ~ to!string(port) ~ "/" ~ dbname;
1002         }
1003         public static string[string] setUserAndPassword(string username, string password) {
1004             string[string] params;
1005             params["user"] = username;
1006             params["password"] = password;
1007             return params;
1008         }
1009         override ddbc.core.Connection connect(string url, string[string] params) {
1010             //writeln("SQLITEDriver.connect " ~ url);
1011             return new SQLITEConnection(url, params);
1012         }
1013     }
1014 
1015     unittest {
1016         if (SQLITE_TESTS_ENABLED) {
1017             
1018             import std.conv;
1019             DataSource ds = createUnitTestSQLITEDataSource();
1020             //writeln("trying to open connection");        
1021             auto conn = ds.getConnection();
1022             //writeln("connection is opened");        
1023             assert(conn !is null);
1024             scope(exit) conn.close();
1025             {
1026                 //writeln("dropping table");
1027                 Statement stmt = conn.createStatement();
1028                 scope(exit) stmt.close();
1029                 stmt.executeUpdate("DROP TABLE IF EXISTS t1");
1030             }
1031             {
1032                 //writeln("creating table");
1033                 Statement stmt = conn.createStatement();
1034                 scope(exit) stmt.close();
1035                 stmt.executeUpdate("CREATE TABLE IF NOT EXISTS t1 (id INTEGER PRIMARY KEY, name VARCHAR(255) NOT NULL, flags int null)");
1036             }
1037             {
1038                 //writeln("populating table");
1039                 PreparedStatement stmt = conn.prepareStatement("INSERT INTO t1 (name) VALUES ('test1')");
1040                 scope(exit) stmt.close();
1041                 Variant id = 0;
1042                 assert(stmt.executeUpdate(id) == 1);
1043                 assert(id.get!long > 0);
1044             }
1045             {
1046                 //writeln("populating table");
1047                 PreparedStatement stmt = conn.prepareStatement("INSERT INTO t1 (name) VALUES ('test2')");
1048                 scope(exit) stmt.close();
1049                 Variant id = 0;
1050                 assert(stmt.executeUpdate(id) == 1);
1051                 assert(id.get!long > 0);
1052             }
1053             {
1054                 //writeln("reading table");
1055                 Statement stmt = conn.createStatement();
1056                 scope(exit) stmt.close();
1057                 ResultSet rs = stmt.executeQuery("SELECT id, name, flags FROM t1");
1058                 assert(rs.getMetaData().getColumnCount() == 3);
1059                 assert(rs.getMetaData().getColumnName(1) == "id");
1060                 assert(rs.getMetaData().getColumnName(2) == "name");
1061                 assert(rs.getMetaData().getColumnName(3) == "flags");
1062                 scope(exit) rs.close();
1063                 //writeln("id" ~ "\t" ~ "name");
1064                 while (rs.next()) {
1065                     long id = rs.getLong(1);
1066                     string name = rs.getString(2);
1067                     assert(rs.isNull(3));
1068                     //writeln("" ~ to!string(id) ~ "\t" ~ name);
1069                 }
1070             }
1071             {
1072                 //writeln("reading table with parameter id=1");
1073                 PreparedStatement stmt = conn.prepareStatement("SELECT id, name, flags FROM t1 WHERE id = ?");
1074                 scope(exit) stmt.close();
1075                 assert(stmt.getMetaData().getColumnCount() == 3);
1076                 assert(stmt.getMetaData().getColumnName(1) == "id");
1077                 assert(stmt.getMetaData().getColumnName(2) == "name");
1078                 assert(stmt.getMetaData().getColumnName(3) == "flags");
1079                 stmt.setLong(1, 1);
1080                 {
1081                     ResultSet rs = stmt.executeQuery();
1082                     scope(exit) rs.close();
1083                     //writeln("id" ~ "\t" ~ "name");
1084                     while (rs.next()) {
1085                         long id = rs.getLong(1);
1086                         string name = rs.getString(2);
1087                         assert(rs.isNull(3));
1088                         //writeln("" ~ to!string(id) ~ "\t" ~ name);
1089                     }
1090                 }
1091                 //writeln("changing parameter id=2");
1092                 stmt.setLong(1, 2);
1093                 {
1094                     ResultSet rs = stmt.executeQuery();
1095                     scope(exit) rs.close();
1096                     //writeln("id" ~ "\t" ~ "name");
1097                     while (rs.next()) {
1098                         long id = rs.getLong(1);
1099                         string name = rs.getString(2);
1100                         //writeln("" ~ to!string(id) ~ "\t" ~ name);
1101                     }
1102                 }
1103             }
1104         }
1105     }
1106 
1107     __gshared static this() {
1108         // register SQLiteDriver
1109         import ddbc.common;
1110         DriverFactory.registerDriverFactory("sqlite", delegate() { return new SQLITEDriver(); });
1111     }
1112 
1113 
1114 } else { // version(USE_SQLITE)
1115     version(unittest) {
1116         immutable bool SQLITE_TESTS_ENABLED = false;
1117     }
1118 }