d016a4cf37af0144c971acbb04b4a6aad35b846c
1 # This file is part of NIT ( http://www.nitlanguage.org ).
3 # Copyright 201 Alexis Laferrière <alexis.laf@xymus.net>
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 # Services to manipulate a Sqlite3 database
19 # For more information, refer to the documentation of http://www.sqlite.org/docs.html
22 private import native_sqlite3
25 # A connection to a Sqlite3 database
27 private var native_connection
: NativeSqlite3
29 # Is this connection to the DB open?
32 # All `Statement` opened from this connection that must be closed with this connection
33 private var open_statements
= new Array[Statement]
35 # Open a connection to the database file at `path`
38 native_connection
= new NativeSqlite3.open
(path
.to_s
)
39 if native_connection
.is_valid
then is_open
= true
42 # Close this connection to the DB and all open statements
47 # close open statements
48 for stmt
in open_statements
do if stmt
.is_open
then
52 native_connection
.close
55 # Prepare and return a `Statement`, return `null` on error
56 fun prepare
(sql
: Text): nullable Statement
58 var native_stmt
= native_connection
.prepare
(sql
.to_s
)
59 if native_stmt
== null then return null
61 var stmt
= new Statement(native_stmt
)
62 open_statements
.add stmt
66 # Execute the `sql` statement and return `true` on success
67 fun execute
(sql
: Text): Bool
69 var err
= native_connection
.exec
(sql
.to_s
)
73 # Create a table on the DB with a statement beginning with "CREATE TABLE ", followed by `rest`
75 # This method does not escape special characters.
76 fun create_table
(rest
: Text): Bool do return execute
("CREATE TABLE " + rest
)
78 # Insert in the DB with a statement beginning with "INSERT ", followed by `rest`
80 # This method does not escape special characters.
81 fun insert
(rest
: Text): Bool do return execute
("INSERT " + rest
)
83 # Replace in the DB with a statement beginning with "REPLACE", followed by `rest`
85 # This method does not escape special characters.
86 fun replace
(rest
: Text): Bool do return execute
("REPLACE " + rest
)
88 # Select from the DB with a statement beginning with "SELECT ", followed by `rest`
90 # This method does not escape special characters.
91 fun select
(rest
: Text): nullable Statement do return prepare
("SELECT " + rest
)
93 # TODO add more prefix here as needed
95 # The latest error message, or `null` if there is none
96 fun error
: nullable String
98 if not native_connection
.is_valid
then
99 var err
= sys
.sqlite_open_error
100 if err
== null then return null
104 var err
= native_connection
.error
105 if err
.is_ok
then return null
110 # A prepared Sqlite3 statement, created from `Sqlite3DB::prepare` or `Sqlite3DB::select`
112 private var native_statement
: NativeStatement
114 private init(ns
: NativeStatement) do self.native_statement
= ns
116 # Is this statement usable?
119 # Close and finalize this statement
123 native_statement
.finalize
126 # Reset this statement and return a `StatementIterator` to iterate over the result
127 fun iterator
: StatementIterator
129 native_statement
.reset
130 return new StatementIterator(self)
135 # Statement linked to `self`
136 var statement
: Statement
138 private init(s
: Statement) do self.statement
= s
140 # Number of entries in this row
142 # require: `self.statement.is_open`
145 assert statement_closed
: statement
.is_open
147 return statement
.native_statement
.column_count
150 # Returns the `i`th entry on this row
151 fun [](i
: Int): StatementEntry do return new StatementEntry(statement
, i
)
154 # An entry on a `StatementRow`
156 # Statement linked to `self`
157 var statement
: Statement
159 private var index
: Int
161 private init(s
: Statement, i
: Int)
169 # require: `self.statement.is_open`
170 fun name
: String is cached
do
171 assert statement_closed
: statement
.is_open
173 return statement
.native_statement
.column_name
(index
)
176 # Get the value of this entry according to its Sqlite type
178 # require: `self.statement.is_open`
179 fun value
: nullable Sqlite3Data
181 assert statement_closed
: statement
.is_open
183 var data_type
= statement
.native_statement
.column_type
(index
)
184 if data_type
.is_integer
then return to_i
185 if data_type
.is_float
then return to_f
186 if data_type
.is_blob
then return to_blob
187 if data_type
.is_null
then return null
188 if data_type
.is_text
then return to_s
192 # Get this entry as `Int`
194 # If the Sqlite type of this entry is not an integer, it will be `CAST` to
195 # integer. If `null`, returns 0.
197 # require: `self.statement.is_open`
200 assert statement_closed
: statement
.is_open
202 return statement
.native_statement
.column_int
(index
)
205 # Get this entry as `Float`
207 # If the Sqlite type of this entry is not a floating point, it will be `CAST`
208 # to float. If `null`, returns 0.0.
210 # require: `self.statement.is_open`
213 assert statement_closed
: statement
.is_open
215 return statement
.native_statement
.column_double
(index
)
218 # Get this entry as `String`
220 # If the Sqlite type of this entry is not text, it will be `CAST` to text.
221 # If null, returns an empty string.
223 # require: `self.statement.is_open`
226 assert statement_closed
: statement
.is_open
228 var native_string
= statement
.native_statement
.column_text
(index
)
229 if native_string
.address_is_null
then return ""
230 return native_string
.to_s_with_copy
233 # Get this entry as `Blob`
235 # If the Sqlite type of this entry is not a blob, it will be `CAST` to text.
236 # If null, returns a NULL pointer.
238 # require: `self.statement.is_open`
241 assert statement_closed
: statement
.is_open
243 # By spec, we must get the pointer before the byte count
244 var pointer
= statement
.native_statement
.column_blob
(index
)
245 var length
= statement
.native_statement
.column_bytes
(index
)
247 return new Blob(pointer
, length
)
251 # Iterator over the rows of a statement result
252 class StatementIterator
253 super Iterator[StatementRow]
255 # Statement linked to `self`
256 var statement
: Statement
258 private init(s
: Statement)
261 self.item
= new StatementRow(s
)
263 self.is_ok
= statement
.native_statement
.step
.is_row
266 redef var item
: StatementRow
268 redef var is_ok
: Bool
270 # require: `self.statement.is_open`
273 assert statement_closed
: statement
.is_open
275 var err
= statement
.native_statement
.step
278 else if err
.is_done
then
283 # FIXME do something with the error?
289 # A data type supported by Sqlite3
290 interface Sqlite3Data end
292 redef universal Int super Sqlite3Data end
293 redef universal Float super Sqlite3Data end
297 # Return `self` between `'`s and escaping any extra `'`
299 # assert "'; DROP TABLE students".to_sql_string == "'''; DROP TABLE students'"
300 fun to_sql_string
: String
302 return "'{self.replace('\'', "''")}'"
310 private init(pointer
: Pointer, length
: Int)
312 self.pointer
= pointer