Class: ActiveRecord::ConnectionAdapters::SQLServerAdapter
Overview
In ADO mode, this adapter will ONLY work on Windows systems, since it relies on Win32OLE, which, to my knowledge, is only available on Windows.
This mode also relies on the ADO support in the DBI module. If you are using the one-click installer of Ruby, then you already have DBI installed, but the ADO module is NOT installed. You will need to get the latest source distribution of Ruby-DBI from ruby-dbi.sourceforge.net/ unzip it, and copy the file src/lib/dbd_ado/ADO.rb
to X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb
(you will more than likely need to create the ADO directory). Once you’ve installed that file, you are ready to go.
In ODBC mode, the adapter requires the ODBC support in the DBI module which requires the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing, and it is available at www.ch-werner.de/rubyodbc/
Options:
-
:mode
– ADO or ODBC. Defaults to ADO.
-
:username
– Defaults to sa.
-
:password
– Defaults to empty string.
ADO specific options:
ODBC specific options:
ADO code tested on Windows 2000 and higher systems, running ruby 1.8.2 (2004-07-29) [i386-mswin32], and SQL Server 2000 SP3.
ODBC code tested on a Fedora Core 4 system, running FreeTDS 0.63, unixODBC 2.2.11, Ruby ODBC 0.996, Ruby DBI 0.0.23 and Ruby 1.8.2.
- Linux strongmad 2.6.11-1.1369_FC4 #1 Thu Jun 2 22:55:56 EDT 2005 i686 i686 i386 GNU/Linux
Instance Method Summary
collapse
-
#active? ⇒ Boolean
Returns true if the connection is active.
-
#adapter_name ⇒ Object
-
#add_column(table_name, column_name, type, options = {}) ⇒ Object
Adds a new column to the named table.
-
#add_limit_offset!(sql, options) ⇒ Object
-
#begin_db_transaction ⇒ Object
-
#change_column(table_name, column_name, type, options = {}) ⇒ Object
-
#columns(table_name, name = nil) ⇒ Object
-
#commit_db_transaction ⇒ Object
-
#create_database(name) ⇒ Object
-
#current_database ⇒ Object
-
#disconnect! ⇒ Object
Disconnects from the database.
-
#drop_database(name) ⇒ Object
-
#execute(sql, name = nil) ⇒ Object
-
#indexes(table_name, name = nil) ⇒ Object
-
#initialize(connection, logger, connection_options = nil) ⇒ SQLServerAdapter
constructor
A new instance of SQLServerAdapter.
-
#insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object
-
#native_database_types ⇒ Object
-
#quote(value, column = nil) ⇒ Object
-
#quote_column_name(name) ⇒ Object
-
#quote_string(string) ⇒ Object
-
#reconnect! ⇒ Object
Reconnects to the database, returns false if no connection could be made.
-
#recreate_database(name) ⇒ Object
-
#remove_check_constraints(table_name, column_name) ⇒ Object
-
#remove_column(table_name, column_name) ⇒ Object
-
#remove_default_constraint(table_name, column_name) ⇒ Object
-
#remove_index(table_name, options = {}) ⇒ Object
-
#rename_column(table, column, new_column_name) ⇒ Object
-
#rename_table(name, new_name) ⇒ Object
-
#rollback_db_transaction ⇒ Object
-
#supports_migrations? ⇒ Boolean
-
#tables(name = nil) ⇒ Object
-
#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object
-
#update(sql, name = nil) ⇒ Object
(also: #delete)
#prefetch_primary_key?, #raw_connection, #requires_reloading?, #reset_runtime, #supports_count_distinct?, #verify!
Methods included from Quoting
#quoted_date, #quoted_false, #quoted_true
#add_column_options!, #add_index, #add_order_by_for_association_limiting!, #change_column_default, #create_table, #distinct, #drop_table, #dump_schema_information, #index_name, #initialize_schema_information, #structure_dump, #table_alias_for, #table_alias_length
Constructor Details
#initialize(connection, logger, connection_options = nil) ⇒ SQLServerAdapter
Returns a new instance of SQLServerAdapter.
189
190
191
192
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 189
def initialize(connection, logger, connection_options=nil)
super(connection, logger)
@connection_options = connection_options
end
|
Instance Method Details
#active? ⇒ Boolean
Returns true if the connection is active.
234
235
236
237
238
239
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 234
def active?
@connection.execute("SELECT 1").finish
true
rescue DBI::DatabaseError, DBI::InterfaceError
false
end
|
#adapter_name ⇒ Object
211
212
213
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 211
def adapter_name
'SQLServer'
end
|
#add_column(table_name, column_name, type, options = {}) ⇒ Object
Adds a new column to the named table. See TableDefinition#column for details of the options you can use.
448
449
450
451
452
453
454
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 448
def add_column(table_name, column_name, type, options = {})
add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
add_column_options!(add_column_sql, options)
execute(add_column_sql)
end
|
#add_limit_offset!(sql, options) ⇒ Object
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 364
def add_limit_offset!(sql, options)
if options[:limit] and options[:offset]
total_rows = @connection.select_all("SELECT count(*) as TotalRows from (#{sql.gsub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally")[0][:TotalRows].to_i
if (options[:limit] + options[:offset]) >= total_rows
options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
end
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]} ")
sql << ") AS tmp1"
if options[:order]
options[:order] = options[:order].split(',').map do |field|
parts = field.split(" ")
tc = parts[0]
if sql =~ /\.\[/ and tc =~ /\./ tc.gsub!(/\./, '\\.\\[')
tc << '\\]'
end
if sql =~ /#{tc} AS (t\d_r\d\d?)/
parts[0] = $1
elsif parts[0] =~ /\w+\.(\w+)/
parts[0] = $1
end
parts.join(' ')
end.join(', ')
sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
else
sql << " ) AS tmp2"
end
elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
"SELECT#{$1} TOP #{options[:limit]}"
end unless options[:limit].nil?
end
end
|
#begin_db_transaction ⇒ Object
326
327
328
329
330
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 326
def begin_db_transaction
@connection["AutoCommit"] = false
rescue Exception => e
@connection["AutoCommit"] = true
end
|
#change_column(table_name, column_name, type, options = {}) ⇒ Object
460
461
462
463
464
465
466
467
468
469
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 460
def change_column(table_name, column_name, type, options = {}) sql_commands = ["ALTER TABLE #{table_name} ALTER COLUMN #{column_name} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"]
if options_include_default?(options)
remove_default_constraint(table_name, column_name)
sql_commands << "ALTER TABLE #{table_name} ADD CONSTRAINT DF_#{table_name}_#{column_name} DEFAULT #{quote(options[:default], options[:column])} FOR #{column_name}"
end
sql_commands.each {|c|
execute(c)
}
end
|
#columns(table_name, name = nil) ⇒ Object
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 256
def columns(table_name, name = nil)
return [] if table_name.blank?
table_name = table_name.to_s if table_name.is_a?(Symbol)
table_name = table_name.split('.')[-1] unless table_name.nil?
table_name = table_name.gsub(/[\[\]]/, '')
sql = %Q{
SELECT
cols.COLUMN_NAME as ColName,
cols.COLUMN_DEFAULT as DefaultValue,
cols.NUMERIC_SCALE as numeric_scale,
cols.NUMERIC_PRECISION as numeric_precision,
cols.DATA_TYPE as ColType,
cols.IS_NULLABLE As IsNullable,
COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,
COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,
cols.NUMERIC_SCALE as Scale
FROM INFORMATION_SCHEMA.COLUMNS cols
WHERE cols.TABLE_NAME = '#{table_name}'
}
result = log(sql, name) { @connection.select_all(sql) }
columns = []
result.each do |field|
default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue]
if field[:ColType] =~ /numeric|decimal/i
type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})"
else
type = "#{field[:ColType]}(#{field[:Length]})"
end
is_identity = field[:IsIdentity] == 1
is_nullable = field[:IsNullable] == 'YES'
columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable)
end
columns
end
|
#commit_db_transaction ⇒ Object
332
333
334
335
336
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 332
def commit_db_transaction
@connection.commit
ensure
@connection["AutoCommit"] = true
end
|
#create_database(name) ⇒ Object
407
408
409
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 407
def create_database(name)
execute "CREATE DATABASE #{name}"
end
|
#current_database ⇒ Object
411
412
413
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 411
def current_database
@connection.select_one("select DB_NAME()")[0]
end
|
#disconnect! ⇒ Object
Disconnects from the database
252
253
254
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 252
def disconnect!
@connection.disconnect rescue nil
end
|
#drop_database(name) ⇒ Object
403
404
405
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 403
def drop_database(name)
execute "DROP DATABASE #{name}"
end
|
#execute(sql, name = nil) ⇒ Object
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 308
def execute(sql, name = nil)
if sql =~ /^\s*INSERT/i && (table_name = query_requires_identity_insert?(sql))
log(sql, name) do
with_identity_insert_enabled(table_name) do
@connection.execute(sql) do |handle|
yield(handle) if block_given?
end
end
end
else
log(sql, name) do
@connection.execute(sql) do |handle|
yield(handle) if block_given?
end
end
end
end
|
#indexes(table_name, name = nil) ⇒ Object
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 425
def indexes(table_name, name = nil)
ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = false
indexes = []
execute("EXEC sp_helpindex '#{table_name}'", name) do |sth|
sth.each do |index|
unique = index[1] =~ /unique/
primary = index[1] =~ /primary key/
if !primary
indexes << IndexDefinition.new(table_name, index[0], unique, index[2].split(", "))
end
end
end
indexes
ensure
ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = true
end
|
#insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil) ⇒ Object
295
296
297
298
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 295
def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
execute(sql, name)
id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
end
|
#native_database_types ⇒ Object
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 194
def native_database_types
{
:primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
:string => { :name => "varchar", :limit => 255 },
:text => { :name => "text" },
:integer => { :name => "int" },
:float => { :name => "float", :limit => 8 },
:decimal => { :name => "decimal" },
:datetime => { :name => "datetime" },
:timestamp => { :name => "datetime" },
:time => { :name => "datetime" },
:date => { :name => "datetime" },
:binary => { :name => "image"},
:boolean => { :name => "bit"}
}
end
|
#quote(value, column = nil) ⇒ Object
344
345
346
347
348
349
350
351
352
353
354
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 344
def quote(value, column = nil)
return value.quoted_id if value.respond_to?(:quoted_id)
case value
when TrueClass then '1'
when FalseClass then '0'
when Time, DateTime then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
when Date then "'#{value.strftime("%Y%m%d")}'"
else super
end
end
|
#quote_column_name(name) ⇒ Object
360
361
362
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 360
def quote_column_name(name)
"[#{name}]"
end
|
#quote_string(string) ⇒ Object
356
357
358
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 356
def quote_string(string)
string.gsub(/\'/, "''")
end
|
#reconnect! ⇒ Object
Reconnects to the database, returns false if no connection could be made.
242
243
244
245
246
247
248
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 242
def reconnect!
disconnect!
@connection = DBI.connect(*@connection_options)
rescue DBI::DatabaseError => e
@logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
false
end
|
#recreate_database(name) ⇒ Object
398
399
400
401
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 398
def recreate_database(name)
drop_database(name)
create_database(name)
end
|
#remove_check_constraints(table_name, column_name) ⇒ Object
485
486
487
488
489
490
491
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 485
def remove_check_constraints(table_name, column_name)
constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
constraints.each do |constraint|
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
end
end
|
#remove_column(table_name, column_name) ⇒ Object
471
472
473
474
475
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 471
def remove_column(table_name, column_name)
remove_check_constraints(table_name, column_name)
remove_default_constraint(table_name, column_name)
execute "ALTER TABLE [#{table_name}] DROP COLUMN [#{column_name}]"
end
|
#remove_default_constraint(table_name, column_name) ⇒ Object
477
478
479
480
481
482
483
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 477
def remove_default_constraint(table_name, column_name)
constraints = select "select def.name from sysobjects def, syscolumns col, sysobjects tab where col.cdefault = def.id and col.name = '#{column_name}' and tab.name = '#{table_name}' and col.id = tab.id"
constraints.each do |constraint|
execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
end
end
|
#remove_index(table_name, options = {}) ⇒ Object
493
494
495
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 493
def remove_index(table_name, options = {})
execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
end
|
#rename_column(table, column, new_column_name) ⇒ Object
456
457
458
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 456
def rename_column(table, column, new_column_name)
execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
end
|
#rename_table(name, new_name) ⇒ Object
442
443
444
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 442
def rename_table(name, new_name)
execute "EXEC sp_rename '#{name}', '#{new_name}'"
end
|
#rollback_db_transaction ⇒ Object
338
339
340
341
342
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 338
def rollback_db_transaction
@connection.rollback
ensure
@connection["AutoCommit"] = true
end
|
#supports_migrations? ⇒ Boolean
215
216
217
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 215
def supports_migrations? true
end
|
#tables(name = nil) ⇒ Object
415
416
417
418
419
420
421
422
423
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 415
def tables(name = nil)
execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", name) do |sth|
sth.inject([]) do |tables, field|
table_name = field[0]
tables << table_name unless table_name == 'dtproperties'
tables
end
end
end
|
#type_to_sql(type, limit = nil, precision = nil, scale = nil) ⇒ Object
219
220
221
222
223
224
225
226
227
228
229
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 219
def type_to_sql(type, limit = nil, precision = nil, scale = nil) return super unless type.to_s == 'integer'
if limit.nil? || limit == 4
'integer'
elsif limit < 4
'smallint'
else
'bigint'
end
end
|
#update(sql, name = nil) ⇒ Object
Also known as:
delete
300
301
302
303
304
|
# File 'lib/active_record/connection_adapters/sqlserver_adapter.rb', line 300
def update(sql, name = nil)
execute(sql, name) do |handle|
handle.rows
end || select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]
end
|