Class ActiveRecord::ConnectionAdapters::SQLServerAdapter
In: vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb
Parent: AbstractAdapter

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:

  • :host — Defaults to localhost.
  • :database — The name of the database. No default, must be provided.

ODBC specific options:

  • :dsn — Defaults to nothing.

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]

Methods

Public Class methods

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 189
189:       def initialize(connection, logger, connection_options=nil)
190:         super(connection, logger)
191:         @connection_options = connection_options
192:       end

Public Instance methods

Returns true if the connection is active.

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 234
234:       def active?
235:         @connection.execute("SELECT 1").finish
236:         true
237:       rescue DBI::DatabaseError, DBI::InterfaceError
238:         false
239:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 211
211:       def adapter_name
212:         'SQLServer'
213:       end

Adds a new column to the named table. See TableDefinition#column for details of the options you can use.

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 457
457:       def add_column(table_name, column_name, type, options = {})
458:         add_column_sql = "ALTER TABLE #{table_name} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
459:         add_column_options!(add_column_sql, options)
460:         # TODO: Add support to mimic date columns, using constraints to mark them as such in the database
461:         # add_column_sql << " CONSTRAINT ck__#{table_name}__#{column_name}__date_only CHECK ( CONVERT(CHAR(12), #{quote_column_name(column_name)}, 14)='00:00:00:000' )" if type == :date       
462:         execute(add_column_sql)
463:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 373
373:       def add_limit_offset!(sql, options)
374:         if options[:limit] and options[:offset]
375:           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
376:           if (options[:limit] + options[:offset]) >= total_rows
377:             options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
378:           end
379:           sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]} ")
380:           sql << ") AS tmp1"
381:           if options[:order]
382:             options[:order] = options[:order].split(',').map do |field|
383:               parts = field.split(" ")
384:               tc = parts[0]
385:               if sql =~ /\.\[/ and tc =~ /\./ # if column quoting used in query
386:                 tc.gsub!(/\./, '\\.\\[')
387:                 tc << '\\]'
388:               end
389:               if sql =~ /#{tc} AS (t\d_r\d\d?)/
390:                 parts[0] = $1
391:               elsif parts[0] =~ /\w+\.(\w+)/
392:                 parts[0] = $1
393:               end
394:               parts.join(' ')
395:             end.join(', ')
396:             sql << " ORDER BY #{change_order_direction(options[:order])}) AS tmp2 ORDER BY #{options[:order]}"
397:           else
398:             sql << " ) AS tmp2"
399:           end
400:         elsif sql !~ /^\s*SELECT (@@|COUNT\()/i
401:           sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i) do
402:             "SELECT#{$1} TOP #{options[:limit]}"
403:           end unless options[:limit].nil?
404:         end
405:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 335
335:       def begin_db_transaction
336:         @connection["AutoCommit"] = false
337:       rescue Exception => e
338:         @connection["AutoCommit"] = true
339:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 266
266:       def columns(table_name, name = nil)
267:         return [] if table_name.blank?
268:         table_name = table_name.to_s if table_name.is_a?(Symbol)
269:         table_name = table_name.split('.')[-1] unless table_name.nil?
270:         sql = %Q{
271:           SELECT 
272:             cols.COLUMN_NAME as ColName,  
273:             cols.COLUMN_DEFAULT as DefaultValue,
274:             cols.NUMERIC_SCALE as numeric_scale,
275:             cols.NUMERIC_PRECISION as numeric_precision, 
276:             cols.DATA_TYPE as ColType, 
277:             cols.IS_NULLABLE As IsNullable,  
278:             COL_LENGTH(cols.TABLE_NAME, cols.COLUMN_NAME) as Length,  
279:             COLUMNPROPERTY(OBJECT_ID(cols.TABLE_NAME), cols.COLUMN_NAME, 'IsIdentity') as IsIdentity,  
280:             cols.NUMERIC_SCALE as Scale 
281:           FROM INFORMATION_SCHEMA.COLUMNS cols 
282:           WHERE cols.TABLE_NAME = '#{table_name}'   
283:         }
284:         # Comment out if you want to have the Columns select statment logged.
285:         # Personally, I think it adds unnecessary bloat to the log. 
286:         # If you do comment it out, make sure to un-comment the "result" line that follows
287:         result = log(sql, name) { @connection.select_all(sql) }
288:         #result = @connection.select_all(sql)
289:         columns = []
290:         result.each do |field|
291:           default = field[:DefaultValue].to_s.gsub!(/[()\']/,"") =~ /null/ ? nil : field[:DefaultValue]
292:           if field[:ColType] =~ /numeric|decimal/i
293:             type = "#{field[:ColType]}(#{field[:numeric_precision]},#{field[:numeric_scale]})"
294:           else
295:             type = "#{field[:ColType]}(#{field[:Length]})"
296:           end
297:           is_identity = field[:IsIdentity] == 1
298:           is_nullable = field[:IsNullable] == 'YES'
299:           columns << SQLServerColumn.new(field[:ColName], default, type, is_identity, is_nullable)
300:         end
301:         columns
302:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 341
341:       def commit_db_transaction
342:         @connection.commit
343:       ensure
344:         @connection["AutoCommit"] = true
345:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 416
416:       def create_database(name)
417:         execute "CREATE DATABASE #{name}"
418:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 420
420:       def current_database
421:         @connection.select_one("select DB_NAME()")[0]
422:       end
delete(sql, name = nil)

Alias for update

Disconnects from the database

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 252
252:       def disconnect!
253:         @connection.disconnect rescue nil
254:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 412
412:       def drop_database(name)
413:         execute "DROP DATABASE #{name}"
414:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 317
317:       def execute(sql, name = nil)
318:         if sql =~ /^\s*INSERT/i && (table_name = query_requires_identity_insert?(sql))
319:           log(sql, name) do
320:             with_identity_insert_enabled(table_name) do 
321:               @connection.execute(sql) do |handle|
322:                 yield(handle) if block_given?
323:               end
324:             end
325:           end
326:         else
327:           log(sql, name) do
328:             @connection.execute(sql) do |handle|
329:               yield(handle) if block_given?
330:             end
331:           end
332:         end
333:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 434
434:       def indexes(table_name, name = nil)
435:         ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = false
436:         indexes = []        
437:         execute("EXEC sp_helpindex #{table_name}", name) do |sth|
438:           sth.each do |index| 
439:             unique = index[1] =~ /unique/
440:             primary = index[1] =~ /primary key/
441:             if !primary
442:               indexes << IndexDefinition.new(table_name, index[0], unique, index[2].split(", "))
443:             end
444:           end
445:         end
446:         indexes
447:         ensure
448:           ActiveRecord::Base.connection.instance_variable_get("@connection")["AutoCommit"] = true
449:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 304
304:       def insert(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
305:         execute(sql, name)
306:         id_value || select_one("SELECT @@IDENTITY AS Ident")["Ident"]
307:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 194
194:       def native_database_types
195:         {
196:           :primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
197:           :string      => { :name => "varchar", :limit => 255  },
198:           :text        => { :name => "text" },
199:           :integer     => { :name => "int" },
200:           :float       => { :name => "float", :limit => 8 },
201:           :decimal     => { :name => "decimal" },
202:           :datetime    => { :name => "datetime" },
203:           :timestamp   => { :name => "datetime" },
204:           :time        => { :name => "datetime" },
205:           :date        => { :name => "datetime" },
206:           :binary      => { :name => "image"},
207:           :boolean     => { :name => "bit"}
208:         }
209:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 353
353:       def quote(value, column = nil)
354:         return value.quoted_id if value.respond_to?(:quoted_id)
355: 
356:         case value
357:           when TrueClass             then '1'
358:           when FalseClass            then '0'
359:           when Time, DateTime        then "'#{value.strftime("%Y%m%d %H:%M:%S")}'"
360:           when Date                  then "'#{value.strftime("%Y%m%d")}'"
361:           else                       super
362:         end
363:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 369
369:       def quote_column_name(name)
370:         "[#{name}]"
371:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 365
365:       def quote_string(string)
366:         string.gsub(/\'/, "''")
367:       end

Reconnects to the database, returns false if no connection could be made.

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 242
242:       def reconnect!
243:         disconnect!
244:         @connection = DBI.connect(*@connection_options)
245:       rescue DBI::DatabaseError => e
246:         @logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
247:         false
248:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 407
407:       def recreate_database(name)
408:         drop_database(name)
409:         create_database(name)
410:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 494
494:       def remove_check_constraints(table_name, column_name)
495:         # TODO remove all constraints in single method
496:         constraints = select "SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{table_name}' and COLUMN_NAME = '#{column_name}'"
497:         constraints.each do |constraint|
498:           execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["CONSTRAINT_NAME"]}"
499:         end
500:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 480
480:       def remove_column(table_name, column_name)
481:         remove_check_constraints(table_name, column_name)
482:         remove_default_constraint(table_name, column_name)
483:         execute "ALTER TABLE [#{table_name}] DROP COLUMN [#{column_name}]"
484:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 486
486:       def remove_default_constraint(table_name, column_name)
487:         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"
488:         
489:         constraints.each do |constraint|
490:           execute "ALTER TABLE #{table_name} DROP CONSTRAINT #{constraint["name"]}"
491:         end
492:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 502
502:       def remove_index(table_name, options = {})
503:         execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
504:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 465
465:       def rename_column(table, column, new_column_name)
466:         execute "EXEC sp_rename '#{table}.#{column}', '#{new_column_name}'"
467:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 451
451:       def rename_table(name, new_name)
452:         execute "EXEC sp_rename '#{name}', '#{new_name}'"
453:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 347
347:       def rollback_db_transaction
348:         @connection.rollback
349:       ensure
350:         @connection["AutoCommit"] = true
351:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 256
256:       def select_all(sql, name = nil)
257:         select(sql, name)
258:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 260
260:       def select_one(sql, name = nil)
261:         add_limit!(sql, :limit => 1)
262:         result = select(sql, name)
263:         result.nil? ? nil : result.first
264:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 424
424:       def tables(name = nil)
425:         execute("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", name) do |sth|
426:           sth.inject([]) do |tables, field|
427:             table_name = field[0]
428:             tables << table_name unless table_name == 'dtproperties'
429:             tables
430:           end
431:         end
432:       end

[Source]

     # File vendor/rails/activerecord/lib/active_record/connection_adapters/sqlserver_adapter.rb, line 309
309:       def update(sql, name = nil)
310:         execute(sql, name) do |handle|
311:           handle.rows
312:         end || select_one("SELECT @@ROWCOUNT AS AffectedRows")["AffectedRows"]        
313:       end

[Validate]