module DataObjects # Abstract base class for adapter-specific Command subclasses class Command # The Connection on which the command will be run attr_reader :connection # Create a new Command object on the specified connection def initialize(connection, text) raise ArgumentError.new("+connection+ must be a DataObjects::Connection") unless DataObjects::Connection === connection @connection, @text = connection, text end # Execute this command and return no dataset def execute_non_query(*args) raise NotImplementedError.new end # Execute this command and return a DataObjects::Reader for a dataset def execute_reader(*args) raise NotImplementedError.new end # Assign an array of types for the columns to be returned by this command def set_types(column_types) raise NotImplementedError.new end # Display the command text def to_s @text end private # Escape a string of SQL with a set of arguments. # The first argument is assumed to be the SQL to escape, # the remaining arguments (if any) are assumed to be # values to escape and interpolate. # # ==== Examples # escape_sql("SELECT * FROM zoos") # # => "SELECT * FROM zoos" # # escape_sql("SELECT * FROM zoos WHERE name = ?", "Dallas") # # => "SELECT * FROM zoos WHERE name = `Dallas`" # # escape_sql("SELECT * FROM zoos WHERE name = ? AND acreage > ?", "Dallas", 40) # # => "SELECT * FROM zoos WHERE name = `Dallas` AND acreage > 40" # # ==== Warning # This method is meant mostly for adapters that don't support # bind-parameters. def escape_sql(args) sql = @text.dup vars = args.dup replacements = 0 mismatch = false sql.gsub!(/'[^']*'|"[^"]*"|`[^`]*`|\?/) do |x| next x unless x == '?' replacements += 1 if vars.empty? mismatch = true else var = vars.shift connection.quote_value(var) end end if !vars.empty? || mismatch raise ArgumentError, "Binding mismatch: #{args.size} for #{replacements}" else sql end end end end