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
| @@ -0,0 +1,36 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              
         | 
| 3 | 
            +
              class LostConnection < WrappedDatabaseException
         | 
| 4 | 
            +
              end
         | 
| 5 | 
            +
              
         | 
| 6 | 
            +
              class DeadlockVictim < WrappedDatabaseException
         | 
| 7 | 
            +
              end
         | 
| 8 | 
            +
              
         | 
| 9 | 
            +
              module ConnectionAdapters
         | 
| 10 | 
            +
                module Sqlserver
         | 
| 11 | 
            +
                  module Errors
         | 
| 12 | 
            +
                    
         | 
| 13 | 
            +
                    LOST_CONNECTION_EXCEPTIONS  = {
         | 
| 14 | 
            +
                      :dblib  => ['TinyTds::Error'],
         | 
| 15 | 
            +
                      :odbc   => ['ODBC::Error']
         | 
| 16 | 
            +
                    }.freeze
         | 
| 17 | 
            +
                    
         | 
| 18 | 
            +
                    LOST_CONNECTION_MESSAGES    = {
         | 
| 19 | 
            +
                      :dblib  => [/closed connection/, /dead or not enabled/, /server failed/i],
         | 
| 20 | 
            +
                      :odbc   => [/link failure/, /server failed/, /connection was already closed/, /invalid handle/i]
         | 
| 21 | 
            +
                    }.freeze
         | 
| 22 | 
            +
                    
         | 
| 23 | 
            +
                    
         | 
| 24 | 
            +
                    def lost_connection_exceptions
         | 
| 25 | 
            +
                      exceptions = LOST_CONNECTION_EXCEPTIONS[@connection_options[:mode]]
         | 
| 26 | 
            +
                      @lost_connection_exceptions ||= exceptions ? exceptions.map{ |e| e.constantize rescue nil }.compact : []
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
                    
         | 
| 29 | 
            +
                    def lost_connection_messages
         | 
| 30 | 
            +
                      LOST_CONNECTION_MESSAGES[@connection_options[:mode]]
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                    
         | 
| 33 | 
            +
                  end
         | 
| 34 | 
            +
                end
         | 
| 35 | 
            +
              end
         | 
| 36 | 
            +
            end
         | 
| @@ -0,0 +1,104 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module Quoting
         | 
| 5 | 
            +
                    
         | 
| 6 | 
            +
                    QUOTED_TRUE, QUOTED_FALSE = '1', '0'
         | 
| 7 | 
            +
                    QUOTED_STRING_PREFIX = 'N'
         | 
| 8 | 
            +
                    
         | 
| 9 | 
            +
                    def quote(value, column = nil)
         | 
| 10 | 
            +
                      case value
         | 
| 11 | 
            +
                      when String, ActiveSupport::Multibyte::Chars
         | 
| 12 | 
            +
                        if column && column.type == :integer && value.blank?
         | 
| 13 | 
            +
                          nil
         | 
| 14 | 
            +
                        elsif column && column.type == :binary
         | 
| 15 | 
            +
                          column.class.string_to_binary(value)
         | 
| 16 | 
            +
                        elsif value.is_utf8? || (column && column.type == :string)
         | 
| 17 | 
            +
                          "#{quoted_string_prefix}'#{quote_string(value)}'"
         | 
| 18 | 
            +
                        else
         | 
| 19 | 
            +
                          super
         | 
| 20 | 
            +
                        end
         | 
| 21 | 
            +
                      when Date, Time
         | 
| 22 | 
            +
                        if column && column.sql_type == 'datetime'
         | 
| 23 | 
            +
                          "'#{quoted_datetime(value)}'"
         | 
| 24 | 
            +
                        elsif column && (column.sql_type == 'datetimeoffset' || column.sql_type == 'time')
         | 
| 25 | 
            +
                          "'#{quoted_full_iso8601(value)}'"
         | 
| 26 | 
            +
                        else
         | 
| 27 | 
            +
                          super
         | 
| 28 | 
            +
                        end
         | 
| 29 | 
            +
                      when nil
         | 
| 30 | 
            +
                        column.respond_to?(:sql_type) && column.sql_type == 'timestamp' ? 'DEFAULT' : super
         | 
| 31 | 
            +
                      else
         | 
| 32 | 
            +
                        super
         | 
| 33 | 
            +
                      end
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
                    
         | 
| 36 | 
            +
                    def quoted_string_prefix
         | 
| 37 | 
            +
                      QUOTED_STRING_PREFIX
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
                    
         | 
| 40 | 
            +
                    def quote_string(string)
         | 
| 41 | 
            +
                      string.to_s.gsub(/\'/, "''")
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def quote_column_name(name)
         | 
| 45 | 
            +
                      schema_cache.quote_name(name)
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def quote_table_name(name)
         | 
| 49 | 
            +
                      quote_column_name(name)
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
                    
         | 
| 52 | 
            +
                    def substitute_at(column, index)
         | 
| 53 | 
            +
                      if column.respond_to?(:sql_type) && column.sql_type == 'timestamp'
         | 
| 54 | 
            +
                        nil
         | 
| 55 | 
            +
                      else
         | 
| 56 | 
            +
                        Arel.sql "@#{index}"
         | 
| 57 | 
            +
                      end
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    def quoted_true
         | 
| 61 | 
            +
                      QUOTED_TRUE
         | 
| 62 | 
            +
                    end
         | 
| 63 | 
            +
             | 
| 64 | 
            +
                    def quoted_false
         | 
| 65 | 
            +
                      QUOTED_FALSE
         | 
| 66 | 
            +
                    end
         | 
| 67 | 
            +
             | 
| 68 | 
            +
                    def quoted_datetime(value)
         | 
| 69 | 
            +
                      if value.acts_like?(:time)
         | 
| 70 | 
            +
                        value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(3).to(22)
         | 
| 71 | 
            +
                      else
         | 
| 72 | 
            +
                        quoted_date(value)
         | 
| 73 | 
            +
                      end
         | 
| 74 | 
            +
                    end
         | 
| 75 | 
            +
                    
         | 
| 76 | 
            +
                    def quoted_full_iso8601(value)
         | 
| 77 | 
            +
                      if value.acts_like?(:time)
         | 
| 78 | 
            +
                        value.is_a?(Date) ? quoted_value_acts_like_time_filter(value).to_time.xmlschema.to(18) : quoted_value_acts_like_time_filter(value).iso8601(7).to(22)
         | 
| 79 | 
            +
                      else
         | 
| 80 | 
            +
                        quoted_date(value)
         | 
| 81 | 
            +
                      end
         | 
| 82 | 
            +
                    end
         | 
| 83 | 
            +
             | 
| 84 | 
            +
                    def quoted_date(value)
         | 
| 85 | 
            +
                      if value.acts_like?(:time) && value.respond_to?(:usec)
         | 
| 86 | 
            +
                        "#{super}.#{sprintf("%03d",value.usec/1000)}"
         | 
| 87 | 
            +
                      elsif value.acts_like?(:date)
         | 
| 88 | 
            +
                        value.to_s(:_sqlserver_dateformat)
         | 
| 89 | 
            +
                      else
         | 
| 90 | 
            +
                        super
         | 
| 91 | 
            +
                      end
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
                    
         | 
| 94 | 
            +
                    protected
         | 
| 95 | 
            +
                    
         | 
| 96 | 
            +
                    def quoted_value_acts_like_time_filter(value)
         | 
| 97 | 
            +
                      zone_conversion_method = ActiveRecord::Base.default_timezone == :utc ? :getutc : :getlocal
         | 
| 98 | 
            +
                      value.respond_to?(zone_conversion_method) ? value.send(zone_conversion_method) : value
         | 
| 99 | 
            +
                    end
         | 
| 100 | 
            +
                    
         | 
| 101 | 
            +
                  end
         | 
| 102 | 
            +
                end
         | 
| 103 | 
            +
              end
         | 
| 104 | 
            +
            end
         | 
| @@ -0,0 +1,85 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  class SchemaCache < ActiveRecord::ConnectionAdapters::SchemaCache
         | 
| 5 | 
            +
                    
         | 
| 6 | 
            +
                    attr_reader :view_information
         | 
| 7 | 
            +
                    
         | 
| 8 | 
            +
                    def initialize(conn)
         | 
| 9 | 
            +
                      super
         | 
| 10 | 
            +
                      @table_names = nil
         | 
| 11 | 
            +
                      @view_names = nil
         | 
| 12 | 
            +
                      @view_information = {}
         | 
| 13 | 
            +
                      @quoted_names = {}
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                    
         | 
| 16 | 
            +
                    # Superclass Overrides
         | 
| 17 | 
            +
                    
         | 
| 18 | 
            +
                    def table_exists?(table_name)
         | 
| 19 | 
            +
                      return false if table_name.blank?
         | 
| 20 | 
            +
                      key = table_name_key(table_name)
         | 
| 21 | 
            +
                      return @tables[key] if @tables.key? key
         | 
| 22 | 
            +
                      @tables[key] = connection.table_exists?(table_name)
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
                    
         | 
| 25 | 
            +
                    def clear!
         | 
| 26 | 
            +
                      super
         | 
| 27 | 
            +
                      @table_names = nil
         | 
| 28 | 
            +
                      @view_names = nil
         | 
| 29 | 
            +
                      @view_information.clear
         | 
| 30 | 
            +
                      @quoted_names.clear
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                    
         | 
| 33 | 
            +
                    def clear_table_cache!(table_name)
         | 
| 34 | 
            +
                      key = table_name_key(table_name)
         | 
| 35 | 
            +
                      super(key)
         | 
| 36 | 
            +
                      super(table_name)
         | 
| 37 | 
            +
                      # SQL Server Specific
         | 
| 38 | 
            +
                      if @table_names
         | 
| 39 | 
            +
                        @table_names.delete key
         | 
| 40 | 
            +
                        @table_names.delete table_name
         | 
| 41 | 
            +
                      end
         | 
| 42 | 
            +
                      if @view_names
         | 
| 43 | 
            +
                        @view_names.delete key
         | 
| 44 | 
            +
                        @view_names.delete table_name
         | 
| 45 | 
            +
                      end
         | 
| 46 | 
            +
                      @view_information.delete key
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    # SQL Server Specific
         | 
| 50 | 
            +
                    
         | 
| 51 | 
            +
                    def table_names
         | 
| 52 | 
            +
                      @table_names ||= connection.tables
         | 
| 53 | 
            +
                    end
         | 
| 54 | 
            +
                    
         | 
| 55 | 
            +
                    def view_names
         | 
| 56 | 
            +
                      @view_names ||= connection.views
         | 
| 57 | 
            +
                    end
         | 
| 58 | 
            +
                    
         | 
| 59 | 
            +
                    def view_exists?(table_name)
         | 
| 60 | 
            +
                      table_exists?(table_name)
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
                    
         | 
| 63 | 
            +
                    def view_information(table_name)
         | 
| 64 | 
            +
                      key = table_name_key(table_name)
         | 
| 65 | 
            +
                      return @view_information[key] if @view_information.key? key
         | 
| 66 | 
            +
                      @view_information[key] = connection.send(:view_information, table_name)
         | 
| 67 | 
            +
                    end
         | 
| 68 | 
            +
                    
         | 
| 69 | 
            +
                    def quote_name(name)
         | 
| 70 | 
            +
                      return @quoted_names[name] if @quoted_names.key? name
         | 
| 71 | 
            +
                      @quoted_names[name] = name.to_s.split('.').map{ |n| n =~ /^\[.*\]$/ ? n : "[#{n.to_s.gsub(']', ']]')}]" }.join('.')
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
                    
         | 
| 74 | 
            +
                    
         | 
| 75 | 
            +
                    private
         | 
| 76 | 
            +
                    
         | 
| 77 | 
            +
                    def table_name_key(table_name)
         | 
| 78 | 
            +
                      Utils.unqualify_table_name(table_name)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
                    
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
                end
         | 
| 83 | 
            +
              end
         | 
| 84 | 
            +
            end
         | 
| 85 | 
            +
             | 
| @@ -0,0 +1,364 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Sqlserver
         | 
| 4 | 
            +
                  module SchemaStatements
         | 
| 5 | 
            +
                    
         | 
| 6 | 
            +
                    def native_database_types
         | 
| 7 | 
            +
                      @native_database_types ||= initialize_native_database_types.freeze
         | 
| 8 | 
            +
                    end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                    def tables(table_type = 'BASE TABLE')
         | 
| 11 | 
            +
                      info_schema_query do
         | 
| 12 | 
            +
                        select_values "SELECT #{lowercase_schema_reflection_sql('TABLE_NAME')} FROM INFORMATION_SCHEMA.TABLES #{"WHERE TABLE_TYPE = '#{table_type}'" if table_type} ORDER BY TABLE_NAME"
         | 
| 13 | 
            +
                      end
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                    def table_exists?(table_name)
         | 
| 17 | 
            +
                      return false if table_name.blank?
         | 
| 18 | 
            +
                      unquoted_table_name = Utils.unqualify_table_name(table_name)
         | 
| 19 | 
            +
                      super || tables.include?(unquoted_table_name) || views.include?(unquoted_table_name)
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    def indexes(table_name, name = nil)
         | 
| 23 | 
            +
                      data = select("EXEC sp_helpindex #{quote(table_name)}",name) rescue []
         | 
| 24 | 
            +
                      data.inject([]) do |indexes,index|
         | 
| 25 | 
            +
                        index = index.with_indifferent_access
         | 
| 26 | 
            +
                        if index[:index_description] =~ /primary key/
         | 
| 27 | 
            +
                          indexes
         | 
| 28 | 
            +
                        else
         | 
| 29 | 
            +
                          name    = index[:index_name]
         | 
| 30 | 
            +
                          unique  = index[:index_description] =~ /unique/
         | 
| 31 | 
            +
                          columns = index[:index_keys].split(',').map do |column|
         | 
| 32 | 
            +
                            column.strip!
         | 
| 33 | 
            +
                            column.gsub! '(-)', '' if column.ends_with?('(-)')
         | 
| 34 | 
            +
                            column
         | 
| 35 | 
            +
                          end
         | 
| 36 | 
            +
                          indexes << IndexDefinition.new(table_name, name, unique, columns)
         | 
| 37 | 
            +
                        end
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    def columns(table_name, name = nil)
         | 
| 42 | 
            +
                      return [] if table_name.blank?
         | 
| 43 | 
            +
                      column_definitions(table_name).collect do |ci|
         | 
| 44 | 
            +
                        sqlserver_options = ci.except(:name,:default_value,:type,:null).merge(:database_year=>database_year)
         | 
| 45 | 
            +
                        SQLServerColumn.new ci[:name], ci[:default_value], ci[:type], ci[:null], sqlserver_options
         | 
| 46 | 
            +
                      end
         | 
| 47 | 
            +
                    end
         | 
| 48 | 
            +
                    
         | 
| 49 | 
            +
                    def rename_table(table_name, new_name)
         | 
| 50 | 
            +
                      do_execute "EXEC sp_rename '#{table_name}', '#{new_name}'"
         | 
| 51 | 
            +
                    end
         | 
| 52 | 
            +
                    
         | 
| 53 | 
            +
                    def remove_column(table_name, *column_names)
         | 
| 54 | 
            +
                      raise ArgumentError.new("You must specify at least one column name.  Example: remove_column(:people, :first_name)") if column_names.empty?
         | 
| 55 | 
            +
                      column_names.flatten.each do |column_name|
         | 
| 56 | 
            +
                        remove_check_constraints(table_name, column_name)
         | 
| 57 | 
            +
                        remove_default_constraint(table_name, column_name)
         | 
| 58 | 
            +
                        remove_indexes(table_name, column_name)
         | 
| 59 | 
            +
                        do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP COLUMN #{quote_column_name(column_name)}"
         | 
| 60 | 
            +
                      end
         | 
| 61 | 
            +
                    end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                    def change_column(table_name, column_name, type, options = {})
         | 
| 64 | 
            +
                      sql_commands = []
         | 
| 65 | 
            +
                      column_object = schema_cache.columns[table_name].detect { |c| c.name.to_s == column_name.to_s }
         | 
| 66 | 
            +
                      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])}"
         | 
| 67 | 
            +
                      change_column_sql << " NOT NULL" if options[:null] == false
         | 
| 68 | 
            +
                      sql_commands << change_column_sql
         | 
| 69 | 
            +
                      if options_include_default?(options) || (column_object && column_object.type != type.to_sym)
         | 
| 70 | 
            +
                       	remove_default_constraint(table_name,column_name)
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                      if options_include_default?(options)
         | 
| 73 | 
            +
                        sql_commands << "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name,column_name)} DEFAULT #{quote(options[:default])} FOR #{quote_column_name(column_name)}"
         | 
| 74 | 
            +
                      end
         | 
| 75 | 
            +
                      sql_commands.each { |c| do_execute(c) }
         | 
| 76 | 
            +
                    end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
                    def change_column_default(table_name, column_name, default)
         | 
| 79 | 
            +
                      remove_default_constraint(table_name, column_name)
         | 
| 80 | 
            +
                      do_execute "ALTER TABLE #{quote_table_name(table_name)} ADD CONSTRAINT #{default_constraint_name(table_name, column_name)} DEFAULT #{quote(default)} FOR #{quote_column_name(column_name)}"
         | 
| 81 | 
            +
                    end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                    def rename_column(table_name, column_name, new_column_name)
         | 
| 84 | 
            +
                      detect_column_for! table_name, column_name
         | 
| 85 | 
            +
                      do_execute "EXEC sp_rename '#{table_name}.#{column_name}', '#{new_column_name}', 'COLUMN'"
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
                    
         | 
| 88 | 
            +
                    def remove_index!(table_name, index_name)
         | 
| 89 | 
            +
                      do_execute "DROP INDEX #{quote_column_name(index_name)} ON #{quote_table_name(table_name)}"
         | 
| 90 | 
            +
                    end
         | 
| 91 | 
            +
             | 
| 92 | 
            +
                    def type_to_sql(type, limit = nil, precision = nil, scale = nil)
         | 
| 93 | 
            +
                      type_limitable = ['string','integer','float','char','nchar','varchar','nvarchar'].include?(type.to_s)
         | 
| 94 | 
            +
                      limit = nil unless type_limitable
         | 
| 95 | 
            +
                      case type.to_s
         | 
| 96 | 
            +
                      when 'integer'
         | 
| 97 | 
            +
                        case limit
         | 
| 98 | 
            +
                          when 1..2       then  'smallint'
         | 
| 99 | 
            +
                          when 3..4, nil  then  'integer'
         | 
| 100 | 
            +
                          when 5..8       then  'bigint'
         | 
| 101 | 
            +
                          else raise(ActiveRecordError, "No integer type has byte size #{limit}. Use a numeric with precision 0 instead.")
         | 
| 102 | 
            +
                        end
         | 
| 103 | 
            +
                      else
         | 
| 104 | 
            +
                        super
         | 
| 105 | 
            +
                      end
         | 
| 106 | 
            +
                    end
         | 
| 107 | 
            +
             | 
| 108 | 
            +
                    def change_column_null(table_name, column_name, null, default = nil)
         | 
| 109 | 
            +
                      column = detect_column_for! table_name, column_name
         | 
| 110 | 
            +
                      unless null || default.nil?
         | 
| 111 | 
            +
                        do_execute("UPDATE #{quote_table_name(table_name)} SET #{quote_column_name(column_name)}=#{quote(default)} WHERE #{quote_column_name(column_name)} IS NULL")
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
                      sql = "ALTER TABLE #{table_name} ALTER COLUMN #{quote_column_name(column_name)} #{type_to_sql column.type, column.limit, column.precision, column.scale}"
         | 
| 114 | 
            +
                      sql << " NOT NULL" unless null
         | 
| 115 | 
            +
                      do_execute sql
         | 
| 116 | 
            +
                    end
         | 
| 117 | 
            +
                    
         | 
| 118 | 
            +
                    # === SQLServer Specific ======================================== #
         | 
| 119 | 
            +
                    
         | 
| 120 | 
            +
                    def views
         | 
| 121 | 
            +
                      tables('VIEW')
         | 
| 122 | 
            +
                    end
         | 
| 123 | 
            +
                    
         | 
| 124 | 
            +
                    
         | 
| 125 | 
            +
                    protected
         | 
| 126 | 
            +
                    
         | 
| 127 | 
            +
                    # === SQLServer Specific ======================================== #
         | 
| 128 | 
            +
                    
         | 
| 129 | 
            +
                    def initialize_native_database_types
         | 
| 130 | 
            +
                      {
         | 
| 131 | 
            +
                        :primary_key  => "int NOT NULL IDENTITY(1,1) PRIMARY KEY",
         | 
| 132 | 
            +
                        :string       => { :name => native_string_database_type, :limit => 255  },
         | 
| 133 | 
            +
                        :text         => { :name => native_text_database_type },
         | 
| 134 | 
            +
                        :integer      => { :name => "int", :limit => 4 },
         | 
| 135 | 
            +
                        :float        => { :name => "float", :limit => 8 },
         | 
| 136 | 
            +
                        :decimal      => { :name => "decimal" },
         | 
| 137 | 
            +
                        :datetime     => { :name => "datetime" },
         | 
| 138 | 
            +
                        :timestamp    => { :name => "datetime" },
         | 
| 139 | 
            +
                        :time         => { :name => native_time_database_type },
         | 
| 140 | 
            +
                        :date         => { :name => native_date_database_type },
         | 
| 141 | 
            +
                        :binary       => { :name => native_binary_database_type },
         | 
| 142 | 
            +
                        :boolean      => { :name => "bit"},
         | 
| 143 | 
            +
                        # These are custom types that may move somewhere else for good schema_dumper.rb hacking to output them.
         | 
| 144 | 
            +
                        :char         => { :name => 'char' },
         | 
| 145 | 
            +
                        :varchar_max  => { :name => 'varchar(max)' },
         | 
| 146 | 
            +
                        :nchar        => { :name => "nchar" },
         | 
| 147 | 
            +
                        :nvarchar     => { :name => "nvarchar", :limit => 255 },
         | 
| 148 | 
            +
                        :nvarchar_max => { :name => "nvarchar(max)" },
         | 
| 149 | 
            +
                        :ntext        => { :name => "ntext" },
         | 
| 150 | 
            +
                        :ss_timestamp => { :name => 'timestamp' }
         | 
| 151 | 
            +
                      }
         | 
| 152 | 
            +
                    end
         | 
| 153 | 
            +
             | 
| 154 | 
            +
                    def column_definitions(table_name)
         | 
| 155 | 
            +
                      db_name = Utils.unqualify_db_name(table_name)
         | 
| 156 | 
            +
                      db_name_with_period = "#{db_name}." if db_name
         | 
| 157 | 
            +
                      table_schema = Utils.unqualify_table_schema(table_name)
         | 
| 158 | 
            +
                      table_name = Utils.unqualify_table_name(table_name)
         | 
| 159 | 
            +
                      sql = %{
         | 
| 160 | 
            +
                        SELECT DISTINCT 
         | 
| 161 | 
            +
                        #{lowercase_schema_reflection_sql('columns.TABLE_NAME')} AS table_name,
         | 
| 162 | 
            +
                        #{lowercase_schema_reflection_sql('columns.COLUMN_NAME')} AS name,
         | 
| 163 | 
            +
                        columns.DATA_TYPE AS type,
         | 
| 164 | 
            +
                        columns.COLUMN_DEFAULT AS default_value,
         | 
| 165 | 
            +
                        columns.NUMERIC_SCALE AS numeric_scale,
         | 
| 166 | 
            +
                        columns.NUMERIC_PRECISION AS numeric_precision,
         | 
| 167 | 
            +
                        columns.ordinal_position,
         | 
| 168 | 
            +
                        CASE
         | 
| 169 | 
            +
                          WHEN columns.DATA_TYPE IN ('nchar','nvarchar') THEN columns.CHARACTER_MAXIMUM_LENGTH
         | 
| 170 | 
            +
                          ELSE COL_LENGTH(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME, columns.COLUMN_NAME)
         | 
| 171 | 
            +
                        END AS [length],
         | 
| 172 | 
            +
                        CASE
         | 
| 173 | 
            +
                          WHEN columns.IS_NULLABLE = 'YES' THEN 1
         | 
| 174 | 
            +
                          ELSE NULL
         | 
| 175 | 
            +
                        END AS [is_nullable],
         | 
| 176 | 
            +
                        CASE 
         | 
| 177 | 
            +
                          WHEN KCU.COLUMN_NAME IS NOT NULL AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' THEN 1
         | 
| 178 | 
            +
                          ELSE NULL
         | 
| 179 | 
            +
                        END AS [is_primary],
         | 
| 180 | 
            +
                        CASE 
         | 
| 181 | 
            +
                          WHEN COLUMNPROPERTY(OBJECT_ID(columns.TABLE_SCHEMA+'.'+columns.TABLE_NAME), columns.COLUMN_NAME, 'IsIdentity') = 1 THEN 1
         | 
| 182 | 
            +
                          ELSE NULL
         | 
| 183 | 
            +
                        END AS [is_identity]
         | 
| 184 | 
            +
                        FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS columns
         | 
| 185 | 
            +
                        LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.TABLE_CONSTRAINTS AS TC 
         | 
| 186 | 
            +
                          ON TC.TABLE_NAME = columns.TABLE_NAME 
         | 
| 187 | 
            +
                          AND TC.CONSTRAINT_TYPE = N'PRIMARY KEY' 
         | 
| 188 | 
            +
                        LEFT OUTER JOIN #{db_name_with_period}INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
         | 
| 189 | 
            +
                          ON KCU.COLUMN_NAME = columns.COLUMN_NAME
         | 
| 190 | 
            +
                          AND KCU.CONSTRAINT_NAME = TC.CONSTRAINT_NAME
         | 
| 191 | 
            +
                          AND KCU.CONSTRAINT_CATALOG = TC.CONSTRAINT_CATALOG
         | 
| 192 | 
            +
                          AND KCU.CONSTRAINT_SCHEMA = TC.CONSTRAINT_SCHEMA
         | 
| 193 | 
            +
                        WHERE columns.TABLE_NAME = @0
         | 
| 194 | 
            +
                        ORDER BY columns.ordinal_position
         | 
| 195 | 
            +
                      }.gsub(/[ \t\r\n]+/,' ')
         | 
| 196 | 
            +
                      binds = [['table_name', table_name]]
         | 
| 197 | 
            +
                      binds << ['table_schema',table_schema] unless table_schema.blank?
         | 
| 198 | 
            +
                      results = info_schema_query { do_exec_query(sql, 'InfoSchema::ColumnDefinitions', binds) }
         | 
| 199 | 
            +
                      results.collect do |ci|
         | 
| 200 | 
            +
                        ci = ci.symbolize_keys
         | 
| 201 | 
            +
                        ci[:type] = case ci[:type]
         | 
| 202 | 
            +
                                     when /^bit|image|text|ntext|datetime$/
         | 
| 203 | 
            +
                                       ci[:type]
         | 
| 204 | 
            +
                                     when /^numeric|decimal$/i
         | 
| 205 | 
            +
                                       "#{ci[:type]}(#{ci[:numeric_precision]},#{ci[:numeric_scale]})"
         | 
| 206 | 
            +
                                     when /^float|real$/i
         | 
| 207 | 
            +
                                       "#{ci[:type]}(#{ci[:numeric_precision]})"
         | 
| 208 | 
            +
                                     when /^char|nchar|varchar|nvarchar|varbinary|bigint|int|smallint$/
         | 
| 209 | 
            +
                                       ci[:length].to_i == -1 ? "#{ci[:type]}(max)" : "#{ci[:type]}(#{ci[:length]})"
         | 
| 210 | 
            +
                                     else
         | 
| 211 | 
            +
                                       ci[:type]
         | 
| 212 | 
            +
                                     end
         | 
| 213 | 
            +
                        if ci[:default_value].nil? && schema_cache.view_names.include?(table_name)
         | 
| 214 | 
            +
                          real_table_name = table_name_or_views_table_name(table_name)
         | 
| 215 | 
            +
                          real_column_name = views_real_column_name(table_name,ci[:name])
         | 
| 216 | 
            +
                          col_default_sql = "SELECT c.COLUMN_DEFAULT FROM #{db_name_with_period}INFORMATION_SCHEMA.COLUMNS c WHERE c.TABLE_NAME = '#{real_table_name}' AND c.COLUMN_NAME = '#{real_column_name}'"
         | 
| 217 | 
            +
                          ci[:default_value] = info_schema_query { select_value(col_default_sql) }
         | 
| 218 | 
            +
                        end
         | 
| 219 | 
            +
                        ci[:default_value] = case ci[:default_value]
         | 
| 220 | 
            +
                                             when nil, '(null)', '(NULL)'
         | 
| 221 | 
            +
                                               nil
         | 
| 222 | 
            +
                                             when /\A\((\w+\(\))\)\Z/
         | 
| 223 | 
            +
                                               ci[:default_function] = $1
         | 
| 224 | 
            +
                                               nil
         | 
| 225 | 
            +
                                             else
         | 
| 226 | 
            +
                                               match_data = ci[:default_value].match(/\A\(+N?'?(.*?)'?\)+\Z/m)
         | 
| 227 | 
            +
                                               match_data ? match_data[1] : nil
         | 
| 228 | 
            +
                                             end
         | 
| 229 | 
            +
                        ci[:null] = ci[:is_nullable].to_i == 1 ; ci.delete(:is_nullable)
         | 
| 230 | 
            +
                        ci[:is_primary] = ci[:is_primary].to_i == 1
         | 
| 231 | 
            +
                        ci[:is_identity] = ci[:is_identity].to_i == 1
         | 
| 232 | 
            +
                        ci
         | 
| 233 | 
            +
                      end
         | 
| 234 | 
            +
                    end
         | 
| 235 | 
            +
                    
         | 
| 236 | 
            +
                    def remove_check_constraints(table_name, column_name)
         | 
| 237 | 
            +
                      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)}'") }
         | 
| 238 | 
            +
                      constraints.each do |constraint|
         | 
| 239 | 
            +
                        do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{quote_column_name(constraint)}"
         | 
| 240 | 
            +
                      end
         | 
| 241 | 
            +
                    end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                    def remove_default_constraint(table_name, column_name)
         | 
| 244 | 
            +
                      # If their are foreign keys in this table, we could still get back a 2D array, so flatten just in case.
         | 
| 245 | 
            +
                      select_all("EXEC sp_helpconstraint '#{quote_string(table_name)}','nomsg'").flatten.select do |row|
         | 
| 246 | 
            +
                        row['constraint_type'] == "DEFAULT on column #{column_name}"
         | 
| 247 | 
            +
                      end.each do |row|
         | 
| 248 | 
            +
                        do_execute "ALTER TABLE #{quote_table_name(table_name)} DROP CONSTRAINT #{row['constraint_name']}"
         | 
| 249 | 
            +
                      end
         | 
| 250 | 
            +
                    end
         | 
| 251 | 
            +
             | 
| 252 | 
            +
                    def remove_indexes(table_name, column_name)
         | 
| 253 | 
            +
                      indexes(table_name).select{ |index| index.columns.include?(column_name.to_s) }.each do |index|
         | 
| 254 | 
            +
                        remove_index(table_name, {:name => index.name})
         | 
| 255 | 
            +
                      end
         | 
| 256 | 
            +
                    end
         | 
| 257 | 
            +
                    
         | 
| 258 | 
            +
                    # === SQLServer Specific (Misc Helpers) ========================= #
         | 
| 259 | 
            +
                    
         | 
| 260 | 
            +
                    def info_schema_query
         | 
| 261 | 
            +
                      log_info_schema_queries ? yield : ActiveRecord::Base.silence{ yield }
         | 
| 262 | 
            +
                    end
         | 
| 263 | 
            +
                    
         | 
| 264 | 
            +
                    def get_table_name(sql)
         | 
| 265 | 
            +
                      if sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)\s+INTO\s+([^\(\s]+)\s*|^\s*update\s+([^\(\s]+)\s*/i
         | 
| 266 | 
            +
                        $2 || $3
         | 
| 267 | 
            +
                      elsif sql =~ /FROM\s+([^\(\s]+)\s*/i
         | 
| 268 | 
            +
                        $1
         | 
| 269 | 
            +
                      else
         | 
| 270 | 
            +
                        nil
         | 
| 271 | 
            +
                      end
         | 
| 272 | 
            +
                    end
         | 
| 273 | 
            +
                    
         | 
| 274 | 
            +
                    def default_constraint_name(table_name, column_name)
         | 
| 275 | 
            +
                      "DF_#{table_name}_#{column_name}"
         | 
| 276 | 
            +
                    end
         | 
| 277 | 
            +
                    
         | 
| 278 | 
            +
                    def detect_column_for!(table_name, column_name)
         | 
| 279 | 
            +
                      unless column = schema_cache.columns[table_name].detect { |c| c.name == column_name.to_s }
         | 
| 280 | 
            +
                        raise ActiveRecordError, "No such column: #{table_name}.#{column_name}"
         | 
| 281 | 
            +
                      end
         | 
| 282 | 
            +
                      column
         | 
| 283 | 
            +
                    end
         | 
| 284 | 
            +
                    
         | 
| 285 | 
            +
                    def lowercase_schema_reflection_sql(node)
         | 
| 286 | 
            +
                      lowercase_schema_reflection ? "LOWER(#{node})" : node
         | 
| 287 | 
            +
                    end
         | 
| 288 | 
            +
                    
         | 
| 289 | 
            +
                    # === SQLServer Specific (View Reflection) ====================== #
         | 
| 290 | 
            +
                    
         | 
| 291 | 
            +
                    def view_table_name(table_name)
         | 
| 292 | 
            +
                      view_info = schema_cache.view_information(table_name)
         | 
| 293 | 
            +
                      view_info ? get_table_name(view_info['VIEW_DEFINITION']) : table_name
         | 
| 294 | 
            +
                    end
         | 
| 295 | 
            +
                    
         | 
| 296 | 
            +
                    def view_information(table_name)
         | 
| 297 | 
            +
                      table_name = Utils.unqualify_table_name(table_name)
         | 
| 298 | 
            +
                      view_info = info_schema_query { select_one("SELECT * FROM INFORMATION_SCHEMA.VIEWS WHERE TABLE_NAME = '#{table_name}'") }
         | 
| 299 | 
            +
                      if view_info
         | 
| 300 | 
            +
                        view_info = view_info.with_indifferent_access
         | 
| 301 | 
            +
                        if view_info[:VIEW_DEFINITION].blank? || view_info[:VIEW_DEFINITION].length == 4000
         | 
| 302 | 
            +
                          view_info[:VIEW_DEFINITION] = info_schema_query do
         | 
| 303 | 
            +
                                                          begin
         | 
| 304 | 
            +
                                                            select_values("EXEC sp_helptext #{quote_table_name(table_name)}").join
         | 
| 305 | 
            +
                                                          rescue
         | 
| 306 | 
            +
                                                            warn "No view definition found, possible permissions problem.\nPlease run GRANT VIEW DEFINITION TO your_user;"
         | 
| 307 | 
            +
                                                            nil
         | 
| 308 | 
            +
                                                          end
         | 
| 309 | 
            +
                                                        end
         | 
| 310 | 
            +
                        end
         | 
| 311 | 
            +
                      end
         | 
| 312 | 
            +
                      view_info
         | 
| 313 | 
            +
                    end
         | 
| 314 | 
            +
                    
         | 
| 315 | 
            +
                    def table_name_or_views_table_name(table_name)
         | 
| 316 | 
            +
                      unquoted_table_name = Utils.unqualify_table_name(table_name)
         | 
| 317 | 
            +
                      schema_cache.view_names.include?(unquoted_table_name) ? view_table_name(unquoted_table_name) : unquoted_table_name
         | 
| 318 | 
            +
                    end
         | 
| 319 | 
            +
                    
         | 
| 320 | 
            +
                    def views_real_column_name(table_name,column_name)
         | 
| 321 | 
            +
                      view_definition = schema_cache.view_information(table_name)[:VIEW_DEFINITION]
         | 
| 322 | 
            +
                      match_data = view_definition.match(/([\w-]*)\s+as\s+#{column_name}/im)
         | 
| 323 | 
            +
                      match_data ? match_data[1] : column_name
         | 
| 324 | 
            +
                    end
         | 
| 325 | 
            +
                    
         | 
| 326 | 
            +
                    # === SQLServer Specific (Identity Inserts) ===================== #
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                    def query_requires_identity_insert?(sql)
         | 
| 329 | 
            +
                      if insert_sql?(sql)
         | 
| 330 | 
            +
                        table_name = get_table_name(sql)
         | 
| 331 | 
            +
                        id_column = identity_column(table_name)
         | 
| 332 | 
            +
                        id_column && sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)[^(]+\([^)]*\b(#{id_column.name})\b,?[^)]*\)/i ? quote_table_name(table_name) : false
         | 
| 333 | 
            +
                      else
         | 
| 334 | 
            +
                        false
         | 
| 335 | 
            +
                      end
         | 
| 336 | 
            +
                    end
         | 
| 337 | 
            +
                    
         | 
| 338 | 
            +
                    def insert_sql?(sql)
         | 
| 339 | 
            +
                      !(sql =~ /^\s*(INSERT|EXEC sp_executesql N'INSERT)/i).nil?
         | 
| 340 | 
            +
                    end
         | 
| 341 | 
            +
                    
         | 
| 342 | 
            +
                    def with_identity_insert_enabled(table_name)
         | 
| 343 | 
            +
                      table_name = quote_table_name(table_name_or_views_table_name(table_name))
         | 
| 344 | 
            +
                      set_identity_insert(table_name, true)
         | 
| 345 | 
            +
                      yield
         | 
| 346 | 
            +
                    ensure
         | 
| 347 | 
            +
                      set_identity_insert(table_name, false)
         | 
| 348 | 
            +
                    end
         | 
| 349 | 
            +
             | 
| 350 | 
            +
                    def set_identity_insert(table_name, enable = true)
         | 
| 351 | 
            +
                      sql = "SET IDENTITY_INSERT #{table_name} #{enable ? 'ON' : 'OFF'}"
         | 
| 352 | 
            +
                      do_execute sql,'InfoSchema::SetIdentityInsert'
         | 
| 353 | 
            +
                    rescue Exception => e
         | 
| 354 | 
            +
                      raise ActiveRecordError, "IDENTITY_INSERT could not be turned #{enable ? 'ON' : 'OFF'} for table #{table_name}"
         | 
| 355 | 
            +
                    end
         | 
| 356 | 
            +
             | 
| 357 | 
            +
                    def identity_column(table_name)
         | 
| 358 | 
            +
                      schema_cache.columns[table_name].detect(&:is_identity?)
         | 
| 359 | 
            +
                    end
         | 
| 360 | 
            +
             | 
| 361 | 
            +
                  end
         | 
| 362 | 
            +
                end
         | 
| 363 | 
            +
              end
         | 
| 364 | 
            +
            end
         |