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,231 @@
1
+ require "sqlite3/errors"
2
+ require "sqlite3/resultset"
3
+
4
+ class String
5
+ def to_blob
6
+ SQLite3::Blob.new(self)
7
+ end
8
+ end
9
+
10
+ module SQLite3
11
+
12
+ # A class for differentiating between strings and blobs, when binding them
13
+ # into statements.
14
+ class Blob < String; end
15
+
16
+ # A statement represents a prepared-but-unexecuted SQL query. It will rarely
17
+ # (if ever) be instantiated directly by a client, and is most often obtained
18
+ # via the Database#prepare method.
19
+ class Statement
20
+
21
+ # This is any text that followed the first valid SQL statement in the text
22
+ # with which the statement was initialized. If there was no trailing text,
23
+ # this will be the empty string.
24
+ attr_reader :remainder
25
+
26
+ # The underlying opaque handle used to access the SQLite @driver.
27
+ attr_reader :handle
28
+
29
+ # Create a new statement attached to the given Database instance, and which
30
+ # encapsulates the given SQL text. If the text contains more than one
31
+ # statement (i.e., separated by semicolons), then the #remainder property
32
+ # will be set to the trailing text.
33
+ def initialize(db, sql, utf16 = false)
34
+ raise ArgumentError, "nil argument passed as sql text" unless sql
35
+ @db = db
36
+ @driver = @db.driver
37
+ @closed = false
38
+ @results = @columns = nil
39
+ result, @handle, @remainder = @driver.prepare(@db.handle, sql)
40
+ Error.check(result, @db)
41
+ end
42
+
43
+ # Closes the statement by finalizing the underlying statement
44
+ # handle. The statement must not be used after being closed.
45
+ def close
46
+ must_be_open!
47
+ @closed = true
48
+ @driver.finalize(@handle)
49
+ end
50
+
51
+ # Returns true if the underlying statement has been closed.
52
+ def closed?
53
+ @closed
54
+ end
55
+
56
+ # Binds the given variables to the corresponding placeholders in the SQL
57
+ # text.
58
+ #
59
+ # See Database#execute for a description of the valid placeholder
60
+ # syntaxes.
61
+ #
62
+ # Example:
63
+ #
64
+ # stmt = db.prepare("select * from table where a=? and b=?")
65
+ # stmt.bind_params(15, "hello")
66
+ #
67
+ # See also #execute, #bind_param, Statement#bind_param, and
68
+ # Statement#bind_params.
69
+ def bind_params(*bind_vars)
70
+ index = 1
71
+ bind_vars.flatten.each do |var|
72
+ if Hash === var
73
+ var.each { |key, val| bind_param key, val }
74
+ else
75
+ bind_param index, var
76
+ index += 1
77
+ end
78
+ end
79
+ end
80
+
81
+ # Binds value to the named (or positional) placeholder. If +param+ is a
82
+ # Fixnum, it is treated as an index for a positional placeholder.
83
+ # Otherwise it is used as the name of the placeholder to bind to.
84
+ #
85
+ # See also #bind_params.
86
+ def bind_param(param, value)
87
+ must_be_open!
88
+ reset! if active?
89
+ if Fixnum === param
90
+ case value
91
+ when Bignum then
92
+ @driver.bind_int64(@handle, param, value)
93
+ when Integer then
94
+ if value >= (2 ** 31)
95
+ @driver.bind_int64(@handle, param, value)
96
+ else
97
+ @driver.bind_int(@handle, param, value)
98
+ end
99
+ when Numeric then
100
+ @driver.bind_double(@handle, param, value.to_f)
101
+ when Blob then
102
+ @driver.bind_blob(@handle, param, value)
103
+ when nil then
104
+ @driver.bind_null(@handle, param)
105
+ else
106
+ @driver.bind_text(@handle, param, value)
107
+ end
108
+ else
109
+ param = param.to_s
110
+ param = ":#{param}" unless param[0] == ?:
111
+ index = @driver.bind_parameter_index(@handle, param)
112
+ raise Exception, "no such bind parameter '#{param}'" if index == 0
113
+ bind_param index, value
114
+ end
115
+ end
116
+
117
+ # Execute the statement. This creates a new ResultSet object for the
118
+ # statement's virtual machine. If a block was given, the new ResultSet will
119
+ # be yielded to it; otherwise, the ResultSet will be returned.
120
+ #
121
+ # Any parameters will be bound to the statement using #bind_params.
122
+ #
123
+ # Example:
124
+ #
125
+ # stmt = db.prepare("select * from table")
126
+ # stmt.execute do |result|
127
+ # ...
128
+ # end
129
+ #
130
+ # See also #bind_params, #execute!.
131
+ def execute(*bind_vars)
132
+ must_be_open!
133
+ reset! if active?
134
+
135
+ bind_params(*bind_vars) unless bind_vars.empty?
136
+ @results = ResultSet.new(@db, self)
137
+
138
+ if block_given?
139
+ yield @results
140
+ else
141
+ return @results
142
+ end
143
+ end
144
+
145
+ # Execute the statement. If no block was given, this returns an array of
146
+ # rows returned by executing the statement. Otherwise, each row will be
147
+ # yielded to the block.
148
+ #
149
+ # Any parameters will be bound to the statement using #bind_params.
150
+ #
151
+ # Example:
152
+ #
153
+ # stmt = db.prepare("select * from table")
154
+ # stmt.execute! do |row|
155
+ # ...
156
+ # end
157
+ #
158
+ # See also #bind_params, #execute.
159
+ def execute!(*bind_vars)
160
+ result = execute(*bind_vars)
161
+ rows = [] unless block_given?
162
+ while row = result.next
163
+ if block_given?
164
+ yield row
165
+ else
166
+ rows << row
167
+ end
168
+ end
169
+ rows
170
+ end
171
+
172
+ # Resets the statement. This is typically done internally, though it might
173
+ # occassionally be necessary to manually reset the statement.
174
+ def reset!(clear_result=true)
175
+ @driver.reset(@handle)
176
+ @results = nil if clear_result
177
+ end
178
+
179
+ # Returns true if the statement is currently active, meaning it has an
180
+ # open result set.
181
+ def active?
182
+ not @results.nil?
183
+ end
184
+
185
+ # Return an array of the column names for this statement. Note that this
186
+ # may execute the statement in order to obtain the metadata; this makes it
187
+ # a (potentially) expensive operation.
188
+ def columns
189
+ get_metadata unless @columns
190
+ return @columns
191
+ end
192
+
193
+ # Return an array of the data types for each column in this statement. Note
194
+ # that this may execute the statement in order to obtain the metadata; this
195
+ # makes it a (potentially) expensive operation.
196
+ def types
197
+ get_metadata unless @types
198
+ return @types
199
+ end
200
+
201
+ # A convenience method for obtaining the metadata about the query. Note
202
+ # that this will actually execute the SQL, which means it can be a
203
+ # (potentially) expensive operation.
204
+ def get_metadata
205
+ must_be_open!
206
+
207
+ @columns = []
208
+ @types = []
209
+
210
+ column_count = @driver.column_count(@handle)
211
+ column_count.times do |column|
212
+ @columns << @driver.column_name(@handle, column)
213
+ @types << @driver.column_decltype(@handle, column)
214
+ end
215
+
216
+ @columns.freeze
217
+ @types.freeze
218
+ end
219
+ private :get_metadata
220
+
221
+ # Performs a sanity check to ensure that the statement is not
222
+ # closed. If it is, an exception is raised.
223
+ def must_be_open! # :nodoc:
224
+ if @closed
225
+ raise SQLite3::Exception, "cannot use a closed statement"
226
+ end
227
+ end
228
+
229
+ end
230
+
231
+ end
@@ -0,0 +1,109 @@
1
+ require "time"
2
+ require "date"
3
+
4
+ module SQLite3
5
+
6
+ # The Translator class encapsulates the logic and callbacks necessary for
7
+ # converting string data to a value of some specified type. Every Database
8
+ # instance may have a Translator instance, in order to assist in type
9
+ # translation (Database#type_translation).
10
+ #
11
+ # Further, applications may define their own custom type translation logic
12
+ # by registering translator blocks with the corresponding database's
13
+ # translator instance (Database#translator).
14
+ class Translator
15
+
16
+ # Create a new Translator instance. It will be preinitialized with default
17
+ # translators for most SQL data types.
18
+ def initialize
19
+ @translators = Hash.new(proc { |type, value| value })
20
+ @type_name_cache = {}
21
+ register_default_translators
22
+ end
23
+
24
+ # Add a new translator block, which will be invoked to process type
25
+ # translations to the given type. The type should be an SQL datatype, and
26
+ # may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
27
+ # information is stripped off and discarded, so type translation decisions
28
+ # are made solely on the "base" type name.
29
+ #
30
+ # The translator block itself should accept two parameters, "type" and
31
+ # "value". In this case, the "type" is the full type name (including
32
+ # parentheses), so the block itself may include logic for changing how a
33
+ # type is translated based on the additional data. The "value" parameter
34
+ # is the (string) data to convert.
35
+ #
36
+ # The block should return the translated value.
37
+ def add_translator(type, &block) # :yields: type, value
38
+ @translators[ type_name(type) ] = block
39
+ end
40
+
41
+ # Translate the given string value to a value of the given type. In the
42
+ # absense of an installed translator block for the given type, the value
43
+ # itself is always returned. Further, +nil+ values are never translated,
44
+ # and are always passed straight through regardless of the type parameter.
45
+ def translate(type, value)
46
+ unless value.nil?
47
+ @translators[ type_name(type) ].call(type, value)
48
+ end
49
+ end
50
+
51
+ # A convenience method for working with type names. This returns the "base"
52
+ # type name, without any parenthetical data.
53
+ def type_name(type)
54
+ @type_name_cache[type] ||= begin
55
+ type = "" if type.nil?
56
+ type = $1 if type =~ /^(.*?)\(/
57
+ type.upcase
58
+ end
59
+ end
60
+ private :type_name
61
+
62
+ # Register the default translators for the current Translator instance.
63
+ # This includes translators for most major SQL data types.
64
+ def register_default_translators
65
+ [ "time",
66
+ "timestamp" ].each { |type| add_translator(type) { |t, v| Time.parse(v) } }
67
+
68
+ add_translator("date") { |t,v| Date.parse(v) }
69
+ add_translator("datetime") { |t,v| DateTime.parse(v) }
70
+
71
+ [ "decimal",
72
+ "float",
73
+ "numeric",
74
+ "double",
75
+ "real",
76
+ "dec",
77
+ "fixed" ].each { |type| add_translator(type) { |t,v| v.to_f } }
78
+
79
+ [ "integer",
80
+ "smallint",
81
+ "mediumint",
82
+ "int",
83
+ "bigint" ].each { |type| add_translator(type) { |t,v| v.to_i } }
84
+
85
+ [ "bit",
86
+ "bool",
87
+ "boolean" ].each do |type|
88
+ add_translator(type) do |t,v|
89
+ !(v.strip.gsub(/00+/,"0") == "0" ||
90
+ v.downcase == "false" ||
91
+ v.downcase == "f" ||
92
+ v.downcase == "no" ||
93
+ v.downcase == "n")
94
+ end
95
+ end
96
+
97
+ add_translator("tinyint") do |type, value|
98
+ if type =~ /\(\s*1\s*\)/
99
+ value.to_i == 1
100
+ else
101
+ value.to_i
102
+ end
103
+ end
104
+ end
105
+ private :register_default_translators
106
+
107
+ end
108
+
109
+ end
@@ -0,0 +1,62 @@
1
+ require "sqlite3/constants"
2
+
3
+ module SQLite3
4
+
5
+ class Value
6
+ attr_reader :handle
7
+
8
+ def initialize(db, handle)
9
+ @driver = db.driver
10
+ @handle = handle
11
+ end
12
+
13
+ def null?
14
+ type == :null
15
+ end
16
+
17
+ def to_blob
18
+ @driver.value_blob(@handle)
19
+ end
20
+
21
+ def length(utf16 = false)
22
+ if utf16
23
+ @driver.value_bytes16(@handle)
24
+ else
25
+ @driver.value_bytes(@handle)
26
+ end
27
+ end
28
+
29
+ def to_f
30
+ @driver.value_double(@handle)
31
+ end
32
+
33
+ def to_i
34
+ @driver.value_int(@handle)
35
+ end
36
+
37
+ def to_int64
38
+ @driver.value_int64(@handle)
39
+ end
40
+
41
+ def to_s(utf16 = false)
42
+ @driver.value_text(@handle, utf16)
43
+ end
44
+
45
+ def type
46
+ case @driver.value_type(@handle)
47
+ when Constants::ColumnType::INTEGER
48
+ :int
49
+ when Constants::ColumnType::FLOAT
50
+ :float
51
+ when Constants::ColumnType::TEXT
52
+ :text
53
+ when Constants::ColumnType::BLOB
54
+ :blob
55
+ when Constants::ColumnType::NULL
56
+ :null
57
+ end
58
+ end
59
+
60
+ end
61
+
62
+ end
@@ -0,0 +1,14 @@
1
+ module SQLite3
2
+
3
+ module Version
4
+
5
+ MAJOR = 1
6
+ MINOR = 2
7
+ TINY = 4
8
+
9
+ STRING = [ MAJOR, MINOR, TINY ].join(".")
10
+ #:beta-tag:
11
+
12
+ end
13
+
14
+ end
@@ -1,10 +1,11 @@
1
- require 'rubygems'
2
- require 'test/unit'
3
- require 'shoulda'
1
+ require "rubygems"
2
+ gem "test-unit"
3
+ require "test/unit"
4
4
 
5
- $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
5
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), "..", "lib"))
6
6
  $LOAD_PATH.unshift(File.dirname(__FILE__))
7
- require 'sqlite3'
7
+ require "sqlite3"
8
8
 
9
9
  class Test::Unit::TestCase
10
+
10
11
  end
@@ -0,0 +1,26 @@
1
+ require "helper"
2
+
3
+ class TestDatabaseInitialization < Test::Unit::TestCase
4
+ def setup
5
+ @db_filename = "test_database.db"
6
+ File.delete(@db_filename) if File.exists?(@db_filename)
7
+ @db = SQLite3::Database.new(@db_filename)
8
+ end
9
+
10
+ def teardown
11
+ File.delete(@db_filename) if File.exists?(@db_filename)
12
+ end
13
+
14
+ def test_database_file_exists
15
+ assert File.exists?(@db_filename)
16
+ end
17
+
18
+ def test_database_opened
19
+ assert_false @db.closed?
20
+ end
21
+
22
+ def test_database_closing
23
+ @db.close
24
+ assert @db.closed?
25
+ end
26
+ end