Merge: SQLite: update and simplify wrapper
[nit.git] / lib / sqlite3 / sqlite3.nit
index e5695af..322e7d7 100644 (file)
@@ -57,8 +57,8 @@ class Sqlite3DB
        # Prepare and return a `Statement`, return `null` on error
        fun prepare(sql: Text): nullable Statement
        do
-               var native_stmt = native_connection.prepare(sql.to_s)
-               if native_stmt == null then return null
+               var native_stmt = native_connection.prepare(sql.to_cstring)
+               if native_stmt.address_is_null then return null
 
                var stmt = new Statement(native_stmt)
                open_statements.add stmt
@@ -68,7 +68,7 @@ class Sqlite3DB
        # Execute the `sql` statement and return `true` on success
        fun execute(sql: Text): Bool
        do
-               var err = native_connection.exec(sql.to_s)
+               var err = native_connection.exec(sql.to_cstring)
                return err.is_ok
        end
 
@@ -99,7 +99,7 @@ class Sqlite3DB
        do
                if not native_connection.is_valid then
                        var err = sys.sqlite_open_error
-                       if err == null then return null
+                       if err.is_ok then return null
                        return err.to_s
                end
 
@@ -112,13 +112,27 @@ class Sqlite3DB
        fun last_insert_rowid: Int do return native_connection.last_insert_rowid
 end
 
-# A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
+# Prepared Sqlite3 statement
+#
+# Instances of this class are created from `Sqlite3DB::prepare` and
+# its shortcuts: `create_table`, `insert`, `replace` and `select`.
+# The results should be explored with an `iterator`,
+# and each call to `iterator` resets the request.
+# If `close_with_iterator` the iterator calls `close`
+# on this request upon finishing.
 class Statement
        private var native_statement: NativeStatement
 
        # Is this statement usable?
        var is_open = true
 
+       # Should any `iterator` close this statement on `Iterator::finish`?
+       #
+       # If `true`, the default, any `StatementIterator` created by calls to
+       # `iterator` invokes `close` on this request when finished iterating.
+       # Otherwise, `close` must be called manually.
+       var close_with_iterator = true is writable
+
        # Close and finalize this statement
        fun close
        do
@@ -168,7 +182,9 @@ class StatementEntry
        var name: String is lazy do
                assert statement_closed: statement.is_open
 
-               return statement.native_statement.column_name(index)
+               var cname = statement.native_statement.column_name(index)
+               assert not cname.address_is_null
+               return cname.to_s
        end
 
        # Get the value of this entry according to its Sqlite type
@@ -281,7 +297,7 @@ class StatementIterator
                end
        end
 
-       redef fun finish do statement.close
+       redef fun finish do if statement.close_with_iterator then statement.close
 end
 
 # A data type supported by Sqlite3
@@ -291,8 +307,9 @@ redef universal Int super Sqlite3Data end
 
 redef universal Float super Sqlite3Data end
 
-redef class String
-       super Sqlite3Data
+redef class String super Sqlite3Data end
+
+redef class Text
 
        # Return `self` between `'`s, escaping `\` and `'`
        #
@@ -301,6 +318,24 @@ redef class String
        do
                return "'{self.replace('\\', "\\\\").replace('\'', "''")}'"
        end
+
+       # Format the date represented by `self` into an escaped string for SQLite
+       #
+       # `self` must be composed of 1 to 3 integers separated by '-'.
+       # An incompatible format will result in an invalid date string.
+       #
+       #     assert "2016-5-1".to_sql_date_string == "'2016-05-01'"
+       #     assert "2016".to_sql_date_string == "'2016-01-01'"
+       fun to_sql_date_string: String
+       do
+               var parts = self.split("-")
+               for i in [parts.length .. 3[ do parts[i] = "1"
+
+               var year = parts[0].justify(4, 1.0, '0')
+               var month = parts[1].justify(2, 1.0, '0')
+               var day = parts[2].justify(2, 1.0, '0')
+               return "{year}-{month}-{day}".to_sql_string
+       end
 end
 
 # A Sqlite3 blob