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_COLUMN_NAMES, ActiveRecord::ConnectionAdapters::MSSQL::Quoting::QUOTED_FALSE, ActiveRecord::ConnectionAdapters::MSSQL::Quoting::QUOTED_TABLE_NAMES, 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, #build_change_column_default_definition, #build_change_column_definition, #change_column, #change_column_default, #change_column_null, #charset, #collation, #columns_for_distinct, #create_database, #create_schema_dumper, #current_database, #drop_database, #drop_table, #extract_foreign_key_action, #foreign_keys, #indexes, #native_database_types, #primary_keys, #quote_database_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

#quote, #quote_default_expression, #quote_string, #quoted_date, #quoted_false, #quoted_time, #quoted_true, #type_cast

Methods included from ArJdbc::MSSQLConfig

#build_connection_config

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



84
85
86
87
88
89
90
91
92
93
94
# File 'lib/arjdbc/mssql/adapter.rb', line 84

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

  # assign arjdbc extra connection params
  conn_params = build_connection_config(@config.compact)

  @raw_connection = nil

  @connection_parameters = conn_params
end

Class Attribute Details

.cs_equality_operatorObject

Returns the value of attribute cs_equality_operator.



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

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



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

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

.new_client(conn_params, adapter_instance) ⇒ Object



69
70
71
72
73
74
75
76
77
78
79
80
81
# File 'lib/arjdbc/mssql/adapter.rb', line 69

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



97
98
99
100
101
102
103
# File 'lib/arjdbc/mssql/adapter.rb', line 97

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

    @raw_connection.active?
  end
end

#build_insert_sql(insert) ⇒ Object

:nodoc:



176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
# File 'lib/arjdbc/mssql/adapter.rb', line 176

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

FIXME: This needs to be fixed when we implement the collation per column



270
271
272
273
274
275
276
277
278
279
280
# File 'lib/arjdbc/mssql/adapter.rb', line 270

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

  case_sensitive = collation && collation.match(/_CS/)

  if i[string text].include?(column.type) && !case_sensitive && !value.nil?
    attribute.eq(Arel::Nodes::Bin.new(value))
  else
    super
  end
end

#check_versionObject

:nodoc:



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

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



228
229
230
231
232
233
234
235
236
237
238
239
240
241
# File 'lib/arjdbc/mssql/adapter.rb', line 228

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



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

def configure_connection
  # Here goes initial settings per connection

  set_session_transaction_isolation
end

#current_userObject

Returns the name of the current security context



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

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.



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

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



262
263
264
265
# File 'lib/arjdbc/mssql/adapter.rb', line 262

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



254
255
256
# File 'lib/arjdbc/mssql/adapter.rb', line 254

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

#disable_referential_integrityObject



212
213
214
215
216
217
218
219
220
221
222
223
# File 'lib/arjdbc/mssql/adapter.rb', line 212

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



106
107
108
109
110
111
112
# File 'lib/arjdbc/mssql/adapter.rb', line 106

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

#get_database_versionObject

:nodoc:



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

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.



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

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

#mssql?Boolean



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

def mssql?
  true
end

#mssql_versionObject



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

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



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

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

#reset!Object

def clear_cache! # reload_type_map super end



205
206
207
208
209
210
# File 'lib/arjdbc/mssql/adapter.rb', line 205

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



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

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.



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

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?



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

def supports_ddl_transactions?
  true
end

#supports_foreign_keys?Boolean

Does this adapter support creating foreign key constraints?



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

def supports_foreign_keys?
  true
end

#supports_index_sort_order?Boolean

Does this adapter support index sort order?



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

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?



164
165
166
# File 'lib/arjdbc/mssql/adapter.rb', line 164

def supports_insert_on_conflict?
  false
end

#supports_insert_returning?Boolean



172
173
174
# File 'lib/arjdbc/mssql/adapter.rb', line 172

def supports_insert_returning?
  true
end

#supports_lazy_transactions?Boolean



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

def supports_lazy_transactions?
  true
end

#supports_partial_index?Boolean

Also known as filtered index



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

def supports_partial_index?
  true
end

#supports_savepoints?Boolean



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

def supports_savepoints?
  true
end

#supports_transaction_isolation?Boolean

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



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

def supports_transaction_isolation?
  true
end

#supports_views?Boolean

Does this adapter support views?



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

def supports_views?
  true
end

#tables_with_referential_integrityObject



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

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
    "#{self.class.mssql_quote_name_part(schema)}.#{self.class.mssql_quote_name_part(table)}"
  end
end

#valid_type?(type) ⇒ Boolean

Overrides abstract method which always returns false



196
197
198
# File 'lib/arjdbc/mssql/adapter.rb', line 196

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