asa-2000 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +501 -0
- data/MIT-LICENSE +20 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/active_record.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/database_statements.rb +97 -0
- data/lib/active_record/connection_adapters/sqlserver/core_ext/odbc.rb +38 -0
- data/lib/active_record/connection_adapters/sqlserver/database_limits.rb +49 -0
- data/lib/active_record/connection_adapters/sqlserver/database_statements.rb +445 -0
- data/lib/active_record/connection_adapters/sqlserver/errors.rb +36 -0
- data/lib/active_record/connection_adapters/sqlserver/quoting.rb +104 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_cache.rb +85 -0
- data/lib/active_record/connection_adapters/sqlserver/schema_statements.rb +364 -0
- data/lib/active_record/connection_adapters/sqlserver/utils.rb +28 -0
- data/lib/active_record/connection_adapters/sqlserver/version.rb +11 -0
- data/lib/active_record/connection_adapters/sqlserver_adapter.rb +516 -0
- data/lib/activerecord-sqlserver-adapter.rb +1 -0
- data/lib/arel/visitors/sqlserver.rb +386 -0
- metadata +81 -0
    
        data/MIT-LICENSE
    ADDED
    
    | @@ -0,0 +1,20 @@ | |
| 1 | 
            +
            Copyright (c) 2008-2011
         | 
| 2 | 
            +
             
         | 
| 3 | 
            +
            Permission is hereby granted, free of charge, to any person obtaining
         | 
| 4 | 
            +
            a copy of this software and associated documentation files (the
         | 
| 5 | 
            +
            "Software"), to deal in the Software without restriction, including
         | 
| 6 | 
            +
            without limitation the rights to use, copy, modify, merge, publish,
         | 
| 7 | 
            +
            distribute, sublicense, and/or sell copies of the Software, and to
         | 
| 8 | 
            +
            permit persons to whom the Software is furnished to do so, subject to
         | 
| 9 | 
            +
            the following conditions:
         | 
| 10 | 
            +
             
         | 
| 11 | 
            +
            The above copyright notice and this permission notice shall be
         | 
| 12 | 
            +
            included in all copies or substantial portions of the Software.
         | 
| 13 | 
            +
             
         | 
| 14 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
         | 
| 15 | 
            +
            EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
         | 
| 16 | 
            +
            MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
         | 
| 17 | 
            +
            NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
         | 
| 18 | 
            +
            LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
         | 
| 19 | 
            +
            OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
         | 
| 20 | 
            +
            WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
         | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            require 'set'
         | 
| 2 | 
            +
            require 'active_record/base'
         | 
| 3 | 
            +
            require 'active_record/version'
         | 
| 4 | 
            +
            require 'active_support/concern'
         | 
| 5 | 
            +
            require 'active_support/core_ext/class/attribute'
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            module ActiveRecord
         | 
| 8 | 
            +
              module ConnectionAdapters
         | 
| 9 | 
            +
                module Sqlserver
         | 
| 10 | 
            +
                  module CoreExt
         | 
| 11 | 
            +
                    module ActiveRecord
         | 
| 12 | 
            +
                      
         | 
| 13 | 
            +
                      extend ActiveSupport::Concern
         | 
| 14 | 
            +
                      
         | 
| 15 | 
            +
                      included do
         | 
| 16 | 
            +
                        class_attribute :coerced_sqlserver_date_columns, :coerced_sqlserver_time_columns
         | 
| 17 | 
            +
                        self.coerced_sqlserver_date_columns = Set.new
         | 
| 18 | 
            +
                        self.coerced_sqlserver_time_columns = Set.new
         | 
| 19 | 
            +
                      end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                      module ClassMethods
         | 
| 22 | 
            +
                        
         | 
| 23 | 
            +
                        def execute_procedure(proc_name, *variables)
         | 
| 24 | 
            +
                          if connection.respond_to?(:execute_procedure)
         | 
| 25 | 
            +
                            connection.execute_procedure(proc_name,*variables)
         | 
| 26 | 
            +
                          else
         | 
| 27 | 
            +
                            []
         | 
| 28 | 
            +
                          end
         | 
| 29 | 
            +
                        end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
                        def coerce_sqlserver_date(*attributes)
         | 
| 32 | 
            +
                          self.coerced_sqlserver_date_columns += attributes.map(&:to_s)
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                        def coerce_sqlserver_time(*attributes)
         | 
| 36 | 
            +
                          self.coerced_sqlserver_time_columns += attributes.map(&:to_s)
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                      end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
              end
         | 
| 45 | 
            +
            end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
             | 
| 48 | 
            +
            ActiveRecord::Base.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ActiveRecord
         | 
| 49 | 
            +
             | 
| @@ -0,0 +1,97 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module CoreExt
         | 
| 5 | 
            +
                    module DatabaseStatements
         | 
| 6 | 
            +
                      
         | 
| 7 | 
            +
                      # This is a copy of the current (3.1.3) ActiveRecord's transaction method. We should propose
         | 
| 8 | 
            +
                      # a patch to the default transaction method to make it more callback for adapters that want to 
         | 
| 9 | 
            +
                      # do deadlock retry logic. Because this is a copy, we really need to keep an eye out on this when
         | 
| 10 | 
            +
                      # upgradding the adapter. 
         | 
| 11 | 
            +
                      def transaction_with_retry_deadlock_victim(options = {})
         | 
| 12 | 
            +
                        options.assert_valid_keys :requires_new, :joinable
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                        last_transaction_joinable = defined?(@transaction_joinable) ? @transaction_joinable : nil
         | 
| 15 | 
            +
                        if options.has_key?(:joinable)
         | 
| 16 | 
            +
                          @transaction_joinable = options[:joinable]
         | 
| 17 | 
            +
                        else
         | 
| 18 | 
            +
                          @transaction_joinable = true
         | 
| 19 | 
            +
                        end
         | 
| 20 | 
            +
                        requires_new = options[:requires_new] || !last_transaction_joinable
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                        transaction_open = false
         | 
| 23 | 
            +
                        @_current_transaction_records ||= []
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                        begin
         | 
| 26 | 
            +
                          if block_given?
         | 
| 27 | 
            +
                            if requires_new || open_transactions == 0
         | 
| 28 | 
            +
                              if open_transactions == 0
         | 
| 29 | 
            +
                                begin_db_transaction
         | 
| 30 | 
            +
                              elsif requires_new
         | 
| 31 | 
            +
                                create_savepoint
         | 
| 32 | 
            +
                              end
         | 
| 33 | 
            +
                              increment_open_transactions
         | 
| 34 | 
            +
                              transaction_open = true
         | 
| 35 | 
            +
                              @_current_transaction_records.push([])
         | 
| 36 | 
            +
                            end
         | 
| 37 | 
            +
                            yield
         | 
| 38 | 
            +
                          end
         | 
| 39 | 
            +
                        rescue Exception => database_transaction_rollback
         | 
| 40 | 
            +
                          if transaction_open && !outside_transaction?
         | 
| 41 | 
            +
                            transaction_open = false
         | 
| 42 | 
            +
                            decrement_open_transactions
         | 
| 43 | 
            +
                            # handle deadlock victim retries at the outermost transaction
         | 
| 44 | 
            +
                            if open_transactions == 0
         | 
| 45 | 
            +
                              if database_transaction_rollback.is_a?(::ActiveRecord::DeadlockVictim)
         | 
| 46 | 
            +
                                # SQL Server has already rolled back, so rollback activerecord's history
         | 
| 47 | 
            +
                                rollback_transaction_records(true)
         | 
| 48 | 
            +
                                retry
         | 
| 49 | 
            +
                              else
         | 
| 50 | 
            +
                                rollback_db_transaction
         | 
| 51 | 
            +
                                rollback_transaction_records(true)
         | 
| 52 | 
            +
                              end
         | 
| 53 | 
            +
                            else
         | 
| 54 | 
            +
                              rollback_to_savepoint
         | 
| 55 | 
            +
                              rollback_transaction_records(false)
         | 
| 56 | 
            +
                            end
         | 
| 57 | 
            +
                          end
         | 
| 58 | 
            +
                          raise unless database_transaction_rollback.is_a?(::ActiveRecord::Rollback)
         | 
| 59 | 
            +
                        end
         | 
| 60 | 
            +
                      ensure
         | 
| 61 | 
            +
                        @transaction_joinable = last_transaction_joinable
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                        if outside_transaction?
         | 
| 64 | 
            +
                          @open_transactions = 0
         | 
| 65 | 
            +
                        elsif transaction_open
         | 
| 66 | 
            +
                          decrement_open_transactions
         | 
| 67 | 
            +
                          begin
         | 
| 68 | 
            +
                            if open_transactions == 0
         | 
| 69 | 
            +
                              commit_db_transaction
         | 
| 70 | 
            +
                              commit_transaction_records
         | 
| 71 | 
            +
                            else
         | 
| 72 | 
            +
                              release_savepoint
         | 
| 73 | 
            +
                              save_point_records = @_current_transaction_records.pop
         | 
| 74 | 
            +
                              unless save_point_records.blank?
         | 
| 75 | 
            +
                                @_current_transaction_records.push([]) if @_current_transaction_records.empty?
         | 
| 76 | 
            +
                                @_current_transaction_records.last.concat(save_point_records)
         | 
| 77 | 
            +
                              end
         | 
| 78 | 
            +
                            end
         | 
| 79 | 
            +
                          rescue Exception => database_transaction_rollback
         | 
| 80 | 
            +
                            if open_transactions == 0
         | 
| 81 | 
            +
                              rollback_db_transaction
         | 
| 82 | 
            +
                              rollback_transaction_records(true)
         | 
| 83 | 
            +
                            else
         | 
| 84 | 
            +
                              rollback_to_savepoint
         | 
| 85 | 
            +
                              rollback_transaction_records(false)
         | 
| 86 | 
            +
                            end
         | 
| 87 | 
            +
                            raise
         | 
| 88 | 
            +
                          end
         | 
| 89 | 
            +
                        end
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                      
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
                end
         | 
| 95 | 
            +
              end
         | 
| 96 | 
            +
            end
         | 
| 97 | 
            +
             | 
| @@ -0,0 +1,38 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module CoreExt
         | 
| 5 | 
            +
                    module ODBC
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                      module Statement
         | 
| 8 | 
            +
                        
         | 
| 9 | 
            +
                        def finished?
         | 
| 10 | 
            +
                          begin
         | 
| 11 | 
            +
                            connected?
         | 
| 12 | 
            +
                            false
         | 
| 13 | 
            +
                          rescue ::ODBC::Error
         | 
| 14 | 
            +
                            true
         | 
| 15 | 
            +
                          end
         | 
| 16 | 
            +
                        end
         | 
| 17 | 
            +
                        
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                      module Database
         | 
| 21 | 
            +
                        
         | 
| 22 | 
            +
                        def run_block(*args)
         | 
| 23 | 
            +
                          yield sth = run(*args)
         | 
| 24 | 
            +
                          sth.drop
         | 
| 25 | 
            +
                        end
         | 
| 26 | 
            +
                        
         | 
| 27 | 
            +
                      end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    end
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
             | 
| 36 | 
            +
            ODBC::Statement.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Statement
         | 
| 37 | 
            +
            ODBC::Database.send :include, ActiveRecord::ConnectionAdapters::Sqlserver::CoreExt::ODBC::Database
         | 
| 38 | 
            +
             | 
| @@ -0,0 +1,49 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module DatabaseLimits
         | 
| 5 | 
            +
                    
         | 
| 6 | 
            +
                    def table_alias_length
         | 
| 7 | 
            +
                      128
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def column_name_length
         | 
| 11 | 
            +
                      128
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
             | 
| 14 | 
            +
                    def table_name_length
         | 
| 15 | 
            +
                      128
         | 
| 16 | 
            +
                    end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def index_name_length
         | 
| 19 | 
            +
                      128
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def columns_per_table
         | 
| 23 | 
            +
                      1024
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    def indexes_per_table
         | 
| 27 | 
            +
                      999
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def columns_per_multicolumn_index
         | 
| 31 | 
            +
                      16
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    def in_clause_length
         | 
| 35 | 
            +
                      65536
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def sql_query_length
         | 
| 39 | 
            +
                      65536 * 4096
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    def joins_per_query
         | 
| 43 | 
            +
                      256
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                    
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
                end
         | 
| 48 | 
            +
              end
         | 
| 49 | 
            +
            end
         | 
| @@ -0,0 +1,445 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module DatabaseStatements
         | 
| 5 | 
            +
                    
         | 
| 6 | 
            +
                    include CoreExt::DatabaseStatements
         | 
| 7 | 
            +
                    
         | 
| 8 | 
            +
                    def select_rows(sql, name = nil)
         | 
| 9 | 
            +
                      raw_select sql, name, [], :fetch => :rows
         | 
| 10 | 
            +
                    end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
                    def execute(sql, name = nil)
         | 
| 13 | 
            +
                      if id_insert_table_name = query_requires_identity_insert?(sql)
         | 
| 14 | 
            +
                        with_identity_insert_enabled(id_insert_table_name) { do_execute(sql,name) }
         | 
| 15 | 
            +
                      else
         | 
| 16 | 
            +
                        do_execute(sql,name)
         | 
| 17 | 
            +
                      end
         | 
| 18 | 
            +
                    end
         | 
| 19 | 
            +
                    
         | 
| 20 | 
            +
                    def exec_query(sql, name = 'SQL', binds = [], sqlserver_options = {})
         | 
| 21 | 
            +
                      if id_insert_table_name = sqlserver_options[:insert] ? query_requires_identity_insert?(sql) : nil
         | 
| 22 | 
            +
                        with_identity_insert_enabled(id_insert_table_name) { do_exec_query(sql, name, binds) }
         | 
| 23 | 
            +
                      else
         | 
| 24 | 
            +
                        do_exec_query(sql, name, binds)
         | 
| 25 | 
            +
                      end
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
                    
         | 
| 28 | 
            +
                    def exec_insert(sql, name, binds)
         | 
| 29 | 
            +
                      exec_query sql, name, binds, :insert => true
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
                    
         | 
| 32 | 
            +
                    def exec_delete(sql, name, binds)
         | 
| 33 | 
            +
                      sql << "; SELECT @@ROWCOUNT AS AffectedRows"
         | 
| 34 | 
            +
                      super.rows.first.first
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    def exec_update(sql, name, binds)
         | 
| 38 | 
            +
                      sql << "; SELECT @@ROWCOUNT AS AffectedRows"
         | 
| 39 | 
            +
                      super.rows.first.first
         | 
| 40 | 
            +
                    end
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                    def outside_transaction?
         | 
| 43 | 
            +
                      info_schema_query { select_value("SELECT @@TRANCOUNT") == 0 }
         | 
| 44 | 
            +
                    end
         | 
| 45 | 
            +
                    
         | 
| 46 | 
            +
                    def supports_statement_cache?
         | 
| 47 | 
            +
                      true
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    def transaction(options = {})
         | 
| 51 | 
            +
                      if retry_deadlock_victim?
         | 
| 52 | 
            +
                        block_given? ? transaction_with_retry_deadlock_victim(options) { yield } : transaction_with_retry_deadlock_victim(options)
         | 
| 53 | 
            +
                      else
         | 
| 54 | 
            +
                        block_given? ? super(options) { yield } : super(options)
         | 
| 55 | 
            +
                      end
         | 
| 56 | 
            +
                    end
         | 
| 57 | 
            +
             | 
| 58 | 
            +
                    def begin_db_transaction
         | 
| 59 | 
            +
                      do_execute "BEGIN TRANSACTION"
         | 
| 60 | 
            +
                    end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                    def commit_db_transaction
         | 
| 63 | 
            +
                      disable_auto_reconnect { do_execute "COMMIT TRANSACTION" }
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    def rollback_db_transaction
         | 
| 67 | 
            +
                      do_execute "IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION"
         | 
| 68 | 
            +
                    end
         | 
| 69 | 
            +
             | 
| 70 | 
            +
                    def create_savepoint
         | 
| 71 | 
            +
                      disable_auto_reconnect { do_execute "SAVE TRANSACTION #{current_savepoint_name}" }
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    def release_savepoint
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    def rollback_to_savepoint
         | 
| 78 | 
            +
                      disable_auto_reconnect { do_execute "ROLLBACK TRANSACTION #{current_savepoint_name}" }
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    def add_limit_offset!(sql, options)
         | 
| 82 | 
            +
                      raise NotImplementedError, 'This has been moved to the SQLServerCompiler in Arel.'
         | 
| 83 | 
            +
                    end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                    def empty_insert_statement_value
         | 
| 86 | 
            +
                      "DEFAULT VALUES"
         | 
| 87 | 
            +
                    end
         | 
| 88 | 
            +
             | 
| 89 | 
            +
                    def case_sensitive_modifier(node)
         | 
| 90 | 
            +
                      node.acts_like?(:string) ? Arel::Nodes::Bin.new(node) : node
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
                    
         | 
| 93 | 
            +
                    # === SQLServer Specific ======================================== #
         | 
| 94 | 
            +
                    
         | 
| 95 | 
            +
                    def execute_procedure(proc_name, *variables)
         | 
| 96 | 
            +
                      vars = variables.map{ |v| quote(v) }.join(', ')
         | 
| 97 | 
            +
                      sql = "EXEC #{proc_name} #{vars}".strip
         | 
| 98 | 
            +
                      name = 'Execute Procedure'
         | 
| 99 | 
            +
                      log(sql, name) do
         | 
| 100 | 
            +
                        case @connection_options[:mode]
         | 
| 101 | 
            +
                        when :dblib
         | 
| 102 | 
            +
                          result = @connection.execute(sql)
         | 
| 103 | 
            +
                          result.each(:as => :hash, :cache_rows => true) do |row|
         | 
| 104 | 
            +
                            r = row.with_indifferent_access
         | 
| 105 | 
            +
                            yield(r) if block_given?
         | 
| 106 | 
            +
                          end
         | 
| 107 | 
            +
                          result.each.map{ |row| row.is_a?(Hash) ? row.with_indifferent_access : row }
         | 
| 108 | 
            +
                        when :odbc
         | 
| 109 | 
            +
                          results = []
         | 
| 110 | 
            +
                          raw_connection_run(sql) do |handle|
         | 
| 111 | 
            +
                            get_rows = lambda {
         | 
| 112 | 
            +
                              rows = handle_to_names_and_values handle, :fetch => :all
         | 
| 113 | 
            +
                              rows.each_with_index { |r,i| rows[i] = r.with_indifferent_access }
         | 
| 114 | 
            +
                              results << rows
         | 
| 115 | 
            +
                            }
         | 
| 116 | 
            +
                            get_rows.call
         | 
| 117 | 
            +
                            while handle_more_results?(handle)
         | 
| 118 | 
            +
                              get_rows.call
         | 
| 119 | 
            +
                            end
         | 
| 120 | 
            +
                          end
         | 
| 121 | 
            +
                          results.many? ? results : results.first
         | 
| 122 | 
            +
                        end
         | 
| 123 | 
            +
                      end
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
                    
         | 
| 126 | 
            +
                    def use_database(database=nil)
         | 
| 127 | 
            +
                      return if sqlserver_azure?
         | 
| 128 | 
            +
                      database ||= @connection_options[:database]
         | 
| 129 | 
            +
                      do_execute "USE #{quote_table_name(database)}" unless database.blank?
         | 
| 130 | 
            +
                    end
         | 
| 131 | 
            +
                    
         | 
| 132 | 
            +
                    def user_options
         | 
| 133 | 
            +
                      return {} if sqlserver_azure?
         | 
| 134 | 
            +
                      info_schema_query do
         | 
| 135 | 
            +
                        select_rows("dbcc useroptions").inject(HashWithIndifferentAccess.new) do |values,row| 
         | 
| 136 | 
            +
                          set_option = row[0].gsub(/\s+/,'_')
         | 
| 137 | 
            +
                          user_value = row[1]
         | 
| 138 | 
            +
                          values[set_option] = user_value
         | 
| 139 | 
            +
                          values
         | 
| 140 | 
            +
                        end
         | 
| 141 | 
            +
                      end
         | 
| 142 | 
            +
                    end
         | 
| 143 | 
            +
                    
         | 
| 144 | 
            +
                    def user_options_dateformat
         | 
| 145 | 
            +
                      if sqlserver_azure?
         | 
| 146 | 
            +
                        info_schema_query { select_value "SELECT [dateformat] FROM [sys].[syslanguages] WHERE [langid] = @@LANGID" }
         | 
| 147 | 
            +
                      else
         | 
| 148 | 
            +
                        user_options['dateformat']
         | 
| 149 | 
            +
                      end
         | 
| 150 | 
            +
                    end
         | 
| 151 | 
            +
                    
         | 
| 152 | 
            +
                    def user_options_isolation_level
         | 
| 153 | 
            +
                      if sqlserver_azure?
         | 
| 154 | 
            +
                        info_schema_query do
         | 
| 155 | 
            +
                          sql = %|SELECT CASE [transaction_isolation_level] 
         | 
| 156 | 
            +
                                  WHEN 0 THEN NULL
         | 
| 157 | 
            +
                                  WHEN 1 THEN 'READ UNCOMITTED' 
         | 
| 158 | 
            +
                                  WHEN 2 THEN 'READ COMITTED' 
         | 
| 159 | 
            +
                                  WHEN 3 THEN 'REPEATABLE' 
         | 
| 160 | 
            +
                                  WHEN 4 THEN 'SERIALIZABLE' 
         | 
| 161 | 
            +
                                  WHEN 5 THEN 'SNAPSHOT' END AS [isolation_level] 
         | 
| 162 | 
            +
                                  FROM [sys].[dm_exec_sessions] 
         | 
| 163 | 
            +
                                  WHERE [session_id] = @@SPID|.squish
         | 
| 164 | 
            +
                          select_value(sql)
         | 
| 165 | 
            +
                        end
         | 
| 166 | 
            +
                      else
         | 
| 167 | 
            +
                        user_options['isolation_level']
         | 
| 168 | 
            +
                      end
         | 
| 169 | 
            +
                    end
         | 
| 170 | 
            +
                    
         | 
| 171 | 
            +
                    def user_options_language
         | 
| 172 | 
            +
                      if sqlserver_azure?
         | 
| 173 | 
            +
                        info_schema_query { select_value "SELECT @@LANGUAGE AS [language]" }
         | 
| 174 | 
            +
                      else
         | 
| 175 | 
            +
                        user_options['language']
         | 
| 176 | 
            +
                      end
         | 
| 177 | 
            +
                    end
         | 
| 178 | 
            +
             | 
| 179 | 
            +
                    def run_with_isolation_level(isolation_level)
         | 
| 180 | 
            +
                      raise ArgumentError, "Invalid isolation level, #{isolation_level}. Supported levels include #{valid_isolation_levels.to_sentence}." if !valid_isolation_levels.include?(isolation_level.upcase)
         | 
| 181 | 
            +
                      initial_isolation_level = user_options_isolation_level || "READ COMMITTED"
         | 
| 182 | 
            +
                      do_execute "SET TRANSACTION ISOLATION LEVEL #{isolation_level}"
         | 
| 183 | 
            +
                      begin
         | 
| 184 | 
            +
                        yield 
         | 
| 185 | 
            +
                      ensure
         | 
| 186 | 
            +
                        do_execute "SET TRANSACTION ISOLATION LEVEL #{initial_isolation_level}"
         | 
| 187 | 
            +
                      end if block_given?
         | 
| 188 | 
            +
                    end
         | 
| 189 | 
            +
                    
         | 
| 190 | 
            +
                    def newid_function
         | 
| 191 | 
            +
                      select_value "SELECT NEWID()"
         | 
| 192 | 
            +
                    end
         | 
| 193 | 
            +
                    
         | 
| 194 | 
            +
                    def newsequentialid_function
         | 
| 195 | 
            +
                      select_value "SELECT NEWSEQUENTIALID()"
         | 
| 196 | 
            +
                    end
         | 
| 197 | 
            +
                    
         | 
| 198 | 
            +
                    def activity_stats
         | 
| 199 | 
            +
                      select_all %|
         | 
| 200 | 
            +
                        SELECT
         | 
| 201 | 
            +
                           [session_id]    = s.session_id,
         | 
| 202 | 
            +
                           [user_process]  = CONVERT(CHAR(1), s.is_user_process),
         | 
| 203 | 
            +
                           [login]         = s.login_name,
         | 
| 204 | 
            +
                           [database]      = ISNULL(db_name(r.database_id), N''),
         | 
| 205 | 
            +
                           [task_state]    = ISNULL(t.task_state, N''),
         | 
| 206 | 
            +
                           [command]       = ISNULL(r.command, N''),
         | 
| 207 | 
            +
                           [application]   = ISNULL(s.program_name, N''),
         | 
| 208 | 
            +
                           [wait_time_ms]  = ISNULL(w.wait_duration_ms, 0),
         | 
| 209 | 
            +
                           [wait_type]     = ISNULL(w.wait_type, N''),
         | 
| 210 | 
            +
                           [wait_resource] = ISNULL(w.resource_description, N''),
         | 
| 211 | 
            +
                           [blocked_by]    = ISNULL(CONVERT (varchar, w.blocking_session_id), ''),
         | 
| 212 | 
            +
                           [head_blocker]  =
         | 
| 213 | 
            +
                                CASE
         | 
| 214 | 
            +
                                    -- session has an active request, is blocked, but is blocking others
         | 
| 215 | 
            +
                                    WHEN r2.session_id IS NOT NULL AND r.blocking_session_id = 0 THEN '1'
         | 
| 216 | 
            +
                                    -- session is idle but has an open tran and is blocking others
         | 
| 217 | 
            +
                                    WHEN r.session_id IS NULL THEN '1'
         | 
| 218 | 
            +
                                    ELSE ''
         | 
| 219 | 
            +
                                END,
         | 
| 220 | 
            +
                           [total_cpu_ms]   = s.cpu_time,
         | 
| 221 | 
            +
                           [total_physical_io_mb]   = (s.reads + s.writes) * 8 / 1024,
         | 
| 222 | 
            +
                           [memory_use_kb]  = s.memory_usage * 8192 / 1024,
         | 
| 223 | 
            +
                           [open_transactions] = ISNULL(r.open_transaction_count,0),
         | 
| 224 | 
            +
                           [login_time]     = s.login_time,
         | 
| 225 | 
            +
                           [last_request_start_time] = s.last_request_start_time,
         | 
| 226 | 
            +
                           [host_name]      = ISNULL(s.host_name, N''),
         | 
| 227 | 
            +
                           [net_address]    = ISNULL(c.client_net_address, N''),
         | 
| 228 | 
            +
                           [execution_context_id] = ISNULL(t.exec_context_id, 0),
         | 
| 229 | 
            +
                           [request_id]     = ISNULL(r.request_id, 0),
         | 
| 230 | 
            +
                           [workload_group] = N''
         | 
| 231 | 
            +
                        FROM sys.dm_exec_sessions s LEFT OUTER JOIN sys.dm_exec_connections c ON (s.session_id = c.session_id)
         | 
| 232 | 
            +
                        LEFT OUTER JOIN sys.dm_exec_requests r ON (s.session_id = r.session_id)
         | 
| 233 | 
            +
                        LEFT OUTER JOIN sys.dm_os_tasks t ON (r.session_id = t.session_id AND r.request_id = t.request_id)
         | 
| 234 | 
            +
                        LEFT OUTER JOIN
         | 
| 235 | 
            +
                        (SELECT *, ROW_NUMBER() OVER (PARTITION BY waiting_task_address ORDER BY wait_duration_ms DESC) AS row_num
         | 
| 236 | 
            +
                            FROM sys.dm_os_waiting_tasks
         | 
| 237 | 
            +
                        ) w ON (t.task_address = w.waiting_task_address) AND w.row_num = 1
         | 
| 238 | 
            +
                        LEFT OUTER JOIN sys.dm_exec_requests r2 ON (r.session_id = r2.blocking_session_id)
         | 
| 239 | 
            +
                        WHERE db_name(r.database_id) = '#{current_database}'
         | 
| 240 | 
            +
                        ORDER BY s.session_id|
         | 
| 241 | 
            +
                    end
         | 
| 242 | 
            +
                    
         | 
| 243 | 
            +
                    # === SQLServer Specific (Rake/Test Helpers) ==================== #
         | 
| 244 | 
            +
                    
         | 
| 245 | 
            +
                    def recreate_database
         | 
| 246 | 
            +
                      remove_database_connections_and_rollback do
         | 
| 247 | 
            +
                        do_execute "EXEC sp_MSforeachtable 'DROP TABLE ?'"
         | 
| 248 | 
            +
                      end
         | 
| 249 | 
            +
                    end
         | 
| 250 | 
            +
             | 
| 251 | 
            +
                    def recreate_database!(database=nil)
         | 
| 252 | 
            +
                      current_db = current_database
         | 
| 253 | 
            +
                      database ||= current_db
         | 
| 254 | 
            +
                      this_db = database.to_s == current_db
         | 
| 255 | 
            +
                      do_execute 'USE master' if this_db
         | 
| 256 | 
            +
                      drop_database(database)
         | 
| 257 | 
            +
                      create_database(database)
         | 
| 258 | 
            +
                    ensure
         | 
| 259 | 
            +
                      use_database(current_db) if this_db
         | 
| 260 | 
            +
                    end
         | 
| 261 | 
            +
             | 
| 262 | 
            +
                    def drop_database(database)
         | 
| 263 | 
            +
                      retry_count = 0
         | 
| 264 | 
            +
                      max_retries = 1
         | 
| 265 | 
            +
                      begin
         | 
| 266 | 
            +
                        do_execute "DROP DATABASE #{quote_table_name(database)}"
         | 
| 267 | 
            +
                      rescue ActiveRecord::StatementInvalid => err
         | 
| 268 | 
            +
                        if err.message =~ /because it is currently in use/i
         | 
| 269 | 
            +
                          raise if retry_count >= max_retries
         | 
| 270 | 
            +
                          retry_count += 1
         | 
| 271 | 
            +
                          remove_database_connections_and_rollback(database)
         | 
| 272 | 
            +
                          retry
         | 
| 273 | 
            +
                        elsif err.message =~ /does not exist/i
         | 
| 274 | 
            +
                          nil
         | 
| 275 | 
            +
                        else
         | 
| 276 | 
            +
                          raise
         | 
| 277 | 
            +
                        end
         | 
| 278 | 
            +
                      end
         | 
| 279 | 
            +
                    end
         | 
| 280 | 
            +
             | 
| 281 | 
            +
                    def create_database(database)
         | 
| 282 | 
            +
                      do_execute "CREATE DATABASE #{quote_table_name(database)}"
         | 
| 283 | 
            +
                    end
         | 
| 284 | 
            +
             | 
| 285 | 
            +
                    def current_database
         | 
| 286 | 
            +
                      select_value 'SELECT DB_NAME()'
         | 
| 287 | 
            +
                    end
         | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    def charset
         | 
| 290 | 
            +
                      select_value "SELECT SERVERPROPERTY('SqlCharSetName')"
         | 
| 291 | 
            +
                    end
         | 
| 292 | 
            +
                    
         | 
| 293 | 
            +
                    
         | 
| 294 | 
            +
                    protected
         | 
| 295 | 
            +
                    
         | 
| 296 | 
            +
                    def select(sql, name = nil, binds = [])
         | 
| 297 | 
            +
                      exec_query(sql, name, binds).to_a
         | 
| 298 | 
            +
                    end
         | 
| 299 | 
            +
                    
         | 
| 300 | 
            +
                    def sql_for_insert(sql, pk, id_value, sequence_name, binds)
         | 
| 301 | 
            +
                      sql = "#{sql}; SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident"# unless binds.empty?
         | 
| 302 | 
            +
                      super
         | 
| 303 | 
            +
                    end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                    def last_inserted_id(result)
         | 
| 306 | 
            +
                      super || select_value("SELECT CAST(SCOPE_IDENTITY() AS bigint) AS Ident")
         | 
| 307 | 
            +
                    end
         | 
| 308 | 
            +
                    
         | 
| 309 | 
            +
                    # === SQLServer Specific ======================================== #
         | 
| 310 | 
            +
                    
         | 
| 311 | 
            +
                    def valid_isolation_levels
         | 
| 312 | 
            +
                      ["READ COMMITTED", "READ UNCOMMITTED", "REPEATABLE READ", "SERIALIZABLE", "SNAPSHOT"]
         | 
| 313 | 
            +
                    end
         | 
| 314 | 
            +
                    
         | 
| 315 | 
            +
                    # === SQLServer Specific (Executing) ============================ #
         | 
| 316 | 
            +
             | 
| 317 | 
            +
                    def do_execute(sql, name = nil)
         | 
| 318 | 
            +
                      name ||= 'EXECUTE'
         | 
| 319 | 
            +
                      log(sql, name) do
         | 
| 320 | 
            +
                        with_sqlserver_error_handling { raw_connection_do(sql) }
         | 
| 321 | 
            +
                      end
         | 
| 322 | 
            +
                    end
         | 
| 323 | 
            +
                    
         | 
| 324 | 
            +
                    def do_exec_query(sql, name, binds)
         | 
| 325 | 
            +
                      statement = quote(sql)
         | 
| 326 | 
            +
                      names_and_types = []
         | 
| 327 | 
            +
                      params = []
         | 
| 328 | 
            +
                      binds.each_with_index do |(column,value),index|
         | 
| 329 | 
            +
                        ar_column = column.is_a?(ActiveRecord::ConnectionAdapters::Column)
         | 
| 330 | 
            +
                        next if ar_column && column.sql_type == 'timestamp'
         | 
| 331 | 
            +
                        v = value
         | 
| 332 | 
            +
                        names_and_types << if ar_column
         | 
| 333 | 
            +
                                             v = value.to_i if column.is_integer? && value.present?
         | 
| 334 | 
            +
                                             "@#{index} #{column.sql_type_for_statement}"
         | 
| 335 | 
            +
                                           elsif column.acts_like?(:string)
         | 
| 336 | 
            +
                                             "@#{index} varchar(256)"
         | 
| 337 | 
            +
                                           elsif column.is_a?(Fixnum)
         | 
| 338 | 
            +
                                             v = value.to_i
         | 
| 339 | 
            +
                                             "@#{index} int"
         | 
| 340 | 
            +
                                           else
         | 
| 341 | 
            +
                                             "@#{index} int"
         | 
| 342 | 
            +
                                             #raise "Unknown bind columns. We can account for this."
         | 
| 343 | 
            +
                                           end
         | 
| 344 | 
            +
                        quoted_value = ar_column ? quote(v,column) : quote(v,nil)
         | 
| 345 | 
            +
                        params << "@#{index} = #{quoted_value}"
         | 
| 346 | 
            +
                      end
         | 
| 347 | 
            +
                      sql = "EXEC sp_executesql #{statement}"
         | 
| 348 | 
            +
                      sql << ", #{quote(names_and_types.join(', '))}, #{params.join(', ')}" unless binds.empty?
         | 
| 349 | 
            +
                      raw_select sql, name, binds, :ar_result => true
         | 
| 350 | 
            +
                    end
         | 
| 351 | 
            +
                    
         | 
| 352 | 
            +
                    def raw_connection_do(sql)
         | 
| 353 | 
            +
                      case @connection_options[:mode]
         | 
| 354 | 
            +
                      when :dblib
         | 
| 355 | 
            +
                        @connection.execute(sql).do
         | 
| 356 | 
            +
                      when :odbc
         | 
| 357 | 
            +
                        @connection.do(sql)
         | 
| 358 | 
            +
                      end
         | 
| 359 | 
            +
                    ensure
         | 
| 360 | 
            +
                      @update_sql = false
         | 
| 361 | 
            +
                    end
         | 
| 362 | 
            +
                    
         | 
| 363 | 
            +
                    # === SQLServer Specific (Selecting) ============================ #
         | 
| 364 | 
            +
             | 
| 365 | 
            +
                    def raw_select(sql, name=nil, binds=[], options={})
         | 
| 366 | 
            +
                      log(sql,name,binds) { _raw_select(sql, options) }
         | 
| 367 | 
            +
                    end
         | 
| 368 | 
            +
                    
         | 
| 369 | 
            +
                    def _raw_select(sql, options={})
         | 
| 370 | 
            +
                      begin
         | 
| 371 | 
            +
                        handle = raw_connection_run(sql)
         | 
| 372 | 
            +
                        handle_to_names_and_values(handle, options)
         | 
| 373 | 
            +
                      ensure
         | 
| 374 | 
            +
                        finish_statement_handle(handle)
         | 
| 375 | 
            +
                      end
         | 
| 376 | 
            +
                    end
         | 
| 377 | 
            +
                    
         | 
| 378 | 
            +
                    def raw_connection_run(sql)
         | 
| 379 | 
            +
                      with_sqlserver_error_handling do
         | 
| 380 | 
            +
                        case @connection_options[:mode]
         | 
| 381 | 
            +
                        when :dblib
         | 
| 382 | 
            +
                          @connection.execute(sql)
         | 
| 383 | 
            +
                        when :odbc
         | 
| 384 | 
            +
                          block_given? ? @connection.run_block(sql) { |handle| yield(handle) } : @connection.run(sql)
         | 
| 385 | 
            +
                        end
         | 
| 386 | 
            +
                      end
         | 
| 387 | 
            +
                    end
         | 
| 388 | 
            +
                    
         | 
| 389 | 
            +
                    def handle_more_results?(handle)
         | 
| 390 | 
            +
                      case @connection_options[:mode]
         | 
| 391 | 
            +
                      when :dblib
         | 
| 392 | 
            +
                      when :odbc
         | 
| 393 | 
            +
                        handle.more_results
         | 
| 394 | 
            +
                      end
         | 
| 395 | 
            +
                    end
         | 
| 396 | 
            +
                    
         | 
| 397 | 
            +
                    def handle_to_names_and_values(handle, options={})
         | 
| 398 | 
            +
                      case @connection_options[:mode]
         | 
| 399 | 
            +
                      when :dblib
         | 
| 400 | 
            +
                        handle_to_names_and_values_dblib(handle, options)
         | 
| 401 | 
            +
                      when :odbc
         | 
| 402 | 
            +
                        handle_to_names_and_values_odbc(handle, options)
         | 
| 403 | 
            +
                      end
         | 
| 404 | 
            +
                    end
         | 
| 405 | 
            +
                    
         | 
| 406 | 
            +
                    def handle_to_names_and_values_dblib(handle, options={})
         | 
| 407 | 
            +
                      query_options = {}.tap do |qo|
         | 
| 408 | 
            +
                        qo[:timezone] = ActiveRecord::Base.default_timezone || :utc
         | 
| 409 | 
            +
                        qo[:as] = (options[:ar_result] || options[:fetch] == :rows) ? :array : :hash
         | 
| 410 | 
            +
                      end
         | 
| 411 | 
            +
                      results = handle.each(query_options)
         | 
| 412 | 
            +
                      columns = lowercase_schema_reflection ? handle.fields.map { |c| c.downcase } : handle.fields
         | 
| 413 | 
            +
                      options[:ar_result] ? ActiveRecord::Result.new(columns, results) : results
         | 
| 414 | 
            +
                    end
         | 
| 415 | 
            +
                    
         | 
| 416 | 
            +
                    def handle_to_names_and_values_odbc(handle, options={})
         | 
| 417 | 
            +
                      @connection.use_utc = ActiveRecord::Base.default_timezone == :utc
         | 
| 418 | 
            +
                      if options[:ar_result]
         | 
| 419 | 
            +
                        columns = lowercase_schema_reflection ? handle.columns(true).map { |c| c.name.downcase } : handle.columns(true).map { |c| c.name }
         | 
| 420 | 
            +
                        rows = handle.fetch_all || []
         | 
| 421 | 
            +
                        ActiveRecord::Result.new(columns, rows)
         | 
| 422 | 
            +
                      else
         | 
| 423 | 
            +
                        case options[:fetch]
         | 
| 424 | 
            +
                        when :all
         | 
| 425 | 
            +
                          handle.each_hash || []
         | 
| 426 | 
            +
                        when :rows
         | 
| 427 | 
            +
                          handle.fetch_all || []
         | 
| 428 | 
            +
                        end
         | 
| 429 | 
            +
                      end
         | 
| 430 | 
            +
                    end
         | 
| 431 | 
            +
                    
         | 
| 432 | 
            +
                    def finish_statement_handle(handle)
         | 
| 433 | 
            +
                      case @connection_options[:mode]
         | 
| 434 | 
            +
                      when :dblib
         | 
| 435 | 
            +
                        handle.cancel if handle
         | 
| 436 | 
            +
                      when :odbc
         | 
| 437 | 
            +
                        handle.drop if handle && handle.respond_to?(:drop) && !handle.finished?
         | 
| 438 | 
            +
                      end
         | 
| 439 | 
            +
                      handle
         | 
| 440 | 
            +
                    end
         | 
| 441 | 
            +
                    
         | 
| 442 | 
            +
                  end
         | 
| 443 | 
            +
                end
         | 
| 444 | 
            +
              end
         | 
| 445 | 
            +
            end
         |