amalgalite 0.10.1-x86-mswin32

Sign up to get free protection for your applications and to get access to all the features.
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