PageRenderTime 67ms CodeModel.GetById 28ms RepoModel.GetById 0ms app.codeStats 1ms

/lib/active_record/connection_adapters/sqlserver_adapter.rb

https://github.com/neomindryan/2000-2005-adapter
Ruby | 990 lines | 781 code | 144 blank | 65 comment | 79 complexity | 67438fa78cef194c940c8e5743c2b351 MD5 | raw file
  1. require 'active_record/connection_adapters/abstract_adapter'
  2. require_library_or_gem 'dbi' unless defined?(DBI)
  3. require 'core_ext/dbi'
  4. require 'core_ext/active_record'
  5. require 'base64'
  6. module ActiveRecord
  7. class Base
  8. def self.sqlserver_connection(config) #:nodoc:
  9. config.symbolize_keys!
  10. mode = config[:mode] ? config[:mode].to_s.upcase : 'ADO'
  11. username = config[:username] ? config[:username].to_s : 'sa'
  12. password = config[:password] ? config[:password].to_s : ''
  13. if mode == "ODBC"
  14. raise ArgumentError, "Missing DSN. Argument ':dsn' must be set in order for this adapter to work." unless config.has_key?(:dsn)
  15. dsn = config[:dsn]
  16. driver_url = "DBI:ODBC:#{dsn}"
  17. else
  18. raise ArgumentError, "Missing Database. Argument ':database' must be set in order for this adapter to work." unless config.has_key?(:database)
  19. database = config[:database]
  20. host = config[:host] ? config[:host].to_s : 'localhost'
  21. driver_url = "DBI:ADO:Provider=SQLOLEDB;Data Source=#{host};Initial Catalog=#{database};User ID=#{username};Password=#{password};"
  22. end
  23. conn = DBI.connect(driver_url, username, password)
  24. conn["AutoCommit"] = true
  25. ConnectionAdapters::SQLServerAdapter.new(conn, logger, [driver_url, username, password])
  26. end
  27. end
  28. module ConnectionAdapters
  29. class SQLServerColumn < Column
  30. def initialize(name, default, sql_type = nil, null = true, sqlserver_options = {})
  31. @sqlserver_options = sqlserver_options
  32. super(name, default, sql_type, null)
  33. end
  34. class << self
  35. def string_to_binary(value)
  36. "0x#{value.unpack("H*")[0]}"
  37. end
  38. def binary_to_string(value)
  39. value =~ /[^[:xdigit:]]/ ? value : [value].pack('H*')
  40. end
  41. end
  42. def is_identity?
  43. @sqlserver_options[:is_identity]
  44. end
  45. def is_special?
  46. # TODO: Not sure if these should be added: varbinary(max), nchar, nvarchar(max)
  47. sql_type =~ /^text|ntext|image$/
  48. end
  49. def is_utf8?
  50. sql_type =~ /nvarchar|ntext|nchar/i
  51. end
  52. def table_name
  53. @sqlserver_options[:table_name]
  54. end
  55. def table_klass
  56. @table_klass ||= table_name.classify.constantize rescue nil
  57. (@table_klass && @table_klass < ActiveRecord::Base) ? @table_klass : nil
  58. end
  59. private
  60. def extract_limit(sql_type)
  61. case sql_type
  62. when /^smallint/i
  63. 2
  64. when /^int/i
  65. 4
  66. when /^bigint/i
  67. 8
  68. when /\(max\)/, /decimal/, /numeric/
  69. nil
  70. else
  71. super
  72. end
  73. end
  74. def simplified_type(field_type)
  75. case field_type
  76. when /real/i then :float
  77. when /money/i then :decimal
  78. when /image/i then :binary
  79. when /bit/i then :boolean
  80. when /uniqueidentifier/i then :string
  81. when /datetime/i then simplified_datetime
  82. else super
  83. end
  84. end
  85. def simplified_datetime
  86. if table_klass && table_klass.coerced_sqlserver_date_columns.include?(name)
  87. :date
  88. elsif table_klass && table_klass.coerced_sqlserver_time_columns.include?(name)
  89. :time
  90. else
  91. :datetime
  92. end
  93. end
  94. end #SQLServerColumn
  95. # In ADO mode, this adapter will ONLY work on Windows systems, since it relies on
  96. # Win32OLE, which, to my knowledge, is only available on Windows.
  97. #
  98. # This mode also relies on the ADO support in the DBI module. If you are using the
  99. # one-click installer of Ruby, then you already have DBI installed, but the ADO module
  100. # is *NOT* installed. You will need to get the latest source distribution of Ruby-DBI
  101. # from http://ruby-dbi.sourceforge.net/ unzip it, and copy the file from
  102. # <tt>src/lib/dbd_ado/ADO.rb</tt> to <tt>X:/Ruby/lib/ruby/site_ruby/1.8/DBD/ADO/ADO.rb</tt>
  103. #
  104. # You will more than likely need to create the ADO directory. Once you've installed
  105. # that file, you are ready to go.
  106. #
  107. # In ODBC mode, the adapter requires the ODBC support in the DBI module which requires
  108. # the Ruby ODBC module. Ruby ODBC 0.996 was used in development and testing,
  109. # and it is available at http://www.ch-werner.de/rubyodbc/
  110. #
  111. # Options:
  112. #
  113. # * <tt>:mode</tt> -- ADO or ODBC. Defaults to ADO.
  114. # * <tt>:username</tt> -- Defaults to sa.
  115. # * <tt>:password</tt> -- Defaults to empty string.
  116. # * <tt>:windows_auth</tt> -- Defaults to "User ID=#{username};Password=#{password}"
  117. #
  118. # ADO specific options:
  119. #
  120. # * <tt>:host</tt> -- Defaults to localhost.
  121. # * <tt>:database</tt> -- The name of the database. No default, must be provided.
  122. # * <tt>:windows_auth</tt> -- Use windows authentication instead of username/password.
  123. #
  124. # ODBC specific options:
  125. #
  126. # * <tt>:dsn</tt> -- Defaults to nothing.
  127. #
  128. class SQLServerAdapter < AbstractAdapter
  129. ADAPTER_NAME = 'SQLServer'.freeze
  130. VERSION = '2.2.13'.freeze
  131. DATABASE_VERSION_REGEXP = /Microsoft SQL Server\s+(\d{4})/
  132. SUPPORTED_VERSIONS = [2000,2005].freeze
  133. LIMITABLE_TYPES = ['string','integer','float','char','nchar','varchar','nvarchar'].freeze
  134. cattr_accessor :native_text_database_type, :native_binary_database_type, :native_string_database_type,
  135. :log_info_schema_queries, :enable_default_unicode_types
  136. class << self
  137. def type_limitable?(type)
  138. LIMITABLE_TYPES.include?(type.to_s)
  139. end
  140. end
  141. def initialize(connection, logger, connection_options=nil)
  142. super(connection, logger)
  143. @connection_options = connection_options
  144. initialize_sqlserver_caches
  145. unless SUPPORTED_VERSIONS.include?(database_year)
  146. raise NotImplementedError, "Currently, only #{SUPPORTED_VERSIONS.to_sentence} are supported."
  147. end
  148. end
  149. # ABSTRACT ADAPTER =========================================#
  150. def adapter_name
  151. ADAPTER_NAME
  152. end
  153. def supports_migrations?
  154. true
  155. end
  156. def supports_ddl_transactions?
  157. true
  158. end
  159. def database_version
  160. @database_version ||= info_schema_query { select_value('SELECT @@version') }
  161. end
  162. def database_year
  163. DATABASE_VERSION_REGEXP.match(database_version)[1].to_i
  164. end
  165. def sqlserver?
  166. true
  167. end
  168. def sqlserver_2000?
  169. database_year == 2000
  170. end
  171. def sqlserver_2005?
  172. database_year == 2005
  173. end
  174. def version
  175. self.class::VERSION
  176. end
  177. def inspect
  178. "#<#{self.class} version: #{version}, year: #{database_year}, connection_options: #{@connection_options.inspect}>"
  179. end
  180. def native_string_database_type
  181. @@native_string_database_type || (enable_default_unicode_types ? 'nvarchar' : 'varchar')
  182. end
  183. def native_text_database_type
  184. @@native_text_database_type ||
  185. if sqlserver_2005?
  186. enable_default_unicode_types ? 'nvarchar(max)' : 'varchar(max)'
  187. else
  188. enable_default_unicode_types ? 'ntext' : 'text'
  189. end
  190. end
  191. def native_binary_database_type
  192. @@native_binary_database_type || (sqlserver_2005? ? 'varbinary(max)' : 'image')
  193. end
  194. # QUOTING ==================================================#
  195. def quote(value, column = nil)
  196. case value
  197. when String, ActiveSupport::Multibyte::Chars
  198. if column && column.type == :binary
  199. column.class.string_to_binary(value)
  200. elsif column && column.respond_to?(:is_utf8?) && column.is_utf8?
  201. quoted_utf8_value(value)
  202. else
  203. super
  204. end
  205. else
  206. super
  207. end
  208. end
  209. def quote_string(string)
  210. string.to_s.gsub(/\'/, "''")
  211. end
  212. def quote_column_name(column_name)
  213. column_name.to_s.split('.').map{ |name| "[#{name}]" }.join('.')
  214. end
  215. def quote_table_name(table_name)
  216. return table_name if table_name =~ /^\[.*\]$/
  217. quote_column_name(table_name)
  218. end
  219. def quoted_true
  220. '1'
  221. end
  222. def quoted_false
  223. '0'
  224. end
  225. def quoted_date(value)
  226. if value.acts_like?(:time) && value.respond_to?(:usec)
  227. "#{super}.#{sprintf("%03d",value.usec/1000)}"
  228. else
  229. super
  230. end
  231. end
  232. def quoted_utf8_value(value)
  233. "N'#{quote_string(value)}'"
  234. end
  235. # REFERENTIAL INTEGRITY ====================================#
  236. def disable_referential_integrity(&block)
  237. do_execute "EXEC sp_MSForEachTable 'ALTER TABLE ? NOCHECK CONSTRAINT ALL'"
  238. yield
  239. ensure
  240. do_execute "EXEC sp_MSForEachTable 'ALTER TABLE ? CHECK CONSTRAINT ALL'"
  241. end
  242. # CONNECTION MANAGEMENT ====================================#
  243. def active?
  244. raw_connection.execute("SELECT 1").finish
  245. true
  246. rescue DBI::DatabaseError, DBI::InterfaceError
  247. false
  248. end
  249. def reconnect!
  250. disconnect!
  251. @connection = DBI.connect(*@connection_options)
  252. rescue DBI::DatabaseError => e
  253. @logger.warn "#{adapter_name} reconnection failed: #{e.message}" if @logger
  254. false
  255. end
  256. def disconnect!
  257. raw_connection.disconnect rescue nil
  258. end
  259. def finish_statement_handle(handle)
  260. handle.finish if handle && handle.respond_to?(:finish) && !handle.finished?
  261. handle
  262. end
  263. # DATABASE STATEMENTS ======================================#
  264. def select_rows(sql, name = nil)
  265. raw_select(sql,name).last
  266. end
  267. def execute(sql, name = nil, &block)
  268. if table_name = query_requires_identity_insert?(sql)
  269. handle = with_identity_insert_enabled(table_name) { raw_execute(sql,name,&block) }
  270. else
  271. handle = raw_execute(sql,name,&block)
  272. end
  273. finish_statement_handle(handle)
  274. end
  275. def execute_procedure(proc_name, *variables)
  276. vars = variables.map{ |v| quote(v) }.join(', ')
  277. sql = "EXEC #{proc_name} #{vars}".strip
  278. select(sql,'Execute Procedure',true).inject([]) do |results,row|
  279. results << row.with_indifferent_access
  280. end
  281. end
  282. def begin_db_transaction
  283. do_execute "BEGIN TRANSACTION"
  284. end
  285. def commit_db_transaction
  286. do_execute "COMMIT TRANSACTION"
  287. end
  288. def rollback_db_transaction
  289. do_execute "ROLLBACK TRANSACTION" rescue nil
  290. end
  291. def add_limit_offset!(sql, options)
  292. # Validate and/or convert integers for :limit and :offets options.
  293. if options[:offset]
  294. raise ArgumentError, "offset should have a limit" unless options[:limit]
  295. unless options[:offset].kind_of?(Integer)
  296. if options[:offset] =~ /^\d+$/
  297. options[:offset] = options[:offset].to_i
  298. else
  299. raise ArgumentError, "offset should be an integer"
  300. end
  301. end
  302. end
  303. if options[:limit] && !(options[:limit].kind_of?(Integer))
  304. if options[:limit] =~ /^\d+$/
  305. options[:limit] = options[:limit].to_i
  306. else
  307. raise ArgumentError, "limit should be an integer"
  308. end
  309. end
  310. # The business of adding limit/offset
  311. if options[:limit] and options[:offset]
  312. total_rows = select_value("SELECT count(*) as TotalRows from (#{sql.sub(/\bSELECT(\s+DISTINCT)?\b/i, "SELECT#{$1} TOP 1000000000")}) tally").to_i
  313. if (options[:limit] + options[:offset]) >= total_rows
  314. options[:limit] = (total_rows - options[:offset] >= 0) ? (total_rows - options[:offset]) : 0
  315. end
  316. # Make sure we do not need a special limit/offset for association limiting. http://gist.github.com/25118
  317. add_limit_offset_for_association_limiting!(sql,options) and return if sql_for_association_limiting?(sql)
  318. # Wrap the SQL query in a bunch of outer SQL queries that emulate proper LIMIT,OFFSET support.
  319. sql.sub!(/^\s*SELECT(\s+DISTINCT)?/i, "SELECT * FROM (SELECT TOP #{options[:limit]} * FROM (SELECT#{$1} TOP #{options[:limit] + options[:offset]}")
  320. sql << ") AS tmp1"
  321. if options[:order]
  322. order = options[:order].split(',').map do |field|
  323. order_by_column, order_direction = field.split(" ")
  324. order_by_column = quote_column_name(order_by_column)
  325. # Investigate the SQL query to figure out if the order_by_column has been renamed.
  326. if sql =~ /#{Regexp.escape(order_by_column)} AS (t\d_r\d\d?)/
  327. # Fx "[foo].[bar] AS t4_r2" was found in the SQL. Use the column alias (ie 't4_r2') for the subsequent orderings
  328. order_by_column = $1
  329. elsif order_by_column =~ /\w+\.\[?(\w+)\]?/
  330. order_by_column = $1
  331. else
  332. # It doesn't appear that the column name has been renamed as part of the query. Use just the column
  333. # name rather than the full identifier for the outer queries.
  334. order_by_column = order_by_column.split('.').last
  335. end
  336. # Put the column name and eventual direction back together
  337. [order_by_column, order_direction].join(' ').strip
  338. end.join(', ')
  339. sql << " ORDER BY #{change_order_direction(order)}) AS tmp2 ORDER BY #{order}"
  340. else
  341. sql << ") AS tmp2"
  342. end
  343. elsif options[:limit] && sql !~ /^\s*SELECT (@@|COUNT\()/i
  344. if md = sql.match(/^(\s*SELECT)(\s+DISTINCT)?(.*)/im)
  345. sql.replace "#{md[1]}#{md[2]} TOP #{options[:limit]}#{md[3]}"
  346. else
  347. # Account for building SQL fragments without SELECT yet. See #update_all and #limited_update_conditions.
  348. sql.replace "TOP #{options[:limit]} #{sql}"
  349. end
  350. end
  351. end
  352. def add_lock!(sql, options)
  353. # http://blog.sqlauthority.com/2007/04/27/sql-server-2005-locking-hints-and-examples/
  354. return unless options[:lock]
  355. lock_type = options[:lock] == true ? 'WITH(HOLDLOCK, ROWLOCK)' : options[:lock]
  356. from_table = sql.match(/FROM(.*)WHERE/im)[1]
  357. sql.sub! from_table, "#{from_table}#{lock_type} "
  358. end
  359. def empty_insert_statement(table_name)
  360. "INSERT INTO #{quote_table_name(table_name)} DEFAULT VALUES"
  361. end
  362. def case_sensitive_equality_operator
  363. "COLLATE Latin1_General_CS_AS ="
  364. end
  365. def limited_update_conditions(where_sql, quoted_table_name, quoted_primary_key)
  366. match_data = where_sql.match(/(.*)WHERE/)
  367. limit = match_data[1]
  368. where_sql.sub!(limit,'')
  369. "WHERE #{quoted_primary_key} IN (SELECT #{limit} #{quoted_primary_key} FROM #{quoted_table_name} #{where_sql})"
  370. end
  371. # SCHEMA STATEMENTS ========================================#
  372. def native_database_types
  373. {
  374. :primary_key => "int NOT NULL IDENTITY(1, 1) PRIMARY KEY",
  375. :string => { :name => native_string_database_type, :limit => 255 },
  376. :text => { :name => native_text_database_type },
  377. :integer => { :name => "int", :limit => 4 },
  378. :float => { :name => "float", :limit => 8 },
  379. :decimal => { :name => "decimal" },
  380. :datetime => { :name => "datetime" },
  381. :timestamp => { :name => "datetime" },
  382. :time => { :name => "datetime" },
  383. :date => { :name => "datetime" },
  384. :binary => { :name => native_binary_database_type },
  385. :boolean => { :name => "bit"},
  386. # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them.
  387. :char => { :name => 'char' },
  388. :varchar_max => { :name => 'varchar(max)' },
  389. :nchar => { :name => "nchar" },
  390. :nvarchar => { :name => "nvarchar", :limit => 255 },
  391. :nvarchar_max => { :name => "nvarchar(max)" },
  392. :ntext => { :name => "ntext" }
  393. }
  394. end
  395. def table_alias_length
  396. 128
  397. end
  398. def tables(name = nil)
  399. info_schema_query do
  400. select_values "SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME <> 'dtproperties'"
  401. end
  402. end
  403. def views(name = nil)
  404. @sqlserver_views_cache ||=
  405. info_schema_query { select_values("SELECT TABLE_NAME FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME NOT IN ('sysconstraints','syssegments')") }
  406. end
  407. def view_information(table_name)
  408. table_name = unqualify_table_name(table_name)
  409. @sqlserver_view_information_cache[table_name] ||= begin
  410. view_info = info_schema_query { select_one("SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'") }
  411. if view_info
  412. view_info['VIEW_DEFINITION'] ||= info_schema_query { select_values("EXEC sp_helptext #{table_name}").join }
  413. end
  414. view_info
  415. end
  416. end
  417. def view_table_name(table_name)
  418. view_info = view_information(table_name)
  419. view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
  420. end
  421. def table_exists?(table_name)
  422. super || tables.include?(unqualify_table_name(table_name)) || views.include?(table_name.to_s)
  423. end
  424. def indexes(table_name, name = nil)
  425. unquoted_table_name = unqualify_table_name(table_name)
  426. select("EXEC sp_helpindex #{quote_table_name(unquoted_table_name)}",name).inject([]) do |indexes,index|
  427. if index['index_description'] =~ /primary key/
  428. indexes
  429. else
  430. name = index['index_name']
  431. unique = index['index_description'] =~ /unique/
  432. columns = index['index_keys'].split(',').map do |column|
  433. column.strip!
  434. column.gsub! '(-)', '' if column.ends_with?('(-)')
  435. column
  436. end
  437. indexes << IndexDefinition.new(table_name, name, unique, columns)
  438. end
  439. end
  440. end
  441. def columns(table_name, name = nil)
  442. return [] if table_name.blank?
  443. cache_key = unqualify_table_name(table_name)
  444. @sqlserver_columns_cache[cache_key] ||= column_definitions(table_name).collect do |ci|
  445. sqlserver_options = ci.except(:name,:default_value,:type,:null)
  446. SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
  447. end
  448. end
  449. def create_table(table_name, options = {})
  450. super
  451. remove_sqlserver_columns_cache_for(table_name)
  452. end
  453. def rename_table(table_name, new_name)
  454. do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
  455. end
  456. def drop_table(table_name, options = {})
  457. super
  458. remove_sqlserver_columns_cache_for(table_name)
  459. end
  460. def add_column(table_name, column_name, type, options = {})
  461. super
  462. remove_sqlserver_columns_cache_for(table_name)
  463. end
  464. def remove_column(table_name, *column_names)
  465. column_names.flatten.each do |column_name|
  466. remove_check_constraints(table_name, column_name)
  467. remove_default_constraint(table_name, column_name)
  468. remove_indexes(table_name, column_name)
  469. do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
  470. end
  471. remove_sqlserver_columns_cache_for(table_name)
  472. end
  473. def change_column(table_name, column_name, type, options = {})
  474. sql_commands = []
  475. change_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}"
  476. change_column_sql << " NOT NULL" if options[:null] == false
  477. sql_commands << change_column_sql
  478. if options_include_default?(options)
  479. remove_default_constraint(table_name, column_name)
  480. sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}"
  481. end
  482. sql_commands.each { |c| do_execute(c) }
  483. remove_sqlserver_columns_cache_for(table_name)
  484. end
  485. def change_column_default(table_name, column_name, default)
  486. remove_default_constraint(table_name, column_name)
  487. do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
  488. remove_sqlserver_columns_cache_for(table_name)
  489. end
  490. def rename_column(table_name, column_name, new_column_name)
  491. column_for(table_name,column_name)
  492. do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
  493. remove_sqlserver_columns_cache_for(table_name)
  494. end
  495. def remove_index(table_name, options = {})
  496. do_execute "DROP INDEX #{table_name}.#{quote_column_name(index_name(table_name, options))}"
  497. end
  498. def type_to_sql(type, limit = nil, precision = nil, scale = nil)
  499. limit = nil unless self.class.type_limitable?(type)
  500. case type.to_s
  501. when 'integer'
  502. case limit
  503. when 1..2 then 'smallint'
  504. when 3..4, nil then 'integer'
  505. when 5..8 then 'bigint'
  506. else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
  507. end
  508. else
  509. super
  510. end
  511. end
  512. def add_order_by_for_association_limiting!(sql, options)
  513. # Disertation http://gist.github.com/24073
  514. # Information http://weblogs.sqlteam.com/jeffs/archive/2007/12/13/select-distinct-order-by-error.aspx
  515. return sql if options[:order].blank?
  516. columns = sql.match(/SELECT\s+DISTINCT(.*)FROM/)[1].strip
  517. sql.sub!(/SELECT\s+DISTINCT/,'SELECT')
  518. sql << "GROUP BY #{columns} ORDER BY #{order_to_min_set(options[:order])}"
  519. end
  520. def change_column_null(table_name, column_name, null, default = nil)
  521. column = column_for(table_name,column_name)
  522. unless null || default.nil?
  523. do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
  524. end
  525. sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
  526. sql << " NOT NULL" unless null
  527. do_execute sql
  528. end
  529. def pk_and_sequence_for(table_name)
  530. idcol = identity_column(table_name)
  531. idcol ? [idcol.name,nil] : nil
  532. end
  533. # RAKE UTILITY METHODS =====================================#
  534. def recreate_database(name)
  535. existing_database = current_database.to_s
  536. if name.to_s == existing_database
  537. do_execute 'USE master'
  538. end
  539. drop_database(name)
  540. create_database(name)
  541. ensure
  542. do_execute "USE #{existing_database}" if name.to_s == existing_database
  543. end
  544. def drop_database(name)
  545. retry_count = 0
  546. max_retries = 1
  547. begin
  548. do_execute "DROP DATABASE #{name}"
  549. rescue ActiveRecord::StatementInvalid => err
  550. # Remove existing connections and rollback any transactions if we received the message
  551. # 'Cannot drop the database 'test' because it is currently in use'
  552. if err.message =~ /because it is currently in use/
  553. raise if retry_count >= max_retries
  554. retry_count += 1
  555. remove_database_connections_and_rollback(name)
  556. retry
  557. else
  558. raise
  559. end
  560. end
  561. end
  562. def create_database(name)
  563. do_execute "CREATE DATABASE #{name}"
  564. end
  565. def current_database
  566. select_value 'SELECT DB_NAME()'
  567. end
  568. def remove_database_connections_and_rollback(name)
  569. # This should disconnect all other users and rollback any transactions for SQL 2000 and 2005
  570. # http://sqlserver2000.databases.aspfaq.com/how-do-i-drop-a-sql-server-database.html
  571. do_execute "ALTER DATABASE #{name} SET SINGLE_USER WITH ROLLBACK IMMEDIATE"
  572. end
  573. protected
  574. # DATABASE STATEMENTS ======================================
  575. def select(sql, name = nil, ignore_special_columns = false)
  576. repair_special_columns(sql) unless ignore_special_columns
  577. fields, rows = raw_select(sql,name)
  578. rows.inject([]) do |results,row|
  579. row_hash = {}
  580. fields.each_with_index do |f, i|
  581. row_hash[f] = row[i]
  582. end
  583. results << row_hash
  584. end
  585. end
  586. def insert_sql(sql, name = nil, pk = nil, id_value = nil, sequence_name = nil)
  587. super || select_value("SELECT SCOPE_IDENTITY() AS Ident")
  588. end
  589. def update_sql(sql, name = nil)
  590. execute(sql, name)
  591. select_value('SELECT @@ROWCOUNT AS AffectedRows')
  592. end
  593. def info_schema_query
  594. log_info_schema_queries ? yield : ActiveRecord::Base.silence{ yield }
  595. end
  596. def raw_execute(sql, name = nil, &block)
  597. log(sql, name) do
  598. if block_given?
  599. raw_connection.execute(sql) { |handle| yield(handle) }
  600. else
  601. raw_connection.execute(sql)
  602. end
  603. end
  604. end
  605. def without_type_conversion
  606. raw_connection.convert_types = false if raw_connection.respond_to?(:convert_types=)
  607. yield
  608. ensure
  609. raw_connection.convert_types = true if raw_connection.respond_to?(:convert_types=)
  610. end
  611. def do_execute(sql,name=nil)
  612. log(sql, name || 'EXECUTE') do
  613. raw_connection.do(sql)
  614. end
  615. end
  616. def raw_select(sql, name = nil)
  617. handle = raw_execute(sql,name)
  618. fields = handle.column_names
  619. results = handle_as_array(handle)
  620. rows = results.inject([]) do |rows,row|
  621. row.each_with_index do |value, i|
  622. # DEPRECATED in DBI 0.4.0 and above. Remove when 0.2.2 and lower is no longer supported.
  623. if value.is_a? DBI::Timestamp
  624. row[i] = value.to_sqlserver_string
  625. end
  626. end
  627. rows << row
  628. end
  629. return fields, rows
  630. end
  631. def handle_as_array(handle)
  632. array = handle.inject([]) do |rows,row|
  633. rows << row.inject([]){ |values,value| values << value }
  634. end
  635. finish_statement_handle(handle)
  636. array
  637. end
  638. def add_limit_offset_for_association_limiting!(sql, options)
  639. sql.replace %|
  640. SET NOCOUNT ON
  641. DECLARE @row_number TABLE (row int identity(1,1), id int)
  642. INSERT INTO @row_number (id)
  643. #{sql}
  644. SET NOCOUNT OFF
  645. SELECT id FROM (
  646. SELECT TOP #{options[:limit]} * FROM (
  647. SELECT TOP #{options[:limit] + options[:offset]} * FROM @row_number ORDER BY row
  648. ) AS tmp1 ORDER BY row DESC
  649. ) AS tmp2 ORDER BY row
  650. |.gsub(/[ \t\r\n]+/,' ')
  651. end
  652. # SCHEMA STATEMENTS ========================================#
  653. def remove_check_constraints(table_name, column_name)
  654. constraints = info_schema_query { select_values("SELECT CONSTRAINT_NAME FROM INFORMATION_SCHEMA.CONSTRAINT_COLUMN_USAGE where TABLE_NAME = '#{quote_string(table_name)}' and COLUMN_NAME = '#{quote_string(column_name)}'") }
  655. constraints.each do |constraint|
  656. do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
  657. end
  658. end
  659. def remove_default_constraint(table_name, column_name)
  660. constraints = select_values("SELECT def.name FROM sysobjects def, syscolumns col, sysobjects tab WHERE col.cdefault = def.id AND col.name = '#{quote_string(column_name)}' AND tab.name = '#{quote_string(table_name)}' AND col.id = tab.id")
  661. constraints.each do |constraint|
  662. do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
  663. end
  664. end
  665. def remove_indexes(table_name, column_name)
  666. indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }.each do |index|
  667. remove_index(table_name, {:name => index.name})
  668. end
  669. end
  670. def default_name(table_name, column_name)
  671. "DF_#{table_name}_#{column_name}"
  672. end
  673. # IDENTITY INSERTS =========================================#
  674. def with_identity_insert_enabled(table_name, &block)
  675. table_name = quote_table_name(table_name_or_views_table_name(table_name))
  676. set_identity_insert(table_name, true)
  677. yield
  678. ensure
  679. set_identity_insert(table_name, false)
  680. end
  681. def set_identity_insert(table_name, enable = true)
  682. sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
  683. do_execute(sql,'IDENTITY_INSERT')
  684. rescue Exception => e
  685. raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
  686. end
  687. def query_requires_identity_insert?(sql)
  688. if insert_sql?(sql)
  689. table_name = get_table_name(sql)
  690. id_column = identity_column(table_name)
  691. id_column && sql =~ /INSERT[^(]+\([^)]*\[#{id_column.name}\][^)]*\)/i ? table_name : false
  692. else
  693. false
  694. end
  695. end
  696. def identity_column(table_name)
  697. columns(table_name).detect(&:is_identity?)
  698. end
  699. def table_name_or_views_table_name(table_name)
  700. unquoted_table_name = unqualify_table_name(table_name)
  701. views.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
  702. end
  703. # HELPER METHODS ===========================================#
  704. def insert_sql?(sql)
  705. !(sql =~ /^\s*INSERT/i).nil?
  706. end
  707. def unqualify_table_name(table_name)
  708. table_name.to_s.split('.').last.gsub(/[\[\]]/,'')
  709. end
  710. def unqualify_db_name(table_name)
  711. table_names = table_name.to_s.split('.')
  712. table_names.length == 3 ? table_names.first.tr('[]','') : nil
  713. end
  714. def get_table_name(sql)
  715. if sql =~ /^\s*insert\s+into\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
  716. $1 || $2
  717. elsif sql =~ /from\s+([^\(\s]+)\s*/i
  718. $1
  719. else
  720. nil
  721. end
  722. end
  723. def orders_and_dirs_set(order)
  724. orders = order.sub('ORDER BY','').split(',').map(&:strip).reject(&:blank?)
  725. orders_dirs = orders.map do |ord|
  726. dir = nil
  727. if match_data = ord.match(/\b(asc|desc)$/i)
  728. dir = match_data[1]
  729. ord.sub!(dir,'').strip!
  730. dir.upcase!
  731. end
  732. [ord,dir]
  733. end
  734. end
  735. def views_real_column_name(table_name,column_name)
  736. view_definition = view_information(table_name)['VIEW_DEFINITION']
  737. match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
  738. match_data ? match_data[1] : column_name
  739. end
  740. def order_to_min_set(order)
  741. orders_dirs = orders_and_dirs_set(order)
  742. orders_dirs.map do |o,d|
  743. "MIN(#{o}) #{d}".strip
  744. end.join(', ')
  745. end
  746. def sql_for_association_limiting?(sql)
  747. if md = sql.match(/^\s*SELECT(.*)FROM.*GROUP BY.*ORDER BY.*/im)
  748. select_froms = md[1].split(',')
  749. select_froms.size == 1 && !select_froms.first.include?('*')
  750. end
  751. end
  752. def remove_sqlserver_columns_cache_for(table_name)
  753. cache_key = unqualify_table_name(table_name)
  754. @sqlserver_columns_cache[cache_key] = nil
  755. initialize_sqlserver_caches(false)
  756. end
  757. def initialize_sqlserver_caches(reset_columns=true)
  758. @sqlserver_columns_cache = {} if reset_columns
  759. @sqlserver_views_cache = nil
  760. @sqlserver_view_information_cache = {}
  761. end
  762. def column_definitions(table_name)
  763. db_name = unqualify_db_name(table_name)
  764. table_name = unqualify_table_name(table_name)
  765. sql = %{
  766. SELECT
  767. columns.TABLE_NAME as table_name,
  768. columns.COLUMN_NAME as name,
  769. columns.DATA_TYPE as type,
  770. columns.COLUMN_DEFAULT as default_value,
  771. columns.NUMERIC_SCALE as numeric_scale,
  772. columns.NUMERIC_PRECISION as numeric_precision,
  773. CASE
  774. WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
  775. ELSE COL_LENGTH(columns.TABLE_NAME, columns.COLUMN_NAME)
  776. END as length,
  777. CASE
  778. WHEN columns.IS_NULLABLE = 'YES' THEN 1
  779. ELSE NULL
  780. end as is_nullable,
  781. CASE
  782. WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 0 THEN NULL
  783. ELSE 1
  784. END as is_identity
  785. FROM #{db_name}INFORMATION_SCHEMA.COLUMNS columns
  786. WHERE columns.TABLE_NAME = '#{table_name}'
  787. ORDER BY columns.ordinal_position
  788. }.gsub(/[ \t\r\n]+/,' ')
  789. results = info_schema_query { without_type_conversion{ select(sql,nil,true) } }
  790. results.collect do |ci|
  791. ci.symbolize_keys!
  792. ci[:type] = case ci[:type]
  793. when /^bit|image|text|ntext|datetime$/
  794. ci[:type]
  795. when /^numeric|decimal$/i
  796. "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})"
  797. when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/
  798. ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})"
  799. else
  800. ci[:type]
  801. end
  802. if ci[:default_value].nil? && views.include?(table_name)
  803. real_table_name = table_name_or_views_table_name(table_name)
  804. real_column_name = views_real_column_name(table_name,ci[:name])
  805. col_default_sql = "SELECT c.COLUMN_DEFAULT FROM INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
  806. ci[:default_value] = info_schema_query { without_type_conversion{ select_value(col_default_sql) } }
  807. end
  808. ci[:default_value] = case ci[:default_value]
  809. when nil, '(null)', '(NULL)'
  810. nil
  811. else
  812. ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/)[1]
  813. end
  814. ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
  815. ci
  816. end
  817. end
  818. def column_for(table_name, column_name)
  819. unless column = columns(table_name).detect { |c| c.name == column_name.to_s }
  820. raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
  821. end
  822. column
  823. end
  824. def change_order_direction(order)
  825. order.split(",").collect {|fragment|
  826. case fragment
  827. when /\bDESC\b/i then fragment.gsub(/\bDESC\b/i, "ASC")
  828. when /\bASC\b/i then fragment.gsub(/\bASC\b/i, "DESC")
  829. else String.new(fragment).split(',').join(' DESC,') + ' DESC'
  830. end
  831. }.join(",")
  832. end
  833. def special_columns(table_name)
  834. columns(table_name).select(&:is_special?).map(&:name)
  835. end
  836. def repair_special_columns(sql)
  837. special_cols = special_columns(get_table_name(sql))
  838. for col in special_cols.to_a
  839. sql.gsub!(/((\.|\s|\()\[?#{col.to_s}\]?)\s?=\s?/, '\1 LIKE ')
  840. sql.gsub!(/ORDER BY #{col.to_s}/i, '')
  841. end
  842. sql
  843. end
  844. end #class SQLServerAdapter < AbstractAdapter
  845. end #module ConnectionAdapters
  846. end #module ActiveRecord