sqlite-ruby 2.2.0-mswin32 → 2.2.1-mswin32

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,168 @@
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 'sqlite_api'
34
+
35
+ module SQLite
36
+
37
+ # The ResultSet object encapsulates the enumerability of a query's output.
38
+ # It is a simple cursor over the data that the query returns. It will
39
+ # very rarely (if ever) be instantiated directly. Instead, client's should
40
+ # obtain a ResultSet instance via Statement#execute.
41
+ class ResultSet
42
+ include Enumerable
43
+
44
+ # A trivial module for adding a +types+ accessor to an object.
45
+ module TypesContainer
46
+ attr_accessor :types
47
+ end
48
+
49
+ # A trivial module for adding a +fields+ accessor to an object.
50
+ module FieldsContainer
51
+ attr_accessor :fields
52
+ end
53
+
54
+ # An array of the column names for this result set (may be empty)
55
+ attr_reader :columns
56
+
57
+ # An array of the column types for this result set (may be empty)
58
+ attr_reader :types
59
+
60
+ # Create a new ResultSet attached to the given database, using the
61
+ # given sql text.
62
+ def initialize( db, sql )
63
+ @db = db
64
+ @sql = sql
65
+ commence
66
+ end
67
+
68
+ # A convenience method for compiling the virtual machine and stepping
69
+ # to the first row of the result set.
70
+ def commence
71
+ @vm, = API.compile( @db.handle, @sql )
72
+
73
+ @current_row = API.step( @vm )
74
+
75
+ @columns = @current_row[ :columns ]
76
+ @types = @current_row[ :types ]
77
+
78
+ check_eof( @current_row )
79
+ end
80
+ private :commence
81
+
82
+ # A convenience method for checking for EOF.
83
+ def check_eof( row )
84
+ @eof = !row.has_key?( :row )
85
+ end
86
+ private :check_eof
87
+
88
+ # Close the result set. Attempting to perform any operation (including
89
+ # #close) on a closed result set will have undefined results.
90
+ def close
91
+ API.finalize( @vm )
92
+ end
93
+
94
+ # Reset the cursor, so that a result set which has reached end-of-file
95
+ # can be rewound and reiterated. _Note_: this uses an experimental API,
96
+ # which is subject to change. Use at your own risk.
97
+ def reset
98
+ API.finalize( @vm )
99
+ commence
100
+ @eof = false
101
+ end
102
+
103
+ # Query whether the cursor has reached the end of the result set or not.
104
+ def eof?
105
+ @eof
106
+ end
107
+
108
+ # Obtain the next row from the cursor. If there are no more rows to be
109
+ # had, this will return +nil+. If type translation is active on the
110
+ # corresponding database, the values in the row will be translated
111
+ # according to their types.
112
+ #
113
+ # The returned value will be an array, unless Database#results_as_hash has
114
+ # been set to +true+, in which case the returned value will be a hash.
115
+ #
116
+ # For arrays, the column names are accessible via the +fields+ property,
117
+ # and the column types are accessible via the +types+ property.
118
+ #
119
+ # For hashes, the column names are the keys of the hash, and the column
120
+ # types are accessible via the +types+ property.
121
+ def next
122
+ return nil if @eof
123
+
124
+ if @current_row
125
+ result, @current_row = @current_row, nil
126
+ else
127
+ result = API.step( @vm )
128
+ check_eof( result )
129
+ end
130
+
131
+ unless @eof
132
+ row = result[:row]
133
+
134
+ if @db.type_translation
135
+ row = @types.zip( row ).map do |type, value|
136
+ @db.translator.translate( type, value )
137
+ end
138
+ end
139
+
140
+ if @db.results_as_hash
141
+ new_row = Hash[ *( @columns.zip( row ).flatten ) ]
142
+ row.each_with_index { |value,idx| new_row[idx] = value }
143
+ row = new_row
144
+ else
145
+ row.extend FieldsContainer unless row.respond_to?(:fields)
146
+ row.fields = @columns
147
+ end
148
+
149
+ row.extend TypesContainer
150
+ row.types = @types
151
+
152
+ return row
153
+ end
154
+
155
+ nil
156
+ end
157
+
158
+ # Required by the Enumerable mixin. Provides an internal iterator over the
159
+ # rows of the result set.
160
+ def each
161
+ while row=self.next
162
+ yield row
163
+ end
164
+ end
165
+
166
+ end
167
+
168
+ end
@@ -0,0 +1,177 @@
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 'sqlite_api'
34
+ require 'sqlite/resultset'
35
+ require 'sqlite/parsed_statement'
36
+
37
+ module SQLite
38
+
39
+ # A statement represents a prepared-but-unexecuted SQL query. It will rarely
40
+ # (if ever) be instantiated directly by a client, and is most often obtained
41
+ # via the Database#prepare method.
42
+ class Statement
43
+
44
+ # This is any text that followed the first valid SQL statement in the text
45
+ # with which the statement was initialized. If there was no trailing text,
46
+ # this will be the empty string.
47
+ attr_reader :remainder
48
+
49
+ # Create a new statement attached to the given Database instance, and which
50
+ # encapsulates the given SQL text. If the text contains more than one
51
+ # statement (i.e., separated by semicolons), then the #remainder property
52
+ # will be set to the trailing text.
53
+ def initialize( db, sql )
54
+ @db = db
55
+ @statement = ParsedStatement.new( sql )
56
+ @remainder = @statement.trailing.strip
57
+ @sql = @statement.to_s
58
+ end
59
+
60
+ # Binds the given variables to the corresponding placeholders in the SQL
61
+ # text.
62
+ #
63
+ # See Database#execute for a description of the valid placeholder
64
+ # syntaxes.
65
+ #
66
+ # Example:
67
+ #
68
+ # stmt = db.prepare( "select * from table where a=? and b=?" )
69
+ # stmt.bind_params( 15, "hello" )
70
+ #
71
+ # See also #execute, #bind_param, Statement#bind_param, and
72
+ # Statement#bind_params.
73
+ def bind_params( *bind_vars )
74
+ @statement.bind_params( *bind_vars )
75
+ end
76
+
77
+ # Binds value to the named (or positional) placeholder. If +param+ is a
78
+ # Fixnum, it is treated as an index for a positional placeholder.
79
+ # Otherwise it is used as the name of the placeholder to bind to.
80
+ #
81
+ # See also #bind_params.
82
+ def bind_param( param, value )
83
+ @statement.bind_param( param, value )
84
+ end
85
+
86
+ # Execute the statement. This creates a new ResultSet object for the
87
+ # statement's virtual machine. If a block was given, the new ResultSet will
88
+ # be yielded to it and then closed; otherwise, the ResultSet will be
89
+ # returned. In that case, it is the client's responsibility to close the
90
+ # ResultSet.
91
+ #
92
+ # Any parameters will be bound to the statement using #bind_params.
93
+ #
94
+ # Example:
95
+ #
96
+ # stmt = db.prepare( "select * from table" )
97
+ # stmt.execute do |result|
98
+ # ...
99
+ # end
100
+ #
101
+ # See also #bind_params, #execute!.
102
+ def execute( *bind_vars )
103
+ bind_params *bind_vars unless bind_vars.empty?
104
+ results = ResultSet.new( @db, @statement.to_s )
105
+
106
+ if block_given?
107
+ begin
108
+ yield results
109
+ ensure
110
+ results.close
111
+ end
112
+ else
113
+ return results
114
+ end
115
+ end
116
+
117
+ # Execute the statement. If no block was given, this returns an array of
118
+ # rows returned by executing the statement. Otherwise, each row will be
119
+ # yielded to the block and then closed.
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 |row|
127
+ # ...
128
+ # end
129
+ #
130
+ # See also #bind_params, #execute.
131
+ def execute!( *bind_vars )
132
+ result = execute( *bind_vars )
133
+ rows = [] unless block_given?
134
+ while row = result.next
135
+ if block_given?
136
+ yield row
137
+ else
138
+ rows << row
139
+ end
140
+ end
141
+ rows
142
+ ensure
143
+ result.close if result
144
+ end
145
+
146
+ # Return an array of the column names for this statement. Note that this
147
+ # may execute the statement in order to obtain the metadata; this makes it
148
+ # a (potentially) expensive operation.
149
+ def columns
150
+ get_metadata unless @columns
151
+ return @columns
152
+ end
153
+
154
+ # Return an array of the data types for each column in this statement. Note
155
+ # that this may execute the statement in order to obtain the metadata; this
156
+ # makes it a (potentially) expensive operation.
157
+ def types
158
+ get_metadata unless @types
159
+ return @types
160
+ end
161
+
162
+ # A convenience method for obtaining the metadata about the query. Note
163
+ # that this will actually execute the SQL, which means it can be a
164
+ # (potentially) expensive operation.
165
+ def get_metadata
166
+ vm, rest = API.compile( @db.handle, @statement.to_s )
167
+ result = API.step( vm )
168
+ API.finalize( vm )
169
+
170
+ @columns = result[:columns]
171
+ @types = result[:types]
172
+ end
173
+ private :get_metadata
174
+
175
+ end
176
+
177
+ end
@@ -0,0 +1,135 @@
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 'time'
34
+
35
+ module SQLite
36
+
37
+ # The Translator class encapsulates the logic and callbacks necessary for
38
+ # converting string data to a value of some specified type. Every Database
39
+ # instance may have a Translator instance, in order to assist in type
40
+ # translation (Database#type_translation).
41
+ #
42
+ # Further, applications may define their own custom type translation logic
43
+ # by registering translator blocks with the corresponding database's
44
+ # translator instance (Database#translator).
45
+ class Translator
46
+
47
+ # Create a new Translator instance. It will be preinitialized with default
48
+ # translators for most SQL data types.
49
+ def initialize
50
+ @translators = Hash.new( proc { |type,value| value } )
51
+ register_default_translators
52
+ end
53
+
54
+ # Add a new translator block, which will be invoked to process type
55
+ # translations to the given type. The type should be an SQL datatype, and
56
+ # may include parentheses (i.e., "VARCHAR(30)"). However, any parenthetical
57
+ # information is stripped off and discarded, so type translation decisions
58
+ # are made solely on the "base" type name.
59
+ #
60
+ # The translator block itself should accept two parameters, "type" and
61
+ # "value". In this case, the "type" is the full type name (including
62
+ # parentheses), so the block itself may include logic for changing how a
63
+ # type is translated based on the additional data. The "value" parameter
64
+ # is the (string) data to convert.
65
+ #
66
+ # The block should return the translated value.
67
+ def add_translator( type, &block ) # :yields: type, value
68
+ @translators[ type_name( type ) ] = block
69
+ end
70
+
71
+ # Translate the given string value to a value of the given type. In the
72
+ # absense of an installed translator block for the given type, the value
73
+ # itself is always returned. Further, +nil+ values are never translated,
74
+ # and are always passed straight through regardless of the type parameter.
75
+ def translate( type, value )
76
+ unless value.nil?
77
+ @translators[ type_name( type ) ].call( type, value )
78
+ end
79
+ end
80
+
81
+ # A convenience method for working with type names. This returns the "base"
82
+ # type name, without any parenthetical data.
83
+ def type_name( type )
84
+ type = $1 if type =~ /^(.*?)\(/
85
+ type.upcase
86
+ end
87
+ private :type_name
88
+
89
+ # Register the default translators for the current Translator instance.
90
+ # This includes translators for most major SQL data types.
91
+ def register_default_translators
92
+ [ "date",
93
+ "datetime",
94
+ "time" ].each { |type| add_translator( type ) { |t,v| Time.parse( v ) } }
95
+
96
+ [ "decimal",
97
+ "float",
98
+ "numeric",
99
+ "double",
100
+ "real",
101
+ "dec",
102
+ "fixed" ].each { |type| add_translator( type ) { |t,v| v.to_f } }
103
+
104
+ [ "integer",
105
+ "smallint",
106
+ "mediumint",
107
+ "int",
108
+ "bigint" ].each { |type| add_translator( type ) { |t,v| v.to_i } }
109
+
110
+ [ "bit",
111
+ "bool",
112
+ "boolean" ].each do |type|
113
+ add_translator( type ) do |t,v|
114
+ !( v.strip.gsub(/00+/,"0") == "0" ||
115
+ v.downcase == "false" ||
116
+ v.downcase == "f" ||
117
+ v.downcase == "no" ||
118
+ v.downcase == "n" )
119
+ end
120
+ end
121
+
122
+ add_translator( "timestamp" ) { |type, value| Time.at( value.to_i ) }
123
+ add_translator( "tinyint" ) do |type, value|
124
+ if type =~ /\(\s*1\s*\)/
125
+ value.to_i == 1
126
+ else
127
+ value.to_i
128
+ end
129
+ end
130
+ end
131
+ private :register_default_translators
132
+
133
+ end
134
+
135
+ end