diff options
author | Jamis Buck <jamis@37signals.com> | 2005-01-09 15:04:16 +0000 |
---|---|---|
committer | Jamis Buck <jamis@37signals.com> | 2005-01-09 15:04:16 +0000 |
commit | c51fa9fc3d7c0ec6f59bcaf3d352fae9325857e2 (patch) | |
tree | fa6c5fdee64da937145d594348100b03225a4207 /lib/sqlite3/statement.rb | |
download | third_party-sqlite3-ruby-c51fa9fc3d7c0ec6f59bcaf3d352fae9325857e2.tar.gz third_party-sqlite3-ruby-c51fa9fc3d7c0ec6f59bcaf3d352fae9325857e2.tar.xz third_party-sqlite3-ruby-c51fa9fc3d7c0ec6f59bcaf3d352fae9325857e2.zip |
Changed layout to support tagging and branching
Diffstat (limited to 'lib/sqlite3/statement.rb')
-rw-r--r-- | lib/sqlite3/statement.rb | 218 |
1 files changed, 218 insertions, 0 deletions
diff --git a/lib/sqlite3/statement.rb b/lib/sqlite3/statement.rb new file mode 100644 index 0000000..8d2e3d5 --- /dev/null +++ b/lib/sqlite3/statement.rb @@ -0,0 +1,218 @@ +#-- +# ============================================================================= +# Copyright (c) 2004, Jamis Buck (jgb3@email.byu.edu) +# All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are met: +# +# * Redistributions of source code must retain the above copyright notice, +# this list of conditions and the following disclaimer. +# +# * Redistributions in binary form must reproduce the above copyright +# notice, this list of conditions and the following disclaimer in the +# documentation and/or other materials provided with the distribution. +# +# * The names of its contributors may not be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE +# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL +# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, +# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# ============================================================================= +#++ + +require 'sqlite3/errors' +require 'sqlite3/resultset' + +class String + def to_blob + SQLite3::Blob.new( self ) + end +end + +module SQLite3 + + # A class for differentiating between strings and blobs, when binding them + # into statements. + class Blob < String; end + + # A statement represents a prepared-but-unexecuted SQL query. It will rarely + # (if ever) be instantiated directly by a client, and is most often obtained + # via the Database#prepare method. + class Statement + + # This is any text that followed the first valid SQL statement in the text + # with which the statement was initialized. If there was no trailing text, + # this will be the empty string. + attr_reader :remainder + + # The underlying opaque handle used to access the SQLite @driver. + attr_reader :handle + + # Create a new statement attached to the given Database instance, and which + # encapsulates the given SQL text. If the text contains more than one + # statement (i.e., separated by semicolons), then the #remainder property + # will be set to the trailing text. + def initialize( db, sql, utf16=false ) + @db = db + @driver = @db.driver + result, @handle, @remainder = @driver.prepare( @db.handle, sql ) + Error.check( result, @db ) + end + + def close + @driver.finalize( @handle ) + end + + # Binds the given variables to the corresponding placeholders in the SQL + # text. + # + # See Database#execute for a description of the valid placeholder + # syntaxes. + # + # Example: + # + # stmt = db.prepare( "select * from table where a=? and b=?" ) + # stmt.bind_params( 15, "hello" ) + # + # See also #execute, #bind_param, Statement#bind_param, and + # Statement#bind_params. + def bind_params( *bind_vars ) + index = 1 + bind_vars.each do |var| + if Hash === var + var.each { |key, val| bind_param key, val } + else + bind_param index, var + index += 1 + end + end + end + + # Binds value to the named (or positional) placeholder. If +param+ is a + # Fixnum, it is treated as an index for a positional placeholder. + # Otherwise it is used as the name of the placeholder to bind to. + # + # See also #bind_params. + def bind_param( param, value ) + if Fixnum === param + case value + when Integer then + @driver.bind_int( @handle, param, value ) + when Numeric then + @driver.bind_double( @handle, param, value.to_f ) + when Blob then + @driver.bind_blob( @handle, param, value ) + when nil then + @driver.bind_null( @handle, param ) + else + @driver.bind_text( @handle, param, value ) + end + else + index = @driver.bind_parameter_index( + @handle, param.to_s ) + raise Exception, "no such bind parameter '#{param}'" if index == 0 + bind_param index, value + end + end + + # Execute the statement. This creates a new ResultSet object for the + # statement's virtual machine. If a block was given, the new ResultSet will + # be yielded to it; otherwise, the ResultSet will be returned. + # + # Any parameters will be bound to the statement using #bind_params. + # + # Example: + # + # stmt = db.prepare( "select * from table" ) + # stmt.execute do |result| + # ... + # end + # + # See also #bind_params, #execute!. + def execute( *bind_vars ) + @driver.reset( @handle ) if @results + + bind_params *bind_vars unless bind_vars.empty? + @results = ResultSet.new( @db, self ) + + if block_given? + yield @results + else + return @results + end + end + + # Execute the statement. If no block was given, this returns an array of + # rows returned by executing the statement. Otherwise, each row will be + # yielded to the block. + # + # Any parameters will be bound to the statement using #bind_params. + # + # Example: + # + # stmt = db.prepare( "select * from table" ) + # stmt.execute! do |row| + # ... + # end + # + # See also #bind_params, #execute. + def execute!( *bind_vars ) + result = execute( *bind_vars ) + rows = [] unless block_given? + while row = result.next + if block_given? + yield row + else + rows << row + end + end + rows + end + + # Return an array of the column names for this statement. Note that this + # may execute the statement in order to obtain the metadata; this makes it + # a (potentially) expensive operation. + def columns + get_metadata unless @columns + return @columns + end + + # Return an array of the data types for each column in this statement. Note + # that this may execute the statement in order to obtain the metadata; this + # makes it a (potentially) expensive operation. + def types + get_metadata unless @types + return @types + end + + # A convenience method for obtaining the metadata about the query. Note + # that this will actually execute the SQL, which means it can be a + # (potentially) expensive operation. + def get_metadata + @columns = [] + @types = [] + + column_count = @driver.column_count( @handle ) + column_count.times do |column| + @columns << @driver.column_name( @handle, column ) + @types << @driver.column_decltype( @handle, column ) + end + + @columns.freeze + @types.freeze + end + private :get_metadata + + end + +end |