module ActiveRecord::ConnectionAdapters::SQLServer::DatabaseStatements

Public Instance Methods

begin_db_transaction() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 36
def begin_db_transaction
  do_execute 'BEGIN TRANSACTION'
end
begin_isolated_db_transaction(isolation) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 44
def begin_isolated_db_transaction(isolation)
  set_transaction_isolation_level transaction_isolation_levels.fetch(isolation)
  begin_db_transaction
end
case_sensitive_comparison(table, attribute, column, value) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 74
def case_sensitive_comparison(table, attribute, column, value)
  if column.collation && !column.case_sensitive?
    table[attribute].eq(Arel::Nodes::Bin.new(value))
  else
    super
  end
end
commit_db_transaction() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 53
def commit_db_transaction
  do_execute 'COMMIT TRANSACTION'
end
create_savepoint(name = current_savepoint_name) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 63
def create_savepoint(name = current_savepoint_name)
  do_execute "SAVE TRANSACTION #{name}"
end
exec_delete(sql, name, binds) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 26
def exec_delete(sql, name, binds)
  sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
  super(sql, name, binds).rows.first.first
end
exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 18
def exec_insert(sql, name = nil, binds = [], pk = nil, _sequence_name = nil)
  if id_insert_table_name = exec_insert_requires_identity?(sql, pk, binds)
    with_identity_insert_enabled(id_insert_table_name) { super(sql, name, binds, pk) }
  else
    super(sql, name, binds, pk)
  end
end
exec_query(sql, name = 'SQL', binds = [], prepare: false) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 14
def exec_query(sql, name = 'SQL', binds = [], prepare: false)
  sp_executesql(sql, name, binds, prepare: prepare)
end
exec_rollback_db_transaction() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 57
def exec_rollback_db_transaction
  do_execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
end
exec_rollback_to_savepoint(name = current_savepoint_name) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 67
def exec_rollback_to_savepoint(name = current_savepoint_name)
  do_execute "ROLLBACK TRANSACTION #{name}"
end
exec_update(sql, name, binds) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 31
def exec_update(sql, name, binds)
  sql = sql.dup << '; SELECT @@ROWCOUNT AS AffectedRows'
  super(sql, name, binds).rows.first.first
end
execute(sql, name = nil) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 6
def execute(sql, name = nil)
  if id_insert_table_name = query_requires_identity_insert?(sql)
    with_identity_insert_enabled(id_insert_table_name) { do_execute(sql, name) }
  else
    do_execute(sql, name)
  end
end
execute_procedure(proc_name, *variables) { |r| ... } click to toggle source

SQLServer Specific ======================================== #

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 127
def execute_procedure(proc_name, *variables)
  vars = if variables.any? && variables.first.is_a?(Hash)
           variables.first.map { |k, v| "@#{k} = #{quote(v)}" }
         else
           variables.map { |v| quote(v) }
         end.join(', ')
  sql = "EXEC #{proc_name} #{vars}".strip
  name = 'Execute Procedure'
  log(sql, name) do
    case @connection_options[:mode]
    when :dblib
      result = @connection.execute(sql)
      options = { as: :hash, cache_rows: true, timezone: ActiveRecord::Base.default_timezone || :utc }
      result.each(options) do |row|
        r = row.with_indifferent_access
        yield(r) if block_given?
      end
      result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
    end
  end
end
insert_fixtures_set(fixture_set, tables_to_delete = []) { || ... } click to toggle source

We should propose this change to Rails team

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 83
def insert_fixtures_set(fixture_set, tables_to_delete = [])
  fixture_inserts = []

  fixture_set.each do |table_name, fixtures|
    fixtures.each_slice(insert_rows_length) do |batch|
      fixture_inserts << build_fixture_sql(batch, table_name)
    end
  end

  table_deletes = tables_to_delete.map { |table| "DELETE FROM #{quote_table_name table}".dup }
  total_sql = Array.wrap(combine_multi_statements(table_deletes + fixture_inserts))

  disable_referential_integrity do
    transaction(requires_new: true) do
      total_sql.each do |sql|
        execute sql, "Fixtures Load"
        yield if block_given?
      end
    end
  end
end
newid_function() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 213
def newid_function
  select_value 'SELECT NEWID()'
end
newsequentialid_function() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 217
def newsequentialid_function
  select_value 'SELECT NEWSEQUENTIALID()'
end
release_savepoint(name = current_savepoint_name) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 71
def release_savepoint(name = current_savepoint_name)
end
set_transaction_isolation_level(isolation_level) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 49
def set_transaction_isolation_level(isolation_level)
  do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
end
transaction_isolation_levels() click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 40
def transaction_isolation_levels
  super.merge snapshot: "SNAPSHOT"
end
use_database(database = nil) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 157
def use_database(database = nil)
  return if sqlserver_azure?
  name = SQLServer::Utils.extract_identifiers(database || @connection_options[:database]).quoted
  do_execute "USE #{name}" unless name.blank?
end
user_options() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 163
def user_options
  return {} if sqlserver_azure?
  rows = select_rows('DBCC USEROPTIONS WITH NO_INFOMSGS', 'SCHEMA')
  rows = rows.first if rows.size == 2 && rows.last.empty?
  rows.reduce(HashWithIndifferentAccess.new) do |values, row|
    if row.instance_of? Hash
      set_option = row.values[0].gsub(/\s+/, '_')
      user_value = row.values[1]
    elsif  row.instance_of? Array
      set_option = row[0].gsub(/\s+/, '_')
      user_value = row[1]
    end
    values[set_option] = user_value
    values
  end
end
user_options_dateformat() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 180
def user_options_dateformat
  if sqlserver_azure?
    select_value 'SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID', 'SCHEMA'
  else
    user_options['dateformat']
  end
end
user_options_isolation_level() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 188
def user_options_isolation_level
  if sqlserver_azure?
    sql = %(SELECT CASE [transaction_isolation_level]
            WHEN 0 THEN NULL
            WHEN 1 THEN 'READ UNCOMMITTED'
            WHEN 2 THEN 'READ COMMITTED'
            WHEN 3 THEN 'REPEATABLE READ'
            WHEN 4 THEN 'SERIALIZABLE'
            WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level]
            FROM [sys].[dm_exec_sessions]
            WHERE [session_id] = @@SPID).squish
    select_value sql, 'SCHEMA'
  else
    user_options['isolation_level']
  end
end
user_options_language() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 205
def user_options_language
  if sqlserver_azure?
    select_value 'SELECT @@LANGUAGE AS [language]', 'SCHEMA'
  else
    user_options['language']
  end
end
with_identity_insert_enabled(table_name) { || ... } click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 149
def with_identity_insert_enabled(table_name)
  table_name = quote_table_name(table_name)
  set_identity_insert(table_name, true)
  yield
ensure
  set_identity_insert(table_name, false)
end

Protected Instance Methods

_raw_select(sql, options = {}) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 380
def _raw_select(sql, options = {})
  handle = raw_connection_run(sql)
  handle_to_names_and_values(handle, options)
ensure
  finish_statement_handle(handle)
end
do_execute(sql, name = 'SQL') click to toggle source

SQLServer Specific (Executing) ============================ #

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 259
def do_execute(sql, name = 'SQL')
  log(sql, name) { raw_connection_do(sql) }
end
exclude_output_inserted_table_name?(table_name, sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 344
def exclude_output_inserted_table_name?(table_name, sql)
  return false unless exclude_output_inserted_table_names?
  table_name ||= get_table_name(sql)
  return false unless table_name
  self.class.exclude_output_inserted_table_names[table_name]
end
exclude_output_inserted_table_names?() click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 340
def exclude_output_inserted_table_names?
  !self.class.exclude_output_inserted_table_names.empty?
end
exec_insert_requires_identity?(sql, pk, binds) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 351
def exec_insert_requires_identity?(sql, pk, binds)
  query_requires_identity_insert?(sql)
end
finish_statement_handle(handle) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 417
def finish_statement_handle(handle)
  case @connection_options[:mode]
  when :dblib
    handle.cancel if handle
  end
  handle
end
handle_more_results?(handle) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 394
def handle_more_results?(handle)
  case @connection_options[:mode]
  when :dblib
  end
end
handle_to_names_and_values(handle, options = {}) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 400
def handle_to_names_and_values(handle, options = {})
  case @connection_options[:mode]
  when :dblib
    handle_to_names_and_values_dblib(handle, options)
  end
end
handle_to_names_and_values_dblib(handle, options = {}) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 407
def handle_to_names_and_values_dblib(handle, options = {})
  query_options = {}.tap do |qo|
    qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
    qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
  end
  results = handle.each(query_options)
  columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields
  options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results
end
identity_columns(table_name) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 370
def identity_columns(table_name)
  schema_cache.columns(table_name).select(&:is_identity?)
end
insert_sql?(sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 366
def insert_sql?(sql)
  !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
end
query_requires_identity_insert?(sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 355
def query_requires_identity_insert?(sql)
  if insert_sql?(sql)
    table_name = get_table_name(sql)
    id_column = identity_columns(table_name).first
    # id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
    id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? table_name : false
  else
    false
  end
end
raw_connection_do(sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 318
def raw_connection_do(sql)
  case @connection_options[:mode]
  when :dblib
    result = @connection.execute(sql)

    # TinyTDS returns false instead of raising an exception if connection fails.
    # Getting around this by raising an exception ourselves while this PR
    # https://github.com/rails-sqlserver/tiny_tds/pull/469 is not released.
    raise TinyTds::Error, "failed to execute statement" if result.is_a?(FalseClass)

    result.do
  end
ensure
  @update_sql = false
end
raw_connection_run(sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 387
def raw_connection_run(sql)
  case @connection_options[:mode]
  when :dblib
    @connection.execute(sql)
  end
end
raw_select(sql, name = 'SQL', binds = [], options = {}) click to toggle source

SQLServer Specific (Selecting) ============================ #

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 376
def raw_select(sql, name = 'SQL', binds = [], options = {})
  log(sql, name, binds) { _raw_select(sql, options) }
end
set_identity_insert(table_name, enable = true) click to toggle source

SQLServer Specific ======================================== #

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 251
def set_identity_insert(table_name, enable = true)
  do_execute "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
rescue Exception
  raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
end
sp_executesql(sql, name, binds, options = {}) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 263
def sp_executesql(sql, name, binds, options = {})
  options[:ar_result] = true if options[:fetch] != :rows
  unless without_prepared_statement?(binds)
    types, params = sp_executesql_types_and_parameters(binds)
    sql = sp_executesql_sql(sql, types, params, name)
  end
  raw_select sql, name, binds, options
end
sp_executesql_sql(sql, types, params, name) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 303
def sp_executesql_sql(sql, types, params, name)
  if name == 'EXPLAIN'
    params.each.with_index do |param, index|
      substitute_at_finder = /(@#{index})(?=(?:[^']|'[^']*')*$)/ # Finds unquoted @n values.
      sql = sql.sub substitute_at_finder, param.to_s
    end
  else
    types = quote(types.join(', '))
    params = params.map.with_index{ |p, i| "@#{i} = #{p}" }.join(', ') # Only p is needed, but with @i helps explain regexp.
    sql = "EXEC sp_executesql #{quote(sql)}"
    sql << ", #{types}, #{params}" unless params.empty?
  end
  sql
end
sp_executesql_sql_param(attr) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 293
def sp_executesql_sql_param(attr)
  case value = attr.value_for_database
  when Type::Binary::Data,
       ActiveRecord::Type::SQLServer::Data
    quote(value)
  else
    quote(type_cast(value))
  end
end
sp_executesql_sql_type(attr) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 283
def sp_executesql_sql_type(attr)
  return attr.type.sqlserver_type if attr.type.respond_to?(:sqlserver_type)
  case value = attr.value_for_database
  when Numeric
    value > 2_147_483_647 ? 'bigint'.freeze : 'int'.freeze
  else
    'nvarchar(max)'.freeze
  end
end
sp_executesql_types_and_parameters(binds) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 272
def sp_executesql_types_and_parameters(binds)
  types, params = [], []
  binds.each_with_index do |attr, index|
    attr = attr.value if attr.is_a?(Arel::Nodes::BindParam)

    types << "@#{index} #{sp_executesql_sql_type(attr)}"
    params << sp_executesql_sql_param(attr)
  end
  [types, params]
end
sql_for_insert(sql, pk, id_value, sequence_name, binds) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 224
        def sql_for_insert(sql, pk, id_value, sequence_name, binds)
          if pk.nil?
            table_name = query_requires_identity_insert?(sql)
            pk = primary_key(table_name)
          end
          sql = if pk && use_output_inserted? && !database_prefix_remote_server?
                  quoted_pk = SQLServer::Utils.extract_identifiers(pk).quoted
                  table_name ||= get_table_name(sql)
                  exclude_output_inserted = exclude_output_inserted_table_name?(table_name, sql)
                  if exclude_output_inserted
                    id_sql_type = exclude_output_inserted.is_a?(TrueClass) ? 'bigint' : exclude_output_inserted
                    <<-SQL.strip_heredoc
                      DECLARE @ssaIdInsertTable table (#{quoted_pk} #{id_sql_type});
                      #{sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk} INTO @ssaIdInsertTable"}
                      SELECT CAST(#{quoted_pk} AS #{id_sql_type}) FROM @ssaIdInsertTable
                    SQL
                  else
                    sql.dup.insert sql.index(/ (DEFAULT )?VALUES/), " OUTPUT INSERTED.#{quoted_pk}"
                  end
                else
                  "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"
                end
          super
        end
use_output_inserted?() click to toggle source

SQLServer Specific (Identity Inserts) ===================== #

# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 336
def use_output_inserted?
  self.class.use_output_inserted
end

Private Instance Methods

can_perform_case_insensitive_comparison_for?(column) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 105
def can_perform_case_insensitive_comparison_for?(column)
  column.type == :string && (!column.collation || column.case_sensitive?)
end
combine_multi_statements(total_sql) click to toggle source
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 110
def combine_multi_statements(total_sql)
  total_sql
end
default_insert_value(column) click to toggle source
Calls superclass method
# File lib/active_record/connection_adapters/sqlserver/database_statements.rb, line 115
def default_insert_value(column)
  if column.is_identity?
    table_name = quote(quote_table_name(column.table_name))
    Arel.sql("IDENT_CURRENT(#{table_name}) + IDENT_INCR(#{table_name})")
  else
    super
  end
end