sqlite-ruby 2.2.0-mswin32 → 2.2.1-mswin32
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.
- data/doc/faq/faq.html +28 -28
- data/lib/sqlite/resultset.rb +2 -2
- data/lib/sqlite/statement.rb +1 -1
- data/lib/sqlite/version.rb +1 -1
- data/lib/sqlite_api.so +0 -0
- data/test/lib/sqlite.rb +34 -0
- data/test/lib/sqlite/database.rb +682 -0
- data/test/lib/sqlite/parsed_statement.rb +233 -0
- data/test/lib/sqlite/pragmas.rb +236 -0
- data/test/lib/sqlite/resultset.rb +168 -0
- data/test/lib/sqlite/statement.rb +177 -0
- data/test/lib/sqlite/translator.rb +135 -0
- data/test/lib/sqlite/version.rb +45 -0
- data/test/lib/sqlite_api.so +0 -0
- data/test/tc_database.rb +4 -0
- metadata +13 -2
@@ -0,0 +1,233 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
17
|
+
# products derived from this software without specific prior written
|
18
|
+
# permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
24
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
# =============================================================================
|
31
|
+
#++
|
32
|
+
|
33
|
+
require 'strscan'
|
34
|
+
|
35
|
+
module SQLite
|
36
|
+
|
37
|
+
# A ParsedStatement instance represents a tokenized version of an SQL
|
38
|
+
# statement. This makes it possible to do bind variable replacements multiple
|
39
|
+
# times, fairly efficiently.
|
40
|
+
#
|
41
|
+
# Within the SQLite interfaces, this is used only by the Statement class.
|
42
|
+
# However, it could be reused by other SQL-reliant classes easily.
|
43
|
+
class ParsedStatement
|
44
|
+
|
45
|
+
# This represents a textual token in an SQL statement. It is only used by
|
46
|
+
# ParsedStatement.
|
47
|
+
class Token # :nodoc:
|
48
|
+
|
49
|
+
# Create a new Token that encapsulates the given text.
|
50
|
+
def initialize( text )
|
51
|
+
@text = text
|
52
|
+
end
|
53
|
+
|
54
|
+
# Append the given text onto the the contents of this Token.
|
55
|
+
def <<( text )
|
56
|
+
@text << text.to_s
|
57
|
+
end
|
58
|
+
|
59
|
+
# Convert this Token into a string. The +vars+ parameter is ignored.
|
60
|
+
def to_s( vars=nil )
|
61
|
+
@text
|
62
|
+
end
|
63
|
+
|
64
|
+
end
|
65
|
+
|
66
|
+
# This represents a bind variable in a tokenized SQL stream. It is used
|
67
|
+
# only by ParsedStatement.
|
68
|
+
class BindVariable # :nodoc:
|
69
|
+
|
70
|
+
# Create a new BindVariable token encapsulating the given name. The
|
71
|
+
# name is used when looking up a bind variable to bind to the place
|
72
|
+
# holder represented by this token. The name may be either a Fixnum
|
73
|
+
# (in which case it represents an positional placeholder) or a
|
74
|
+
# a String (in which case it represents a named placeholder).
|
75
|
+
def initialize( name )
|
76
|
+
@name = name
|
77
|
+
end
|
78
|
+
|
79
|
+
# Convert the token to a string. If the +vars+ parameter is +nil+, then
|
80
|
+
# the string will be in the format <tt>?nnn</tt> (where _nnn_ is the
|
81
|
+
# index that was used to initialize this token). Otherwise, the +vars+
|
82
|
+
# parameter must be a hash, and the value bound to this token is the
|
83
|
+
# element with the key given to this token when it was created. If that
|
84
|
+
# element is +nil+, this will return the string "NULL". If the element
|
85
|
+
# is a String, then it will be quoted and escaped and returned.
|
86
|
+
# Otherwise, the "to_s" method of the element will be called and the
|
87
|
+
# result returned.
|
88
|
+
def to_s( vars=nil )
|
89
|
+
if vars.nil?
|
90
|
+
":#{@name}"
|
91
|
+
else
|
92
|
+
var = vars[ @name ]
|
93
|
+
case var
|
94
|
+
when nil
|
95
|
+
"NULL"
|
96
|
+
when String
|
97
|
+
"'#{var.gsub(/'/,"''")}'"
|
98
|
+
else
|
99
|
+
var.to_s
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
# The text trailing the first recognized SQL statement that was parsed from
|
107
|
+
# the buffer given to this object. If there was no trailing SQL statement,
|
108
|
+
# this property will be the empty string.
|
109
|
+
attr_reader :trailing
|
110
|
+
|
111
|
+
# Create a new ParsedStatement. This will tokenize the given buffer. As an
|
112
|
+
# optimization, the tokenization is only performed if the string matches
|
113
|
+
# /[?:;]/, otherwise the string is used as-is.
|
114
|
+
def initialize( sql )
|
115
|
+
@bind_values = Hash.new
|
116
|
+
|
117
|
+
if sql.index( /[?:;]/ )
|
118
|
+
@tokens, @trailing = tokenize( sql )
|
119
|
+
else
|
120
|
+
@tokens, @trailing = [ Token.new(sql) ], ""
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Returns an array of the placeholders known to this statement. This will
|
125
|
+
# either be empty (if the statement has no placeholders), or will contain
|
126
|
+
# numbers (indexes) and strings (names).
|
127
|
+
def placeholders
|
128
|
+
@bind_values.keys
|
129
|
+
end
|
130
|
+
|
131
|
+
# Returns the SQL that was given to this parsed statement when it was
|
132
|
+
# created, with bind placeholders intact.
|
133
|
+
def sql
|
134
|
+
@tokens.inject( "" ) { |sql,tok| sql << tok.to_s }
|
135
|
+
end
|
136
|
+
|
137
|
+
# Returns the statement as an SQL string, with all placeholders bound to
|
138
|
+
# their corresponding values.
|
139
|
+
def to_s
|
140
|
+
@tokens.inject( "" ) { |sql,tok| sql << tok.to_s( @bind_values ) }
|
141
|
+
end
|
142
|
+
|
143
|
+
alias :to_str :to_s
|
144
|
+
|
145
|
+
# Binds the given parameters to the placeholders in the statement. It does
|
146
|
+
# this by iterating over each argument and calling #bind_param with the
|
147
|
+
# corresponding index (starting at 1). However, if any element is a hash,
|
148
|
+
# the hash is iterated through and #bind_param called for each key/value
|
149
|
+
# pair. Hash's do not increment the index.
|
150
|
+
def bind_params( *bind_vars )
|
151
|
+
index = 1
|
152
|
+
bind_vars.each do |value|
|
153
|
+
if value.is_a?( Hash )
|
154
|
+
value.each_pair { |key, value| bind_param( key, value ) }
|
155
|
+
else
|
156
|
+
bind_param index, value
|
157
|
+
index += 1
|
158
|
+
end
|
159
|
+
end
|
160
|
+
self
|
161
|
+
end
|
162
|
+
|
163
|
+
# Binds the given value to the placeholder indicated by +param+, which may
|
164
|
+
# be either a Fixnum or a String. If the indicated placeholder does not
|
165
|
+
# exist in the statement, this method does nothing.
|
166
|
+
def bind_param( param, value )
|
167
|
+
return unless @bind_values.has_key?( param )
|
168
|
+
@bind_values[ param ] = value
|
169
|
+
end
|
170
|
+
|
171
|
+
# Tokenizes the given SQL string, returning a tuple containing the array of
|
172
|
+
# tokens (optimized so that each text token contains the longest run of
|
173
|
+
# text possible), and any trailing text that follows the statement.
|
174
|
+
def tokenize( sql )
|
175
|
+
tokens = []
|
176
|
+
|
177
|
+
scanner = StringScanner.new( sql )
|
178
|
+
variable_index = 0
|
179
|
+
|
180
|
+
until scanner.eos?
|
181
|
+
tokens << " " unless tokens.empty? if scanner.scan( /\s+/ )
|
182
|
+
break if scanner.eos?
|
183
|
+
|
184
|
+
if scanner.scan( /;/ )
|
185
|
+
break
|
186
|
+
elsif scanner.scan( /---.*$/ ) || scanner.scan( %r{/\*.*?\*/}m )
|
187
|
+
# comments
|
188
|
+
next
|
189
|
+
elsif scanner.scan( /[-+*\/\w=<>!(),.]+/ )
|
190
|
+
tokens << Token.new( scanner.matched )
|
191
|
+
elsif scanner.scan( /['"]/ )
|
192
|
+
delim = scanner.matched
|
193
|
+
token = delim.dup
|
194
|
+
loop do
|
195
|
+
token << scanner.scan_until( /#{delim}/ )
|
196
|
+
match = scanner.matched
|
197
|
+
break if match.length % 2 == 1
|
198
|
+
end
|
199
|
+
tokens << Token.new( token )
|
200
|
+
elsif scanner.scan( /\?(\d+)?/ )
|
201
|
+
variable_index = ( scanner[1] ? scanner[1].to_i : variable_index+1 )
|
202
|
+
tokens << BindVariable.new( variable_index )
|
203
|
+
@bind_values[variable_index] = nil
|
204
|
+
elsif scanner.scan( /:(\w+):?/ )
|
205
|
+
name = scanner[1]
|
206
|
+
variable_index = name = name.to_i if name !~ /\D/
|
207
|
+
tokens << BindVariable.new( name )
|
208
|
+
@bind_values[name] = nil
|
209
|
+
else
|
210
|
+
raise "unknown token #{scanner.rest.inspect}"
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
# optimize the parsed list
|
215
|
+
tokens.pop while tokens.last == " "
|
216
|
+
optimized = []
|
217
|
+
tokens.each do |tok|
|
218
|
+
last = optimized.last
|
219
|
+
if tok.is_a?( BindVariable ) || last.nil? || last.is_a?( BindVariable )
|
220
|
+
tok = Token.new(tok) if tok == " "
|
221
|
+
optimized << tok
|
222
|
+
else
|
223
|
+
last << tok
|
224
|
+
end
|
225
|
+
end
|
226
|
+
|
227
|
+
return optimized, scanner.rest
|
228
|
+
end
|
229
|
+
private :tokenize
|
230
|
+
|
231
|
+
end
|
232
|
+
|
233
|
+
end
|
@@ -0,0 +1,236 @@
|
|
1
|
+
#--
|
2
|
+
# =============================================================================
|
3
|
+
# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu)
|
4
|
+
# All rights reserved.
|
5
|
+
#
|
6
|
+
# Redistribution and use in source and binary forms, with or without
|
7
|
+
# modification, are permitted provided that the following conditions are met:
|
8
|
+
#
|
9
|
+
# * Redistributions of source code must retain the above copyright notice,
|
10
|
+
# this list of conditions and the following disclaimer.
|
11
|
+
#
|
12
|
+
# * Redistributions in binary form must reproduce the above copyright
|
13
|
+
# notice, this list of conditions and the following disclaimer in the
|
14
|
+
# documentation and/or other materials provided with the distribution.
|
15
|
+
#
|
16
|
+
# * The names of its contributors may not be used to endorse or promote
|
17
|
+
# products derived from this software without specific prior written
|
18
|
+
# permission.
|
19
|
+
#
|
20
|
+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
21
|
+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
22
|
+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
23
|
+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
24
|
+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
25
|
+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
26
|
+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
27
|
+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
28
|
+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
29
|
+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
30
|
+
# =============================================================================
|
31
|
+
#++
|
32
|
+
|
33
|
+
module SQLite
|
34
|
+
|
35
|
+
# This module is intended for inclusion solely by the Database class. It
|
36
|
+
# defines convenience methods for the various pragmas supported by SQLite.
|
37
|
+
#
|
38
|
+
# Two pragmas that have been intentionally excluded are SHOW_DATATYPES
|
39
|
+
# and EMPTY_RESULT_SETS, since these apply only to queries that use the
|
40
|
+
# SQLite "exec" function. The SQLite API does not employ that function,
|
41
|
+
# preferring instead the compile/step/finalize interface.
|
42
|
+
#
|
43
|
+
# However, if you really must have those pragmas, you can always execute
|
44
|
+
# a pragma as if it were an SQL statement.
|
45
|
+
#
|
46
|
+
# For a detailed description of these pragmas, see the SQLite documentation
|
47
|
+
# at http://sqlite.org/lang.html#pragma.
|
48
|
+
module Pragmas
|
49
|
+
|
50
|
+
# Returns +true+ or +false+ depending on the value of the named pragma.
|
51
|
+
def get_boolean_pragma( name )
|
52
|
+
get_first_value( "PRAGMA #{name}" ) != "0"
|
53
|
+
end
|
54
|
+
private :get_boolean_pragma
|
55
|
+
|
56
|
+
# Sets the given pragma to the given boolean value. The value itself
|
57
|
+
# may be +true+ or +false+, or any other commonly used string or
|
58
|
+
# integer that represents truth.
|
59
|
+
def set_boolean_pragma( name, mode )
|
60
|
+
case mode
|
61
|
+
when String
|
62
|
+
case mode.downcase
|
63
|
+
when "on", "yes", "true", "y", "t": mode = "'ON'"
|
64
|
+
when "off", "no", "false", "n", "f": mode = "'OFF'"
|
65
|
+
else
|
66
|
+
raise Exceptions::DatabaseException,
|
67
|
+
"unrecognized pragma parameter #{mode.inspect}"
|
68
|
+
end
|
69
|
+
when true, 1
|
70
|
+
mode = "ON"
|
71
|
+
when false, 0, nil
|
72
|
+
mode = "OFF"
|
73
|
+
else
|
74
|
+
raise Exceptions::DatabaseException,
|
75
|
+
"unrecognized pragma parameter #{mode.inspect}"
|
76
|
+
end
|
77
|
+
|
78
|
+
execute( "PRAGMA #{name}=#{mode}" )
|
79
|
+
end
|
80
|
+
private :set_boolean_pragma
|
81
|
+
|
82
|
+
# Requests the given pragma (and parameters), and if the block is given,
|
83
|
+
# each row of the result set will be yielded to it. Otherwise, the results
|
84
|
+
# are returned as an array.
|
85
|
+
def get_query_pragma( name, *parms, &block ) # :yields: row
|
86
|
+
if parms.empty?
|
87
|
+
execute( "PRAGMA #{name}", &block )
|
88
|
+
else
|
89
|
+
args = "'" + parms.join("','") + "'"
|
90
|
+
execute( "PRAGMA #{name}( #{args} )", &block )
|
91
|
+
end
|
92
|
+
end
|
93
|
+
private :get_query_pragma
|
94
|
+
|
95
|
+
# Return the value of the given pragma.
|
96
|
+
def get_enum_pragma( name )
|
97
|
+
get_first_value( "PRAGMA #{name}" )
|
98
|
+
end
|
99
|
+
private :get_enum_pragma
|
100
|
+
|
101
|
+
# Set the value of the given pragma to +mode+. The +mode+ parameter must
|
102
|
+
# conform to one of the values in the given +enum+ array. Each entry in
|
103
|
+
# the array is another array comprised of elements in the enumeration that
|
104
|
+
# have duplicate values. See #synchronous, #default_synchronous,
|
105
|
+
# #temp_store, and #default_temp_store for usage examples.
|
106
|
+
def set_enum_pragma( name, mode, enums )
|
107
|
+
match = enums.find { |p| p.find { |i| i.to_s.downcase == mode.to_s.downcase } }
|
108
|
+
raise Exceptions::DatabaseException,
|
109
|
+
"unrecognized #{name} #{mode.inspect}" unless match
|
110
|
+
execute( "PRAGMA #{name}='#{match.first.upcase}'" )
|
111
|
+
end
|
112
|
+
private :set_enum_pragma
|
113
|
+
|
114
|
+
# Returns the value of the given pragma as an integer.
|
115
|
+
def get_int_pragma( name )
|
116
|
+
get_first_value( "PRAGMA #{name}" ).to_i
|
117
|
+
end
|
118
|
+
private :get_int_pragma
|
119
|
+
|
120
|
+
# Set the value of the given pragma to the integer value of the +value+
|
121
|
+
# parameter.
|
122
|
+
def set_int_pragma( name, value )
|
123
|
+
execute( "PRAGMA #{name}=#{value.to_i}" )
|
124
|
+
end
|
125
|
+
private :set_int_pragma
|
126
|
+
|
127
|
+
# The enumeration of valid synchronous modes.
|
128
|
+
SYNCHRONOUS_MODES = [ [ 'full', 2 ], [ 'normal', 1 ], [ 'off', 0 ] ]
|
129
|
+
|
130
|
+
# The enumeration of valid temp store modes.
|
131
|
+
TEMP_STORE_MODES = [ [ 'default', 0 ], [ 'file', 1 ], [ 'memory', 2 ] ]
|
132
|
+
|
133
|
+
# Does an integrity check on the database. If the check fails, a
|
134
|
+
# SQLite::Exceptions::DatabaseException will be raised. Otherwise it
|
135
|
+
# returns silently.
|
136
|
+
def integrity_check
|
137
|
+
execute( "PRAGMA integrity_check" ) do |row|
|
138
|
+
raise Exceptions::DatabaseException, row[0] if row[0] != "ok"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
def cache_size
|
143
|
+
get_int_pragma "cache_size"
|
144
|
+
end
|
145
|
+
|
146
|
+
def cache_size=( size )
|
147
|
+
set_int_pragma "cache_size", size
|
148
|
+
end
|
149
|
+
|
150
|
+
def default_cache_size
|
151
|
+
get_int_pragma "default_cache_size"
|
152
|
+
end
|
153
|
+
|
154
|
+
def default_cache_size=( size )
|
155
|
+
set_int_pragma "default_cache_size", size
|
156
|
+
end
|
157
|
+
|
158
|
+
def default_synchronous
|
159
|
+
get_enum_pragma "default_synchronous"
|
160
|
+
end
|
161
|
+
|
162
|
+
def default_synchronous=( mode )
|
163
|
+
set_enum_pragma "default_synchronous", mode, SYNCHRONOUS_MODES
|
164
|
+
end
|
165
|
+
|
166
|
+
def synchronous
|
167
|
+
get_enum_pragma "synchronous"
|
168
|
+
end
|
169
|
+
|
170
|
+
def synchronous=( mode )
|
171
|
+
set_enum_pragma "synchronous", mode, SYNCHRONOUS_MODES
|
172
|
+
end
|
173
|
+
|
174
|
+
def default_temp_store
|
175
|
+
get_enum_pragma "default_temp_store"
|
176
|
+
end
|
177
|
+
|
178
|
+
def default_temp_store=( mode )
|
179
|
+
set_enum_pragma "default_temp_store", mode, TEMP_STORE_MODES
|
180
|
+
end
|
181
|
+
|
182
|
+
def temp_store
|
183
|
+
get_enum_pragma "temp_store"
|
184
|
+
end
|
185
|
+
|
186
|
+
def temp_store=( mode )
|
187
|
+
set_enum_pragma "temp_store", mode, TEMP_STORE_MODES
|
188
|
+
end
|
189
|
+
|
190
|
+
def full_column_names
|
191
|
+
get_boolean_pragma "full_column_names"
|
192
|
+
end
|
193
|
+
|
194
|
+
def full_column_names=( mode )
|
195
|
+
set_boolean_pragma "full_column_names", mode
|
196
|
+
end
|
197
|
+
|
198
|
+
def parser_trace
|
199
|
+
get_boolean_pragma "parser_trace"
|
200
|
+
end
|
201
|
+
|
202
|
+
def parser_trace=( mode )
|
203
|
+
set_boolean_pragma "parser_trace", mode
|
204
|
+
end
|
205
|
+
|
206
|
+
def vdbe_trace
|
207
|
+
get_boolean_pragma "vdbe_trace"
|
208
|
+
end
|
209
|
+
|
210
|
+
def vdbe_trace=( mode )
|
211
|
+
set_boolean_pragma "vdbe_trace", mode
|
212
|
+
end
|
213
|
+
|
214
|
+
def database_list( &block ) # :yields: row
|
215
|
+
get_query_pragma "database_list", &block
|
216
|
+
end
|
217
|
+
|
218
|
+
def foreign_key_list( table, &block ) # :yields: row
|
219
|
+
get_query_pragma "foreign_key_list", table, &block
|
220
|
+
end
|
221
|
+
|
222
|
+
def index_info( index, &block ) # :yields: row
|
223
|
+
get_query_pragma "index_info", index, &block
|
224
|
+
end
|
225
|
+
|
226
|
+
def index_list( table, &block ) # :yields: row
|
227
|
+
get_query_pragma "index_list", table, &block
|
228
|
+
end
|
229
|
+
|
230
|
+
def table_info( table, &block ) # :yields: row
|
231
|
+
get_query_pragma "table_info", table, &block
|
232
|
+
end
|
233
|
+
|
234
|
+
end
|
235
|
+
|
236
|
+
end
|