Module: ActiveRecord::ConnectionAdapters::SQLServer::DatabaseStatements

Included in:
ActiveRecord::ConnectionAdapters::SQLServerAdapter
Defined in:
lib/active_record/connection_adapters/sqlserver/database_statements.rb

Instance Method Summary collapse

Instance Method Details

#affected_rows(raw_result) ⇒ Object



41
42
43
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 41

def affected_rows(raw_result)
  raw_result.first['AffectedRows']
end

#begin_db_transactionObject



71
72
73
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 71

def begin_db_transaction
  internal_execute("BEGIN TRANSACTION", "TRANSACTION", allow_retry: true, materialize_transactions: false)
end

#begin_isolated_db_transaction(isolation) ⇒ Object



79
80
81
82
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 79

def begin_isolated_db_transaction(isolation)
  set_transaction_isolation_level(transaction_isolation_levels.fetch(isolation))
  begin_db_transaction
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 144

def build_insert_sql(insert) # :nodoc:
  sql = +"INSERT #{insert.into}"

  if returning = insert.send(:insert_all).returning
    returning_sql = if returning.is_a?(String)
                      returning
                    else
                      Array(returning).map { |column| "INSERTED.#{quote_column_name(column)}" }.join(", ")
                    end
    sql << " OUTPUT #{returning_sql}"
  end

  sql << " #{insert.values_list}"
  sql
end

#case_sensitive_comparison(attribute, value) ⇒ Object



96
97
98
99
100
101
102
103
104
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 96

def case_sensitive_comparison(attribute, value)
  column = column_for_attribute(attribute)

  if column.collation && !column.case_sensitive?
    attribute.eq(Arel::Nodes::Bin.new(value))
  else
    super
  end
end

#cast_result(raw_result) ⇒ Object



33
34
35
36
37
38
39
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 33

def cast_result(raw_result)
  if raw_result.columns.empty?
    ActiveRecord::Result.empty
  else
    ActiveRecord::Result.new(raw_result.columns, raw_result.rows)
  end
end

#commit_db_transactionObject



88
89
90
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 88

def commit_db_transaction
  internal_execute("COMMIT TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true)
end

#exec_delete(sql, name = nil, binds = []) ⇒ Object



61
62
63
64
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 61

def exec_delete(sql, name = nil, binds = [])
  sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
  super(sql, name, binds)
end

#exec_rollback_db_transactionObject



92
93
94
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 92

def exec_rollback_db_transaction
  internal_execute("IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION", "TRANSACTION", allow_retry: false, materialize_transactions: true)
end

#exec_update(sql, name = nil, binds = []) ⇒ Object



66
67
68
69
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 66

def exec_update(sql, name = nil, binds = [])
  sql = sql.dup << "; SELECT @@ROWCOUNT AS AffectedRows"
  super(sql, name, binds)
end

#execute_procedure(proc_name, *variables) ⇒ Object

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



162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 162

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

  log(sql, "Execute Procedure") do |notification_payload|
    with_raw_connection do |conn|
      result = internal_raw_execute(sql, conn)
      verified!
      options = { as: :hash, cache_rows: true, timezone: ActiveRecord.default_timezone || :utc }

      result.each(options) do |row|
        r = row.with_indifferent_access
        yield(r) if block_given?
      end

      result = result.each.map { |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
      notification_payload[:row_count] = result.count
      result
    end
  end
end

#insert_fixtures_set(fixture_set, tables_to_delete = []) ⇒ Object

We should propose this change to Rails team



107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 107

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}" }
  total_sqls = Array.wrap(table_deletes + fixture_inserts)

  disable_referential_integrity do
    transaction(requires_new: true) do
      total_sqls.each do |sql|
        execute sql, "Fixtures Load"
        yield if block_given?
      end
    end
  end
end

#internal_exec_sql_query(sql, conn) ⇒ Object



54
55
56
57
58
59
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 54

def internal_exec_sql_query(sql, conn)
  handle = internal_raw_execute(sql, conn)
  handle_to_names_and_values(handle, ar_result: true)
ensure
  finish_statement_handle(handle)
end

#newid_functionObject



254
255
256
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 254

def newid_function
  select_value "SELECT NEWID()"
end

#newsequentialid_functionObject



258
259
260
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 258

def newsequentialid_function
  select_value "SELECT NEWSEQUENTIALID()"
end

#perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:) ⇒ Object



16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 16

def perform_query(raw_connection, sql, binds, type_casted_binds, prepare:, notification_payload:, batch:)
  result = if id_insert_table_name = query_requires_identity_insert?(sql)
             # If the table name is a view, we need to get the base table name for enabling identity insert.
             id_insert_table_name = view_table_name(id_insert_table_name) if view_exists?(id_insert_table_name)

             with_identity_insert_enabled(id_insert_table_name, raw_connection) do
               internal_exec_sql_query(sql, raw_connection)
             end
           else
             internal_exec_sql_query(sql, raw_connection)
           end

  verified!
  notification_payload[:row_count] = result.count
  result
end

#raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false) ⇒ Object



45
46
47
48
49
50
51
52
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 45

def raw_execute(sql, name = nil, binds = [], prepare: false, async: false, allow_retry: false, materialize_transactions: true, batch: false)
  unless binds.nil? || binds.empty?
    types, params = sp_executesql_types_and_parameters(binds)
    sql = sp_executesql_sql(sql, types, params, name)
  end

  super
end

#set_transaction_isolation_level(isolation_level) ⇒ Object



84
85
86
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 84

def set_transaction_isolation_level(isolation_level)
  internal_execute("SET TRANSACTION ISOLATION LEVEL #{isolation_level}", "TRANSACTION", allow_retry: true, materialize_transactions: false)
end

#transaction_isolation_levelsObject



75
76
77
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 75

def transaction_isolation_levels
  super.merge snapshot: "SNAPSHOT"
end

#use_database(database = nil) ⇒ Object



196
197
198
199
200
201
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 196

def use_database(database = nil)
  return if sqlserver_azure?

  name = SQLServer::Utils.extract_identifiers(database || @connection_parameters[:database]).quoted
  execute("USE #{name}", "SCHEMA") unless name.blank?
end

#user_optionsObject



203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 203

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_dateformatObject



221
222
223
224
225
226
227
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 221

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_levelObject



229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 229

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_languageObject



246
247
248
249
250
251
252
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 246

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, conn) ⇒ Object



188
189
190
191
192
193
194
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 188

def with_identity_insert_enabled(table_name, conn)
  table_name = quote_table_name(table_name)
  set_identity_insert(table_name, conn, true)
  yield
ensure
  set_identity_insert(table_name, conn, false)
end

#write_query?(sql) ⇒ Boolean

:nodoc:

Returns:

  • (Boolean)


10
11
12
13
14
# File 'lib/active_record/connection_adapters/sqlserver/database_statements.rb', line 10

def write_query?(sql) # :nodoc:
  !READ_QUERY.match?(sql)
rescue ArgumentError # Invalid encoding
  !READ_QUERY.match?(sql.b)
end