In ADO mode, this adapter will ONLY work on Windows systems, since it relies on Win32OLE, which, to my knowledge, is only available on Windows.

This mode also relies on the ADO support in the DBI module. If you are using the one-click installer of Ruby, then you already have DBI installed, but the ADO module is NOT installed. You will need to get the latest source distribution of Ruby-DBI from ruby-dbi.sourceforge.net/ unzip it, and copy the file src/lib/dbd_ado/ADO.rb to X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb (you will more than likely need to create the ADO directory). Once you’ve installed that file, you are ready to go.

In ODBC mode, the adapter requires the ODBC support in the DBI module which requires the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing, and it is available at www.ch-werner.de/rubyodbc/

Options:

  • :mode -- ADO or ODBC. Defaults to ADO.

  • :username -- Defaults to sa.

  • :password -- Defaults to empty string.

ADO specific options:

  • :host -- Defaults to localhost.

  • :database -- The name of the database. No default, must be provided.

ODBC specific options:

  • :dsn -- Defaults to nothing.

ADO code tested on Windows 2000 and higher systems, running ruby 1.8.2 (2004-07-29) [i386-mswin32], and SQL Server 2000 SP3.

ODBC code tested on a Fedora Core 4 system, running FreeTDS 0.63, unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.

Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux
Methods
A
B
C
D
E
I
N
Q
R
T
U
Class Public methods
new(connection, logger, connection_options=nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 189
def initialize(connection, logger, connection_options=nil)
  super(connection, logger)
  @connection_options = connection_options
end
Instance Public methods
active?()

Returns true if the connection is active.

# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 234
def active?
  @connection.execute("SELECT 1").finish
  true
rescue DBI::DatabaseError, DBI::InterfaceError
  false
end
adapter_name()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 211
def adapter_name
  'SQLServer'
end
add_column(table_name, column_name, type, options = {})

Adds a new column to the named table. See ActiveRecord::ConnectionAdapters::TableDefinition#column for details of the options you can use.

# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 448
def add_column(table_name, column_name, type, options = {})
  add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  add_column_options!(add_column_sql, options)
  # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
  # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date       
  execute(add_column_sql)
end
add_limit_offset!(sql, options)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 364
def add_limit_offset!(sql, options)
  if options[:limit] and options[:offset]
    total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally")[0][:TotalRows].to_i
    if (options[:limit] + options[:offset]) >= total_rows
      options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
    end
    sql.sub!(%r^\s*SELECT(\s+DISTINCT)?/, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]} ")
    sql << ") AS tmp1"
    if options[:order]
      options[:order] = options[:order].split(',').map do |field|
        parts = field.split(" ")
        tc = parts[0]
        if sql =~ %r\.\[/ and tc =~ %r\./ # if column quoting used in query
          tc.gsub!(%r\./, '\.\[')
          tc << '\]'
        end
        if sql =~ %r#{tc} AS (t\d_r\d\d?)/
          parts[0] = $1
        elsif parts[0] =~ %r\w+\.(\w+)/
          parts[0] = $1
        end
        parts.join(' ')
      end.join(', ')
      sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
    else
      sql << " ) AS tmp2"
    end
  elsif sql !~ %r^\s*SELECT (@@|COUNT\()/
    sql.sub!(%r^\s*SELECT(\s+DISTINCT)?/) do
      "SELECT#{$1} TOP #{options[:limit]}"
    end unless options[:limit].nil?
  end
end
begin_db_transaction()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 326
def begin_db_transaction
  @connection["AutoCommit"] = false
rescue Exception => e
  @connection["AutoCommit"] = true
end
columns(table_name, name = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 256
def columns(table_name, name = nil)
  return [] if table_name.blank?
  table_name = table_name.to_s if table_name.is_a?(Symbol)
  table_name = table_name.split('.')[-1] unless table_name.nil?
  table_name = table_name.gsub(%r[\[\]]/, '')
  sql = %Q{
    SELECT 
      cols.COLUMN_NAME as ColName,  
      cols.COLUMN_DEFAULT as DefaultValue,
      cols.NUMERIC_SCALE as numeric_scale,
      cols.NUMERIC_PRECISION as numeric_precision, 
      cols.DATA_TYPE as ColType, 
      cols.IS_NULLABLE As IsNullable,  
      COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,  
      COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,  
      cols.NUMERIC_SCALE as Scale 
    FROM INFORMATION_SCHEMA.COLUMNS cols 
    WHERE cols.TABLE_NAME = '#{table_name}'   
  }
  # Comment out if you want to have the Columns select statment logged.
  # Personally, I think it adds unnecessary bloat to the log. 
  # If you do comment it out, make sure to un-comment the "result" line that follows
  result = log(sql, name) { @connection.select_all(sql) }
  #result = @connection.select_all(sql)
  columns = []
  result.each do |field|
    default = field[:DefaultValue].to_s.gsub!(%r[()\']/,"") =~ %rnull/ ? nil : field[:DefaultValue]
    if field[:ColType] =~ %rnumeric|decimal/
      type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})"
    else
      type = "#{field[:ColType]}(#{field[:Length]})"
    end
    is_identity = field[:IsIdentity] == 1
    is_nullable = field[:IsNullable] == 'YES'
    columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable)
  end
  columns
end
commit_db_transaction()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 332
def commit_db_transaction
  @connection.commit
ensure
  @connection["AutoCommit"] = true
end
create_database(name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 407
def create_database(name)
  execute "CREATE DATABASE #{name}"
end
current_database()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 411
def current_database
  @connection.select_one("select DB_NAME()")[0]
end
delete(sql, name = nil)
disconnect!()

Disconnects from the database

# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 252
def disconnect!
  @connection.disconnect rescue nil
end
drop_database(name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 403
def drop_database(name)
  execute "DROP DATABASE #{name}"
end
execute(sql, name = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 308
def execute(sql, name = nil)
  if sql =~ %r^\s*INSERT/ && (table_name = query_requires_identity_insert?(sql))
    log(sql, name) do
      with_identity_insert_enabled(table_name) do 
        @connection.execute(sql) do |handle|
          yield(handle) if block_given?
        end
      end
    end
  else
    log(sql, name) do
      @connection.execute(sql) do |handle|
        yield(handle) if block_given?
      end
    end
  end
end
indexes(table_name, name = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 425
def indexes(table_name, name = nil)
  ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = false
  indexes = []        
  execute("EXEC sp_helpindex '#{table_name}'", name) do |sth|
    sth.each do |index| 
      unique = index[1] =~ %runique/
      primary = index[1] =~ %rprimary key/
      if !primary
        indexes << IndexDefinition.new(table_name, index[0], unique, index[2].split(", "))
      end
    end
  end
  indexes
  ensure
    ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = true
end
insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 295
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  execute(sql, name)
  id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
end
native_database_types()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 194
def native_database_types
  {
    :primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
    :string      => { :name => "varchar", :limit => 255  },
    :text        => { :name => "text" },
    :integer     => { :name => "int" },
    :float       => { :name => "float", :limit => 8 },
    :decimal     => { :name => "decimal" },
    :datetime    => { :name => "datetime" },
    :timestamp   => { :name => "datetime" },
    :time        => { :name => "datetime" },
    :date        => { :name => "datetime" },
    :binary      => { :name => "image"},
    :boolean     => { :name => "bit"}
  }
end
quote(value, column = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 344
def quote(value, column = nil)
  return value.quoted_id if value.respond_to?(:quoted_id)

  case value
    when TrueClass             then '1'
    when FalseClass            then '0'
    when Time, DateTime        then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
    when Date                  then "'#{value.strftime("%Y%m%d")}'"
    else                       super
  end
end
quote_column_name(name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 360
def quote_column_name(name)
  "[#{name}]"
end
quote_string(string)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 356
def quote_string(string)
  string.gsub(%r\'/, "''")
end
reconnect!()

Reconnects to the database, returns false if no connection could be made.

# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 242
def reconnect!
  disconnect!
  @connection = DBI.connect(*@connection_options)
rescue DBI::DatabaseError => e
  @logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
  false
end
recreate_database(name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 398
def recreate_database(name)
  drop_database(name)
  create_database(name)
end
remove_check_constraints(table_name, column_name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 485
def remove_check_constraints(table_name, column_name)
  # TODO remove all constraints in single method
  constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
  constraints.each do |constraint|
    execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
  end
end
remove_column(table_name, column_name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 471
def remove_column(table_name, column_name)
  remove_check_constraints(table_name, column_name)
  remove_default_constraint(table_name, column_name)
  execute "ALTER TABLE [#{table_name}] DROP COLUMN [#{column_name}]"
end
remove_default_constraint(table_name, column_name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 477
def remove_default_constraint(table_name, column_name)
  constraints = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
  
  constraints.each do |constraint|
    execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
  end
end
remove_index(table_name, options = {})
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 493
def remove_index(table_name, options = {})
  execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
end
rename_column(table, column, new_column_name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 456
def rename_column(table, column, new_column_name)
  execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
end
rename_table(name, new_name)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 442
def rename_table(name, new_name)
  execute "EXEC sp_rename '#{name}', '#{new_name}'"
end
rollback_db_transaction()
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 338
def rollback_db_transaction
  @connection.rollback
ensure
  @connection["AutoCommit"] = true
end
tables(name = nil)
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 415
def tables(name = nil)
  execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", name) do |sth|
    sth.inject([]) do |tables, field|
      table_name = field[0]
      tables << table_name unless table_name == 'dtproperties'
      tables
    end
  end
end
update(sql, name = nil)
Also aliased as: delete
# File rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 300
def update(sql, name = nil)
  execute(sql, name) do |handle|
    handle.rows
  end || select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]        
end