Class: ActiveRecord::ConnectionAdapters::SQLServerAdapter

Inherits:
AbstractAdapter
  • Object
show all
Defined in:
lib/active_record/connection_adapters/sqlserver_adapter.rb

Overview

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 from 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.

  • :windows_auth – Defaults to “User ID=#username;Password=#password”

ADO specific options:

  • :host – Defaults to localhost.

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

  • :windows_auth – Use windows authentication instead of username/password.

ODBC specific options:

  • :dsn – Defaults to nothing.

Constant Summary collapse

ADAPTER_NAME =
'SQLServer'.freeze
VERSION =
'2.2.17'.freeze
DATABASE_VERSION_REGEXP =
/Microsoft SQL Server\s+(\d{4})/
SUPPORTED_VERSIONS =
[2000,2005].freeze
LIMITABLE_TYPES =
['string','integer','float','char','nchar','varchar','nvarchar'].freeze
LOST_CONNECTION_EXCEPTIONS =
[DBI::DatabaseError, DBI::InterfaceError]
LOST_CONNECTION_MESSAGES =
[
'Communication link failure',
'Read from the server failed',
'Write to the server failed',
'Database connection was already closed']
VALID_ISOLATION_LEVELS =
["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(logger, connection_options) ⇒ SQLServerAdapter

Returns a new instance of SQLServerAdapter.



205
206
207
208
209
210
211
212
213
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 205

def initialize(logger, connection_options)
  @connection_options = connection_options
  connect
  super(raw_connection, logger)
  initialize_sqlserver_caches
  unless SUPPORTED_VERSIONS.include?(database_year)
    raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported."
  end
end

Class Method Details

.type_limitable?(type) ⇒ Boolean

Returns:

  • (Boolean)


199
200
201
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 199

def type_limitable?(type)
  LIMITABLE_TYPES.include?(type.to_s)
end

Instance Method Details

#active?Boolean

CONNECTION MANAGEMENT ====================================#

Returns:

  • (Boolean)


343
344
345
346
347
348
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 343

def active?
  raw_connection.execute("SELECT 1").finish
  true
rescue *LOST_CONNECTION_EXCEPTIONS
  false
end

#adapter_nameObject

ABSTRACT ADAPTER =========================================#



217
218
219
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 217

def adapter_name
  ADAPTER_NAME
end

#add_column(table_name, column_name, type, options = {}) ⇒ Object



630
631
632
633
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 630

def add_column(table_name, column_name, type, options = {})
  super
  remove_sqlserver_columns_cache_for(table_name)
end

#add_limit_offset!(sql, options) ⇒ Object



439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 439

def add_limit_offset!(sql, options)
  # Validate and/or convert integers for :limit and :offets options.
  if options[:offset]
    raise ArgumentError, "offset should have a limit" unless options[:limit]
    unless options[:offset].kind_of?(Integer)
      if options[:offset] =~ /^\d+$/
        options[:offset] = options[:offset].to_i
      else
        raise ArgumentError, "offset should be an integer"
      end
    end
  end
  if options[:limit] && !(options[:limit].kind_of?(Integer))
    if options[:limit] =~ /^\d+$/
      options[:limit] = options[:limit].to_i
    else
      raise ArgumentError, "limit should be an integer"
    end
  end
  # The business of adding limit/offset
  if options[:limit] and options[:offset]
    tally_sql = "SELECT count(*) as TotalRows from (#{sql.sub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally"
    add_lock! tally_sql, options
    total_rows = select_value(tally_sql).to_i
    if (options[:limit] + options[:offset]) >= total_rows
      options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
    end
    # Make sure we do not need a special limit/offset for association limiting. http://gist.github.com/25118
    add_limit_offset_for_association_limiting!(sql,options) and return if sql_for_association_limiting?(sql)
    # Wrap the SQL query in a bunch of outer SQL queries that emulate proper LIMIT,OFFSET support.
    sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]}")
    sql << ") AS tmp1"
    if options[:order]
      order = options[:order].split(',').map do |field|
        order_by_column, order_direction = field.split(" ")
        order_by_column = quote_column_name(order_by_column)
        # Investigate the SQL query to figure out if the order_by_column has been renamed.
        if sql =~ /#{Regexp.escape(order_by_column)} AS (t\d_r\d\d?)/
          # Fx "[foo].[bar] AS t4_r2" was found in the SQL. Use the column alias (ie 't4_r2') for the subsequent orderings
          order_by_column = $1
        elsif order_by_column =~ /\w+\.\[?(\w+)\]?/
          order_by_column = $1
        else
          # It doesn't appear that the column name has been renamed as part of the query. Use just the column
          # name rather than the full identifier for the outer queries.
          order_by_column = order_by_column.split('.').last
        end
        # Put the column name and eventual direction back together
        [order_by_column, order_direction].join(' ').strip
      end.join(', ')
      sql << " ORDER BY #{change_order_direction(order)}) AS tmp2 ORDER BY #{order}"
    else
      sql << ") AS tmp2"
    end
  elsif options[:limit] && sql !~ /^\s*SELECT (@@|COUNT\()/i
    if md = sql.match(/^(\s*SELECT)(\s+DISTINCT)?(.*)/im)
      sql.replace "#{md[1]}#{md[2]} TOP #{options[:limit]}#{md[3]}"
    else
      # Account for building SQL fragments without SELECT yet. See #update_all and #limited_update_conditions.
      sql.replace "TOP #{options[:limit]} #{sql}"
    end
  end
end

#add_lock!(sql, options) ⇒ Object



503
504
505
506
507
508
509
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 503

def add_lock!(sql, options)
  # http://blog.sqlauthority.com/2007/04/27/sql-server-2005-locking-hints-and-examples/
  return unless options[:lock]
  lock_type = options[:lock] == true ? 'WITH(HOLDLOCK, ROWLOCK)' : options[:lock]
  sql.gsub! %r|LEFT OUTER JOIN\s+(.*?)\s+ON|im, "LEFT OUTER JOIN \\1 #{lock_type} ON"
  sql.gsub! %r{FROM\s([\w\[\]\.]+)}im, "FROM \\1 #{lock_type}"
end

#add_order_by_for_association_limiting!(sql, options) ⇒ Object



689
690
691
692
693
694
695
696
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 689

def add_order_by_for_association_limiting!(sql, options)
  # Disertation http://gist.github.com/24073
  # Information http://weblogs.sqlteam.com/jeffs/archive/2007/12/13/select-distinct-order-by-error.aspx
  return sql if options[:order].blank?
  columns = sql.match(/SELECT\s+DISTINCT(.*)FROM/)[1].strip
  sql.sub!(/SELECT\s+DISTINCT/,'SELECT')
  sql << "GROUP BY #{columns} ORDER BY #{order_to_min_set(options[:order])}"
end

#auto_connectObject



261
262
263
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 261

def auto_connect
  @@auto_connect.is_a?(FalseClass) ? false : true
end

#begin_db_transactionObject



416
417
418
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 416

def begin_db_transaction
  do_execute "BEGIN TRANSACTION"
end

#case_sensitive_equality_operatorObject



515
516
517
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 515

def case_sensitive_equality_operator
  "COLLATE Latin1_General_CS_AS ="
end

#change_column(table_name, column_name, type, options = {}) ⇒ Object



645
646
647
648
649
650
651
652
653
654
655
656
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 645

def change_column(table_name, column_name, type, options = {})
  sql_commands = []
  change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  change_column_sql << " NOT NULL" if options[:null] == false
  sql_commands << change_column_sql
  if options_include_default?(options)
    remove_default_constraint(table_name, column_name)
    sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}"
  end
  sql_commands.each { |c| do_execute(c) }
  remove_sqlserver_columns_cache_for(table_name)
end

#change_column_default(table_name, column_name, default) ⇒ Object



658
659
660
661
662
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 658

def change_column_default(table_name, column_name, default)
  remove_default_constraint(table_name, column_name)
  do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
  remove_sqlserver_columns_cache_for(table_name)
end

#change_column_null(table_name, column_name, null, default = nil) ⇒ Object



698
699
700
701
702
703
704
705
706
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 698

def change_column_null(table_name, column_name, null, default = nil)
  column = column_for(table_name,column_name)
  unless null || default.nil?
    do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  end
  sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
  sql << " NOT NULL" unless null
  do_execute sql
end

#columns(table_name, name = nil) ⇒ Object



607
608
609
610
611
612
613
614
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 607

def columns(table_name, name = nil)
  return [] if table_name.blank?
  cache_key = unqualify_table_name(table_name)
  @sqlserver_columns_cache[cache_key] ||= column_definitions(table_name).collect do |ci|
    sqlserver_options = ci.except(:name,:default_value,:type,:null)
    SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
  end
end

#commit_db_transactionObject



420
421
422
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 420

def commit_db_transaction
  do_execute "COMMIT TRANSACTION"
end

#create_database(name) ⇒ Object



745
746
747
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 745

def create_database(name)
  do_execute "CREATE DATABASE #{name}"
end

#create_savepointObject



428
429
430
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 428

def create_savepoint
  do_execute "SAVE TRANSACTION #{current_savepoint_name}"
end

#create_table(table_name, options = {}) ⇒ Object



616
617
618
619
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 616

def create_table(table_name, options = {})
  super
  remove_sqlserver_columns_cache_for(table_name)
end

#current_databaseObject



749
750
751
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 749

def current_database
  select_value 'SELECT DB_NAME()'
end

#database_versionObject



233
234
235
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 233

def database_version
  @database_version ||= info_schema_query { select_value('SELECT @@version') }
end

#database_yearObject



237
238
239
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 237

def database_year
  DATABASE_VERSION_REGEXP.match(database_version)[1].to_i
end

#disable_referential_integrity(&block) ⇒ Object

REFERENTIAL INTEGRITY ====================================#



334
335
336
337
338
339
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 334

def disable_referential_integrity(&block)
  do_execute "EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
  yield
ensure
  do_execute "EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
end

#disconnect!Object



356
357
358
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 356

def disconnect!
  raw_connection.disconnect rescue nil
end

#drop_database(name) ⇒ Object



726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 726

def drop_database(name)
  retry_count = 0
  max_retries = 1
  begin
    do_execute "DROP DATABASE #{name}"
  rescue ActiveRecord::StatementInvalid => err
    # Remove existing connections and rollback any transactions if we received the message
    #  'Cannot drop the database 'test' because it is currently in use'
    if err.message =~ /because it is currently in use/
      raise if retry_count >= max_retries
      retry_count += 1
      remove_database_connections_and_rollback(name)
      retry
    else
      raise
    end
  end
end

#drop_table(table_name, options = {}) ⇒ Object



625
626
627
628
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 625

def drop_table(table_name, options = {})
  super
  remove_sqlserver_columns_cache_for(table_name)
end

#empty_insert_statement(table_name) ⇒ Object



511
512
513
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 511

def empty_insert_statement(table_name)
  "INSERT INTO #{quote_table_name(table_name)} DEFAULT VALUES"
end

#execute(sql, name = nil, &block) ⇒ Object



395
396
397
398
399
400
401
402
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 395

def execute(sql, name = nil, &block)
  if table_name = query_requires_identity_insert?(sql)
    handle = with_identity_insert_enabled(table_name) { raw_execute(sql,name,&block) }
  else
    handle = raw_execute(sql,name,&block)
  end
  finish_statement_handle(handle)
end

#execute_procedure(proc_name, *variables) ⇒ Object



404
405
406
407
408
409
410
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 404

def execute_procedure(proc_name, *variables)
  vars = variables.map{ |v| quote(v) }.join(', ')
  sql = "EXEC #{proc_name} #{vars}".strip
  select(sql,'Execute Procedure',true).inject([]) do |results,row|
    results << row.with_indifferent_access
  end
end

#finish_statement_handle(handle) ⇒ Object



360
361
362
363
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 360

def finish_statement_handle(handle)
  handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
  handle
end

#indexes(table_name, name = nil) ⇒ Object



589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 589

def indexes(table_name, name = nil)
  unquoted_table_name = unqualify_table_name(table_name)
  select("EXEC sp_helpindex #{quote_table_name(unquoted_table_name)}",name).inject([]) do |indexes,index|
    if index['index_description'] =~ /primary key/
      indexes
    else
      name    = index['index_name']
      unique  = index['index_description'] =~ /unique/
      columns = index['index_keys'].split(',').map do |column|
        column.strip!
        column.gsub! '(-)', '' if column.ends_with?('(-)')
        column
      end
      indexes << IndexDefinition.new(table_name, name, unique, columns)
    end
  end
end

#inspectObject



257
258
259
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 257

def inspect
  "#<#{self.class} version: #{version}, year: #{database_year}, connection_options: #{@connection_options.inspect}>"
end

#limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key) ⇒ Object



519
520
521
522
523
524
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 519

def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  match_data = where_sql.match(/(.*)WHERE/)
  limit = match_data[1]
  where_sql.sub!(limit,'')
  "WHERE #{quoted_primary_key} IN (SELECT #{limit} #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
end

#native_binary_database_typeObject



278
279
280
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 278

def native_binary_database_type
  @@native_binary_database_type || (sqlserver_2005? ? 'varbinary(max)' : 'image')
end

#native_database_typesObject

SCHEMA STATEMENTS ========================================#



528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 528

def native_database_types
  {
    :primary_key  => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
    :string       => { :name => native_string_database_type, :limit => 255  },
    :text         => { :name => native_text_database_type },
    :integer      => { :name => "int", :limit => 4 },
    :float        => { :name => "float", :limit => 8 },
    :decimal      => { :name => "decimal" },
    :datetime     => { :name => "datetime" },
    :timestamp    => { :name => "datetime" },
    :time         => { :name => "datetime" },
    :date         => { :name => "datetime" },
    :binary       => { :name => native_binary_database_type },
    :boolean      => { :name => "bit"},
    # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them.
    :char         => { :name => 'char' },
    :varchar_max  => { :name => 'varchar(max)' },
    :nchar        => { :name => "nchar" },
    :nvarchar     => { :name => "nvarchar", :limit => 255 },
    :nvarchar_max => { :name => "nvarchar(max)" },
    :ntext        => { :name => "ntext" }
  }
end

#native_string_database_typeObject



265
266
267
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 265

def native_string_database_type
  @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar') 
end

#native_text_database_typeObject



269
270
271
272
273
274
275
276
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 269

def native_text_database_type
  @@native_text_database_type || 
  if sqlserver_2005?
    enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)'
  else
    enable_default_unicode_types ? 'ntext' : 'text'
  end
end

#outside_transaction?Boolean

Returns:

  • (Boolean)


412
413
414
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 412

def outside_transaction?
  info_schema_query { select_value("SELECT @@TRANCOUNT") == 0 }
end

#pk_and_sequence_for(table_name) ⇒ Object



708
709
710
711
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 708

def pk_and_sequence_for(table_name)
  idcol = identity_column(table_name)
  idcol ? [idcol.name,nil] : nil
end

#quote(value, column = nil) ⇒ Object

QUOTING ==================================================#



284
285
286
287
288
289
290
291
292
293
294
295
296
297
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 284

def quote(value, column = nil)
  case value
  when String, ActiveSupport::Multibyte::Chars
    if column && column.type == :binary
      column.class.string_to_binary(value)
    elsif column && column.respond_to?(:is_utf8?) && column.is_utf8?
      quoted_utf8_value(value)
    else
      super
    end
  else
    super
  end
end

#quote_column_name(column_name) ⇒ Object



303
304
305
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 303

def quote_column_name(column_name)
  column_name.to_s.split('.').map{ |name| "[#{name}]" }.join('.')
end

#quote_string(string) ⇒ Object



299
300
301
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 299

def quote_string(string)
  string.to_s.gsub(/\'/, "''")
end

#quote_table_name(table_name) ⇒ Object



307
308
309
310
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 307

def quote_table_name(table_name)
  return table_name if table_name =~ /^\[.*\]$/
  quote_column_name(table_name)
end

#quoted_date(value) ⇒ Object



320
321
322
323
324
325
326
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 320

def quoted_date(value)
  if value.acts_like?(:time) && value.respond_to?(:usec)
    "#{super}.#{sprintf("%03d",value.usec/1000)}"
  else
    super
  end
end

#quoted_falseObject



316
317
318
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 316

def quoted_false
  '0'
end

#quoted_trueObject



312
313
314
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 312

def quoted_true
  '1'
end

#quoted_utf8_value(value) ⇒ Object



328
329
330
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 328

def quoted_utf8_value(value)
  "N'#{quote_string(value)}'"
end

#reconnect!Object



350
351
352
353
354
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 350

def reconnect!
  disconnect!
  connect
  active?
end

#recreate_database(name) ⇒ Object

RAKE UTILITY METHODS =====================================#



715
716
717
718
719
720
721
722
723
724
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 715

def recreate_database(name)
  existing_database = current_database.to_s
  if name.to_s == existing_database
    do_execute 'USE master' 
  end
  drop_database(name)
  create_database(name)
ensure
  do_execute "USE #{existing_database}" if name.to_s == existing_database 
end

#release_savepointObject



432
433
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 432

def release_savepoint
end

#remove_column(table_name, *column_names) ⇒ Object



635
636
637
638
639
640
641
642
643
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 635

def remove_column(table_name, *column_names)
  column_names.flatten.each do |column_name|
    remove_check_constraints(table_name, column_name)
    remove_default_constraint(table_name, column_name)
    remove_indexes(table_name, column_name)
    do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
  end
  remove_sqlserver_columns_cache_for(table_name)
end

#remove_database_connections_and_rollback(name) ⇒ Object



753
754
755
756
757
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 753

def remove_database_connections_and_rollback(name)
  # This should disconnect all other users and rollback any transactions for SQL 2000 and 2005
  # http://sqlserver2000.databases.aspfaq.com/how-do-i-drop-a-sql-server-database.html
  do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
end

#remove_index(table_name, options = {}) ⇒ Object



670
671
672
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 670

def remove_index(table_name, options = {})
  do_execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
end

#rename_column(table_name, column_name, new_column_name) ⇒ Object



664
665
666
667
668
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 664

def rename_column(table_name, column_name, new_column_name)
  column_for(table_name,column_name)
  do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
  remove_sqlserver_columns_cache_for(table_name)
end

#rename_table(table_name, new_name) ⇒ Object



621
622
623
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 621

def rename_table(table_name, new_name)
  do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
end

#rollback_db_transactionObject



424
425
426
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 424

def rollback_db_transaction
  do_execute "ROLLBACK TRANSACTION" rescue nil
end

#rollback_to_savepointObject



435
436
437
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 435

def rollback_to_savepoint
  do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}"
end

#run_with_isolation_level(isolation_level) ⇒ Object

Raises:

  • (ArgumentError)


380
381
382
383
384
385
386
387
388
389
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 380

def run_with_isolation_level(isolation_level)
  raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{VALID_ISOLATION_LEVELS.to_sentence}." if !VALID_ISOLATION_LEVELS.include?(isolation_level.upcase)
  initial_isolation_level = user_options[:isolation_level] || "READ COMMITTED"
  do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
  begin
    yield 
  ensure
    do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
  end if block_given?
end

#select_rows(sql, name = nil) ⇒ Object



391
392
393
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 391

def select_rows(sql, name = nil)
  raw_select(sql,name).last
end

#sqlserver?Boolean

Returns:

  • (Boolean)


241
242
243
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 241

def sqlserver?
  true
end

#sqlserver_2000?Boolean

Returns:

  • (Boolean)


245
246
247
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 245

def sqlserver_2000?
  database_year == 2000
end

#sqlserver_2005?Boolean

Returns:

  • (Boolean)


249
250
251
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 249

def sqlserver_2005?
  database_year == 2005
end

#supports_ddl_transactions?Boolean

Returns:

  • (Boolean)


225
226
227
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 225

def supports_ddl_transactions?
  true
end

#supports_migrations?Boolean

Returns:

  • (Boolean)


221
222
223
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 221

def supports_migrations?
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


229
230
231
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 229

def supports_savepoints?
  true
end

#table_alias_lengthObject



552
553
554
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 552

def table_alias_length
  128
end

#table_exists?(table_name) ⇒ Boolean

Returns:

  • (Boolean)


585
586
587
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 585

def table_exists?(table_name)
  super || tables.include?(unqualify_table_name(table_name)) || views.include?(table_name.to_s)
end

#tables(name = nil) ⇒ Object



556
557
558
559
560
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 556

def tables(name = nil)
  info_schema_query do
    select_values "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME <> 'dtproperties'"
  end
end

#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object



674
675
676
677
678
679
680
681
682
683
684
685
686
687
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 674

def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  limit = nil unless self.class.type_limitable?(type)
  case type.to_s
  when 'integer'
    case limit
      when 1..2       then  'smallint'
      when 3..4, nil  then  'integer'
      when 5..8       then  'bigint'
      else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
    end
  else
    super
  end
end

#user_optionsObject

DATABASE STATEMENTS ======================================#



367
368
369
370
371
372
373
374
375
376
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 367

def user_options
  info_schema_query do
    select_rows("dbcc useroptions").inject(HashWithIndifferentAccess.new) do |values,row| 
      set_option = row[0].gsub(/\s+/,'_')
      user_value = row[1]
      values[set_option] = user_value
      values
    end
  end
end

#versionObject



253
254
255
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 253

def version
  self.class::VERSION
end

#view_information(table_name) ⇒ Object



567
568
569
570
571
572
573
574
575
576
577
578
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 567

def view_information(table_name)
  table_name = unqualify_table_name(table_name)
  @sqlserver_view_information_cache[table_name] ||= begin
    view_info = info_schema_query { select_one("SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'") }
    if view_info
      if view_info['VIEW_DEFINITION'].blank? || view_info['VIEW_DEFINITION'].length == 4000
        view_info['VIEW_DEFINITION'] = info_schema_query { select_values("EXEC sp_helptext #{table_name}").join }
      end
    end
    view_info
  end
end

#view_table_name(table_name) ⇒ Object



580
581
582
583
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 580

def view_table_name(table_name)
  view_info = view_information(table_name)
  view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
end

#views(name = nil) ⇒ Object



562
563
564
565
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 562

def views(name = nil)
  @sqlserver_views_cache ||= 
    info_schema_query { select_values("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME NOT IN ('sysconstraints','syssegments')") }
end