require 'win32ole' module Sequel # The ADO adapter provides connectivity to ADO databases in Windows. module ADO class Database < Sequel::Database set_adapter_scheme :ado def initialize(opts) super(opts) opts[:driver] ||= 'SQL Server' case opts[:driver] when 'SQL Server' Sequel.require 'adapters/ado/mssql' extend Sequel::ADO::MSSQL::DatabaseMethods end end # Connect to the database. In addition to the usual database options, # the following options have an effect: # # * :command_timeout - Sets the time in seconds to wait while attempting # to execute a command before cancelling the attempt and generating # an error. Specifically, it sets the ADO CommandTimeout property. # If this property is not set, the default of 30 seconds is used. # * :conn_string - The full ADO connection string. If this is provided, # the usual options are ignored. # * :provider - Sets the Provider of this ADO connection (for example, "SQLOLEDB") def connect(server) opts = server_opts(server) s = opts[:conn_string] || "driver=#{opts[:driver]};server=#{opts[:host]};database=#{opts[:database]}#{";uid=#{opts[:user]};pwd=#{opts[:password]}" if opts[:user]}" handle = WIN32OLE.new('ADODB.Connection') handle.CommandTimeout = opts[:command_timeout] if opts[:command_timeout] handle.Provider = opts[:provider] if opts[:provider] handle.Open(s) handle end def dataset(opts = nil) ADO::Dataset.new(self, opts) end def execute(sql, opts={}) log_info(sql) synchronize(opts[:server]) do |conn| begin r = conn.Execute(sql) yield(r) if block_given? rescue ::WIN32OLERuntimeError => e raise_error(e) end end nil end alias do execute private # The ADO adapter doesn't support transactions, since it appears not to # use a single native connection for each connection in the pool def _transaction(conn) th = Thread.current begin @transactions << th yield conn rescue Sequel::Rollback ensure @transactions.delete(th) end end def connection_pool_default_options super.merge(:pool_convert_exceptions=>false) end def disconnect_connection(conn) conn.Close end end class Dataset < Sequel::Dataset def fetch_rows(sql) execute(sql) do |s| @columns = cols = s.Fields.extend(Enumerable).map{|column| output_identifier(column.Name)} s.getRows.transpose.each{|r| yield cols.inject({}){|m,c| m[c] = r.shift; m}} unless s.eof end end end end end