Class: ActiveRecord::ConnectionAdapters::MSSQLAdapter

Overview

MSSQL (SQLServer) adapter class definition

Constant Summary collapse

ADAPTER_NAME =
'MSSQL'.freeze
MSSQL_VERSION_YEAR =
{
  8  => '2000',
  9  => '2005',
  10 => '2008',
  11 => '2012',
  12 => '2014',
  13 => '2016',
  14 => '2017',
  15 => '2019',
  16 => '2022'
}.freeze
TYPE_MAP =
Type::TypeMap.new.tap { |m| initialize_type_map(m) }

Constants included from ArJdbc::MSSQL

ArJdbc::MSSQL::NATIVE_DATABASE_TYPES

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

Constants included from ArJdbc::Abstract::DatabaseStatements

ArJdbc::Abstract::DatabaseStatements::NO_BINDS

Class Attribute Summary collapse

Attributes included from ArJdbc::Abstract::Core

#config

Attributes inherited from JdbcAdapter

#prepared_statements

Class Method Summary collapse

Instance Method Summary collapse

Methods included from ArJdbc::Util::QuotedCache

#quote_column_name, #quote_table_name

Methods included from ArJdbc::MSSQL

#adapter_name, #add_column, #change_column, #change_column_default, #change_column_type, #change_order_direction, #charset, #clear_cached_table, #collation, column_selector, #columns, #columns_for_distinct, #create_database, #current_database, #database_exists?, #distinct, #drop_database, #exec_proc, #exec_query, #exec_query_raw, jdbc_connection_class, #modify_types, #native_database_types, #quote, #quote_column_name, #quote_database_name, #quote_default_value, #quote_table_name, #quoted_date, #release_savepoint, #remove_check_constraints, #remove_column, #remove_columns, #remove_default_constraint, #remove_index, #remove_indexes, #rename_column, #rename_table, #reset_column_information, #set_identity_insert, #sqlserver_version, #tables, #truncate, #type_to_sql, #update_lob_value?, update_lob_values=, update_lob_values?, #use_database, #with_identity_insert_enabled

Methods included from ArJdbc::MSSQL::Utils

remove_identifier_delimiters, unqualify_db_name, unqualify_table_name, unqualify_table_schema, unquote_column_name, unquote_string, unquote_table_name

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, #execute, #insert_fixtures_set, #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::StatementCache

#delete_cached_statement, #fetch_cached_statement

Methods included from ArJdbc::Abstract::DatabaseStatements

#exec_insert, #exec_query, #exec_update, #execute, #select_all

Methods included from ArJdbc::Abstract::ConnectionManagement

#active?, #disconnect!, #really_valid?, #reconnect!

Methods included from ArJdbc::Abstract::Core

#jdbc_connection

Methods included from Jdbc::ConnectionPoolCallbacks

#on_checkin, #on_checkout

Methods inherited from JdbcAdapter

#adapter_name, #adapter_spec, arel2_visitors, #columns, configure_arel2_visitors, #data_source_exists?, #data_sources, #database_name, #exec_query_raw, #execute, #execute_quietly, #foreign_keys, #indexes, #is_a?, #last_inserted_id, #modify_types, #native_database_types, #pk_and_sequence_for, prepared_statements?, #prepared_statements?, #primary_keys, #structure_dump, #supports_migrations?, #table_definition, #table_exists?, #tables, #update_lob_value, #write_large_object

Constructor Details

#initialize(*args) ⇒ MSSQLAdapter

Returns a new instance of MSSQLAdapter.



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

def initialize(connection, logger, _connection_parameters, config = {})
  # configure_connection happens in super
  super(connection, logger, config)

  if database_version < '11'
    raise "Your #{mssql_product_name} #{mssql_version_year} is too old. This adapter supports #{mssql_product_name} >= 2012."
  end
end

Class Attribute Details

.cs_equality_operatorObject

Returns the value of attribute cs_equality_operator.



65
66
67
# File 'lib/arjdbc/mssql/adapter.rb', line 65

def cs_equality_operator
  @cs_equality_operator
end

Class Method Details

.database_exists?(config) ⇒ Boolean

Returns:

  • (Boolean)


77
78
79
80
81
82
83
84
85
86
# File 'lib/arjdbc/mssql/adapter.rb', line 77

def self.database_exists?(config)
  !!ActiveRecord::Base.sqlserver_connection(config)
rescue ActiveRecord::JDBCError => e
  case e.message
  when /Cannot open database .* requested by the login/
    false
  else
    raise
  end
end

Instance Method Details

#arel_visitorObject

:nodoc:



484
485
486
# File 'lib/arjdbc/mssql/adapter.rb', line 484

def arel_visitor # :nodoc:
  ::Arel::Visitors::SQLServer.new(self)
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/arjdbc/mssql/adapter.rb', line 156

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



259
260
261
262
263
264
265
266
267
# File 'lib/arjdbc/mssql/adapter.rb', line 259

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:



311
312
313
314
# File 'lib/arjdbc/mssql/adapter.rb', line 311

def check_version # :nodoc:
  # NOTE: hitting the database from here causes trouble when adapter
  # uses JNDI or Data Source setup.
end

#clear_cache!Object



180
181
182
183
# File 'lib/arjdbc/mssql/adapter.rb', line 180

def clear_cache!
  # reload_type_map
  super
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



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

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



269
270
271
272
273
# File 'lib/arjdbc/mssql/adapter.rb', line 269

def configure_connection
  # Here goes initial settings per connection

  set_session_transaction_isolation
end

#current_userObject

Returns the name of the current security context



224
225
226
# File 'lib/arjdbc/mssql/adapter.rb', line 224

def current_user
  @current_user ||= select_value('SELECT CURRENT_USER')
end

#default_schemaObject Also known as: current_schema

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



230
231
232
# File 'lib/arjdbc/mssql/adapter.rb', line 230

def default_schema
  @default_schema ||= select_value('SELECT default_schema_name FROM sys.database_principals WHERE name = CURRENT_USER')
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).



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

def default_schema=(default_schema)
  execute("ALTER #{current_user} WITH DEFAULT_SCHEMA=#{default_schema}")
  @default_schema = nil if defined?(@default_schema)
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



247
248
249
250
251
252
253
254
255
256
257
# File 'lib/arjdbc/mssql/adapter.rb', line 247

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



192
193
194
195
196
197
198
199
200
201
202
203
# File 'lib/arjdbc/mssql/adapter.rb', line 192

def disable_referential_integrity
  tables = tables_with_referential_integrity

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

#get_database_versionObject

:nodoc:



307
308
309
# File 'lib/arjdbc/mssql/adapter.rb', line 307

def get_database_version # :nodoc:
  MSSQLAdapter::Version.new(mssql_product_version)
end

#jdbc_column_classObject

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



96
97
98
# File 'lib/arjdbc/mssql/adapter.rb', line 96

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

#jdbc_connection_class(_spec) ⇒ Object

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



90
91
92
# File 'lib/arjdbc/mssql/adapter.rb', line 90

def jdbc_connection_class(_spec)
  ::ActiveRecord::ConnectionAdapters::MSSQLJdbcConnection
end

#mssql?Boolean

Returns:

  • (Boolean)


281
282
283
# File 'lib/arjdbc/mssql/adapter.rb', line 281

def mssql?
  true
end

#mssql_major_versionObject



285
286
287
288
289
# File 'lib/arjdbc/mssql/adapter.rb', line 285

def mssql_major_version
  return @mssql_major_version if defined? @mssql_major_version

  @mssql_major_version = @connection.database_major_version
end

#mssql_product_nameObject



301
302
303
304
305
# File 'lib/arjdbc/mssql/adapter.rb', line 301

def mssql_product_name
  return @mssql_product_name if defined? @mssql_product_name

  @mssql_product_name = @connection.database_product_name
end

#mssql_product_versionObject



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

def mssql_product_version
  return @mssql_product_version if defined? @mssql_product_version

  @mssql_product_version = @connection.database_product_version
end

#mssql_version_yearObject



291
292
293
# File 'lib/arjdbc/mssql/adapter.rb', line 291

def mssql_version_year
  MSSQL_VERSION_YEAR[mssql_major_version.to_i]
end

#reset!Object



185
186
187
188
189
190
# File 'lib/arjdbc/mssql/adapter.rb', line 185

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



275
276
277
278
279
# File 'lib/arjdbc/mssql/adapter.rb', line 275

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)


125
126
127
# File 'lib/arjdbc/mssql/adapter.rb', line 125

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)


102
103
104
# File 'lib/arjdbc/mssql/adapter.rb', line 102

def supports_ddl_transactions?
  true
end

#supports_foreign_keys?Boolean

Does this adapter support creating foreign key constraints?

Returns:

  • (Boolean)


107
108
109
# File 'lib/arjdbc/mssql/adapter.rb', line 107

def supports_foreign_keys?
  true
end

#supports_index_sort_order?Boolean

Does this adapter support index sort order?

Returns:

  • (Boolean)


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

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)


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

def supports_insert_on_conflict?
  false
end

#supports_insert_returning?Boolean

Returns:

  • (Boolean)


152
153
154
# File 'lib/arjdbc/mssql/adapter.rb', line 152

def supports_insert_returning?
  true
end

#supports_lazy_transactions?Boolean

Returns:

  • (Boolean)


120
121
122
# File 'lib/arjdbc/mssql/adapter.rb', line 120

def supports_lazy_transactions?
  true
end

#supports_partial_index?Boolean

Also known as filtered index

Returns:

  • (Boolean)


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

def supports_partial_index?
  true
end

#supports_savepoints?Boolean

Returns:

  • (Boolean)


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

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

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

Returns:

  • (Boolean)


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

def supports_transaction_isolation?
  true
end

#supports_views?Boolean

Does this adapter support views?

Returns:

  • (Boolean)


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

def supports_views?
  true
end

#tables_with_referential_integrityObject



316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
# File 'lib/arjdbc/mssql/adapter.rb', line 316

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)


176
177
178
# File 'lib/arjdbc/mssql/adapter.rb', line 176

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