sqlite3 0.0.0 → 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,68 @@
1
+ require "sqlite3/constants"
2
+
3
+ module SQLite3
4
+
5
+ class Exception < ::StandardError
6
+ @code = 0
7
+
8
+ # The numeric error code that this exception represents.
9
+ def self.code
10
+ @code
11
+ end
12
+
13
+ # A convenience for accessing the error code for this exception.
14
+ def code
15
+ self.class.code
16
+ end
17
+ end
18
+
19
+ class SQLException < Exception; end
20
+ class InternalException < Exception; end
21
+ class PermissionException < Exception; end
22
+ class AbortException < Exception; end
23
+ class BusyException < Exception; end
24
+ class LockedException < Exception; end
25
+ class MemoryException < Exception; end
26
+ class ReadOnlyException < Exception; end
27
+ class InterruptException < Exception; end
28
+ class IOException < Exception; end
29
+ class CorruptException < Exception; end
30
+ class NotFoundException < Exception; end
31
+ class FullException < Exception; end
32
+ class CantOpenException < Exception; end
33
+ class ProtocolException < Exception; end
34
+ class EmptyException < Exception; end
35
+ class SchemaChangedException < Exception; end
36
+ class TooBigException < Exception; end
37
+ class ConstraintException < Exception; end
38
+ class MismatchException < Exception; end
39
+ class MisuseException < Exception; end
40
+ class UnsupportedException < Exception; end
41
+ class AuthorizationException < Exception; end
42
+ class FormatException < Exception; end
43
+ class RangeException < Exception; end
44
+ class NotADatabaseException < Exception; end
45
+
46
+ EXCEPTIONS = [
47
+ nil,
48
+ SQLException, InternalException, PermissionException,
49
+ AbortException, BusyException, LockedException, MemoryException,
50
+ ReadOnlyException, InterruptException, IOException, CorruptException,
51
+ NotFoundException, FullException, CantOpenException, ProtocolException,
52
+ EmptyException, SchemaChangedException, TooBigException,
53
+ ConstraintException, MismatchException, MisuseException,
54
+ UnsupportedException, AuthorizationException, FormatException,
55
+ RangeException, NotADatabaseException
56
+ ].each_with_index { |e,i| e.instance_variable_set( :@code, i ) if e }
57
+
58
+ module Error
59
+ def check( result, db=nil, msg=nil )
60
+ unless result == Constants::ErrorCode::OK
61
+ msg = ( msg ? msg + ": " : "" ) + db.errmsg if db
62
+ raise(( EXCEPTIONS[result] || SQLite3::Exception ), msg)
63
+ end
64
+ end
65
+ module_function :check
66
+ end
67
+
68
+ end
@@ -0,0 +1,272 @@
1
+ require "sqlite3/errors"
2
+
3
+ module SQLite3
4
+
5
+ # This module is intended for inclusion solely by the Database class. It
6
+ # defines convenience methods for the various pragmas supported by SQLite3.
7
+ #
8
+ # For a detailed description of these pragmas, see the SQLite3 documentation
9
+ # at http://sqlite.org/pragma.html.
10
+ module Pragmas
11
+
12
+ # Returns +true+ or +false+ depending on the value of the named pragma.
13
+ def get_boolean_pragma(name)
14
+ get_first_value("PRAGMA #{name}") != "0"
15
+ end
16
+ private :get_boolean_pragma
17
+
18
+ # Sets the given pragma to the given boolean value. The value itself
19
+ # may be +true+ or +false+, or any other commonly used string or
20
+ # integer that represents truth.
21
+ def set_boolean_pragma(name, mode)
22
+ case mode
23
+ when String
24
+ case mode.downcase
25
+ when "on", "yes", "true", "y", "t"
26
+ mode = "'ON'"
27
+ when "off", "no", "false", "n", "f"
28
+ mode = "'OFF'"
29
+ else
30
+ raise Exception, "unrecognized pragma parameter #{mode.inspect}"
31
+ end
32
+ when true, 1
33
+ mode = "ON"
34
+ when false, 0, nil
35
+ mode = "OFF"
36
+ else
37
+ raise Exception,
38
+ "unrecognized pragma parameter #{mode.inspect}"
39
+ end
40
+
41
+ execute("PRAGMA #{name}=#{mode}")
42
+ end
43
+ private :set_boolean_pragma
44
+
45
+ # Requests the given pragma (and parameters), and if the block is given,
46
+ # each row of the result set will be yielded to it. Otherwise, the results
47
+ # are returned as an array.
48
+ def get_query_pragma(name, *parms, &block) # :yields: row
49
+ if parms.empty?
50
+ execute("PRAGMA #{name}", &block)
51
+ else
52
+ args = "'" + parms.join("','") + "'"
53
+ execute("PRAGMA #{name}(#{args})", &block)
54
+ end
55
+ end
56
+ private :get_query_pragma
57
+
58
+ # Return the value of the given pragma.
59
+ def get_enum_pragma(name)
60
+ get_first_value("PRAGMA #{name}")
61
+ end
62
+ private :get_enum_pragma
63
+
64
+ # Set the value of the given pragma to +mode+. The +mode+ parameter must
65
+ # conform to one of the values in the given +enum+ array. Each entry in
66
+ # the array is another array comprised of elements in the enumeration that
67
+ # have duplicate values. See #synchronous, #default_synchronous,
68
+ # #temp_store, and #default_temp_store for usage examples.
69
+ def set_enum_pragma(name, mode, enums)
70
+ match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } }
71
+ raise Exception,
72
+ "unrecognized #{name} #{mode.inspect}" unless match
73
+ execute("PRAGMA #{name}='#{match.first.upcase}'")
74
+ end
75
+ private :set_enum_pragma
76
+
77
+ # Returns the value of the given pragma as an integer.
78
+ def get_int_pragma(name)
79
+ get_first_value("PRAGMA #{name}").to_i
80
+ end
81
+ private :get_int_pragma
82
+
83
+ # Set the value of the given pragma to the integer value of the +value+
84
+ # parameter.
85
+ def set_int_pragma(name, value)
86
+ execute("PRAGMA #{name}=#{value.to_i}")
87
+ end
88
+ private :set_int_pragma
89
+
90
+ # The enumeration of valid synchronous modes.
91
+ SYNCHRONOUS_MODES = [ [ "full", 2 ], [ "normal", 1 ], [ "off", 0 ] ]
92
+
93
+ # The enumeration of valid temp store modes.
94
+ TEMP_STORE_MODES = [ [ "default", 0 ], [ "file", 1 ], [ "memory", 2 ] ]
95
+
96
+ # Does an integrity check on the database. If the check fails, a
97
+ # SQLite3::Exception will be raised. Otherwise it
98
+ # returns silently.
99
+ def integrity_check
100
+ execute("PRAGMA integrity_check") do |row|
101
+ raise Exception, row[0] if row[0] != "ok"
102
+ end
103
+ end
104
+
105
+ def auto_vacuum
106
+ get_boolean_pragma "auto_vacuum"
107
+ end
108
+
109
+ def auto_vacuum=(mode)
110
+ set_boolean_pragma "auto_vacuum", mode
111
+ end
112
+
113
+ def schema_cookie
114
+ get_int_pragma "schema_cookie"
115
+ end
116
+
117
+ def schema_cookie=(cookie)
118
+ set_int_pragma "schema_cookie", cookie
119
+ end
120
+
121
+ def user_cookie
122
+ get_int_pragma "user_cookie"
123
+ end
124
+
125
+ def user_cookie=(cookie)
126
+ set_int_pragma "user_cookie", cookie
127
+ end
128
+
129
+ def cache_size
130
+ get_int_pragma "cache_size"
131
+ end
132
+
133
+ def cache_size=(size)
134
+ set_int_pragma "cache_size", size
135
+ end
136
+
137
+ def default_cache_size
138
+ get_int_pragma "default_cache_size"
139
+ end
140
+
141
+ def default_cache_size=(size)
142
+ set_int_pragma "default_cache_size", size
143
+ end
144
+
145
+ def default_synchronous
146
+ get_enum_pragma "default_synchronous"
147
+ end
148
+
149
+ def default_synchronous=(mode)
150
+ set_enum_pragma "default_synchronous", mode, SYNCHRONOUS_MODES
151
+ end
152
+
153
+ def synchronous
154
+ get_enum_pragma "synchronous"
155
+ end
156
+
157
+ def synchronous=(mode)
158
+ set_enum_pragma "synchronous", mode, SYNCHRONOUS_MODES
159
+ end
160
+
161
+ def default_temp_store
162
+ get_enum_pragma "default_temp_store"
163
+ end
164
+
165
+ def default_temp_store=(mode)
166
+ set_enum_pragma "default_temp_store", mode, TEMP_STORE_MODES
167
+ end
168
+
169
+ def temp_store
170
+ get_enum_pragma "temp_store"
171
+ end
172
+
173
+ def temp_store=(mode)
174
+ set_enum_pragma "temp_store", mode, TEMP_STORE_MODES
175
+ end
176
+
177
+ def full_column_names
178
+ get_boolean_pragma "full_column_names"
179
+ end
180
+
181
+ def full_column_names=(mode)
182
+ set_boolean_pragma "full_column_names", mode
183
+ end
184
+
185
+ def parser_trace
186
+ get_boolean_pragma "parser_trace"
187
+ end
188
+
189
+ def parser_trace=(mode)
190
+ set_boolean_pragma "parser_trace", mode
191
+ end
192
+
193
+ def vdbe_trace
194
+ get_boolean_pragma "vdbe_trace"
195
+ end
196
+
197
+ def vdbe_trace=(mode)
198
+ set_boolean_pragma "vdbe_trace", mode
199
+ end
200
+
201
+ def database_list(&block) # :yields: row
202
+ get_query_pragma "database_list", &block
203
+ end
204
+
205
+ def foreign_key_list(table, &block) # :yields: row
206
+ get_query_pragma "foreign_key_list", table, &block
207
+ end
208
+
209
+ def index_info(index, &block) # :yields: row
210
+ get_query_pragma "index_info", index, &block
211
+ end
212
+
213
+ def index_list(table, &block) # :yields: row
214
+ get_query_pragma "index_list", table, &block
215
+ end
216
+
217
+ def table_info(table, &block) # :yields: row
218
+ columns, *rows = execute2("PRAGMA table_info(#{table})")
219
+
220
+ needs_tweak_default = version_compare(driver.libversion, "3.3.7") > 0
221
+
222
+ result = [] unless block_given?
223
+ rows.each do |row|
224
+ new_row = {}
225
+ columns.each_with_index do |name, index|
226
+ new_row[name] = row[index]
227
+ end
228
+
229
+ tweak_default(new_row) if needs_tweak_default
230
+
231
+ if block_given?
232
+ yield new_row
233
+ else
234
+ result << new_row
235
+ end
236
+ end
237
+
238
+ result
239
+ end
240
+
241
+ private
242
+
243
+ # Compares two version strings
244
+ def version_compare(v1, v2)
245
+ v1 = v1.split(".").map { |i| i.to_i }
246
+ v2 = v2.split(".").map { |i| i.to_i }
247
+ parts = [v1.length, v2.length].max
248
+ v1.push 0 while v1.length < parts
249
+ v2.push 0 while v2.length < parts
250
+ v1.zip(v2).each do |a,b|
251
+ return -1 if a < b
252
+ return 1 if a > b
253
+ end
254
+ return 0
255
+ end
256
+
257
+ # Since SQLite 3.3.8, the table_info pragma has returned the default
258
+ # value of the row as a quoted SQL value. This method essentially
259
+ # unquotes those values.
260
+ def tweak_default(hash)
261
+ case hash["dflt_value"]
262
+ when /^null$/i
263
+ hash["dflt_value"] = nil
264
+ when /^'(.*)'$/
265
+ hash["dflt_value"] = $1.gsub(/''/, "'")
266
+ when /^"(.*)"$/
267
+ hash["dflt_value"] = $1.gsub(/""/, '"')
268
+ end
269
+ end
270
+ end
271
+
272
+ end
@@ -0,0 +1,176 @@
1
+ require "sqlite3/constants"
2
+ require "sqlite3/errors"
3
+
4
+ module SQLite3
5
+
6
+ # The ResultSet object encapsulates the enumerability of a query's output.
7
+ # It is a simple cursor over the data that the query returns. It will
8
+ # very rarely (if ever) be instantiated directly. Instead, client's should
9
+ # obtain a ResultSet instance via Statement#execute.
10
+ class ResultSet
11
+ include Enumerable
12
+
13
+ # The class of which we return an object in case we want an Array as
14
+ # result. (ArrayFields is installed.)
15
+ class ArrayWithTypes < Array
16
+ attr_accessor :types
17
+ end
18
+
19
+ # The class of which we return an object in case we want an Array as
20
+ # result. (ArrayFields is not installed.)
21
+ class ArrayWithTypesAndFields < Array
22
+ attr_accessor :types
23
+ attr_accessor :fields
24
+ end
25
+
26
+ # The class of which we return an object in case we want a Hash as
27
+ # result.
28
+ class HashWithTypes < Hash
29
+ attr_accessor :types
30
+ end
31
+
32
+ # Create a new ResultSet attached to the given database, using the
33
+ # given sql text.
34
+ def initialize(db, stmt)
35
+ @db = db
36
+ @driver = @db.driver
37
+ @stmt = stmt
38
+ commence
39
+ end
40
+
41
+ # A convenience method for compiling the virtual machine and stepping
42
+ # to the first row of the result set.
43
+ def commence
44
+ result = @driver.step(@stmt.handle)
45
+ if result == Constants::ErrorCode::ERROR
46
+ @driver.reset(@stmt.handle)
47
+ end
48
+ check result
49
+ @first_row = true
50
+ end
51
+ private :commence
52
+
53
+ def check(result)
54
+ @eof = (result == Constants::ErrorCode::DONE)
55
+ found = (result == Constants::ErrorCode::ROW)
56
+ Error.check(result, @db) unless @eof || found
57
+ end
58
+ private :check
59
+
60
+ # Reset the cursor, so that a result set which has reached end-of-file
61
+ # can be rewound and reiterated.
62
+ def reset(*bind_params)
63
+ @stmt.must_be_open!
64
+ @stmt.reset!(false)
65
+ @driver.reset(@stmt.handle)
66
+ @stmt.bind_params(*bind_params)
67
+ @eof = false
68
+ commence
69
+ end
70
+
71
+ # Query whether the cursor has reached the end of the result set or not.
72
+ def eof?
73
+ @eof
74
+ end
75
+
76
+ # Obtain the next row from the cursor. If there are no more rows to be
77
+ # had, this will return +nil+. If type translation is active on the
78
+ # corresponding database, the values in the row will be translated
79
+ # according to their types.
80
+ #
81
+ # The returned value will be an array, unless Database#results_as_hash has
82
+ # been set to +true+, in which case the returned value will be a hash.
83
+ #
84
+ # For arrays, the column names are accessible via the +fields+ property,
85
+ # and the column types are accessible via the +types+ property.
86
+ #
87
+ # For hashes, the column names are the keys of the hash, and the column
88
+ # types are accessible via the +types+ property.
89
+ def next
90
+ return nil if @eof
91
+
92
+ @stmt.must_be_open!
93
+
94
+ unless @first_row
95
+ result = @driver.step(@stmt.handle)
96
+ check result
97
+ end
98
+
99
+ @first_row = false
100
+
101
+ unless @eof
102
+ row = []
103
+ @driver.data_count(@stmt.handle).times do |column|
104
+ type = @driver.column_type(@stmt.handle, column)
105
+
106
+ if type == Constants::ColumnType::TEXT
107
+ row << @driver.column_text(@stmt.handle, column)
108
+ elsif type == Constants::ColumnType::NULL
109
+ row << nil
110
+ elsif type == Constants::ColumnType::BLOB
111
+ row << @driver.column_blob(@stmt.handle, column)
112
+ else
113
+ row << @driver.column_text(@stmt.handle, column)
114
+ end
115
+ end
116
+
117
+ if @db.type_translation
118
+ row = @stmt.types.zip(row).map do |type, value|
119
+ @db.translator.translate(type, value)
120
+ end
121
+ end
122
+
123
+ if @db.results_as_hash
124
+ new_row = HashWithTypes[ *(@stmt.columns.zip(row).to_a.flatten) ]
125
+ row.each_with_index { |value,idx| new_row[idx] = value }
126
+ row = new_row
127
+ else
128
+ if row.respond_to?(:fields)
129
+ row = ArrayWithTypes.new(row)
130
+ else
131
+ row = ArrayWithTypesAndFields.new(row)
132
+ end
133
+ row.fields = @stmt.columns
134
+ end
135
+
136
+ row.types = @stmt.types
137
+
138
+ return row
139
+ end
140
+
141
+ nil
142
+ end
143
+
144
+ # Required by the Enumerable mixin. Provides an internal iterator over the
145
+ # rows of the result set.
146
+ def each
147
+ while row=self.next
148
+ yield row
149
+ end
150
+ end
151
+
152
+ # Closes the statement that spawned this result set.
153
+ # <em>Use with caution!</em> Closing a result set will automatically
154
+ # close any other result sets that were spawned from the same statement.
155
+ def close
156
+ @stmt.close
157
+ end
158
+
159
+ # Queries whether the underlying statement has been closed or not.
160
+ def closed?
161
+ @stmt.closed?
162
+ end
163
+
164
+ # Returns the types of the columns returned by this result set.
165
+ def types
166
+ @stmt.types
167
+ end
168
+
169
+ # Returns the names of the columns returned by this result set.
170
+ def columns
171
+ @stmt.columns
172
+ end
173
+
174
+ end
175
+
176
+ end