amalgalite 0.10.1-x86-mingw32

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.
Files changed (100) hide show
  1. data/HISTORY +201 -0
  2. data/LICENSE +29 -0
  3. data/README +51 -0
  4. data/bin/amalgalite-pack +126 -0
  5. data/examples/a.rb +9 -0
  6. data/examples/blob.rb +88 -0
  7. data/examples/bootstrap.rb +36 -0
  8. data/examples/define_aggregate.rb +75 -0
  9. data/examples/define_function.rb +104 -0
  10. data/examples/gem-db.rb +94 -0
  11. data/examples/gems.db +0 -0
  12. data/examples/require_me.rb +11 -0
  13. data/examples/requires.rb +42 -0
  14. data/examples/schema-info.rb +34 -0
  15. data/ext/amalgalite/amalgalite3.c +290 -0
  16. data/ext/amalgalite/amalgalite3.h +151 -0
  17. data/ext/amalgalite/amalgalite3_blob.c +240 -0
  18. data/ext/amalgalite/amalgalite3_constants.c +221 -0
  19. data/ext/amalgalite/amalgalite3_database.c +1148 -0
  20. data/ext/amalgalite/amalgalite3_requires_bootstrap.c +210 -0
  21. data/ext/amalgalite/amalgalite3_statement.c +639 -0
  22. data/ext/amalgalite/extconf.rb +36 -0
  23. data/ext/amalgalite/gen_constants.rb +130 -0
  24. data/ext/amalgalite/sqlite3.c +106729 -0
  25. data/ext/amalgalite/sqlite3.h +5626 -0
  26. data/ext/amalgalite/sqlite3_options.h +4 -0
  27. data/ext/amalgalite/sqlite3ext.h +380 -0
  28. data/gemspec.rb +60 -0
  29. data/lib/amalgalite.rb +43 -0
  30. data/lib/amalgalite/1.8/amalgalite3.so +0 -0
  31. data/lib/amalgalite/1.9/amalgalite3.so +0 -0
  32. data/lib/amalgalite/aggregate.rb +67 -0
  33. data/lib/amalgalite/blob.rb +186 -0
  34. data/lib/amalgalite/boolean.rb +42 -0
  35. data/lib/amalgalite/busy_timeout.rb +47 -0
  36. data/lib/amalgalite/column.rb +97 -0
  37. data/lib/amalgalite/core_ext/kernel/require.rb +21 -0
  38. data/lib/amalgalite/database.rb +947 -0
  39. data/lib/amalgalite/function.rb +61 -0
  40. data/lib/amalgalite/index.rb +43 -0
  41. data/lib/amalgalite/packer.rb +226 -0
  42. data/lib/amalgalite/paths.rb +70 -0
  43. data/lib/amalgalite/profile_tap.rb +131 -0
  44. data/lib/amalgalite/progress_handler.rb +21 -0
  45. data/lib/amalgalite/requires.rb +120 -0
  46. data/lib/amalgalite/schema.rb +191 -0
  47. data/lib/amalgalite/sqlite3.rb +6 -0
  48. data/lib/amalgalite/sqlite3/constants.rb +80 -0
  49. data/lib/amalgalite/sqlite3/database/function.rb +48 -0
  50. data/lib/amalgalite/sqlite3/database/status.rb +68 -0
  51. data/lib/amalgalite/sqlite3/status.rb +60 -0
  52. data/lib/amalgalite/sqlite3/version.rb +37 -0
  53. data/lib/amalgalite/statement.rb +414 -0
  54. data/lib/amalgalite/table.rb +90 -0
  55. data/lib/amalgalite/taps.rb +2 -0
  56. data/lib/amalgalite/taps/console.rb +27 -0
  57. data/lib/amalgalite/taps/io.rb +71 -0
  58. data/lib/amalgalite/trace_tap.rb +35 -0
  59. data/lib/amalgalite/type_map.rb +63 -0
  60. data/lib/amalgalite/type_maps/default_map.rb +167 -0
  61. data/lib/amalgalite/type_maps/storage_map.rb +40 -0
  62. data/lib/amalgalite/type_maps/text_map.rb +22 -0
  63. data/lib/amalgalite/version.rb +37 -0
  64. data/lib/amalgalite/view.rb +26 -0
  65. data/spec/aggregate_spec.rb +169 -0
  66. data/spec/amalgalite_spec.rb +4 -0
  67. data/spec/blob_spec.rb +81 -0
  68. data/spec/boolean_spec.rb +23 -0
  69. data/spec/busy_handler.rb +165 -0
  70. data/spec/database_spec.rb +494 -0
  71. data/spec/default_map_spec.rb +87 -0
  72. data/spec/function_spec.rb +94 -0
  73. data/spec/integeration_spec.rb +111 -0
  74. data/spec/packer_spec.rb +60 -0
  75. data/spec/paths_spec.rb +28 -0
  76. data/spec/progress_handler_spec.rb +105 -0
  77. data/spec/requires_spec.rb +23 -0
  78. data/spec/rtree_spec.rb +71 -0
  79. data/spec/schema_spec.rb +120 -0
  80. data/spec/spec_helper.rb +27 -0
  81. data/spec/sqlite3/constants_spec.rb +65 -0
  82. data/spec/sqlite3/database_status_spec.rb +36 -0
  83. data/spec/sqlite3/status_spec.rb +18 -0
  84. data/spec/sqlite3/version_spec.rb +14 -0
  85. data/spec/sqlite3_spec.rb +53 -0
  86. data/spec/statement_spec.rb +161 -0
  87. data/spec/storage_map_spec.rb +41 -0
  88. data/spec/tap_spec.rb +59 -0
  89. data/spec/text_map_spec.rb +23 -0
  90. data/spec/type_map_spec.rb +17 -0
  91. data/spec/version_spec.rb +15 -0
  92. data/tasks/announce.rake +43 -0
  93. data/tasks/config.rb +107 -0
  94. data/tasks/distribution.rake +77 -0
  95. data/tasks/documentation.rake +32 -0
  96. data/tasks/extension.rake +141 -0
  97. data/tasks/rspec.rake +33 -0
  98. data/tasks/rubyforge.rake +59 -0
  99. data/tasks/utils.rb +80 -0
  100. metadata +237 -0
@@ -0,0 +1,68 @@
1
+ require 'amalgalite/sqlite3/constants'
2
+ module Amalgalite::SQLite3
3
+ class Database
4
+ #
5
+ # A Stat represents a single Database Status code and its current highwater mark.
6
+ #
7
+ # Some stats may not have a current or a highwater value, in those cases
8
+ # the associated _has_current?_ or _has_highwater?_ method returns false and the
9
+ # _current_ or _highwater_ method also returns +nil+.
10
+ #
11
+ class Stat
12
+ attr_reader :name
13
+ attr_reader :code
14
+
15
+ def initialize( api_db, name )
16
+ @name = name
17
+ @code = ::Amalgalite::SQLite3::Constants::DBStatus.value_from_name( name )
18
+ @current = nil
19
+ @highwater = nil
20
+ @api_db = api_db
21
+ end
22
+
23
+ def current
24
+ update!
25
+ return @current
26
+ end
27
+
28
+ def highwater
29
+ update!
30
+ return @highwater
31
+ end
32
+
33
+ #
34
+ # reset the given stat's highwater mark. This will also populate the
35
+ # _@current_ and _@highwater_ instance variables
36
+ #
37
+ def reset!
38
+ update!( true )
39
+ end
40
+ end
41
+
42
+ #
43
+ # Top level Status object holding all the Stat objects indicating the DBStatus
44
+ # of the SQLite3 C library.
45
+ #
46
+ class DBStatus
47
+ ::Amalgalite::SQLite3::Constants::DBStatus.constants.each do |const_name|
48
+ method_name = const_name.downcase
49
+ module_eval( <<-code, __FILE__, __LINE__ )
50
+ def #{method_name}
51
+ @#{method_name} ||= Amalgalite::SQLite3::Database::Stat.new( self.api_db, '#{method_name}' )
52
+ end
53
+ code
54
+ end
55
+
56
+ attr_reader :api_db
57
+
58
+ def initialize( api_db )
59
+ @api_db = api_db
60
+ end
61
+ end
62
+
63
+ # return the DBstatus object for the sqlite database
64
+ def status
65
+ @status ||= DBStatus.new( self )
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,60 @@
1
+ require 'amalgalite/sqlite3/constants'
2
+ module Amalgalite::SQLite3
3
+
4
+ #
5
+ # A Stat represents a single Status code and its current highwater mark.
6
+ #
7
+ # Some stats may not have a current or a highwater value, in those cases
8
+ # the associated _has_current?_ or _has_highwater?_ method returns false and the
9
+ # _current_ or _highwater_ method also returns +nil+.
10
+ #
11
+ class Stat
12
+ attr_reader :name
13
+ attr_reader :code
14
+
15
+ def initialize( name )
16
+ @name = name
17
+ @code = ::Amalgalite::SQLite3::Constants::Status.value_from_name( name )
18
+ @current = nil
19
+ @highwater = nil
20
+ end
21
+
22
+ def current
23
+ update!
24
+ return @current
25
+ end
26
+
27
+ def highwater
28
+ update!
29
+ return @highwater
30
+ end
31
+
32
+ #
33
+ # reset the given stat's highwater mark. This will also populate the
34
+ # _@current_ and _@highwater_ instance variables
35
+ #
36
+ def reset!
37
+ update!( true )
38
+ end
39
+ end
40
+
41
+ #
42
+ # Top level Status object holding all the Stat objects indicating the Status
43
+ # of the SQLite3 C library.
44
+ #
45
+ class Status
46
+ ::Amalgalite::SQLite3::Constants::Status.constants.each do |const_name|
47
+ method_name = const_name.downcase
48
+ module_eval( <<-code, __FILE__, __LINE__ )
49
+ def #{method_name}
50
+ @#{method_name} ||= Amalgalite::SQLite3::Stat.new( '#{method_name}' )
51
+ end
52
+ code
53
+ end
54
+ end
55
+
56
+ # return the status object for the sqlite database
57
+ def self.status
58
+ @status ||= Status.new
59
+ end
60
+ end
@@ -0,0 +1,37 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ module Amalgalite
6
+ module SQLite3
7
+ module Version
8
+ # Sqlite3 version number is equal to
9
+ # MAJOR * 1_000_000 + MINOR * 1_000 + RELEASE
10
+
11
+ # major version number of the SQLite C library
12
+ MAJOR = (to_i / 1_000_000).freeze
13
+
14
+ # minor version number of the SQLite C library
15
+ MINOR = ((to_i % 1_000_000) / 1_000).freeze
16
+
17
+ # release version number of the SQLite C library
18
+ RELEASE = (to_i % 1_000).freeze
19
+
20
+ #
21
+ # call-seq:
22
+ # Amalgalite::SQLite3::Version.to_a -> [ MAJOR, MINOR, RELEASE ]
23
+ #
24
+ # Return the SQLite C library version number as an array of MAJOR, MINOR,
25
+ # RELEASE
26
+ #
27
+ def self.to_a
28
+ [ MAJOR, MINOR, RELEASE ]
29
+ end
30
+
31
+ end
32
+
33
+ # Version of SQLite that ships with Amalgalite
34
+ VERSION = Version.to_s.freeze
35
+ end
36
+ Version.freeze
37
+ end
@@ -0,0 +1,414 @@
1
+ #--
2
+ # Copyright (c) 2008 Jeremy Hinegardner
3
+ # All rights reserved. See LICENSE and/or COPYING for details.
4
+ #++
5
+ #
6
+ require 'date'
7
+ require 'arrayfields'
8
+ require 'ostruct'
9
+
10
+ module Amalgalite
11
+ class Statement
12
+
13
+ include ::Amalgalite::SQLite3::Constants
14
+
15
+ attr_reader :db
16
+ attr_reader :api
17
+
18
+ class << self
19
+ # special column names that indicate that indicate the column is a rowid
20
+ def rowid_column_names
21
+ @rowid_column_names ||= %w[ ROWID OID _ROWID_ ]
22
+ end
23
+ end
24
+
25
+ ##
26
+ # Initialize a new statement on the database.
27
+ #
28
+ def initialize( db, sql )
29
+ @db = db
30
+ #prepare_method = @db.utf16? ? :prepare16 : :prepare
31
+ prepare_method = :prepare
32
+ @param_positions = {}
33
+ @stmt_api = @db.api.send( prepare_method, sql )
34
+ @blobs_to_write = []
35
+ @rowid_index = nil
36
+ @result_meta = nil
37
+ @open = true
38
+ end
39
+
40
+ ##
41
+ # is the statement open for business
42
+ #
43
+ def open?
44
+ @open
45
+ end
46
+
47
+ ##
48
+ # Is the special column "ROWID", "OID", or "_ROWID_" used?
49
+ #
50
+ def using_rowid_column?
51
+ not @rowid_index.nil?
52
+ end
53
+
54
+ ##
55
+ # reset the Statement back to it state right after the constructor returned,
56
+ # except if any variables have been bound to parameters, those are still
57
+ # bound.
58
+ #
59
+ def reset!
60
+ @stmt_api.reset!
61
+ @param_positions = {}
62
+ @blobs_to_write.clear
63
+ @rowid_index = nil
64
+ end
65
+
66
+ ##
67
+ # reset the Statement back to it state right after the constructor returned,
68
+ # AND clear all parameter bindings.
69
+ #
70
+ def reset_and_clear_bindings!
71
+ reset!
72
+ @stmt_api.clear_bindings!
73
+ end
74
+
75
+ ##
76
+ # reset the statment in preparation for executing it again
77
+ #
78
+ def reset_for_next_execute!
79
+ @stmt_api.reset!
80
+ @stmt_api.clear_bindings!
81
+ @blobs_to_write.clear
82
+ end
83
+
84
+ ##
85
+ # Execute the statement with the given parameters
86
+ #
87
+ # If a block is given, then yield each returned row to the block. If no
88
+ # block is given then return all rows from the result. No matter what the
89
+ # prepared statement should be reset before returning the final time.
90
+ #
91
+ def execute( *params )
92
+ bind( *params )
93
+ begin
94
+ # save the error state at the beginning of the execution. We only want to
95
+ # reraise the error if it was raised during this execution.
96
+ s_before = $!
97
+ if block_given? then
98
+ while row = next_row
99
+ yield row
100
+ end
101
+ else
102
+ all_rows
103
+ end
104
+ ensure
105
+ s = $!
106
+ begin
107
+ reset_for_next_execute!
108
+ rescue => e
109
+ end
110
+ raise s if s != s_before
111
+ end
112
+ end
113
+
114
+ ##
115
+ # Bind parameters to the sql statement.
116
+ #
117
+ # Bindings in SQLite can have a number of formats:
118
+ #
119
+ # ?
120
+ # ?num
121
+ # :var
122
+ # @var
123
+ # $var
124
+ #
125
+ # Where 'num' is an Integer and 'var'is an alphanumerical variable.
126
+ # They may exist in the SQL for which this Statement was created.
127
+ #
128
+ # Amalgalite binds parameters to these variables in the following manner:
129
+ #
130
+ # If bind is passed in an Array, either as +bind( "foo", "bar", "baz")+ or
131
+ # as bind( ["foo", "bar", "baz"] ) then each of the params is assumed to be
132
+ # positionally bound to the statement( ?, ?num ).
133
+ #
134
+ # If bind is passed a Hash, either as +bind( :foo => 1, :bar => 'sqlite' )+
135
+ # or as bind( { :foo => 1, 'bar' => 'sqlite' }) then it is assumed that each
136
+ # parameter should be bound as a named parameter (:var, @var, $var).
137
+ #
138
+ # If bind is not passed any parameters, or nil, then nothing happens.
139
+ #
140
+ def bind( *params )
141
+ if params.nil? or params.empty? then
142
+ check_parameter_count!( 0 )
143
+ return nil
144
+ end
145
+
146
+ if params.first.instance_of?( Hash ) then
147
+ bind_named_parameters( params.first )
148
+ else
149
+ bind_positional_parameters( params )
150
+ end
151
+ end
152
+
153
+ ##
154
+ # Bind parameters to the statement based upon named parameters
155
+ #
156
+ def bind_named_parameters( params )
157
+ check_parameter_count!( params.size )
158
+ params.each_pair do | param, value |
159
+ position = param_position_of( param )
160
+ if position > 0 then
161
+ bind_parameter_to( position, value )
162
+ else
163
+ raise Amalgalite::Error, "Unable to find parameter '#{param}' in SQL statement [#{sql}]"
164
+ end
165
+ end
166
+ end
167
+
168
+ ##
169
+ # Bind parameters to the statements based upon positions.
170
+ #
171
+ def bind_positional_parameters( params )
172
+ check_parameter_count!( params.size )
173
+ params.each_with_index do |value, index|
174
+ position = index + 1
175
+ bind_parameter_to( position, value )
176
+ end
177
+ end
178
+
179
+ ##
180
+ # bind a single parameter to a particular position
181
+ #
182
+ def bind_parameter_to( position, value )
183
+ bind_type = db.type_map.bind_type_of( value )
184
+ case bind_type
185
+ when DataType::FLOAT
186
+ @stmt_api.bind_double( position, value )
187
+ when DataType::INTEGER
188
+ @stmt_api.bind_int64( position, value )
189
+ when DataType::NULL
190
+ @stmt_api.bind_null( position )
191
+ when DataType::TEXT
192
+ @stmt_api.bind_text( position, value.to_s )
193
+ when DataType::BLOB
194
+ if value.incremental? then
195
+ @stmt_api.bind_zeroblob( position, value.length )
196
+ @blobs_to_write << value
197
+ else
198
+ @stmt_api.bind_blob( position, value.source )
199
+ end
200
+ else
201
+ raise ::Amalgalite::Error, "Unknown binding type of #{bind_type} from #{db.type_map.class.name}.bind_type_of"
202
+ end
203
+ end
204
+
205
+
206
+ ##
207
+ # Find and cache the binding parameter indexes
208
+ #
209
+ def param_position_of( name )
210
+ ns = name.to_s
211
+ unless pos = @param_positions[ns]
212
+ pos = @param_positions[ns] = @stmt_api.parameter_index( ns )
213
+ end
214
+ return pos
215
+ end
216
+
217
+ ##
218
+ # Check and make sure that the number of parameters aligns with the number
219
+ # that sqlite expects
220
+ #
221
+ def check_parameter_count!( num )
222
+ expected = @stmt_api.parameter_count
223
+ if num != expected then
224
+ raise Amalgalite::Error, "#{sql} has #{expected} parameters, but #{num} were passed to bind."
225
+ end
226
+ return expected
227
+ end
228
+
229
+ ##
230
+ # Write any blobs that have been bound to parameters to the database. This
231
+ # assumes that the blobs go into the last inserted row
232
+ #
233
+ def write_blobs
234
+ unless @blobs_to_write.empty?
235
+ @blobs_to_write.each do |blob|
236
+ blob.write_to_column!
237
+ end
238
+ end
239
+ end
240
+
241
+ ##
242
+ # Iterate over the results of the statement returning each row of results
243
+ # as a hash by +column_name+. The column names are the value after an
244
+ # 'AS' in the query or default chosen by sqlite.
245
+ #
246
+ def each
247
+ while row = next_row
248
+ yield row
249
+ end
250
+ return self
251
+ end
252
+
253
+ ##
254
+ # Return the next row of data, with type conversion as indicated by the
255
+ # Database#type_map
256
+ #
257
+ def next_row
258
+ row = []
259
+ case rc = @stmt_api.step
260
+ when ResultCode::ROW
261
+ result_meta.each_with_index do |col, idx|
262
+ value = nil
263
+ column_type = @stmt_api.column_type( idx )
264
+ case column_type
265
+ when DataType::TEXT
266
+ value = @stmt_api.column_text( idx )
267
+ when DataType::FLOAT
268
+ value = @stmt_api.column_double( idx )
269
+ when DataType::INTEGER
270
+ value = @stmt_api.column_int64( idx )
271
+ when DataType::NULL
272
+ value = nil
273
+ when DataType::BLOB
274
+ # if the rowid column is encountered, then we can use an incremental
275
+ # blob api, otherwise we have to use the all at once version.
276
+ if using_rowid_column? then
277
+ value = Amalgalite::Blob.new( :db_blob => SQLite3::Blob.new( db.api,
278
+ col.schema.db,
279
+ col.schema.table,
280
+ col.schema.name,
281
+ @stmt_api.column_int64( @rowid_index ),
282
+ "r"),
283
+ :column => col.schema)
284
+ else
285
+ value = Amalgalite::Blob.new( :string => @stmt_api.column_blob( idx ), :column => col.schema )
286
+ end
287
+ else
288
+ raise ::Amalgalite::Error, "BUG! : Unknown SQLite column type of #{column_type}"
289
+ end
290
+
291
+ row << db.type_map.result_value_of( col.schema.declared_data_type, value )
292
+ end
293
+ row.fields = result_fields
294
+ when ResultCode::DONE
295
+ row = nil
296
+ write_blobs
297
+ else
298
+ self.close # must close so that the error message is guaranteed to be pushed into the database handler
299
+ # and we can can call last_error_message on it
300
+ msg = "SQLITE ERROR #{rc} (#{Amalgalite::SQLite3::Constants::ResultCode.name_from_value( rc )}) : #{@db.api.last_error_message}"
301
+ raise Amalgalite::SQLite3::Error, msg
302
+ end
303
+ return row
304
+ end
305
+
306
+ ##
307
+ # Return all rows from the statement as one array
308
+ #
309
+ def all_rows
310
+ rows = []
311
+ while row = next_row
312
+ rows << row
313
+ end
314
+ return rows
315
+ end
316
+
317
+ ##
318
+ # Inspect the statement and gather all the meta information about the
319
+ # results, include the name of the column result column and the origin
320
+ # column. The origin column is the original database.table.column the value
321
+ # comes from.
322
+ #
323
+ # The full meta information from teh origin column is also obtained for help
324
+ # in doing type conversion.
325
+ #
326
+ # As iteration over the row meta informatio happens, record if the special
327
+ # "ROWID", "OID", or "_ROWID_" column is encountered. If that column is
328
+ # encountered then we make note of it.
329
+ #
330
+ def result_meta
331
+ unless @result_meta
332
+ meta = []
333
+ column_count.times do |idx|
334
+ column_meta = ::OpenStruct.new
335
+ column_meta.name = @stmt_api.column_name( idx )
336
+
337
+ db_name = @stmt_api.column_database_name( idx )
338
+ tbl_name = @stmt_api.column_table_name( idx )
339
+ col_name = @stmt_api.column_origin_name( idx )
340
+
341
+ column_meta.schema = ::Amalgalite::Column.new( db_name, tbl_name, col_name, idx )
342
+ column_meta.schema.declared_data_type = @stmt_api.column_declared_type( idx )
343
+
344
+ # only check for rowid if we have a table name and it is not the
345
+ # sqlite_master table. We could get recursion in those cases.
346
+ if not using_rowid_column? and tbl_name and tbl_name != 'sqlite_master' and is_column_rowid?( tbl_name, col_name ) then
347
+ @rowid_index = idx
348
+ end
349
+
350
+ meta << column_meta
351
+ end
352
+
353
+ @result_meta = meta
354
+ end
355
+ return @result_meta
356
+ end
357
+
358
+ ##
359
+ # is the column indicated by the Column a 'rowid' column
360
+ #
361
+ def is_column_rowid?( table_name, column_name )
362
+ column_schema = @db.schema.tables[table_name].columns[column_name]
363
+ if column_schema then
364
+ if column_schema.primary_key? and column_schema.declared_data_type and column_schema.declared_data_type.upcase == "INTEGER" then
365
+ return true
366
+ end
367
+ else
368
+ return true if Statement.rowid_column_names.include?( column_name.upcase )
369
+ end
370
+ return false
371
+ end
372
+
373
+ ##
374
+ # Return the array of field names for the result set, the field names are
375
+ # all strings
376
+ #
377
+ def result_fields
378
+ @fields ||= result_meta.collect { |m| m.name }
379
+ end
380
+
381
+ ##
382
+ # Return any unsued SQL from the statement
383
+ #
384
+ def remaining_sql
385
+ @stmt_api.remaining_sql
386
+ end
387
+
388
+
389
+ ##
390
+ # return the number of columns in the result of this query
391
+ #
392
+ def column_count
393
+ @stmt_api.column_count
394
+ end
395
+
396
+ ##
397
+ # return the raw sql that was originally used to prepare the statement
398
+ #
399
+ def sql
400
+ @stmt_api.sql
401
+ end
402
+
403
+ ##
404
+ # Close the statement. The statement is no longer valid for use after it
405
+ # has been closed.
406
+ #
407
+ def close
408
+ if open? then
409
+ @stmt_api.close
410
+ @open = false
411
+ end
412
+ end
413
+ end
414
+ end