X-Git-Url: http://nitlanguage.org diff --git a/lib/sqlite3/sqlite3.nit b/lib/sqlite3/sqlite3.nit index 05cbed0..43af40d 100644 --- a/lib/sqlite3/sqlite3.nit +++ b/lib/sqlite3/sqlite3.nit @@ -20,7 +20,7 @@ module sqlite3 private import native_sqlite3 -import standard +import core # A connection to a Sqlite3 database class Sqlite3DB @@ -35,13 +35,15 @@ class Sqlite3DB # Open a connection to the database file at `path` init open(path: Text) do - native_connection = new NativeSqlite3.open(path.to_s) + init(new NativeSqlite3.open(path.to_cstring)) if native_connection.is_valid then is_open = true end # Close this connection to the DB and all open statements fun close do + if not is_open then return + is_open = false # close open statements @@ -55,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 @@ -66,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 @@ -97,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 @@ -110,18 +112,32 @@ 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 - private init(ns: NativeStatement) do self.native_statement = ns - # 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 + if not is_open then return + is_open = false native_statement.finalize end @@ -134,11 +150,21 @@ class Statement end end +# A row from a `Statement` class StatementRow # Statement linked to `self` var statement: Statement - private init(s: Statement) do self.statement = s + # Maps the column name to its value + fun map: Map[String, nullable Sqlite3Data] + do + var ret = new ArrayMap[String, nullable Sqlite3Data] + for i in [0 .. length[ do + var st = self[i] + ret[st.name] = st.value + end + return ret + end # Number of entries in this row # @@ -161,19 +187,15 @@ class StatementEntry private var index: Int - private init(s: Statement, i: Int) - do - self.statement = s - self.index = i - end - # Name of the column # # require: `self.statement.is_open` - fun name: String is cached do + 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 @@ -228,9 +250,9 @@ class StatementEntry do assert statement_closed: statement.is_open - var native_string = statement.native_statement.column_text(index) - if native_string.address_is_null then return "" - return native_string.to_s_with_copy + var c_string = statement.native_statement.column_text(index) + if c_string.address_is_null then return "" + return c_string.to_s_with_copy end # Get this entry as `Blob` @@ -258,17 +280,15 @@ class StatementIterator # Statement linked to `self` var statement: Statement - private init(s: Statement) + init do - self.statement = s - self.item = new StatementRow(s) - + self.item = new StatementRow(statement) self.is_ok = statement.native_statement.step.is_row end - redef var item: StatementRow + redef var item: StatementRow is noinit - redef var is_ok: Bool + redef var is_ok is noinit # require: `self.statement.is_open` redef fun next @@ -287,22 +307,45 @@ class StatementIterator is_ok = false end end + + redef fun finish do if statement.close_with_iterator then statement.close end # A data type supported by Sqlite3 interface Sqlite3Data end redef universal Int super Sqlite3Data end + redef universal Float super Sqlite3Data end -redef class String - super Sqlite3Data - # Return `self` between `'`s and escaping any extra `'` +redef class String super Sqlite3Data end + +redef class Text + + # Return `self` between `'`s, escaping `\` and `'` # # assert "'; DROP TABLE students".to_sql_string == "'''; DROP TABLE students'" fun to_sql_string: String do - return "'{self.replace('\'', "''")}'" + 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 @@ -310,12 +353,9 @@ end class Blob super Sqlite3Data - private init(pointer: Pointer, length: Int) - do - self.pointer = pointer - self.length = length - end - + # Pointer to the beginning of the blob var pointer: Pointer + + # Size of the blob var length: Int end