activerecord-rdb-adapter 0.4.1
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.
- checksums.yaml +7 -0
- data/README.md +3 -0
- data/lib/active_record/connection_adapters/rdb/database_limits.rb +35 -0
- data/lib/active_record/connection_adapters/rdb/database_statements.rb +99 -0
- data/lib/active_record/connection_adapters/rdb/quoting.rb +147 -0
- data/lib/active_record/connection_adapters/rdb/schema_creation.rb +53 -0
- data/lib/active_record/connection_adapters/rdb/schema_dumper.rb +23 -0
- data/lib/active_record/connection_adapters/rdb/schema_statements.rb +425 -0
- data/lib/active_record/connection_adapters/rdb/table_definition.rb +28 -0
- data/lib/active_record/connection_adapters/rdb_adapter.rb +152 -0
- data/lib/active_record/connection_adapters/rdb_column.rb +69 -0
- data/lib/active_record/rdb_base.rb +33 -0
- data/lib/active_record/tasks/rdb_database_tasks.rb +82 -0
- data/lib/activerecord-rdb-adapter.rb +10 -0
- data/lib/arel/visitors/rdb_visitor.rb +117 -0
- metadata +86 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA256:
         | 
| 3 | 
            +
              metadata.gz: eae56962a83ba7f51da156eb041ef6b9961e450d29088e2481b3085c4913d1b8
         | 
| 4 | 
            +
              data.tar.gz: 38fcc781c4852ef2dd1897d0affebaac9006e038dbb90a67efe4ba9995a4e408
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 1f68f8293992979c5f6168746cabed86d7c68290424aa566134db01e40cf3ec9097e53628c05bf71906197ebaeb4f6dec9271a062ba6e9d6b4b6050e00ebd064
         | 
| 7 | 
            +
              data.tar.gz: c5dc9601b8205894ca3f668f25848643a69b79093b2069d87d9a60e2d80eb889f77e00b465e797ee765fea0d77b8c5eeb5746776017ca0a5d432c153f6b94890
         | 
    
        data/README.md
    ADDED
    
    
| @@ -0,0 +1,35 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  module DatabaseLimits # :nodoc:
         | 
| 5 | 
            +
                    def table_alias_length
         | 
| 6 | 
            +
                      31
         | 
| 7 | 
            +
                    end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                    def column_name_length
         | 
| 10 | 
            +
                      31
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def table_name_length
         | 
| 14 | 
            +
                      31
         | 
| 15 | 
            +
                    end
         | 
| 16 | 
            +
             | 
| 17 | 
            +
                    def index_name_length
         | 
| 18 | 
            +
                      31
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                    def indexes_per_table
         | 
| 22 | 
            +
                      65_535
         | 
| 23 | 
            +
                    end
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                    def in_clause_length
         | 
| 26 | 
            +
                      1_499
         | 
| 27 | 
            +
                    end
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                    def sql_query_length
         | 
| 30 | 
            +
                      32_767
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
                  end
         | 
| 33 | 
            +
                end
         | 
| 34 | 
            +
              end
         | 
| 35 | 
            +
            end
         | 
| @@ -0,0 +1,99 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  module DatabaseStatements # :nodoc:
         | 
| 5 | 
            +
                    def execute(sql, name = nil)
         | 
| 6 | 
            +
                      log(sql, name) do
         | 
| 7 | 
            +
                        ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
         | 
| 8 | 
            +
                          @connection.query(sql)
         | 
| 9 | 
            +
                        end
         | 
| 10 | 
            +
                      end
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def exec_query(sql, name = 'SQL', binds = [], prepare: false)
         | 
| 14 | 
            +
                      type_casted_binds = type_casted_binds(binds)
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                      log(sql, name, binds, type_casted_binds) do
         | 
| 17 | 
            +
                        ActiveSupport::Dependencies.interlock.permit_concurrent_loads do
         | 
| 18 | 
            +
                          result = @connection.execute(sql, *type_casted_binds)
         | 
| 19 | 
            +
                          if result.is_a?(Fb::Cursor)
         | 
| 20 | 
            +
                            fields = result.fields.map(&:name)
         | 
| 21 | 
            +
                            rows = result.fetchall.map do |row|
         | 
| 22 | 
            +
                              row.map do |col|
         | 
| 23 | 
            +
                                col.encode('UTF-8', @connection.encoding)
         | 
| 24 | 
            +
                              rescue StandardError
         | 
| 25 | 
            +
                                col
         | 
| 26 | 
            +
                              end
         | 
| 27 | 
            +
                            end
         | 
| 28 | 
            +
                            result.close
         | 
| 29 | 
            +
                            ActiveRecord::Result.new(fields, rows)
         | 
| 30 | 
            +
                          else
         | 
| 31 | 
            +
                            result
         | 
| 32 | 
            +
                          end
         | 
| 33 | 
            +
                        end
         | 
| 34 | 
            +
                      end
         | 
| 35 | 
            +
                    rescue StandardError => e
         | 
| 36 | 
            +
                      raise e.message.encode('UTF-8', @connection.encoding)
         | 
| 37 | 
            +
                    end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    def explain(arel, binds = [])
         | 
| 40 | 
            +
                      to_sql(arel, binds)
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    # Begins the transaction (and turns off auto-committing).
         | 
| 44 | 
            +
                    def begin_db_transaction
         | 
| 45 | 
            +
                      log('begin transaction', nil) do
         | 
| 46 | 
            +
                        begin_isolated_db_transaction(default_transaction_isolation)
         | 
| 47 | 
            +
                      end
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                    # Default isolation levels for transactions. This method exists
         | 
| 51 | 
            +
                    # in 4.0.2+, so it's here for backward compatibility with AR 3
         | 
| 52 | 
            +
                    def transaction_isolation_levels
         | 
| 53 | 
            +
                      {
         | 
| 54 | 
            +
                        read_committed: 'READ COMMITTED',
         | 
| 55 | 
            +
                        repeatable_read: 'REPEATABLE READ',
         | 
| 56 | 
            +
                        serializable: 'SERIALIZABLE'
         | 
| 57 | 
            +
                      }
         | 
| 58 | 
            +
                    end
         | 
| 59 | 
            +
             | 
| 60 | 
            +
                    # Allows providing the :transaction option to ActiveRecord::Base.transaction
         | 
| 61 | 
            +
                    # in 4.0.2+. Can accept verbatim isolation options like 'WAIT READ COMMITTED'
         | 
| 62 | 
            +
                    def begin_isolated_db_transaction(isolation)
         | 
| 63 | 
            +
                      @connection.transaction transaction_isolation_levels.fetch(isolation, isolation)
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    # Commits the transaction (and turns on auto-committing).
         | 
| 67 | 
            +
                    def commit_db_transaction
         | 
| 68 | 
            +
                      log('commit transaction', nil) { @connection.commit }
         | 
| 69 | 
            +
                    end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                    # Rolls back the transaction (and turns on auto-committing). Must be
         | 
| 72 | 
            +
                    # done if the transaction block raises an exception or returns false.
         | 
| 73 | 
            +
                    def rollback_db_transaction
         | 
| 74 | 
            +
                      log('rollback transaction', nil) { @connection.rollback }
         | 
| 75 | 
            +
                    end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                    def default_sequence_name(table_name, _column = nil)
         | 
| 78 | 
            +
                      "#{table_name.to_s.tr('-', '_')[0, table_name_length - 4]}_seq"
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    # Set the sequence to the max value of the table's column.
         | 
| 82 | 
            +
                    def reset_sequence!(table, column, sequence = nil)
         | 
| 83 | 
            +
                      sequence ||= default_sequence_name(table, column)
         | 
| 84 | 
            +
                      max_id = select_value("select max(#{column}) from #{table}")
         | 
| 85 | 
            +
                      execute("alter sequence #{sequence} restart with #{max_id}")
         | 
| 86 | 
            +
                    end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                    # Uses the raw connection to get the next sequence value.
         | 
| 89 | 
            +
                    def next_sequence_value(sequence_name)
         | 
| 90 | 
            +
                      @connection.query("SELECT NEXT VALUE FOR #{sequence_name} FROM RDB$DATABASE")[0][0]
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def last_inserted_id(_result)
         | 
| 94 | 
            +
                      nil
         | 
| 95 | 
            +
                    end
         | 
| 96 | 
            +
                  end
         | 
| 97 | 
            +
                end
         | 
| 98 | 
            +
              end
         | 
| 99 | 
            +
            end
         | 
| @@ -0,0 +1,147 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  module Quoting # :nodoc:
         | 
| 5 | 
            +
                    QUOTED_FALSE = "'false'".freeze
         | 
| 6 | 
            +
                    QUOTED_TRUE = "'true'".freeze
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    QUOTED_POSITION = '"POSITION"'.freeze
         | 
| 9 | 
            +
                    QUOTED_VALUE = '"VALUE"'.freeze
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                    def quote_string(string) # :nodoc:
         | 
| 12 | 
            +
                      string.gsub(/'/, "''")
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def quoted_date(time)
         | 
| 16 | 
            +
                      if time.is_a?(Time) || time.is_a?(DateTime)
         | 
| 17 | 
            +
                        time.localtime.strftime('%d.%m.%Y %H:%M:%S')
         | 
| 18 | 
            +
                      else
         | 
| 19 | 
            +
                        time.strftime('%d.%m.%Y')
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def quote_column_name(column_name) # :nodoc:
         | 
| 24 | 
            +
                      column = column_name.dup.to_s
         | 
| 25 | 
            +
                      column.gsub!(/(?<=[^\"\w]|^)position(?=[^\"\w]|$)/i, QUOTED_POSITION)
         | 
| 26 | 
            +
                      column.gsub!(/(?<=[^\"\w]|^)value(?=[^\"\w]|$)/i, QUOTED_VALUE)
         | 
| 27 | 
            +
                      column.delete!('"')
         | 
| 28 | 
            +
                      column.upcase!
         | 
| 29 | 
            +
                      @connection.dialect == 1 ? column.to_s : %("#{column}")
         | 
| 30 | 
            +
                    end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                    def quote_table_name_for_assignment(_table, attr)
         | 
| 33 | 
            +
                      quote_column_name(attr)
         | 
| 34 | 
            +
                    end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                    def unquoted_true
         | 
| 37 | 
            +
                      true
         | 
| 38 | 
            +
                    end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                    def quoted_true # :nodoc:
         | 
| 41 | 
            +
                      QUOTED_TRUE
         | 
| 42 | 
            +
                    end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                    def unquoted_false
         | 
| 45 | 
            +
                      false
         | 
| 46 | 
            +
                    end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                    def quoted_false # :nodoc:
         | 
| 49 | 
            +
                      QUOTED_FALSE
         | 
| 50 | 
            +
                    end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                    def type_cast_from_column(column, value) # :nodoc:
         | 
| 53 | 
            +
                      if column
         | 
| 54 | 
            +
                        type = column.type || lookup_cast_type_from_column(column)
         | 
| 55 | 
            +
                        type.serialize(value)
         | 
| 56 | 
            +
                      else
         | 
| 57 | 
            +
                        value
         | 
| 58 | 
            +
                      end
         | 
| 59 | 
            +
                    end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                    def lookup_cast_type_from_column(column) # :nodoc:
         | 
| 62 | 
            +
                      type = column.try(:sql_type) || column.try(:type)
         | 
| 63 | 
            +
                      lookup_cast_type(type)
         | 
| 64 | 
            +
                    end
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                    def type_casted_binds(binds) # :nodoc:
         | 
| 67 | 
            +
                      if binds.first.is_a?(Array)
         | 
| 68 | 
            +
                        binds.map { |column, value| type_cast(value, column) }
         | 
| 69 | 
            +
                      else
         | 
| 70 | 
            +
                        binds.map { |attr| type_cast(attr.value_for_database, attr) }
         | 
| 71 | 
            +
                      end
         | 
| 72 | 
            +
                    end
         | 
| 73 | 
            +
             | 
| 74 | 
            +
                    private
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                    def id_value_for_database(value)
         | 
| 77 | 
            +
                      if primary_key = value.class.primary_key
         | 
| 78 | 
            +
                        value.instance_variable_get(:@attributes)[primary_key].value_for_database
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    def _quote(value)
         | 
| 83 | 
            +
                      case value
         | 
| 84 | 
            +
                      when Time, DateTime
         | 
| 85 | 
            +
                        "'#{value.strftime('%d.%m.%Y %H:%M')}'"
         | 
| 86 | 
            +
                      when Date
         | 
| 87 | 
            +
                        "'#{value.strftime('%d.%m.%Y')}'"
         | 
| 88 | 
            +
                      else
         | 
| 89 | 
            +
                        super
         | 
| 90 | 
            +
                      end
         | 
| 91 | 
            +
                    end
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    def _type_cast(value)
         | 
| 94 | 
            +
                      case value
         | 
| 95 | 
            +
                      when Symbol, ActiveSupport::Multibyte::Chars, Type::Binary::Data
         | 
| 96 | 
            +
                        value.to_s
         | 
| 97 | 
            +
                      when Array
         | 
| 98 | 
            +
                        value.to_yaml
         | 
| 99 | 
            +
                      when Hash then
         | 
| 100 | 
            +
                        encode_hash(value)
         | 
| 101 | 
            +
                      when true then
         | 
| 102 | 
            +
                        unquoted_true
         | 
| 103 | 
            +
                      when false then
         | 
| 104 | 
            +
                        unquoted_false
         | 
| 105 | 
            +
                        # BigDecimals need to be put in a non-normalized form and quoted.
         | 
| 106 | 
            +
                      when BigDecimal then
         | 
| 107 | 
            +
                        value.to_s('F')
         | 
| 108 | 
            +
                      when Type::Time::Value then
         | 
| 109 | 
            +
                        quoted_time(value)
         | 
| 110 | 
            +
                      when Date, Time, DateTime then
         | 
| 111 | 
            +
                        quoted_date(value)
         | 
| 112 | 
            +
                      when *types_which_need_no_typecasting
         | 
| 113 | 
            +
                        value
         | 
| 114 | 
            +
                      else
         | 
| 115 | 
            +
                        raise TypeError
         | 
| 116 | 
            +
                      end
         | 
| 117 | 
            +
                    end
         | 
| 118 | 
            +
             | 
| 119 | 
            +
                    def rdb_to_ar_case(column_name)
         | 
| 120 | 
            +
                      /[[:lower:]]/.match?(column_name) ? column_name : column_name.downcase
         | 
| 121 | 
            +
                    end
         | 
| 122 | 
            +
             | 
| 123 | 
            +
                    def ar_to_rdb_case(column_name)
         | 
| 124 | 
            +
                      /[[:upper:]]/.match?(column_name) ? column_name : column_name.upcase
         | 
| 125 | 
            +
                    end
         | 
| 126 | 
            +
             | 
| 127 | 
            +
                    def encode_hash(value)
         | 
| 128 | 
            +
                      if value.is_a?(Hash)
         | 
| 129 | 
            +
                        value.to_yaml
         | 
| 130 | 
            +
                      else
         | 
| 131 | 
            +
                        value
         | 
| 132 | 
            +
                      end
         | 
| 133 | 
            +
                    end
         | 
| 134 | 
            +
             | 
| 135 | 
            +
                    if defined? Encoding
         | 
| 136 | 
            +
                      def decode(str)
         | 
| 137 | 
            +
                        Base64.decode64(str).force_encoding(@connection.encoding)
         | 
| 138 | 
            +
                      end
         | 
| 139 | 
            +
                    else
         | 
| 140 | 
            +
                      def decode(str)
         | 
| 141 | 
            +
                        Base64.decode64(str)
         | 
| 142 | 
            +
                      end
         | 
| 143 | 
            +
                    end
         | 
| 144 | 
            +
                  end
         | 
| 145 | 
            +
                end
         | 
| 146 | 
            +
              end
         | 
| 147 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  class SchemaCreation < AbstractAdapter::SchemaCreation # :nodoc:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    private
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    def visit_ColumnDefinition(o)
         | 
| 9 | 
            +
                      o.sql_type = type_to_sql(o.type, o.options)
         | 
| 10 | 
            +
                      column_sql = "#{quote_column_name(o.name)} #{o.sql_type}"
         | 
| 11 | 
            +
                      add_column_options!(column_sql, column_options(o)) unless o.type == :primary_key
         | 
| 12 | 
            +
                      column_sql
         | 
| 13 | 
            +
                    end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    def add_column_options!(sql, options)
         | 
| 16 | 
            +
                      sql << " DEFAULT #{quote_default_expression(options[:default], options[:column])}" if options_include_default?(options)
         | 
| 17 | 
            +
                      # must explicitly check for :null to allow change_column to work on migrations
         | 
| 18 | 
            +
                      if !options[:null]
         | 
| 19 | 
            +
                        sql << " NOT NULL"
         | 
| 20 | 
            +
                      end
         | 
| 21 | 
            +
                      if options[:auto_increment]
         | 
| 22 | 
            +
                        sql << " AUTO_INCREMENT"
         | 
| 23 | 
            +
                      end
         | 
| 24 | 
            +
                      if options[:primary_key]
         | 
| 25 | 
            +
                        sql << " PRIMARY KEY"
         | 
| 26 | 
            +
                      end
         | 
| 27 | 
            +
                      sql
         | 
| 28 | 
            +
                    end
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                    def visit_TableDefinition(o)
         | 
| 31 | 
            +
                      create_sql = "CREATE#{' TEMPORARY' if o.temporary} TABLE #{quote_table_name(o.name)} "
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                      statements = o.columns.map(&method(:accept))
         | 
| 34 | 
            +
                      statements << accept(o.primary_keys) if o.primary_keys
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                      if supports_indexes_in_create?
         | 
| 37 | 
            +
                        statements.concat(o.indexes.map { |column_name, options| index_in_create(o.name, column_name, options) })
         | 
| 38 | 
            +
                      end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                      if supports_foreign_keys_in_create?
         | 
| 41 | 
            +
                        statements.concat(o.foreign_keys.map { |to_table, options| foreign_key_in_create(o.name, to_table, options) })
         | 
| 42 | 
            +
                      end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                      create_sql << "(#{statements.join(', ')})" if statements.present?
         | 
| 45 | 
            +
                      add_table_options!(create_sql, table_options(o))
         | 
| 46 | 
            +
                      create_sql << " AS #{@conn.to_sql(o.as)}" if o.as
         | 
| 47 | 
            +
                      create_sql
         | 
| 48 | 
            +
                    end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| @@ -0,0 +1,23 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  class SchemaDumper < ConnectionAdapters::SchemaDumper # :nodoc:
         | 
| 5 | 
            +
                    private
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                    def column_spec_for_primary_key(column)
         | 
| 8 | 
            +
                      spec = super
         | 
| 9 | 
            +
                      spec.delete(:auto_increment) if column.type == :integer && column.auto_increment?
         | 
| 10 | 
            +
                      spec
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                    def schema_type(column)
         | 
| 14 | 
            +
                      if column.bigint?
         | 
| 15 | 
            +
                        :bigint
         | 
| 16 | 
            +
                      else
         | 
| 17 | 
            +
                        column.type.type
         | 
| 18 | 
            +
                      end
         | 
| 19 | 
            +
                    end
         | 
| 20 | 
            +
                  end
         | 
| 21 | 
            +
                end
         | 
| 22 | 
            +
              end
         | 
| 23 | 
            +
            end
         | 
| @@ -0,0 +1,425 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  module SchemaStatements # :nodoc:
         | 
| 5 | 
            +
                    methods_to_commit = %i[add_column
         | 
| 6 | 
            +
                                           create_table
         | 
| 7 | 
            +
                                           rename_column
         | 
| 8 | 
            +
                                           remove_column
         | 
| 9 | 
            +
                                           change_column
         | 
| 10 | 
            +
                                           change_column_default
         | 
| 11 | 
            +
                                           change_column_null
         | 
| 12 | 
            +
                                           remove_index
         | 
| 13 | 
            +
                                           remove_index!
         | 
| 14 | 
            +
                                           drop_table
         | 
| 15 | 
            +
                                           create_sequence
         | 
| 16 | 
            +
                                           drop_sequence
         | 
| 17 | 
            +
                                           drop_trigger]
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    def tables(_name = nil)
         | 
| 20 | 
            +
                      @connection.table_names
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                    def views
         | 
| 24 | 
            +
                      @connection.view_names
         | 
| 25 | 
            +
                    end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                    def indexes(table_name, _name = nil)
         | 
| 28 | 
            +
                      @connection.indexes.values.map do |ix|
         | 
| 29 | 
            +
                        IndexDefinition.new(table_name, ix.index_name, ix.unique, ix.columns) if ix.table_name == table_name.to_s && ix.index_name !~ /^rdb\$/
         | 
| 30 | 
            +
                      end.compact
         | 
| 31 | 
            +
                    end
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def index_name_exists?(table_name, index_name)
         | 
| 34 | 
            +
                      index_name = index_name.to_s.upcase
         | 
| 35 | 
            +
                      indexes(table_name).detect { |i| i.name.upcase == index_name }
         | 
| 36 | 
            +
                    end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
                    def columns(table_name, _name = nil)
         | 
| 39 | 
            +
                      @col_definitions ||= {}
         | 
| 40 | 
            +
                      @col_definitions[table_name] = column_definitions(table_name).map do |field|
         | 
| 41 | 
            +
                        sql_type_metadata = column_type_for(field)
         | 
| 42 | 
            +
                        rdb_opt = { domain: field[:domain], sub_type: field[:sql_subtype] }
         | 
| 43 | 
            +
                        RdbColumn.new(field[:name], field[:default], sql_type_metadata, field[:nullable], table_name, rdb_opt)
         | 
| 44 | 
            +
                      end
         | 
| 45 | 
            +
                    end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                    def create_table(name, options = {}) # :nodoc:
         | 
| 48 | 
            +
                      raise ActiveRecordError, 'Firebird does not support temporary tables' if options.key? :temporary
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                      raise ActiveRecordError, 'Firebird does not support creating tables with a select' if options.key? :as
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                      drop_table name, if_exists: true if options.key? :force
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                      needs_sequence = options[:id]
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                      super name, options do |table_def|
         | 
| 57 | 
            +
                        yield table_def if block_given?
         | 
| 58 | 
            +
                        needs_sequence ||= table_def.needs_sequence
         | 
| 59 | 
            +
                      end
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                      return if options[:sequence] == false || !needs_sequence
         | 
| 62 | 
            +
                      create_sequence(options[:sequence] || default_sequence_name(name))
         | 
| 63 | 
            +
                      trg_sql = <<-END_SQL
         | 
| 64 | 
            +
                        CREATE TRIGGER N$#{name.upcase} FOR #{name.upcase}
         | 
| 65 | 
            +
                        ACTIVE BEFORE INSERT
         | 
| 66 | 
            +
                        AS
         | 
| 67 | 
            +
                        declare variable gen_val bigint;
         | 
| 68 | 
            +
                        BEGIN
         | 
| 69 | 
            +
                          if (new.ID is null) then
         | 
| 70 | 
            +
                            new.ID = next value for #{options[:sequence] || default_sequence_name(name)};
         | 
| 71 | 
            +
                          else begin
         | 
| 72 | 
            +
                            gen_val = gen_id(#{options[:sequence] || default_sequence_name(name)}, 1);
         | 
| 73 | 
            +
                            if (new.ID > gen_val) then
         | 
| 74 | 
            +
                              gen_val = gen_id(#{options[:sequence] || default_sequence_name(name)}, new.ID - gen_val);
         | 
| 75 | 
            +
                          end
         | 
| 76 | 
            +
                        END
         | 
| 77 | 
            +
                      END_SQL
         | 
| 78 | 
            +
                      execute(trg_sql)
         | 
| 79 | 
            +
                    end
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                    def drop_table(name, options = {}) # :nodoc:
         | 
| 82 | 
            +
                      drop_sql = "DROP TABLE #{quote_table_name(name)}"
         | 
| 83 | 
            +
                      if options[:if_exists]
         | 
| 84 | 
            +
                        drop = !execute(squish_sql(<<-END_SQL))
         | 
| 85 | 
            +
                        select 1 from rdb$relations where rdb$relation_name = #{quote_table_name(name).tr('"', '\'')}
         | 
| 86 | 
            +
                        END_SQL
         | 
| 87 | 
            +
                               .empty?
         | 
| 88 | 
            +
                      end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                      trigger_name = "N$#{name.upcase}"
         | 
| 91 | 
            +
                      drop_trigger(trigger_name) if trigger_exists?(trigger_name)
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                      sequence_name = options[:sequence] || default_sequence_name(name)
         | 
| 94 | 
            +
                      drop_sequence(sequence_name) if sequence_exists?(sequence_name)
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                      execute(drop_sql) if drop
         | 
| 97 | 
            +
                    end
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                    def create_sequence(sequence_name)
         | 
| 100 | 
            +
                      execute("CREATE SEQUENCE #{sequence_name}")
         | 
| 101 | 
            +
                    rescue StandardError
         | 
| 102 | 
            +
                      nil
         | 
| 103 | 
            +
                    end
         | 
| 104 | 
            +
             | 
| 105 | 
            +
                    def drop_sequence(sequence_name)
         | 
| 106 | 
            +
                      execute("DROP SEQUENCE #{sequence_name}")
         | 
| 107 | 
            +
                    rescue StandardError
         | 
| 108 | 
            +
                      nil
         | 
| 109 | 
            +
                    end
         | 
| 110 | 
            +
             | 
| 111 | 
            +
                    def drop_trigger(trigger_name)
         | 
| 112 | 
            +
                      execute("DROP TRIGGER #{trigger_name}")
         | 
| 113 | 
            +
                    rescue StandardError
         | 
| 114 | 
            +
                      nil
         | 
| 115 | 
            +
                    end
         | 
| 116 | 
            +
             | 
| 117 | 
            +
                    def trigger_exists?(trigger_name)
         | 
| 118 | 
            +
                      !execute(squish_sql(<<-END_SQL))
         | 
| 119 | 
            +
                        select 1
         | 
| 120 | 
            +
                        from rdb$triggers
         | 
| 121 | 
            +
                         where rdb$trigger_name = '#{trigger_name}'
         | 
| 122 | 
            +
                      END_SQL
         | 
| 123 | 
            +
                        .empty?
         | 
| 124 | 
            +
                    end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                    def add_column(table_name, column_name, type, options = {})
         | 
| 127 | 
            +
                      super
         | 
| 128 | 
            +
             | 
| 129 | 
            +
                      create_sequence(options[:sequence] || default_sequence_name(table_name)) if type == :primary_key && options[:sequence] != false
         | 
| 130 | 
            +
             | 
| 131 | 
            +
                      return unless options[:position]
         | 
| 132 | 
            +
                      # position is 1-based but add 1 to skip id column
         | 
| 133 | 
            +
                      execute(squish_sql(<<-end_sql))
         | 
| 134 | 
            +
                        ALTER TABLE #{quote_table_name(table_name)}
         | 
| 135 | 
            +
                        ALTER COLUMN #{quote_column_name(column_name)}
         | 
| 136 | 
            +
                        POSITION #{options[:position] + 1}
         | 
| 137 | 
            +
                      end_sql
         | 
| 138 | 
            +
                    end
         | 
| 139 | 
            +
             | 
| 140 | 
            +
                    def remove_column(table_name, column_name, type = nil, options = {})
         | 
| 141 | 
            +
                      indexes(table_name).each do |i|
         | 
| 142 | 
            +
                        remove_index! i.table, i.name if i.columns.any? { |c| c == column_name.to_s }
         | 
| 143 | 
            +
                      end
         | 
| 144 | 
            +
             | 
| 145 | 
            +
                      column_exist = !execute(squish_sql(<<-END_SQL))
         | 
| 146 | 
            +
                      select 1 from RDB$RELATION_FIELDS rf
         | 
| 147 | 
            +
                        where lower(rf.RDB$RELATION_NAME) = '#{table_name.downcase}' and lower(rf.RDB$FIELD_NAME) = '#{column_name.downcase}'
         | 
| 148 | 
            +
                      END_SQL
         | 
| 149 | 
            +
                                     .empty?
         | 
| 150 | 
            +
                      super if column_exist
         | 
| 151 | 
            +
                    end
         | 
| 152 | 
            +
             | 
| 153 | 
            +
                    def remove_column_for_alter(_table_name, column_name, _type = nil, _options = {})
         | 
| 154 | 
            +
                      "DROP #{quote_column_name(column_name)}"
         | 
| 155 | 
            +
                    end
         | 
| 156 | 
            +
             | 
| 157 | 
            +
                    def change_column(table_name, column_name, type, options = {})
         | 
| 158 | 
            +
                      type_sql = type_to_sql(type, *options.values_at(:limit, :precision, :scale))
         | 
| 159 | 
            +
             | 
| 160 | 
            +
                      if %i[text string].include?(type)
         | 
| 161 | 
            +
                        copy_column = 'c_temp'
         | 
| 162 | 
            +
                        add_column table_name, copy_column, type, options
         | 
| 163 | 
            +
                        execute(squish_sql(<<-END_SQL))
         | 
| 164 | 
            +
                        UPDATE #{table_name} SET #{copy_column.quote_column_name} = #{column_name.to_s.quote_column_name};
         | 
| 165 | 
            +
                        END_SQL
         | 
| 166 | 
            +
                        remove_column table_name, column_name
         | 
| 167 | 
            +
                        rename_column table_name, copy_column, column_name
         | 
| 168 | 
            +
                      else
         | 
| 169 | 
            +
                        execute(squish_sql(<<-END_SQL))
         | 
| 170 | 
            +
                        ALTER TABLE #{quote_table_name(table_name)}
         | 
| 171 | 
            +
                        ALTER COLUMN #{quote_column_name(column_name)} TYPE #{type_sql}
         | 
| 172 | 
            +
                        END_SQL
         | 
| 173 | 
            +
                      end
         | 
| 174 | 
            +
                      change_column_null(table_name, column_name, !!options[:null]) if options.key?(:null)
         | 
| 175 | 
            +
                      change_column_default(table_name, column_name, options[:default]) if options.key?(:default)
         | 
| 176 | 
            +
                    end
         | 
| 177 | 
            +
             | 
| 178 | 
            +
                    def change_column_default(table_name, column_name, default)
         | 
| 179 | 
            +
                      execute(squish_sql(<<-END_SQL))
         | 
| 180 | 
            +
                        ALTER TABLE #{quote_table_name(table_name)}
         | 
| 181 | 
            +
                        ALTER #{quote_column_name(column_name)}
         | 
| 182 | 
            +
                        SET DEFAULT #{quote(default)}
         | 
| 183 | 
            +
                      END_SQL
         | 
| 184 | 
            +
                    end
         | 
| 185 | 
            +
             | 
| 186 | 
            +
                    def change_column_null(table_name, column_name, null, default = nil)
         | 
| 187 | 
            +
                      change_column_default(table_name, column_name, default) if default
         | 
| 188 | 
            +
             | 
| 189 | 
            +
                      db_column = columns(table_name).find { |c| c.name == column_name.to_s }
         | 
| 190 | 
            +
                      options = { null: null }
         | 
| 191 | 
            +
                      options[:default] = db_column.default if !default && db_column.default
         | 
| 192 | 
            +
                      options[:default] = default if default
         | 
| 193 | 
            +
                      ar_type = db_column.type
         | 
| 194 | 
            +
                      type = type_to_sql(ar_type.type, ar_type.limit, ar_type.precision, ar_type.scale)
         | 
| 195 | 
            +
             | 
| 196 | 
            +
                      copy_column = 'c_temp'
         | 
| 197 | 
            +
                      add_column table_name, copy_column, type, options
         | 
| 198 | 
            +
                      execute(squish_sql(<<-END_SQL))
         | 
| 199 | 
            +
                        UPDATE #{table_name} SET #{copy_column.quote_column_name} = #{column_name.to_s.quote_column_name};
         | 
| 200 | 
            +
                      END_SQL
         | 
| 201 | 
            +
                      remove_column table_name, column_name
         | 
| 202 | 
            +
                      rename_column table_name, copy_column, column_name
         | 
| 203 | 
            +
                    end
         | 
| 204 | 
            +
             | 
| 205 | 
            +
                    def rename_column(table_name, column_name, new_column_name)
         | 
| 206 | 
            +
                      execute(squish_sql(<<-END_SQL))
         | 
| 207 | 
            +
                        ALTER TABLE #{quote_table_name(table_name)}
         | 
| 208 | 
            +
                        ALTER #{quote_column_name(column_name)}
         | 
| 209 | 
            +
                        TO #{quote_column_name(new_column_name)}
         | 
| 210 | 
            +
                      END_SQL
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                      rename_column_indexes(table_name, column_name, new_column_name)
         | 
| 213 | 
            +
                    end
         | 
| 214 | 
            +
             | 
| 215 | 
            +
                    def remove_index!(_table_name, index_name)
         | 
| 216 | 
            +
                      execute "DROP INDEX #{quote_column_name(index_name)}"
         | 
| 217 | 
            +
                    end
         | 
| 218 | 
            +
             | 
| 219 | 
            +
                    def remove_index(table_name, options = {})
         | 
| 220 | 
            +
                      index_name = index_name(table_name, options)
         | 
| 221 | 
            +
                      execute "DROP INDEX #{quote_column_name(index_name)}"
         | 
| 222 | 
            +
                    end
         | 
| 223 | 
            +
             | 
| 224 | 
            +
                    def index_name(table_name, options) #:nodoc:
         | 
| 225 | 
            +
                      if options.respond_to?(:keys) # legacy support
         | 
| 226 | 
            +
                        if options[:column]
         | 
| 227 | 
            +
                          index_name = "#{table_name}_#{Array.wrap(options[:column]) * '_'}"
         | 
| 228 | 
            +
                          if index_name.length > 31
         | 
| 229 | 
            +
                            "IDX_#{Digest::SHA256.hexdigest(index_name)[0..22]}"
         | 
| 230 | 
            +
                          else
         | 
| 231 | 
            +
                            index_name
         | 
| 232 | 
            +
                          end
         | 
| 233 | 
            +
                        elsif options[:name]
         | 
| 234 | 
            +
                          options[:name]
         | 
| 235 | 
            +
                        else
         | 
| 236 | 
            +
                          raise ArgumentError 'You must specify the index name'
         | 
| 237 | 
            +
                        end
         | 
| 238 | 
            +
                      else
         | 
| 239 | 
            +
                        index_name(table_name, column: options)
         | 
| 240 | 
            +
                      end
         | 
| 241 | 
            +
                    end
         | 
| 242 | 
            +
             | 
| 243 | 
            +
                    def index_exists?(table_name, column_name, options = {})
         | 
| 244 | 
            +
                      column_names = Array(column_name).map(&:to_s)
         | 
| 245 | 
            +
                      checks = []
         | 
| 246 | 
            +
                      checks << lambda { |i| i.columns == column_names }
         | 
| 247 | 
            +
                      checks << lambda(&:unique) if options[:unique]
         | 
| 248 | 
            +
                      checks << lambda { |i| i.name.upcase == options[:name].to_s.upcase } if options[:name]
         | 
| 249 | 
            +
             | 
| 250 | 
            +
                      indexes(table_name).any? { |i| checks.all? { |check| check[i] } }
         | 
| 251 | 
            +
                    end
         | 
| 252 | 
            +
             | 
| 253 | 
            +
                    def type_to_sql(type, limit = nil, precision = nil, scale = nil, **args)
         | 
| 254 | 
            +
                      if !args.nil? && !args.empty?
         | 
| 255 | 
            +
                        limit = args[:limit] if limit.nil?
         | 
| 256 | 
            +
                        precision = args[:precision] if precision.nil?
         | 
| 257 | 
            +
                        scale = args[:scale] if scale.nil?
         | 
| 258 | 
            +
                      end
         | 
| 259 | 
            +
                      case type
         | 
| 260 | 
            +
                      when :integer
         | 
| 261 | 
            +
                        integer_to_sql(limit)
         | 
| 262 | 
            +
                      when :float
         | 
| 263 | 
            +
                        float_to_sql(limit)
         | 
| 264 | 
            +
                      when :text
         | 
| 265 | 
            +
                        text_to_sql(limit)
         | 
| 266 | 
            +
                      # when :blob
         | 
| 267 | 
            +
                      #   binary_to_sql(limit)
         | 
| 268 | 
            +
                      when :string
         | 
| 269 | 
            +
                        string_to_sql(limit)
         | 
| 270 | 
            +
                      else
         | 
| 271 | 
            +
                        type = type.to_sym if type
         | 
| 272 | 
            +
                        if native = native_database_types[type]
         | 
| 273 | 
            +
                          column_type_sql = (native.is_a?(Hash) ? native[:name] : native).dup
         | 
| 274 | 
            +
             | 
| 275 | 
            +
                          if type == :decimal # ignore limit, use precision and scale
         | 
| 276 | 
            +
                            scale ||= native[:scale]
         | 
| 277 | 
            +
             | 
| 278 | 
            +
                            if precision ||= native[:precision]
         | 
| 279 | 
            +
                              column_type_sql << if scale
         | 
| 280 | 
            +
                                                   "(#{precision},#{scale})"
         | 
| 281 | 
            +
                                                 else
         | 
| 282 | 
            +
                                                   "(#{precision})"
         | 
| 283 | 
            +
                                                 end
         | 
| 284 | 
            +
                            elsif scale
         | 
| 285 | 
            +
                              raise ArgumentError, 'Error adding decimal column: precision cannot be empty if scale is specified'
         | 
| 286 | 
            +
                            end
         | 
| 287 | 
            +
             | 
| 288 | 
            +
                          elsif %i[datetime timestamp time interval].include?(type) && precision ||= native[:precision]
         | 
| 289 | 
            +
                            if (0..6) === precision
         | 
| 290 | 
            +
                              column_type_sql << "(#{precision})"
         | 
| 291 | 
            +
                            else
         | 
| 292 | 
            +
                              raise(ActiveRecordError, "No #{native[:name]} type has precision of #{precision}. The allowed range of precision is from 0 to 6")
         | 
| 293 | 
            +
                            end
         | 
| 294 | 
            +
                          elsif (type != :primary_key) && (limit ||= native.is_a?(Hash) && native[:limit])
         | 
| 295 | 
            +
                            column_type_sql << "(#{limit})"
         | 
| 296 | 
            +
                          end
         | 
| 297 | 
            +
             | 
| 298 | 
            +
                          column_type_sql
         | 
| 299 | 
            +
                        else
         | 
| 300 | 
            +
                          type.to_s
         | 
| 301 | 
            +
                        end
         | 
| 302 | 
            +
                      end
         | 
| 303 | 
            +
                    end
         | 
| 304 | 
            +
             | 
| 305 | 
            +
                    def primary_key(table_name)
         | 
| 306 | 
            +
                      row = @connection.query(<<-END_SQL)
         | 
| 307 | 
            +
                        SELECT s.rdb$field_name
         | 
| 308 | 
            +
                        FROM rdb$indices i
         | 
| 309 | 
            +
                        JOIN rdb$index_segments s ON i.rdb$index_name = s.rdb$index_name
         | 
| 310 | 
            +
                        LEFT JOIN rdb$relation_constraints c ON i.rdb$index_name = c.rdb$index_name
         | 
| 311 | 
            +
                        WHERE i.rdb$relation_name = '#{ar_to_rdb_case(table_name)}'
         | 
| 312 | 
            +
                        AND c.rdb$constraint_type = 'PRIMARY KEY';
         | 
| 313 | 
            +
                      END_SQL
         | 
| 314 | 
            +
             | 
| 315 | 
            +
                      row.first && rdb_to_ar_case(row.first[0].rstrip)
         | 
| 316 | 
            +
                    end
         | 
| 317 | 
            +
             | 
| 318 | 
            +
                    def native_database_types
         | 
| 319 | 
            +
                      @native_database_types ||= initialize_native_database_types.freeze
         | 
| 320 | 
            +
                    end
         | 
| 321 | 
            +
             | 
| 322 | 
            +
                    def create_schema_dumper(options)
         | 
| 323 | 
            +
                      Rdb::SchemaDumper.create(self, options)
         | 
| 324 | 
            +
                    end
         | 
| 325 | 
            +
             | 
| 326 | 
            +
                    private
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                    def column_definitions(table_name)
         | 
| 329 | 
            +
                      @connection.columns(table_name)
         | 
| 330 | 
            +
                    end
         | 
| 331 | 
            +
             | 
| 332 | 
            +
                    def new_column_from_field(table_name, field)
         | 
| 333 | 
            +
                      type_metadata = fetch_type_metadata(field['sql_type'])
         | 
| 334 | 
            +
                      ActiveRecord::ConnectionAdapters::Column.new(field['name'], field['default'], type_metadata, field['nullable'], table_name)
         | 
| 335 | 
            +
                    end
         | 
| 336 | 
            +
             | 
| 337 | 
            +
                    def column_type_for(field)
         | 
| 338 | 
            +
                      sql_type = RdbColumn.sql_type_for(field)
         | 
| 339 | 
            +
                      type = lookup_cast_type(sql_type)
         | 
| 340 | 
            +
                      { type: type, sql_type: type.type }
         | 
| 341 | 
            +
                    end
         | 
| 342 | 
            +
             | 
| 343 | 
            +
                    def integer_to_sql(limit)
         | 
| 344 | 
            +
                      return 'integer' if limit.nil?
         | 
| 345 | 
            +
                      case limit
         | 
| 346 | 
            +
                      when 1..2 then
         | 
| 347 | 
            +
                        'smallint'
         | 
| 348 | 
            +
                      when 3..4 then
         | 
| 349 | 
            +
                        'integer'
         | 
| 350 | 
            +
                      when 5..8 then
         | 
| 351 | 
            +
                        'bigint'
         | 
| 352 | 
            +
                      else
         | 
| 353 | 
            +
                        raise ActiveRecordError "No integer type has byte size #{limit}. " \
         | 
| 354 | 
            +
                                                'Use a NUMERIC with PRECISION 0 instead.'
         | 
| 355 | 
            +
                      end
         | 
| 356 | 
            +
                    end
         | 
| 357 | 
            +
             | 
| 358 | 
            +
                    def float_to_sql(limit)
         | 
| 359 | 
            +
                      limit.nil? || limit <= 4 ? 'float' : 'double precision'
         | 
| 360 | 
            +
                    end
         | 
| 361 | 
            +
             | 
| 362 | 
            +
                    def text_to_sql(limit)
         | 
| 363 | 
            +
                      if limit && limit > 0
         | 
| 364 | 
            +
                        "VARCHAR(#{limit})"
         | 
| 365 | 
            +
                      else
         | 
| 366 | 
            +
                        'BLOB SUB_TYPE TEXT'
         | 
| 367 | 
            +
                      end
         | 
| 368 | 
            +
                    end
         | 
| 369 | 
            +
             | 
| 370 | 
            +
                    def string_to_sql(limit)
         | 
| 371 | 
            +
                      if limit && limit > 0
         | 
| 372 | 
            +
                        "VARCHAR(#{limit})"
         | 
| 373 | 
            +
                      else
         | 
| 374 | 
            +
                        'VARCHAR(150)'
         | 
| 375 | 
            +
                      end
         | 
| 376 | 
            +
                    end
         | 
| 377 | 
            +
             | 
| 378 | 
            +
                    def initialize_native_database_types
         | 
| 379 | 
            +
                      { primary_key: 'integer not null primary key',
         | 
| 380 | 
            +
                        string: { name: 'varchar', limit: 255 },
         | 
| 381 | 
            +
                        text: { name: 'blob sub_type text' },
         | 
| 382 | 
            +
                        integer: { name: 'integer' },
         | 
| 383 | 
            +
                        bigint: { name: 'bigint' },
         | 
| 384 | 
            +
                        float: { name: 'float' },
         | 
| 385 | 
            +
                        decimal: { name: 'decimal' },
         | 
| 386 | 
            +
                        datetime: { name: 'timestamp' },
         | 
| 387 | 
            +
                        timestamp: { name: 'timestamp' },
         | 
| 388 | 
            +
                        time: { name: 'time' },
         | 
| 389 | 
            +
                        date: { name: 'date' },
         | 
| 390 | 
            +
                        binary: { name: 'blob' },
         | 
| 391 | 
            +
                        boolean: { name: 'boolean' } }
         | 
| 392 | 
            +
                    end
         | 
| 393 | 
            +
             | 
| 394 | 
            +
                    def sequence_exists?(sequence_name)
         | 
| 395 | 
            +
                      @connection.generator_names.include?(sequence_name)
         | 
| 396 | 
            +
                    end
         | 
| 397 | 
            +
             | 
| 398 | 
            +
                    def create_table_definition(*args)
         | 
| 399 | 
            +
                      Rdb::TableDefinition.new(*args)
         | 
| 400 | 
            +
                    end
         | 
| 401 | 
            +
             | 
| 402 | 
            +
                    def squish_sql(sql)
         | 
| 403 | 
            +
                      sql.strip.gsub(/\s+/, ' ')
         | 
| 404 | 
            +
                    end
         | 
| 405 | 
            +
             | 
| 406 | 
            +
                    class << self
         | 
| 407 | 
            +
                      def after(*names)
         | 
| 408 | 
            +
                        names.flatten.each do |name|
         | 
| 409 | 
            +
                          m = ActiveRecord::ConnectionAdapters::Rdb::SchemaStatements.instance_method(name)
         | 
| 410 | 
            +
                          define_method(name) do |*args, &block|
         | 
| 411 | 
            +
                            m.bind(self).call(*args, &block)
         | 
| 412 | 
            +
                            yield
         | 
| 413 | 
            +
                            commit_db_transaction
         | 
| 414 | 
            +
                          end
         | 
| 415 | 
            +
                        end
         | 
| 416 | 
            +
                      end
         | 
| 417 | 
            +
                    end
         | 
| 418 | 
            +
             | 
| 419 | 
            +
                    after(methods_to_commit) do
         | 
| 420 | 
            +
                      puts 'Commiting transaction'
         | 
| 421 | 
            +
                    end
         | 
| 422 | 
            +
                  end
         | 
| 423 | 
            +
                end
         | 
| 424 | 
            +
              end
         | 
| 425 | 
            +
            end
         | 
| @@ -0,0 +1,28 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                module Rdb
         | 
| 4 | 
            +
                  module ColumnMethods # :nodoc:
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                    attr_accessor :needs_sequence
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                    def primary_key(name, type = :primary_key, **options)
         | 
| 9 | 
            +
                      self.needs_sequence = true
         | 
| 10 | 
            +
                      super
         | 
| 11 | 
            +
                    end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                  class TableDefinition < ActiveRecord::ConnectionAdapters::TableDefinition # :nodoc:
         | 
| 16 | 
            +
                    include ColumnMethods
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                    def new_column_definition(name, type, **options)
         | 
| 19 | 
            +
                      super
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  class Table < ActiveRecord::ConnectionAdapters::Table # :nodoc:
         | 
| 24 | 
            +
                    include ColumnMethods
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
                end
         | 
| 27 | 
            +
              end
         | 
| 28 | 
            +
            end
         | 
| @@ -0,0 +1,152 @@ | |
| 1 | 
            +
            require 'fb'
         | 
| 2 | 
            +
            require 'base64'
         | 
| 3 | 
            +
            require 'arel'
         | 
| 4 | 
            +
            require 'arel/visitors/rdb_visitor'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            require 'active_record'
         | 
| 7 | 
            +
            require 'active_record/base'
         | 
| 8 | 
            +
            require 'active_record/connection_adapters/abstract_adapter'
         | 
| 9 | 
            +
            require 'active_record/connection_adapters/rdb/database_statements'
         | 
| 10 | 
            +
            require 'active_record/connection_adapters/rdb/database_limits'
         | 
| 11 | 
            +
            require 'active_record/connection_adapters/rdb/schema_creation'
         | 
| 12 | 
            +
            require 'active_record/connection_adapters/rdb/schema_dumper'
         | 
| 13 | 
            +
            require 'active_record/connection_adapters/rdb/schema_statements'
         | 
| 14 | 
            +
            require 'active_record/connection_adapters/rdb/quoting'
         | 
| 15 | 
            +
            require 'active_record/connection_adapters/rdb/table_definition'
         | 
| 16 | 
            +
            require 'active_record/connection_adapters/rdb_column'
         | 
| 17 | 
            +
            require 'active_record/rdb_base'
         | 
| 18 | 
            +
             | 
| 19 | 
            +
            module ActiveRecord
         | 
| 20 | 
            +
              module ConnectionAdapters
         | 
| 21 | 
            +
                class RdbAdapter < AbstractAdapter # :nodoc:
         | 
| 22 | 
            +
                  include Rdb::DatabaseLimits
         | 
| 23 | 
            +
                  include Rdb::DatabaseStatements
         | 
| 24 | 
            +
                  include Rdb::Quoting
         | 
| 25 | 
            +
                  include Rdb::SchemaStatements
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  @@default_transaction_isolation = :read_committed
         | 
| 28 | 
            +
                  cattr_accessor :default_transaction_isolation
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  ADAPTER_NAME = 'rdb'.freeze
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def initialize(connection, logger = nil, config = {})
         | 
| 33 | 
            +
                    super(connection, logger, config)
         | 
| 34 | 
            +
                    # Our Responsibility
         | 
| 35 | 
            +
                    @config = config
         | 
| 36 | 
            +
                    @visitor = Arel::Visitors::Rdb.new self
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                  def arel_visitor
         | 
| 40 | 
            +
                    Arel::Visitors::Rdb.new self
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                  def valid_type?(type)
         | 
| 44 | 
            +
                    !native_database_types[type].nil? || !native_database_types[type.type].nil?
         | 
| 45 | 
            +
                  end
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                  def adapter_name
         | 
| 48 | 
            +
                    ADAPTER_NAME
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                  def schema_creation
         | 
| 52 | 
            +
                    Rdb::SchemaCreation.new self
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
             | 
| 55 | 
            +
                  def supports_migrations?
         | 
| 56 | 
            +
                    true
         | 
| 57 | 
            +
                  end
         | 
| 58 | 
            +
             | 
| 59 | 
            +
                  def supports_primary_key?
         | 
| 60 | 
            +
                    true
         | 
| 61 | 
            +
                  end
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  def supports_count_distinct?
         | 
| 64 | 
            +
                    true
         | 
| 65 | 
            +
                  end
         | 
| 66 | 
            +
             | 
| 67 | 
            +
                  def supports_ddl_transactions?
         | 
| 68 | 
            +
                    true
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
             | 
| 71 | 
            +
                  def supports_transaction_isolation?
         | 
| 72 | 
            +
                    true
         | 
| 73 | 
            +
                  end
         | 
| 74 | 
            +
             | 
| 75 | 
            +
                  def supports_savepoints?
         | 
| 76 | 
            +
                    true
         | 
| 77 | 
            +
                  end
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  def prefetch_primary_key?(_table_name = nil)
         | 
| 80 | 
            +
                    true
         | 
| 81 | 
            +
                  end
         | 
| 82 | 
            +
             | 
| 83 | 
            +
                  def ids_in_list_limit
         | 
| 84 | 
            +
                    1499
         | 
| 85 | 
            +
                  end
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  def active?
         | 
| 88 | 
            +
                    return false unless @connection.open?
         | 
| 89 | 
            +
                    # return true if @connection.transaction_started
         | 
| 90 | 
            +
                    @connection.query('SELECT 1 FROM RDB$DATABASE')
         | 
| 91 | 
            +
                    true
         | 
| 92 | 
            +
                  rescue StandardError
         | 
| 93 | 
            +
                    false
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
             | 
| 96 | 
            +
                  def reconnect!
         | 
| 97 | 
            +
                    disconnect!
         | 
| 98 | 
            +
                    @connection = ::Fb::Database.connect(@config)
         | 
| 99 | 
            +
                  end
         | 
| 100 | 
            +
             | 
| 101 | 
            +
                  def disconnect!
         | 
| 102 | 
            +
                    super
         | 
| 103 | 
            +
                    begin
         | 
| 104 | 
            +
                      @connection.close
         | 
| 105 | 
            +
                    rescue StandardError
         | 
| 106 | 
            +
                      nil
         | 
| 107 | 
            +
                    end
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def reset!
         | 
| 111 | 
            +
                    reconnect!
         | 
| 112 | 
            +
                  end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                  def requires_reloading?
         | 
| 115 | 
            +
                    false
         | 
| 116 | 
            +
                  end
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                  def create_savepoint(name = current_savepoint_name)
         | 
| 119 | 
            +
                    execute("SAVEPOINT #{name}")
         | 
| 120 | 
            +
                  end
         | 
| 121 | 
            +
             | 
| 122 | 
            +
                  def rollback_to_savepoint(name = current_savepoint_name)
         | 
| 123 | 
            +
                    execute("ROLLBACK TO SAVEPOINT #{name}")
         | 
| 124 | 
            +
                  end
         | 
| 125 | 
            +
             | 
| 126 | 
            +
                  def release_savepoint(name = current_savepoint_name)
         | 
| 127 | 
            +
                    execute("RELEASE SAVEPOINT #{name}")
         | 
| 128 | 
            +
                  end
         | 
| 129 | 
            +
             | 
| 130 | 
            +
                  protected
         | 
| 131 | 
            +
             | 
| 132 | 
            +
                  def initialize_type_map(map)
         | 
| 133 | 
            +
                    super
         | 
| 134 | 
            +
                    map.register_type(/timestamp/i, Type::DateTime.new)
         | 
| 135 | 
            +
                    map.alias_type(/blob sub_type text/i, 'text')
         | 
| 136 | 
            +
                  end
         | 
| 137 | 
            +
             | 
| 138 | 
            +
                  def translate_exception(e, message)
         | 
| 139 | 
            +
                    case e.message
         | 
| 140 | 
            +
                    when /violation of FOREIGN KEY constraint/
         | 
| 141 | 
            +
                      ActiveRecord::InvalidForeignKey.new(message)
         | 
| 142 | 
            +
                    when /violation of PRIMARY or UNIQUE KEY constraint/, /attempt to store duplicate value/
         | 
| 143 | 
            +
                      ActiveRecord::RecordNotUnique.new(message)
         | 
| 144 | 
            +
                    when /This operation is not defined for system tables/
         | 
| 145 | 
            +
                      ActiveRecord::ActiveRecordError.new(message)
         | 
| 146 | 
            +
                    else
         | 
| 147 | 
            +
                      super
         | 
| 148 | 
            +
                    end
         | 
| 149 | 
            +
                  end
         | 
| 150 | 
            +
                end
         | 
| 151 | 
            +
              end
         | 
| 152 | 
            +
            end
         | 
| @@ -0,0 +1,69 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionAdapters
         | 
| 3 | 
            +
                class RdbColumn < Column # :nodoc:
         | 
| 4 | 
            +
                  class << self
         | 
| 5 | 
            +
                    def sql_type_for(field)
         | 
| 6 | 
            +
                      sql_type = field[:sql_type]
         | 
| 7 | 
            +
                      sub_type = field[:sql_subtype]
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                      sql_type << case sql_type
         | 
| 10 | 
            +
                                  when /(numeric|decimal)/i
         | 
| 11 | 
            +
                                    "(#{field[:precision]},#{field[:scale].abs})"
         | 
| 12 | 
            +
                                  when /(int|float|double|char|varchar|bigint)/i
         | 
| 13 | 
            +
                                    "(#{field[:length]})"
         | 
| 14 | 
            +
                                  else
         | 
| 15 | 
            +
                                    ''
         | 
| 16 | 
            +
                                  end
         | 
| 17 | 
            +
             | 
| 18 | 
            +
                      sql_type << ' sub_type text' if /blob/i.match?(sql_type) && sub_type == 1
         | 
| 19 | 
            +
                      sql_type
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
                  end
         | 
| 22 | 
            +
             | 
| 23 | 
            +
                  attr_reader :sub_type, :domain
         | 
| 24 | 
            +
             | 
| 25 | 
            +
                  def initialize(name, default, sql_type_metadata = nil, null = true, table_name = nil, rdb_options = {})
         | 
| 26 | 
            +
                    @domain, @sub_type = rdb_options.values_at(:domain, :sub_type)
         | 
| 27 | 
            +
                    name = name.dup
         | 
| 28 | 
            +
                    name.downcase!
         | 
| 29 | 
            +
                    super(name, parse_default(default), sql_type_metadata, null, table_name)
         | 
| 30 | 
            +
                  end
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def sql_type
         | 
| 33 | 
            +
                    @sql_type_metadata[:sql_type]
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def type
         | 
| 37 | 
            +
                    @sql_type_metadata[:type]
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def precision
         | 
| 41 | 
            +
                    @sql_type_metadata[:precision]
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  def scale
         | 
| 45 | 
            +
                    @sql_type_metadata[:scale]
         | 
| 46 | 
            +
                  end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                  def limit
         | 
| 49 | 
            +
                    @sql_type_metadata[:limit]
         | 
| 50 | 
            +
                  end
         | 
| 51 | 
            +
             | 
| 52 | 
            +
                  private
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                  def parse_default(default)
         | 
| 55 | 
            +
                    return if default.nil? || /null/i.match?(default)
         | 
| 56 | 
            +
                    d = default.dup
         | 
| 57 | 
            +
                    d.gsub!(/^\s*DEFAULT\s+/i, '')
         | 
| 58 | 
            +
                    d.gsub!(/(^'|'$)/, '')
         | 
| 59 | 
            +
                    d
         | 
| 60 | 
            +
                  end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
                  def simplified_type(field_type)
         | 
| 63 | 
            +
                    return :datetime if /timestamp/i.match?(field_type)
         | 
| 64 | 
            +
                    return :text if /blob sub_type text/i.match?(field_type)
         | 
| 65 | 
            +
                    super
         | 
| 66 | 
            +
                  end
         | 
| 67 | 
            +
                end
         | 
| 68 | 
            +
              end
         | 
| 69 | 
            +
            end
         | 
| @@ -0,0 +1,33 @@ | |
| 1 | 
            +
            module ActiveRecord
         | 
| 2 | 
            +
              module ConnectionHandling # :nodoc:
         | 
| 3 | 
            +
                def rdb_connection(config)
         | 
| 4 | 
            +
                  require 'fb'
         | 
| 5 | 
            +
                  config = rdb_connection_config(config)
         | 
| 6 | 
            +
                  db = ::Fb::Database.new(config)
         | 
| 7 | 
            +
                  begin
         | 
| 8 | 
            +
                    connection = db.connect
         | 
| 9 | 
            +
                  rescue StandardError
         | 
| 10 | 
            +
                    unless config[:create]
         | 
| 11 | 
            +
                      require 'pp'
         | 
| 12 | 
            +
                      pp config
         | 
| 13 | 
            +
                      raise ConnectionNotEstablished, 'No Firebird connections established.'
         | 
| 14 | 
            +
                    end
         | 
| 15 | 
            +
                    connection = db.create.connect
         | 
| 16 | 
            +
                  end
         | 
| 17 | 
            +
                  ConnectionAdapters::RdbAdapter.new(connection, logger, config)
         | 
| 18 | 
            +
                end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
                def rdb_connection_config(config)
         | 
| 21 | 
            +
                  config = config.symbolize_keys.dup.reverse_merge(downcase_names: true)
         | 
| 22 | 
            +
                  port = config[:port] || 3050
         | 
| 23 | 
            +
                  raise ArgumentError, 'No database specified. Missing argument: database.' unless config[:database]
         | 
| 24 | 
            +
                  config[:database] = File.expand_path(config[:database], defined?(Rails) && Rails.root) if config[:host].nil? || /localhost/i.match?(config[:host])
         | 
| 25 | 
            +
                  config[:database] = "#{config[:host]}/#{port}:#{config[:database]}" if config[:host]
         | 
| 26 | 
            +
                  # config[:charset] = config[:charset].gsub(/-/, '') if config[:charset]
         | 
| 27 | 
            +
                  # config[:encoding] = config[:encoding].gsub(/-/, '') if config[:encoding]
         | 
| 28 | 
            +
                  config[:page_size] = 8192 unless config[:page_size]
         | 
| 29 | 
            +
                  config[:readonly_selects] = true unless config[:readonly_selects].present?
         | 
| 30 | 
            +
                  config
         | 
| 31 | 
            +
                end
         | 
| 32 | 
            +
              end
         | 
| 33 | 
            +
            end
         | 
| @@ -0,0 +1,82 @@ | |
| 1 | 
            +
            require 'active_record/tasks/database_tasks'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            module ActiveRecord
         | 
| 4 | 
            +
              module Tasks
         | 
| 5 | 
            +
                class RdbDatabaseTasks # :nodoc:
         | 
| 6 | 
            +
                  delegate :rdb_connection_config, :establish_connection, to: ::ActiveRecord::Base
         | 
| 7 | 
            +
             | 
| 8 | 
            +
                  def initialize(configuration, root = ::ActiveRecord::Tasks::DatabaseTasks.root)
         | 
| 9 | 
            +
                    @root = root
         | 
| 10 | 
            +
                    @configuration = rdb_connection_config(configuration)
         | 
| 11 | 
            +
                  end
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def create
         | 
| 14 | 
            +
                    rdb_database.create
         | 
| 15 | 
            +
                    establish_connection configuration
         | 
| 16 | 
            +
                  rescue ::Fb::Error => e
         | 
| 17 | 
            +
                    raise unless e.message.include?('database or file exists')
         | 
| 18 | 
            +
                    raise DatabaseAlreadyExists
         | 
| 19 | 
            +
                  end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def drop
         | 
| 22 | 
            +
                    rdb_database.drop
         | 
| 23 | 
            +
                  rescue ::Fb::Error => e
         | 
| 24 | 
            +
                    raise ::ActiveRecord::ConnectionNotEstablished, e.message
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                  def purge
         | 
| 28 | 
            +
                    begin
         | 
| 29 | 
            +
                      drop
         | 
| 30 | 
            +
                    rescue StandardError
         | 
| 31 | 
            +
                      nil
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
                    create
         | 
| 34 | 
            +
                  end
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                  def structure_dump(filename)
         | 
| 37 | 
            +
                    isql :extract, output: filename
         | 
| 38 | 
            +
                  end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
                  def structure_load(filename)
         | 
| 41 | 
            +
                    isql input: filename
         | 
| 42 | 
            +
                  end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
                  private
         | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def rdb_database
         | 
| 47 | 
            +
                    ::Fb::Database.new(configuration)
         | 
| 48 | 
            +
                  end
         | 
| 49 | 
            +
             | 
| 50 | 
            +
                  # Executes isql commands to load/dump the schema.
         | 
| 51 | 
            +
                  # The generated command might look like this:
         | 
| 52 | 
            +
                  #   isql db/development.fdb -user SYSDBA -password masterkey -extract
         | 
| 53 | 
            +
                  def isql(*args)
         | 
| 54 | 
            +
                    opts = args.extract_options!
         | 
| 55 | 
            +
                    user, pass = configuration.values_at(:username, :password)
         | 
| 56 | 
            +
                    user ||= configuration[:user]
         | 
| 57 | 
            +
                    opts.reverse_merge!(user: user, password: pass)
         | 
| 58 | 
            +
                    cmd = [isql_executable, configuration[:database]]
         | 
| 59 | 
            +
                    cmd += opts.map { |name, val| "-#{name} #{val}" }
         | 
| 60 | 
            +
                    cmd += args.map { |flag| "-#{flag}" }
         | 
| 61 | 
            +
                    cmd = cmd.join(' ')
         | 
| 62 | 
            +
                    raise "Error running: #{cmd}" unless Kernel.system(cmd)
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                  def isql_create(*_args)
         | 
| 66 | 
            +
                    "#{isql_executable} -input "
         | 
| 67 | 
            +
                  end
         | 
| 68 | 
            +
             | 
| 69 | 
            +
                  # Finds the isql command line utility from the PATH
         | 
| 70 | 
            +
                  # Many linux distros call this program isql-fb, instead of isql
         | 
| 71 | 
            +
                  def isql_executable
         | 
| 72 | 
            +
                    require 'mkmf'
         | 
| 73 | 
            +
                    exe = %w[isql-fb isql].detect(&method(:find_executable0))
         | 
| 74 | 
            +
                    exe || abort('Unable to find isql or isql-fb in your $PATH')
         | 
| 75 | 
            +
                  end
         | 
| 76 | 
            +
             | 
| 77 | 
            +
                  attr_reader :configuration
         | 
| 78 | 
            +
             | 
| 79 | 
            +
                  attr_reader :root
         | 
| 80 | 
            +
                end
         | 
| 81 | 
            +
              end
         | 
| 82 | 
            +
            end
         | 
| @@ -0,0 +1,10 @@ | |
| 1 | 
            +
            require 'active_record/connection_adapters/rdb_adapter'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            if defined?(::Rails::Railtie) && ::ActiveRecord::VERSION::MAJOR > 3
         | 
| 4 | 
            +
              class Railtie < ::Rails::Railtie # :nodoc:
         | 
| 5 | 
            +
                rake_tasks do
         | 
| 6 | 
            +
                  load 'active_record/tasks/rdb_database_tasks.rb'
         | 
| 7 | 
            +
                  ActiveRecord::Tasks::DatabaseTasks.register_task(/rdb/, ActiveRecord::Tasks::RdbDatabaseTasks)
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
              end
         | 
| 10 | 
            +
            end
         | 
| @@ -0,0 +1,117 @@ | |
| 1 | 
            +
            module Arel
         | 
| 2 | 
            +
              module Visitors
         | 
| 3 | 
            +
                class Rdb < Arel::Visitors::ToSql # :nodoc
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                  def preparable
         | 
| 6 | 
            +
                    false
         | 
| 7 | 
            +
                  end
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                  private
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def visit_Arel_Nodes_SelectStatement o, collector
         | 
| 12 | 
            +
                    collector << "SELECT "
         | 
| 13 | 
            +
                    collector = visit o.offset, collector if o.offset && !o.limit
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                    collector = o.cores.inject(collector) {|c, x|
         | 
| 16 | 
            +
                      visit_Arel_Nodes_SelectCore(x, c)
         | 
| 17 | 
            +
                    }
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                    unless o.orders.empty?
         | 
| 20 | 
            +
                      collector << ORDER_BY
         | 
| 21 | 
            +
                      len = o.orders.length - 1
         | 
| 22 | 
            +
                      o.orders.each_with_index {|x, i|
         | 
| 23 | 
            +
                        collector = visit(x, collector)
         | 
| 24 | 
            +
                        collector << COMMA unless len == i
         | 
| 25 | 
            +
                      }
         | 
| 26 | 
            +
                    end
         | 
| 27 | 
            +
             | 
| 28 | 
            +
                    if o.limit && o.offset
         | 
| 29 | 
            +
                      collector = limit_with_rows o, collector
         | 
| 30 | 
            +
                    elsif o.limit && !o.offset
         | 
| 31 | 
            +
                      collector = visit o.limit, collector
         | 
| 32 | 
            +
                    end
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    maybe_visit o.lock, collector
         | 
| 35 | 
            +
                  end
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                  def visit_Arel_Nodes_SelectCore o, collector
         | 
| 38 | 
            +
                    if o.set_quantifier
         | 
| 39 | 
            +
                      collector = visit o.set_quantifier, collector
         | 
| 40 | 
            +
                      collector << SPACE
         | 
| 41 | 
            +
                    end
         | 
| 42 | 
            +
             | 
| 43 | 
            +
                    unless o.projections.empty?
         | 
| 44 | 
            +
                      len = o.projections.length - 1
         | 
| 45 | 
            +
                      o.projections.each_with_index do |x, i|
         | 
| 46 | 
            +
                        collector = visit(x, collector)
         | 
| 47 | 
            +
                        collector << COMMA unless len == i
         | 
| 48 | 
            +
                      end
         | 
| 49 | 
            +
                    end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                    if o.source && !o.source.empty?
         | 
| 52 | 
            +
                      collector << " FROM "
         | 
| 53 | 
            +
                      collector = visit o.source, collector
         | 
| 54 | 
            +
                    end
         | 
| 55 | 
            +
             | 
| 56 | 
            +
                    unless o.wheres.empty?
         | 
| 57 | 
            +
                      collector << WHERE
         | 
| 58 | 
            +
                      len = o.wheres.length - 1
         | 
| 59 | 
            +
                      o.wheres.each_with_index do |x, i|
         | 
| 60 | 
            +
                        collector = visit(x, collector)
         | 
| 61 | 
            +
                        collector << AND unless len == i
         | 
| 62 | 
            +
                      end
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                    unless o.groups.empty?
         | 
| 65 | 
            +
                      collector << GROUP_BY
         | 
| 66 | 
            +
                      len = o.groups.length - 1
         | 
| 67 | 
            +
                      o.groups.each_with_index do |x, i|
         | 
| 68 | 
            +
                        collector = visit(x, collector)
         | 
| 69 | 
            +
                        collector << COMMA unless len == i
         | 
| 70 | 
            +
                      end
         | 
| 71 | 
            +
                    end
         | 
| 72 | 
            +
             | 
| 73 | 
            +
                    if Rails::VERSION::MAJOR < 5
         | 
| 74 | 
            +
                      collector = maybe_visit o.having, collector
         | 
| 75 | 
            +
                    else
         | 
| 76 | 
            +
                      unless o.havings.empty?
         | 
| 77 | 
            +
                        collector << " HAVING "
         | 
| 78 | 
            +
                        inject_join o.havings, collector, AND
         | 
| 79 | 
            +
                      end
         | 
| 80 | 
            +
                    end
         | 
| 81 | 
            +
             | 
| 82 | 
            +
                    collector
         | 
| 83 | 
            +
                  end
         | 
| 84 | 
            +
             | 
| 85 | 
            +
                  def visit_Arel_Nodes_Limit o, collector
         | 
| 86 | 
            +
                    collector << " ROWS "
         | 
| 87 | 
            +
                    visit o.expr, collector
         | 
| 88 | 
            +
                  end
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  def visit_Arel_Nodes_Offset o, collector
         | 
| 91 | 
            +
                    collector << " SKIP "
         | 
| 92 | 
            +
                    visit o.expr, collector
         | 
| 93 | 
            +
                  end
         | 
| 94 | 
            +
             | 
| 95 | 
            +
                  def limit_with_rows o, collector
         | 
| 96 | 
            +
                    o.offset.expr.value = ActiveModel::Attribute.with_cast_value("OFFSET".freeze,
         | 
| 97 | 
            +
                                                                                 o.offset.expr.value.value + 1,
         | 
| 98 | 
            +
                                                                                 ActiveModel::Type.default_value)
         | 
| 99 | 
            +
                    offset = o.offset.expr.value
         | 
| 100 | 
            +
                    o.limit.expr.value = ActiveModel::Attribute.with_cast_value("LIMIT".freeze,
         | 
| 101 | 
            +
                                                                                (o.limit.expr.value.value) + (offset.value - 1),
         | 
| 102 | 
            +
                                                                                ActiveModel::Type.default_value)
         | 
| 103 | 
            +
                    limit = o.limit.expr.value
         | 
| 104 | 
            +
                    collector << " ROWS "
         | 
| 105 | 
            +
                    collector.add_bind(offset) {|i| "?"}
         | 
| 106 | 
            +
                    collector << " TO "
         | 
| 107 | 
            +
                    collector.add_bind(limit) {|i| "?"}
         | 
| 108 | 
            +
                  end
         | 
| 109 | 
            +
             | 
| 110 | 
            +
                  def quote_column_name name
         | 
| 111 | 
            +
                    return name if Arel::Nodes::SqlLiteral === name
         | 
| 112 | 
            +
                    @connection.quote_column_name(name)
         | 
| 113 | 
            +
                  end
         | 
| 114 | 
            +
             | 
| 115 | 
            +
                end
         | 
| 116 | 
            +
              end
         | 
| 117 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,86 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: activerecord-rdb-adapter
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.4.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Andrey Lobanov (RedSoft)
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2018-03-06 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: fb
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0.9'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0.9'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: rails
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '5.1'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '5.1'
         | 
| 41 | 
            +
            description: ActiveRecord Firebird and RedDatabase Adapter for Rails 5+
         | 
| 42 | 
            +
            email: andrey.lobanov@red-soft.ru
         | 
| 43 | 
            +
            executables: []
         | 
| 44 | 
            +
            extensions: []
         | 
| 45 | 
            +
            extra_rdoc_files: []
         | 
| 46 | 
            +
            files:
         | 
| 47 | 
            +
            - README.md
         | 
| 48 | 
            +
            - lib/active_record/connection_adapters/rdb/database_limits.rb
         | 
| 49 | 
            +
            - lib/active_record/connection_adapters/rdb/database_statements.rb
         | 
| 50 | 
            +
            - lib/active_record/connection_adapters/rdb/quoting.rb
         | 
| 51 | 
            +
            - lib/active_record/connection_adapters/rdb/schema_creation.rb
         | 
| 52 | 
            +
            - lib/active_record/connection_adapters/rdb/schema_dumper.rb
         | 
| 53 | 
            +
            - lib/active_record/connection_adapters/rdb/schema_statements.rb
         | 
| 54 | 
            +
            - lib/active_record/connection_adapters/rdb/table_definition.rb
         | 
| 55 | 
            +
            - lib/active_record/connection_adapters/rdb_adapter.rb
         | 
| 56 | 
            +
            - lib/active_record/connection_adapters/rdb_column.rb
         | 
| 57 | 
            +
            - lib/active_record/rdb_base.rb
         | 
| 58 | 
            +
            - lib/active_record/tasks/rdb_database_tasks.rb
         | 
| 59 | 
            +
            - lib/activerecord-rdb-adapter.rb
         | 
| 60 | 
            +
            - lib/arel/visitors/rdb_visitor.rb
         | 
| 61 | 
            +
            homepage: http://gitlab.red-soft.biz/andrey.lobanov/activerecord-rdb-adapter
         | 
| 62 | 
            +
            licenses:
         | 
| 63 | 
            +
            - MIT
         | 
| 64 | 
            +
            metadata: {}
         | 
| 65 | 
            +
            post_install_message: 
         | 
| 66 | 
            +
            rdoc_options: []
         | 
| 67 | 
            +
            require_paths:
         | 
| 68 | 
            +
            - lib
         | 
| 69 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 70 | 
            +
              requirements:
         | 
| 71 | 
            +
              - - ">="
         | 
| 72 | 
            +
                - !ruby/object:Gem::Version
         | 
| 73 | 
            +
                  version: '0'
         | 
| 74 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 75 | 
            +
              requirements:
         | 
| 76 | 
            +
              - - ">="
         | 
| 77 | 
            +
                - !ruby/object:Gem::Version
         | 
| 78 | 
            +
                  version: '0'
         | 
| 79 | 
            +
            requirements:
         | 
| 80 | 
            +
            - Firebird library fb
         | 
| 81 | 
            +
            rubyforge_project: 
         | 
| 82 | 
            +
            rubygems_version: 2.7.6
         | 
| 83 | 
            +
            signing_key: 
         | 
| 84 | 
            +
            specification_version: 4
         | 
| 85 | 
            +
            summary: ActiveRecord Firebird and RedDatabase Adapter
         | 
| 86 | 
            +
            test_files: []
         |