Class: ActiveRecord::ConnectionAdapters::MSSQLAdapter

Overview

MSSQL (SQLServer) adapter class definition

Constant Summary collapse

ADAPTER_NAME =
'MSSQL'.freeze
TYPE_MAP =
Type::TypeMap.new.tap { |m| initialize_type_map(m) }

Constants included from ActiveRecord::ConnectionAdapters::MSSQL::ExplainSupport

ActiveRecord::ConnectionAdapters::MSSQL::ExplainSupport::DISABLED

Constants included from ActiveRecord::ConnectionAdapters::MSSQL::SchemaStatements

ActiveRecord::ConnectionAdapters::MSSQL::SchemaStatements::NATIVE_DATABASE_TYPES

Constants included from ActiveRecord::ConnectionAdapters::MSSQL::Quoting

ActiveRecord::ConnectionAdapters::MSSQL::Quoting::QUOTED_FALSE, ActiveRecord::ConnectionAdapters::MSSQL::Quoting::QUOTED_TRUE

Class Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ActiveRecord::ConnectionAdapters::MSSQL::DatabaseLimits

#in_clause_length

Methods included from ActiveRecord::ConnectionAdapters::MSSQL::ExplainSupport

#explain, #interpolate_sql_statement, #set_showplan_option, #supports_explain?, #with_showplan_on

Methods included from ActiveRecord::ConnectionAdapters::MSSQL::DatabaseStatements

#exec_insert, #exec_proc, #exec_update, #insert_fixtures_set, #internal_exec_query, #supports_transaction_isolation_level?, #transaction_isolation, #transaction_isolation=, #truncate, #truncate_tables, #write_query?

Methods included from ActiveRecord::ConnectionAdapters::MSSQL::SchemaStatements

#add_column, #add_timestamps, #change_column, #change_column_default, #change_column_null, #charset, #collation, #columns_for_distinct, #create_database, #create_schema_dumper, #current_database, #drop_database, #drop_table, #foreign_keys, #indexes, #native_database_types, #primary_keys, #quote_column_name, #quote_database_name, #quote_table_name, #recreate_database, #remove_column, #remove_columns, #rename_column, #rename_table, #type_to_sql, #update_table_definition, #use_database

Methods included from ActiveRecord::ConnectionAdapters::MSSQL::Quoting

#column_name_matcher, #column_name_with_order_matcher, #quote, #quote_default_expression, #quote_string, #quoted_date, #quoted_false, #quoted_time, #quoted_true

Methods included from ArJdbc::Abstract::TransactionSupport

#begin_db_transaction, #begin_isolated_db_transaction, #commit_db_transaction, #create_savepoint, #exec_rollback_db_transaction, #exec_rollback_to_savepoint, #release_savepoint

Methods included from ArJdbc::Abstract::Core

#jdbc_connection

Constructor Details

#initializeMSSQLAdapter

Returns a new instance of MSSQLAdapter.



80
81
82
83
84
85
86
87
88
89
# File 'lib/arjdbc/mssql/adapter.rb', line 80

def initialize(...)
  # configure_connection happens in super
  super

  conn_params = @config.compact

  @raw_connection = nil

  @connection_parameters = conn_params
end

Class Attribute Details

.cs_equality_operatorObject

Returns the value of attribute cs_equality_operator.



57
58
59
# File 'lib/arjdbc/mssql/adapter.rb', line 57

def cs_equality_operator
  @cs_equality_operator
end

Class Method Details

.jdbc_connection_classObject

Returns the (JDBC) connection class to be used for this adapter. The class is defined in the java part



61
62
63
# File 'lib/arjdbc/mssql/adapter.rb', line 61

def jdbc_connection_class
  ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
end

.new_client(conn_params, adapter_instance) ⇒ Object



65
66
67
68
69
70
71
72
73
74
75
76
77
# File 'lib/arjdbc/mssql/adapter.rb', line 65

def new_client(conn_params, adapter_instance)
  jdbc_connection_class.new(conn_params, adapter_instance)
rescue ActiveRecord::JDBCError => error
  if conn_params && conn_params[:database] && error.message.include?(conn_params[:database])
    raise ActiveRecord::NoDatabaseError.db_error(conn_params[:database])
  elsif conn_params && conn_params[:username] && error.message.include?(conn_params[:username])
    raise ActiveRecord::DatabaseConnectionError.username_error(conn_params[:username])
  elsif conn_params && conn_params[:host] && error.message.include?(conn_params[:host])
    raise ActiveRecord::DatabaseConnectionError.hostname_error(conn_params[:host])
  else
    raise ActiveRecord::ConnectionNotEstablished, error.message
  end
end

Instance Method Details

#active?Boolean

Returns:

  • (Boolean)


92
93
94
95
96
97
98
# File 'lib/arjdbc/mssql/adapter.rb', line 92

def active?
  @lock.synchronize do
    return false unless @raw_connection

    @raw_connection.active?
  end
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
# File 'lib/arjdbc/mssql/adapter.rb', line 171

def build_insert_sql(insert) # :nodoc:
  # TODO: hope we can implement an upsert like feature
  # "INSERT #{insert.into} #{insert.values_list}"
  sql = +"INSERT #{insert.into}"

  if returning = insert.send(:insert_all).returning
    returning_sql = if returning.is_a?(String)
                      returning
                    else
                      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



278
279
280
281
282
283
284
285
286
# File 'lib/arjdbc/mssql/adapter.rb', line 278

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

  if [:string, :text].include?(column.type) && collation && !collation.match(/_CS/) && !value.nil?
    attribute.eq(Arel::Nodes::Bin.new(value))
  else
    super
  end
end

#check_versionObject

:nodoc:



308
309
310
311
312
# File 'lib/arjdbc/mssql/adapter.rb', line 308

def check_version # :nodoc:
  return unless database_version.to_s <= mssql_version.min_major

  raise "Your #{mssql_version.product_name} is too old. #{mssql_version.support_message}"
end

#combine_bind_parameters(from_clause: [], join_clause: [], where_clause: [], having_clause: [], limit: nil, offset: nil) ⇒ Object

Overrides the method in abstract adapter to set the limit and offset in the right order. (SQLServer specific) Called by bound_attributes



223
224
225
226
227
228
229
230
231
232
233
234
235
236
# File 'lib/arjdbc/mssql/adapter.rb', line 223

def combine_bind_parameters(
  from_clause: [],
  join_clause: [],
  where_clause: [],
  having_clause: [],
  limit: nil,
  offset: nil
)

  result = from_clause + join_clause + where_clause + having_clause
  result << offset if offset
  result << limit if limit
  result
end

#configure_connectionObject



288
289
290
291
292
# File 'lib/arjdbc/mssql/adapter.rb', line 288

def configure_connection
  # Here goes initial settings per connection

  set_session_transaction_isolation
end

#current_userObject

Returns the name of the current security context



239
240
241
# File 'lib/arjdbc/mssql/adapter.rb', line 239

def current_user
  @current_user ||= internal_execute('SELECT CURRENT_USER').rows.flatten.first
end

#default_schemaObject Also known as: current_schema

Returns the default schema (to be used for table resolution) used for the #current_user.



245
246
247
# File 'lib/arjdbc/mssql/adapter.rb', line 245

def default_schema
  @default_schema ||= internal_execute(default_schema_query).rows.flatten.first
end

#default_schema=(default_schema) ⇒ Object Also known as: current_schema=

Allows for changing of the default schema. (to be used during unqualified table name resolution).



257
258
259
260
# File 'lib/arjdbc/mssql/adapter.rb', line 257

def default_schema=(default_schema)
  internal_execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
  @default_schema = nil if defined?(@default_schema)
end

#default_schema_queryObject



249
250
251
# File 'lib/arjdbc/mssql/adapter.rb', line 249

def default_schema_query
  'SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER'
end

#default_uniqueness_comparison(attribute, value) ⇒ Object

FIXME: This needs to be fixed when we implement the collation per column basis. At the moment we only use the global database collation



266
267
268
269
270
271
272
273
274
275
276
# File 'lib/arjdbc/mssql/adapter.rb', line 266

def default_uniqueness_comparison(attribute, value) # :nodoc:
  column = column_for_attribute(attribute)

  if [:string, :text].include?(column.type) && collation && !collation.match(/_CS/) && !value.nil?
    # NOTE: there is a deprecation warning here in the mysql adapter
    # no sure if it's required.
    attribute.eq(Arel::Nodes::Bin.new(value))
  else
    super
  end
end

#disable_referential_integrityObject



207
208
209
210
211
212
213
214
215
216
217
218
# File 'lib/arjdbc/mssql/adapter.rb', line 207

def disable_referential_integrity
  tables = tables_with_referential_integrity

  tables.each do |table_name|
    internal_execute("ALTER TABLE #{table_name} NOCHECK CONSTRAINT ALL")
  end
  yield
ensure
  tables.each do |table_name|
    internal_execute("ALTER TABLE #{table_name} CHECK CONSTRAINT ALL")
  end
end

#disconnect!Object



101
102
103
104
105
106
107
# File 'lib/arjdbc/mssql/adapter.rb', line 101

def disconnect!
  @lock.synchronize do
    super # clear_cache! && reset_transaction
    @raw_connection&.disconnect!
    @raw_connection = nil
  end
end

#get_database_versionObject

:nodoc:



304
305
306
# File 'lib/arjdbc/mssql/adapter.rb', line 304

def get_database_version # :nodoc:
  MSSQLAdapter::Version.new(mssql_version.major, mssql_version.complete)
end

#jdbc_column_classObject

Returns the (JDBC) ActiveRecord column class for this adapter. Used in the java part.



111
112
113
# File 'lib/arjdbc/mssql/adapter.rb', line 111

def jdbc_column_class
  ::ActiveRecord::ConnectionAdapters::MSSQLColumn
end

#mssql?Boolean

Returns:

  • (Boolean)


300
301
302
# File 'lib/arjdbc/mssql/adapter.rb', line 300

def mssql?
  true
end

#mssql_versionObject



314
315
316
317
318
319
320
# File 'lib/arjdbc/mssql/adapter.rb', line 314

def mssql_version
  return @mssql_version if defined? @mssql_version

  result = internal_execute("SELECT #{mssql_version_properties.join(', ')}").rows

  @mssql_version = MSSQL::Version.new(result.flatten)
end

#mssql_version_propertiesObject



322
323
324
325
326
327
328
329
# File 'lib/arjdbc/mssql/adapter.rb', line 322

def mssql_version_properties
  [
    "SERVERPROPERTY('productVersion')",
    "SERVERPROPERTY('productMajorVersion')",
    "SERVERPROPERTY('productLevel')",
    "SERVERPROPERTY('edition')"
  ]
end

#reset!Object

def clear_cache! # reload_type_map super end



200
201
202
203
204
205
# File 'lib/arjdbc/mssql/adapter.rb', line 200

def reset!
  # execute 'IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION'
  # NOTE: it seems the above line interferes with the jdbc driver
  # and ending up in connection closed, issue seen in rails 5.2 and 6.0
  reconnect!
end

#set_session_transaction_isolationObject



294
295
296
297
298
# File 'lib/arjdbc/mssql/adapter.rb', line 294

def set_session_transaction_isolation
  isolation_level = @config[:transaction_isolation]

  self.transaction_isolation = isolation_level if isolation_level
end

#supports_datetime_with_precision?Boolean

The MSSQL datetime type doe have precision.

Returns:

  • (Boolean)


140
141
142
# File 'lib/arjdbc/mssql/adapter.rb', line 140

def supports_datetime_with_precision?
  true
end

#supports_ddl_transactions?Boolean

Does this adapter support DDL rollbacks in transactions? That is, would CREATE TABLE or ALTER TABLE get rolled back by a transaction?

Returns:

  • (Boolean)


117
118
119
# File 'lib/arjdbc/mssql/adapter.rb', line 117

def supports_ddl_transactions?
  true
end

#supports_foreign_keys?Boolean

Does this adapter support creating foreign key constraints?

Returns:

  • (Boolean)


122
123
124
# File 'lib/arjdbc/mssql/adapter.rb', line 122

def supports_foreign_keys?
  true
end

#supports_index_sort_order?Boolean

Does this adapter support index sort order?

Returns:

  • (Boolean)


145
146
147
# File 'lib/arjdbc/mssql/adapter.rb', line 145

def supports_index_sort_order?
  true
end

#supports_insert_on_conflict?Boolean Also known as: supports_insert_on_duplicate_skip?, supports_insert_on_duplicate_update?, supports_insert_conflict_target?

Returns:

  • (Boolean)


159
160
161
# File 'lib/arjdbc/mssql/adapter.rb', line 159

def supports_insert_on_conflict?
  false
end

#supports_insert_returning?Boolean

Returns:

  • (Boolean)


167
168
169
# File 'lib/arjdbc/mssql/adapter.rb', line 167

def supports_insert_returning?
  true
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


135
136
137
# File 'lib/arjdbc/mssql/adapter.rb', line 135

def supports_lazy_transactions?
  true
end

#supports_partial_index?Boolean

Also known as filtered index

Returns:

  • (Boolean)


150
151
152
# File 'lib/arjdbc/mssql/adapter.rb', line 150

def supports_partial_index?
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


131
132
133
# File 'lib/arjdbc/mssql/adapter.rb', line 131

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

Does this adapter support setting the isolation level for a transaction?

Returns:

  • (Boolean)


127
128
129
# File 'lib/arjdbc/mssql/adapter.rb', line 127

def supports_transaction_isolation?
  true
end

#supports_views?Boolean

Does this adapter support views?

Returns:

  • (Boolean)


155
156
157
# File 'lib/arjdbc/mssql/adapter.rb', line 155

def supports_views?
  true
end

#tables_with_referential_integrityObject



331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
# File 'lib/arjdbc/mssql/adapter.rb', line 331

def tables_with_referential_integrity
  schema_and_tables_sql = %(
    SELECT s.name, o.name
    FROM sys.foreign_keys i
    INNER JOIN sys.objects o ON i.parent_object_id = o.OBJECT_ID
    INNER JOIN sys.schemas s ON o.schema_id = s.schema_id
  ).squish

  schemas_and_tables = select_rows(schema_and_tables_sql)

  schemas_and_tables.map do |schema_table|
    schema, table = schema_table
    "#{quote_name_part(schema)}.#{quote_name_part(table)}"
  end
end

#valid_type?(type) ⇒ Boolean

Overrides abstract method which always returns false

Returns:

  • (Boolean)


191
192
193
# File 'lib/arjdbc/mssql/adapter.rb', line 191

def valid_type?(type)
  !native_database_types[type].nil?
end