db_schema 0.3 → 0.3.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 +4 -4
 - data/.rspec +1 -0
 - data/README.md +8 -8
 - data/db_schema.gemspec +1 -1
 - data/lib/db_schema/awesome_print.rb +2 -2
 - data/lib/db_schema/changes.rb +155 -141
 - data/lib/db_schema/definitions/check_constraint.rb +4 -0
 - data/lib/db_schema/definitions/field/base.rb +4 -0
 - data/lib/db_schema/definitions/index.rb +10 -0
 - data/lib/db_schema/definitions/table.rb +24 -14
 - data/lib/db_schema/dsl.rb +4 -4
 - data/lib/db_schema/migrator.rb +1 -1
 - data/lib/db_schema/normalizer.rb +67 -14
 - data/lib/db_schema/reader.rb +11 -11
 - data/lib/db_schema/runner.rb +1 -1
 - data/lib/db_schema/utils.rb +6 -0
 - data/lib/db_schema/validator.rb +1 -1
 - data/lib/db_schema/version.rb +1 -1
 - metadata +3 -3
 
    
        checksums.yaml
    CHANGED
    
    | 
         @@ -1,7 +1,7 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            ---
         
     | 
| 
       2 
2 
     | 
    
         
             
            SHA1:
         
     | 
| 
       3 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       4 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 3 
     | 
    
         
            +
              metadata.gz: 92f4601dd7a4513014fd782391bfe69f8400f06d
         
     | 
| 
      
 4 
     | 
    
         
            +
              data.tar.gz: 291caae43bf4582dbf0636b8601de86ebdcf3c25
         
     | 
| 
       5 
5 
     | 
    
         
             
            SHA512:
         
     | 
| 
       6 
     | 
    
         
            -
              metadata.gz:  
     | 
| 
       7 
     | 
    
         
            -
              data.tar.gz:  
     | 
| 
      
 6 
     | 
    
         
            +
              metadata.gz: 7a8af71a493a89113166e8a1ee0fa1808ba8bbbcbb07b263fa0d9f0884eafe4d297e64d79815a854f460ad78af3bd0c847c82741c96d5175e127452a1333120d
         
     | 
| 
      
 7 
     | 
    
         
            +
              data.tar.gz: 41fb922d5f307c481f3595d27983c39a13640cd2c5cdc5e9e6a395ad34c509cf712f4d08f79825c730462855c9d66d178961bb1d48d77b599386e10ab90b831a
         
     | 
    
        data/.rspec
    CHANGED
    
    
    
        data/README.md
    CHANGED
    
    | 
         @@ -1,4 +1,4 @@ 
     | 
|
| 
       1 
     | 
    
         
            -
            # DbSchema [](https://travis-ci.org/db-schema/core) [](https://badge.fury.io/rb/db_schema) [](https://gitter.im/7even/db_schema?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
         
     | 
| 
       2 
2 
     | 
    
         | 
| 
       3 
3 
     | 
    
         
             
            DbSchema is an opinionated database schema management tool that lets you maintain your DB schema with a single ruby file.
         
     | 
| 
       4 
4 
     | 
    
         | 
| 
         @@ -53,7 +53,7 @@ But you would lose it even with manual migrations. 
     | 
|
| 
       53 
53 
     | 
    
         
             
            Add this line to your application's Gemfile:
         
     | 
| 
       54 
54 
     | 
    
         | 
| 
       55 
55 
     | 
    
         
             
            ``` ruby
         
     | 
| 
       56 
     | 
    
         
            -
            gem 'db_schema', '~> 0.3. 
     | 
| 
      
 56 
     | 
    
         
            +
            gem 'db_schema', '~> 0.3.1'
         
     | 
| 
       57 
57 
     | 
    
         
             
            ```
         
     | 
| 
       58 
58 
     | 
    
         | 
| 
       59 
59 
     | 
    
         
             
            And then execute:
         
     | 
| 
         @@ -83,7 +83,7 @@ DbSchema.configure( 
     | 
|
| 
       83 
83 
     | 
    
         
             
            ```
         
     | 
| 
       84 
84 
     | 
    
         | 
| 
       85 
85 
     | 
    
         
             
            There is also a Rails' `database.yml`-compatible `configure_from_yaml` method. DbSchema configuration
         
     | 
| 
       86 
     | 
    
         
            -
            is discussed in detail [here](https://github.com/ 
     | 
| 
      
 86 
     | 
    
         
            +
            is discussed in detail [here](https://github.com/db-schema/core/wiki/Configuration).
         
     | 
| 
       87 
87 
     | 
    
         | 
| 
       88 
88 
     | 
    
         
             
            After DbSchema is configured you can load your schema definition file:
         
     | 
| 
       89 
89 
     | 
    
         | 
| 
         @@ -113,7 +113,7 @@ DbSchema.describe do |db| 
     | 
|
| 
       113 
113 
     | 
    
         
             
            end
         
     | 
| 
       114 
114 
     | 
    
         
             
            ```
         
     | 
| 
       115 
115 
     | 
    
         | 
| 
       116 
     | 
    
         
            -
            Database schema definition DSL is documented [here](https://github.com/ 
     | 
| 
      
 116 
     | 
    
         
            +
            Database schema definition DSL is documented [here](https://github.com/db-schema/core/wiki/Schema-definition-DSL).
         
     | 
| 
       117 
117 
     | 
    
         | 
| 
       118 
118 
     | 
    
         
             
            If you want to analyze your database structure in any way from your app (e.g. defining methods
         
     | 
| 
       119 
119 
     | 
    
         
             
            with `#define_method` for each enum value) you can use `DbSchema.current_schema` - it returns
         
     | 
| 
         @@ -121,7 +121,7 @@ a cached copy of the database structure as a `DbSchema::Definitions::Schema` obj 
     | 
|
| 
       121 
121 
     | 
    
         
             
            can query in different ways. It is available after the schema was applied by DbSchema
         
     | 
| 
       122 
122 
     | 
    
         
             
            (`DbSchema.describe` remembers the current schema of the database and exposes it
         
     | 
| 
       123 
123 
     | 
    
         
             
            at `.current_schema`). Documentation for schema analysis DSL can be found
         
     | 
| 
       124 
     | 
    
         
            -
            [here](https://github.com/ 
     | 
| 
      
 124 
     | 
    
         
            +
            [here](https://github.com/db-schema/core/wiki/Schema-analysis-DSL).
         
     | 
| 
       125 
125 
     | 
    
         | 
| 
       126 
126 
     | 
    
         
             
            ### Production setup
         
     | 
| 
       127 
127 
     | 
    
         | 
| 
         @@ -217,12 +217,12 @@ instead you have to provide some conditions required to run the migration (the g 
     | 
|
| 
       217 
217 
     | 
    
         
             
            conditions that a) will only trigger if the migration wasn't applied yet and b) are necessary for the
         
     | 
| 
       218 
218 
     | 
    
         
             
            migration to work) - like "rename the `users`
         
     | 
| 
       219 
219 
     | 
    
         
             
            table to `people` only if the database has a `users` table" (DbSchema also provides
         
     | 
| 
       220 
     | 
    
         
            -
            a [simple DSL](https://github.com/ 
     | 
| 
      
 220 
     | 
    
         
            +
            a [simple DSL](https://github.com/db-schema/core/wiki/Schema-analysis-DSL) for schema analysis).
         
     | 
| 
       221 
221 
     | 
    
         
             
            This way the migration won't be applied again and the whole DbSchema process stays idempotent.
         
     | 
| 
       222 
222 
     | 
    
         
             
            Also you don't have to keep these migrations forever - once a migration is applied to databases
         
     | 
| 
       223 
223 
     | 
    
         
             
            in all environments you can safely delete it (though you can give your teammates a week or two to keep up).
         
     | 
| 
       224 
224 
     | 
    
         | 
| 
       225 
     | 
    
         
            -
            Conditional migrations are described [here](https://github.com/ 
     | 
| 
      
 225 
     | 
    
         
            +
            Conditional migrations are described [here](https://github.com/db-schema/core/wiki/Conditional-Migrations).
         
     | 
| 
       226 
226 
     | 
    
         | 
| 
       227 
227 
     | 
    
         
             
            ## Known problems and limitations
         
     | 
| 
       228 
228 
     | 
    
         | 
| 
         @@ -237,7 +237,7 @@ After checking out the repo, run `bin/setup` to install dependencies. Then, run 
     | 
|
| 
       237 
237 
     | 
    
         | 
| 
       238 
238 
     | 
    
         
             
            ## Contributing
         
     | 
| 
       239 
239 
     | 
    
         | 
| 
       240 
     | 
    
         
            -
            Bug reports and pull requests are welcome on GitHub at [ 
     | 
| 
      
 240 
     | 
    
         
            +
            Bug reports and pull requests are welcome on GitHub at [db-schema/core](https://github.com/db-schema/core).
         
     | 
| 
       241 
241 
     | 
    
         | 
| 
       242 
242 
     | 
    
         
             
            ## License
         
     | 
| 
       243 
243 
     | 
    
         | 
    
        data/db_schema.gemspec
    CHANGED
    
    | 
         @@ -10,7 +10,7 @@ Gem::Specification.new do |spec| 
     | 
|
| 
       10 
10 
     | 
    
         | 
| 
       11 
11 
     | 
    
         
             
              spec.summary       = 'Declarative database schema definition.'
         
     | 
| 
       12 
12 
     | 
    
         
             
              spec.description   = 'A database schema management tool that reads a "single-source-of-truth" schema definition from a ruby file and auto-migrates the database to conform to it.'
         
     | 
| 
       13 
     | 
    
         
            -
              spec.homepage      = 'https://github.com/ 
     | 
| 
      
 13 
     | 
    
         
            +
              spec.homepage      = 'https://github.com/db-schema/core'
         
     | 
| 
       14 
14 
     | 
    
         
             
              spec.license       = 'MIT'
         
     | 
| 
       15 
15 
     | 
    
         | 
| 
       16 
16 
     | 
    
         
             
              spec.files         = `git ls-files -z`.split("\x0").reject { |f| f.match(%r(^spec/)) }
         
     | 
| 
         @@ -87,7 +87,7 @@ if defined?(AwesomePrint) 
     | 
|
| 
       87 
87 
     | 
    
         | 
| 
       88 
88 
     | 
    
         
             
                  def awesome_dbschema_table(object)
         
     | 
| 
       89 
89 
     | 
    
         
             
                    data = ["fields: #{object.fields.ai}"]
         
     | 
| 
       90 
     | 
    
         
            -
                    data << " 
     | 
| 
      
 90 
     | 
    
         
            +
                    data << "indexes: #{object.indexes.ai}" if object.indexes.any?
         
     | 
| 
       91 
91 
     | 
    
         
             
                    data << "checks: #{object.checks.ai}" if object.checks.any?
         
     | 
| 
       92 
92 
     | 
    
         
             
                    data << "foreign_keys: #{object.foreign_keys.ai}" if object.foreign_keys.any?
         
     | 
| 
       93 
93 
     | 
    
         | 
| 
         @@ -190,7 +190,7 @@ if defined?(AwesomePrint) 
     | 
|
| 
       190 
190 
     | 
    
         | 
| 
       191 
191 
     | 
    
         
             
                  def awesome_dbschema_create_table(object)
         
     | 
| 
       192 
192 
     | 
    
         
             
                    data = ["fields: #{object.table.fields.ai}"]
         
     | 
| 
       193 
     | 
    
         
            -
                    data << " 
     | 
| 
      
 193 
     | 
    
         
            +
                    data << "indexes: #{object.table.indexes.ai}" if object.table.indexes.any?
         
     | 
| 
       194 
194 
     | 
    
         
             
                    data << "checks: #{object.table.checks.ai}" if object.table.checks.any?
         
     | 
| 
       195 
195 
     | 
    
         | 
| 
       196 
196 
     | 
    
         
             
                    data_string = indent_lines(data.join(', '))
         
     | 
    
        data/lib/db_schema/changes.rb
    CHANGED
    
    | 
         @@ -5,59 +5,155 @@ module DbSchema 
     | 
|
| 
       5 
5 
     | 
    
         
             
              module Changes
         
     | 
| 
       6 
6 
     | 
    
         
             
                class << self
         
     | 
| 
       7 
7 
     | 
    
         
             
                  def between(desired_schema, actual_schema)
         
     | 
| 
       8 
     | 
    
         
            -
                     
     | 
| 
       9 
     | 
    
         
            -
             
     | 
| 
       10 
     | 
    
         
            -
             
     | 
| 
       11 
     | 
    
         
            -
             
     | 
| 
       12 
     | 
    
         
            -
             
     | 
| 
       13 
     | 
    
         
            -
             
     | 
| 
       14 
     | 
    
         
            -
             
     | 
| 
       15 
     | 
    
         
            -
             
     | 
| 
      
 8 
     | 
    
         
            +
                    sort_all_changes(
         
     | 
| 
      
 9 
     | 
    
         
            +
                      [
         
     | 
| 
      
 10 
     | 
    
         
            +
                        table_changes(desired_schema, actual_schema),
         
     | 
| 
      
 11 
     | 
    
         
            +
                        enum_changes(desired_schema, actual_schema),
         
     | 
| 
      
 12 
     | 
    
         
            +
                        extension_changes(desired_schema, actual_schema)
         
     | 
| 
      
 13 
     | 
    
         
            +
                      ].reduce(:+)
         
     | 
| 
      
 14 
     | 
    
         
            +
                    )
         
     | 
| 
      
 15 
     | 
    
         
            +
                  end
         
     | 
| 
       16 
16 
     | 
    
         | 
| 
       17 
     | 
    
         
            -
             
     | 
| 
       18 
     | 
    
         
            -
             
     | 
| 
      
 17 
     | 
    
         
            +
                private
         
     | 
| 
      
 18 
     | 
    
         
            +
                  def table_changes(desired_schema, actual_schema)
         
     | 
| 
      
 19 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 20 
     | 
    
         
            +
                      desired_schema.tables,
         
     | 
| 
      
 21 
     | 
    
         
            +
                      actual_schema.tables,
         
     | 
| 
      
 22 
     | 
    
         
            +
                      create: -> (table) do
         
     | 
| 
      
 23 
     | 
    
         
            +
                        fkey_operations = table.foreign_keys.map do |fkey|
         
     | 
| 
      
 24 
     | 
    
         
            +
                          Operations::CreateForeignKey.new(table.name, fkey)
         
     | 
| 
       19 
25 
     | 
    
         
             
                        end
         
     | 
| 
       20 
     | 
    
         
            -
                        changes.concat(fkey_operations)
         
     | 
| 
       21 
     | 
    
         
            -
                      elsif actual && !desired
         
     | 
| 
       22 
     | 
    
         
            -
                        changes << Operations::DropTable.new(table_name)
         
     | 
| 
       23 
26 
     | 
    
         | 
| 
       24 
     | 
    
         
            -
                         
     | 
| 
       25 
     | 
    
         
            -
             
     | 
| 
      
 27 
     | 
    
         
            +
                        [Operations::CreateTable.new(table), *fkey_operations]
         
     | 
| 
      
 28 
     | 
    
         
            +
                      end,
         
     | 
| 
      
 29 
     | 
    
         
            +
                      drop: -> (table) do
         
     | 
| 
      
 30 
     | 
    
         
            +
                        fkey_operations = table.foreign_keys.map do |fkey|
         
     | 
| 
      
 31 
     | 
    
         
            +
                          Operations::DropForeignKey.new(table.name, fkey.name)
         
     | 
| 
       26 
32 
     | 
    
         
             
                        end
         
     | 
| 
       27 
     | 
    
         
            -
             
     | 
| 
       28 
     | 
    
         
            -
                         
     | 
| 
       29 
     | 
    
         
            -
             
     | 
| 
       30 
     | 
    
         
            -
             
     | 
| 
       31 
     | 
    
         
            -
                        fkey_operations 
     | 
| 
       32 
     | 
    
         
            -
             
     | 
| 
       33 
     | 
    
         
            -
                         
     | 
| 
       34 
     | 
    
         
            -
                           
     | 
| 
       35 
     | 
    
         
            -
             
     | 
| 
       36 
     | 
    
         
            -
             
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                        [Operations::DropTable.new(table.name), *fkey_operations]
         
     | 
| 
      
 35 
     | 
    
         
            +
                      end,
         
     | 
| 
      
 36 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
      
 37 
     | 
    
         
            +
                        fkey_operations = foreign_key_changes(desired, actual)
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                        alter_table_operations = [
         
     | 
| 
      
 40 
     | 
    
         
            +
                          field_changes(desired, actual),
         
     | 
| 
      
 41 
     | 
    
         
            +
                          index_changes(desired, actual),
         
     | 
| 
      
 42 
     | 
    
         
            +
                          check_changes(desired, actual)
         
     | 
| 
      
 43 
     | 
    
         
            +
                        ].reduce(:+)
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                        if alter_table_operations.any?
         
     | 
| 
      
 46 
     | 
    
         
            +
                          alter_table = Operations::AlterTable.new(
         
     | 
| 
      
 47 
     | 
    
         
            +
                            desired.name,
         
     | 
| 
      
 48 
     | 
    
         
            +
                            sort_alter_table_changes(alter_table_operations)
         
     | 
| 
       37 
49 
     | 
    
         
             
                          )
         
     | 
| 
      
 50 
     | 
    
         
            +
             
     | 
| 
      
 51 
     | 
    
         
            +
                          [alter_table, *fkey_operations]
         
     | 
| 
      
 52 
     | 
    
         
            +
                        else
         
     | 
| 
      
 53 
     | 
    
         
            +
                          fkey_operations
         
     | 
| 
       38 
54 
     | 
    
         
             
                        end
         
     | 
| 
      
 55 
     | 
    
         
            +
                      end
         
     | 
| 
      
 56 
     | 
    
         
            +
                    )
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                  def field_changes(desired_table, actual_table)
         
     | 
| 
      
 60 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 61 
     | 
    
         
            +
                      desired_table.fields,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      actual_table.fields,
         
     | 
| 
      
 63 
     | 
    
         
            +
                      create: -> (field) { Operations::CreateColumn.new(field) },
         
     | 
| 
      
 64 
     | 
    
         
            +
                      drop:   -> (field) { Operations::DropColumn.new(field.name) },
         
     | 
| 
      
 65 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
      
 66 
     | 
    
         
            +
                        [].tap do |operations|
         
     | 
| 
      
 67 
     | 
    
         
            +
                          if (actual.type != desired.type) || (actual.attributes != desired.attributes)
         
     | 
| 
      
 68 
     | 
    
         
            +
                            operations << Operations::AlterColumnType.new(
         
     | 
| 
      
 69 
     | 
    
         
            +
                              actual.name,
         
     | 
| 
      
 70 
     | 
    
         
            +
                              new_type: desired.type,
         
     | 
| 
      
 71 
     | 
    
         
            +
                              **desired.attributes
         
     | 
| 
      
 72 
     | 
    
         
            +
                            )
         
     | 
| 
      
 73 
     | 
    
         
            +
                          end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                          if desired.primary_key? && !actual.primary_key?
         
     | 
| 
      
 76 
     | 
    
         
            +
                            operations << Operations::CreatePrimaryKey.new(actual.name)
         
     | 
| 
      
 77 
     | 
    
         
            +
                          end
         
     | 
| 
      
 78 
     | 
    
         
            +
             
     | 
| 
      
 79 
     | 
    
         
            +
                          if actual.primary_key? && !desired.primary_key?
         
     | 
| 
      
 80 
     | 
    
         
            +
                            operations << Operations::DropPrimaryKey.new(actual.name)
         
     | 
| 
      
 81 
     | 
    
         
            +
                          end
         
     | 
| 
       39 
82 
     | 
    
         | 
| 
       40 
     | 
    
         
            -
             
     | 
| 
      
 83 
     | 
    
         
            +
                          if desired.null? && !actual.null?
         
     | 
| 
      
 84 
     | 
    
         
            +
                            operations << Operations::AllowNull.new(actual.name)
         
     | 
| 
      
 85 
     | 
    
         
            +
                          end
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                          if actual.null? && !desired.null?
         
     | 
| 
      
 88 
     | 
    
         
            +
                            operations << Operations::DisallowNull.new(actual.name)
         
     | 
| 
      
 89 
     | 
    
         
            +
                          end
         
     | 
| 
      
 90 
     | 
    
         
            +
             
     | 
| 
      
 91 
     | 
    
         
            +
                          if actual.default != desired.default
         
     | 
| 
      
 92 
     | 
    
         
            +
                            operations << Operations::AlterColumnDefault.new(actual.name, new_default: desired.default)
         
     | 
| 
      
 93 
     | 
    
         
            +
                          end
         
     | 
| 
      
 94 
     | 
    
         
            +
                        end
         
     | 
| 
       41 
95 
     | 
    
         
             
                      end
         
     | 
| 
       42 
     | 
    
         
            -
                     
     | 
| 
      
 96 
     | 
    
         
            +
                    )
         
     | 
| 
      
 97 
     | 
    
         
            +
                  end
         
     | 
| 
       43 
98 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
             
     | 
| 
      
 99 
     | 
    
         
            +
                  def index_changes(desired_table, actual_table)
         
     | 
| 
      
 100 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 101 
     | 
    
         
            +
                      desired_table.indexes,
         
     | 
| 
      
 102 
     | 
    
         
            +
                      actual_table.indexes,
         
     | 
| 
      
 103 
     | 
    
         
            +
                      create: -> (index) { Operations::CreateIndex.new(index) },
         
     | 
| 
      
 104 
     | 
    
         
            +
                      drop:   -> (index) { Operations::DropIndex.new(index.name) },
         
     | 
| 
      
 105 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
      
 106 
     | 
    
         
            +
                        [
         
     | 
| 
      
 107 
     | 
    
         
            +
                          Operations::DropIndex.new(actual.name),
         
     | 
| 
      
 108 
     | 
    
         
            +
                          Operations::CreateIndex.new(desired)
         
     | 
| 
      
 109 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 110 
     | 
    
         
            +
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
                    )
         
     | 
| 
      
 112 
     | 
    
         
            +
                  end
         
     | 
| 
       45 
113 
     | 
    
         | 
| 
       46 
     | 
    
         
            -
             
     | 
| 
       47 
     | 
    
         
            -
             
     | 
| 
       48 
     | 
    
         
            -
                       
     | 
| 
      
 114 
     | 
    
         
            +
                  def check_changes(desired_table, actual_table)
         
     | 
| 
      
 115 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 116 
     | 
    
         
            +
                      desired_table.checks,
         
     | 
| 
      
 117 
     | 
    
         
            +
                      actual_table.checks,
         
     | 
| 
      
 118 
     | 
    
         
            +
                      create: -> (check) { Operations::CreateCheckConstraint.new(check) },
         
     | 
| 
      
 119 
     | 
    
         
            +
                      drop:   -> (check) { Operations::DropCheckConstraint.new(check.name) },
         
     | 
| 
      
 120 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
      
 121 
     | 
    
         
            +
                        [
         
     | 
| 
      
 122 
     | 
    
         
            +
                          Operations::DropCheckConstraint.new(actual.name),
         
     | 
| 
      
 123 
     | 
    
         
            +
                          Operations::CreateCheckConstraint.new(desired)
         
     | 
| 
      
 124 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 125 
     | 
    
         
            +
                      end
         
     | 
| 
      
 126 
     | 
    
         
            +
                    )
         
     | 
| 
      
 127 
     | 
    
         
            +
                  end
         
     | 
| 
       49 
128 
     | 
    
         | 
| 
       50 
     | 
    
         
            -
             
     | 
| 
       51 
     | 
    
         
            -
             
     | 
| 
       52 
     | 
    
         
            -
                       
     | 
| 
       53 
     | 
    
         
            -
             
     | 
| 
       54 
     | 
    
         
            -
                       
     | 
| 
      
 129 
     | 
    
         
            +
                  def foreign_key_changes(desired_table, actual_table)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 131 
     | 
    
         
            +
                      desired_table.foreign_keys,
         
     | 
| 
      
 132 
     | 
    
         
            +
                      actual_table.foreign_keys,
         
     | 
| 
      
 133 
     | 
    
         
            +
                      create: -> (foreign_key) { Operations::CreateForeignKey.new(actual_table.name, foreign_key) },
         
     | 
| 
      
 134 
     | 
    
         
            +
                      drop:   -> (foreign_key) { Operations::DropForeignKey.new(actual_table.name, foreign_key.name) },
         
     | 
| 
      
 135 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
      
 136 
     | 
    
         
            +
                        [
         
     | 
| 
      
 137 
     | 
    
         
            +
                          Operations::DropForeignKey.new(actual_table.name, actual.name),
         
     | 
| 
      
 138 
     | 
    
         
            +
                          Operations::CreateForeignKey.new(actual_table.name, desired)
         
     | 
| 
      
 139 
     | 
    
         
            +
                        ]
         
     | 
| 
      
 140 
     | 
    
         
            +
                      end
         
     | 
| 
      
 141 
     | 
    
         
            +
                    )
         
     | 
| 
      
 142 
     | 
    
         
            +
                  end
         
     | 
| 
      
 143 
     | 
    
         
            +
             
     | 
| 
      
 144 
     | 
    
         
            +
                  def enum_changes(desired_schema, actual_schema)
         
     | 
| 
      
 145 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 146 
     | 
    
         
            +
                      desired_schema.enums,
         
     | 
| 
      
 147 
     | 
    
         
            +
                      actual_schema.enums,
         
     | 
| 
      
 148 
     | 
    
         
            +
                      create: -> (enum) { Operations::CreateEnum.new(enum) },
         
     | 
| 
      
 149 
     | 
    
         
            +
                      drop:   -> (enum) { Operations::DropEnum.new(enum.name) },
         
     | 
| 
      
 150 
     | 
    
         
            +
                      change: -> (desired, actual) do
         
     | 
| 
       55 
151 
     | 
    
         
             
                        fields = actual_schema.tables.flat_map do |table|
         
     | 
| 
       56 
152 
     | 
    
         
             
                          table.fields.select do |field|
         
     | 
| 
       57 
153 
     | 
    
         
             
                            if field.array?
         
     | 
| 
       58 
     | 
    
         
            -
                              field.attributes[:element_type] ==  
     | 
| 
      
 154 
     | 
    
         
            +
                              field.attributes[:element_type] == actual.name
         
     | 
| 
       59 
155 
     | 
    
         
             
                            else
         
     | 
| 
       60 
     | 
    
         
            -
                              field.type ==  
     | 
| 
      
 156 
     | 
    
         
            +
                              field.type == actual.name
         
     | 
| 
       61 
157 
     | 
    
         
             
                            end
         
     | 
| 
       62 
158 
     | 
    
         
             
                          end.map do |field|
         
     | 
| 
       63 
159 
     | 
    
         
             
                            if desired_field = desired_schema[table.name][field.name]
         
     | 
| 
         @@ -73,115 +169,33 @@ module DbSchema 
     | 
|
| 
       73 
169 
     | 
    
         
             
                          end
         
     | 
| 
       74 
170 
     | 
    
         
             
                        end
         
     | 
| 
       75 
171 
     | 
    
         | 
| 
       76 
     | 
    
         
            -
                         
     | 
| 
       77 
     | 
    
         
            -
                      end
         
     | 
| 
       78 
     | 
    
         
            -
                    end
         
     | 
| 
       79 
     | 
    
         
            -
             
     | 
| 
       80 
     | 
    
         
            -
                    extension_changes = (desired_schema.extensions - actual_schema.extensions).map do |extension|
         
     | 
| 
       81 
     | 
    
         
            -
                      Operations::CreateExtension.new(extension)
         
     | 
| 
       82 
     | 
    
         
            -
                    end + (actual_schema.extensions - desired_schema.extensions).map do |extension|
         
     | 
| 
       83 
     | 
    
         
            -
                      Operations::DropExtension.new(extension.name)
         
     | 
| 
       84 
     | 
    
         
            -
                    end
         
     | 
| 
       85 
     | 
    
         
            -
             
     | 
| 
       86 
     | 
    
         
            -
                    sort_all_changes(table_changes + enum_changes + extension_changes)
         
     | 
| 
       87 
     | 
    
         
            -
                  end
         
     | 
| 
       88 
     | 
    
         
            -
             
     | 
| 
       89 
     | 
    
         
            -
                private
         
     | 
| 
       90 
     | 
    
         
            -
                  def field_changes(desired_fields, actual_fields)
         
     | 
| 
       91 
     | 
    
         
            -
                    field_names = [desired_fields, actual_fields].flatten.map(&:name).uniq
         
     | 
| 
       92 
     | 
    
         
            -
             
     | 
| 
       93 
     | 
    
         
            -
                    field_names.each.with_object([]) do |field_name, table_changes|
         
     | 
| 
       94 
     | 
    
         
            -
                      desired = desired_fields.find { |field| field.name == field_name }
         
     | 
| 
       95 
     | 
    
         
            -
                      actual  = actual_fields.find  { |field| field.name == field_name }
         
     | 
| 
       96 
     | 
    
         
            -
             
     | 
| 
       97 
     | 
    
         
            -
                      if desired && !actual
         
     | 
| 
       98 
     | 
    
         
            -
                        table_changes << Operations::CreateColumn.new(desired)
         
     | 
| 
       99 
     | 
    
         
            -
                      elsif actual && !desired
         
     | 
| 
       100 
     | 
    
         
            -
                        table_changes << Operations::DropColumn.new(field_name)
         
     | 
| 
       101 
     | 
    
         
            -
                      elsif actual != desired
         
     | 
| 
       102 
     | 
    
         
            -
                        if (actual.type != desired.type) || (actual.attributes != desired.attributes)
         
     | 
| 
       103 
     | 
    
         
            -
                          table_changes << Operations::AlterColumnType.new(
         
     | 
| 
       104 
     | 
    
         
            -
                            field_name,
         
     | 
| 
       105 
     | 
    
         
            -
                            new_type: desired.type,
         
     | 
| 
       106 
     | 
    
         
            -
                            **desired.attributes
         
     | 
| 
       107 
     | 
    
         
            -
                          )
         
     | 
| 
       108 
     | 
    
         
            -
                        end
         
     | 
| 
       109 
     | 
    
         
            -
             
     | 
| 
       110 
     | 
    
         
            -
                        if desired.primary_key? && !actual.primary_key?
         
     | 
| 
       111 
     | 
    
         
            -
                          table_changes << Operations::CreatePrimaryKey.new(field_name)
         
     | 
| 
       112 
     | 
    
         
            -
                        end
         
     | 
| 
       113 
     | 
    
         
            -
             
     | 
| 
       114 
     | 
    
         
            -
                        if actual.primary_key? && !desired.primary_key?
         
     | 
| 
       115 
     | 
    
         
            -
                          table_changes << Operations::DropPrimaryKey.new(field_name)
         
     | 
| 
       116 
     | 
    
         
            -
                        end
         
     | 
| 
       117 
     | 
    
         
            -
             
     | 
| 
       118 
     | 
    
         
            -
                        if desired.null? && !actual.null?
         
     | 
| 
       119 
     | 
    
         
            -
                          table_changes << Operations::AllowNull.new(field_name)
         
     | 
| 
       120 
     | 
    
         
            -
                        end
         
     | 
| 
       121 
     | 
    
         
            -
             
     | 
| 
       122 
     | 
    
         
            -
                        if actual.null? && !desired.null?
         
     | 
| 
       123 
     | 
    
         
            -
                          table_changes << Operations::DisallowNull.new(field_name)
         
     | 
| 
       124 
     | 
    
         
            -
                        end
         
     | 
| 
       125 
     | 
    
         
            -
             
     | 
| 
       126 
     | 
    
         
            -
                        if actual.default != desired.default
         
     | 
| 
       127 
     | 
    
         
            -
                          table_changes << Operations::AlterColumnDefault.new(field_name, new_default: desired.default)
         
     | 
| 
       128 
     | 
    
         
            -
                        end
         
     | 
| 
       129 
     | 
    
         
            -
                      end
         
     | 
| 
       130 
     | 
    
         
            -
                    end
         
     | 
| 
       131 
     | 
    
         
            -
                  end
         
     | 
| 
       132 
     | 
    
         
            -
             
     | 
| 
       133 
     | 
    
         
            -
                  def index_changes(desired_indices, actual_indices)
         
     | 
| 
       134 
     | 
    
         
            -
                    index_names = [desired_indices, actual_indices].flatten.map(&:name).uniq
         
     | 
| 
       135 
     | 
    
         
            -
             
     | 
| 
       136 
     | 
    
         
            -
                    index_names.each.with_object([]) do |index_name, table_changes|
         
     | 
| 
       137 
     | 
    
         
            -
                      desired = desired_indices.find { |index| index.name == index_name }
         
     | 
| 
       138 
     | 
    
         
            -
                      actual  = actual_indices.find  { |index| index.name == index_name }
         
     | 
| 
       139 
     | 
    
         
            -
             
     | 
| 
       140 
     | 
    
         
            -
                      if desired && !actual
         
     | 
| 
       141 
     | 
    
         
            -
                        table_changes << Operations::CreateIndex.new(desired)
         
     | 
| 
       142 
     | 
    
         
            -
                      elsif actual && !desired
         
     | 
| 
       143 
     | 
    
         
            -
                        table_changes << Operations::DropIndex.new(index_name)
         
     | 
| 
       144 
     | 
    
         
            -
                      elsif actual != desired
         
     | 
| 
       145 
     | 
    
         
            -
                        table_changes << Operations::DropIndex.new(index_name)
         
     | 
| 
       146 
     | 
    
         
            -
                        table_changes << Operations::CreateIndex.new(desired)
         
     | 
| 
      
 172 
     | 
    
         
            +
                        Operations::AlterEnumValues.new(actual.name, desired.values, fields)
         
     | 
| 
       147 
173 
     | 
    
         
             
                      end
         
     | 
| 
       148 
     | 
    
         
            -
                     
     | 
| 
      
 174 
     | 
    
         
            +
                    )
         
     | 
| 
       149 
175 
     | 
    
         
             
                  end
         
     | 
| 
       150 
176 
     | 
    
         | 
| 
       151 
     | 
    
         
            -
                  def  
     | 
| 
       152 
     | 
    
         
            -
                     
     | 
| 
       153 
     | 
    
         
            -
             
     | 
| 
       154 
     | 
    
         
            -
             
     | 
| 
       155 
     | 
    
         
            -
                       
     | 
| 
       156 
     | 
    
         
            -
                       
     | 
| 
       157 
     | 
    
         
            -
             
     | 
| 
       158 
     | 
    
         
            -
                      if desired && !actual
         
     | 
| 
       159 
     | 
    
         
            -
                        table_changes << Operations::CreateCheckConstraint.new(desired)
         
     | 
| 
       160 
     | 
    
         
            -
                      elsif actual && !desired
         
     | 
| 
       161 
     | 
    
         
            -
                        table_changes << Operations::DropCheckConstraint.new(check_name)
         
     | 
| 
       162 
     | 
    
         
            -
                      elsif actual != desired
         
     | 
| 
       163 
     | 
    
         
            -
                        table_changes << Operations::DropCheckConstraint.new(check_name)
         
     | 
| 
       164 
     | 
    
         
            -
                        table_changes << Operations::CreateCheckConstraint.new(desired)
         
     | 
| 
       165 
     | 
    
         
            -
                      end
         
     | 
| 
       166 
     | 
    
         
            -
                    end
         
     | 
| 
      
 177 
     | 
    
         
            +
                  def extension_changes(desired_schema, actual_schema)
         
     | 
| 
      
 178 
     | 
    
         
            +
                    compare_collections(
         
     | 
| 
      
 179 
     | 
    
         
            +
                      desired_schema.extensions,
         
     | 
| 
      
 180 
     | 
    
         
            +
                      actual_schema.extensions,
         
     | 
| 
      
 181 
     | 
    
         
            +
                      create: -> (extension) { Operations::CreateExtension.new(extension) },
         
     | 
| 
      
 182 
     | 
    
         
            +
                      drop:   -> (extension) { Operations::DropExtension.new(extension.name) }
         
     | 
| 
      
 183 
     | 
    
         
            +
                    )
         
     | 
| 
       167 
184 
     | 
    
         
             
                  end
         
     | 
| 
       168 
185 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
                  def  
     | 
| 
       170 
     | 
    
         
            -
                     
     | 
| 
       171 
     | 
    
         
            -
             
     | 
| 
       172 
     | 
    
         
            -
             
     | 
| 
       173 
     | 
    
         
            -
             
     | 
| 
       174 
     | 
    
         
            -
                       
     | 
| 
       175 
     | 
    
         
            -
             
     | 
| 
       176 
     | 
    
         
            -
                       
     | 
| 
       177 
     | 
    
         
            -
                         
     | 
| 
       178 
     | 
    
         
            -
                      elsif  
     | 
| 
       179 
     | 
    
         
            -
                         
     | 
| 
       180 
     | 
    
         
            -
                      elsif actual != desired
         
     | 
| 
       181 
     | 
    
         
            -
                        table_changes << Operations::DropForeignKey.new(table_name, key_name)
         
     | 
| 
       182 
     | 
    
         
            -
                        table_changes << Operations::CreateForeignKey.new(table_name, desired)
         
     | 
| 
      
 186 
     | 
    
         
            +
                  def compare_collections(desired, actual, create:, drop:, change: -> (*) {})
         
     | 
| 
      
 187 
     | 
    
         
            +
                    desired_hash = Utils.to_hash(desired, :name)
         
     | 
| 
      
 188 
     | 
    
         
            +
                    actual_hash  = Utils.to_hash(actual, :name)
         
     | 
| 
      
 189 
     | 
    
         
            +
             
     | 
| 
      
 190 
     | 
    
         
            +
                    (desired_hash.keys + actual_hash.keys).uniq.flat_map do |name|
         
     | 
| 
      
 191 
     | 
    
         
            +
                      if desired_hash.key?(name) && !actual_hash.key?(name)
         
     | 
| 
      
 192 
     | 
    
         
            +
                        create.(desired_hash[name])
         
     | 
| 
      
 193 
     | 
    
         
            +
                      elsif actual_hash.key?(name) && !desired_hash.key?(name)
         
     | 
| 
      
 194 
     | 
    
         
            +
                        drop.(actual_hash[name])
         
     | 
| 
      
 195 
     | 
    
         
            +
                      elsif actual_hash[name] != desired_hash[name]
         
     | 
| 
      
 196 
     | 
    
         
            +
                        change.(desired_hash[name], actual_hash[name])
         
     | 
| 
       183 
197 
     | 
    
         
             
                      end
         
     | 
| 
       184 
     | 
    
         
            -
                    end
         
     | 
| 
      
 198 
     | 
    
         
            +
                    end.compact
         
     | 
| 
       185 
199 
     | 
    
         
             
                  end
         
     | 
| 
       186 
200 
     | 
    
         | 
| 
       187 
201 
     | 
    
         
             
                  def sort_all_changes(changes)
         
     | 
| 
         @@ -64,6 +64,10 @@ module DbSchema 
     | 
|
| 
       64 
64 
     | 
    
         
             
                      Field.build(name, type, **options, primary_key: primary_key?, attr_name => attr_value)
         
     | 
| 
       65 
65 
     | 
    
         
             
                    end
         
     | 
| 
       66 
66 
     | 
    
         | 
| 
      
 67 
     | 
    
         
            +
                    def with_default(new_default)
         
     | 
| 
      
 68 
     | 
    
         
            +
                      Field.build(name, type, **options, primary_key: primary_key?, default: new_default)
         
     | 
| 
      
 69 
     | 
    
         
            +
                    end
         
     | 
| 
      
 70 
     | 
    
         
            +
             
     | 
| 
       67 
71 
     | 
    
         
             
                    class << self
         
     | 
| 
       68 
72 
     | 
    
         
             
                      def register(*types)
         
     | 
| 
       69 
73 
     | 
    
         
             
                        types.each do |type|
         
     | 
| 
         @@ -41,6 +41,16 @@ module DbSchema 
     | 
|
| 
       41 
41 
     | 
    
         
             
                      condition: condition
         
     | 
| 
       42 
42 
     | 
    
         
             
                    )
         
     | 
| 
       43 
43 
     | 
    
         
             
                  end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                  def with_condition(new_condition)
         
     | 
| 
      
 46 
     | 
    
         
            +
                    Index.new(
         
     | 
| 
      
 47 
     | 
    
         
            +
                      name:      name,
         
     | 
| 
      
 48 
     | 
    
         
            +
                      columns:   columns,
         
     | 
| 
      
 49 
     | 
    
         
            +
                      unique:    unique?,
         
     | 
| 
      
 50 
     | 
    
         
            +
                      type:      type,
         
     | 
| 
      
 51 
     | 
    
         
            +
                      condition: new_condition
         
     | 
| 
      
 52 
     | 
    
         
            +
                    )
         
     | 
| 
      
 53 
     | 
    
         
            +
                  end
         
     | 
| 
       44 
54 
     | 
    
         
             
                end
         
     | 
| 
       45 
55 
     | 
    
         | 
| 
       46 
56 
     | 
    
         
             
                class NullIndex < Index
         
     | 
| 
         @@ -1,20 +1,20 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            module DbSchema
         
     | 
| 
       2 
2 
     | 
    
         
             
              module Definitions
         
     | 
| 
       3 
3 
     | 
    
         
             
                class Table
         
     | 
| 
       4 
     | 
    
         
            -
                  include Dry::Equalizer(:name, :fields, : 
     | 
| 
       5 
     | 
    
         
            -
                  attr_reader :name, :fields, : 
     | 
| 
      
 4 
     | 
    
         
            +
                  include Dry::Equalizer(:name, :fields, :indexes, :checks, :foreign_keys)
         
     | 
| 
      
 5 
     | 
    
         
            +
                  attr_reader :name, :fields, :indexes, :checks, :foreign_keys
         
     | 
| 
       6 
6 
     | 
    
         | 
| 
       7 
     | 
    
         
            -
                  def initialize(name, fields: [],  
     | 
| 
      
 7 
     | 
    
         
            +
                  def initialize(name, fields: [], indexes: [], checks: [], foreign_keys: [])
         
     | 
| 
       8 
8 
     | 
    
         
             
                    @name         = name.to_sym
         
     | 
| 
       9 
9 
     | 
    
         
             
                    @fields       = fields
         
     | 
| 
       10 
     | 
    
         
            -
                    @ 
     | 
| 
      
 10 
     | 
    
         
            +
                    @indexes      = indexes
         
     | 
| 
       11 
11 
     | 
    
         
             
                    @checks       = checks
         
     | 
| 
       12 
12 
     | 
    
         
             
                    @foreign_keys = foreign_keys
         
     | 
| 
       13 
13 
     | 
    
         
             
                  end
         
     | 
| 
       14 
14 
     | 
    
         | 
| 
       15 
15 
     | 
    
         
             
                  def has_expressions?
         
     | 
| 
       16 
16 
     | 
    
         
             
                    fields.any?(&:default_is_expression?) ||
         
     | 
| 
       17 
     | 
    
         
            -
                       
     | 
| 
      
 17 
     | 
    
         
            +
                      indexes.any?(&:has_expressions?) ||
         
     | 
| 
       18 
18 
     | 
    
         
             
                      checks.any?
         
     | 
| 
       19 
19 
     | 
    
         
             
                  end
         
     | 
| 
       20 
20 
     | 
    
         | 
| 
         @@ -28,7 +28,7 @@ module DbSchema 
     | 
|
| 
       28 
28 
     | 
    
         
             
                  end
         
     | 
| 
       29 
29 
     | 
    
         | 
| 
       30 
30 
     | 
    
         
             
                  def index(index_name)
         
     | 
| 
       31 
     | 
    
         
            -
                     
     | 
| 
      
 31 
     | 
    
         
            +
                    indexes.find { |index| index.name == index_name } || NullIndex.new
         
     | 
| 
       32 
32 
     | 
    
         
             
                  end
         
     | 
| 
       33 
33 
     | 
    
         | 
| 
       34 
34 
     | 
    
         
             
                  def has_index?(index_name)
         
     | 
| 
         @@ -36,13 +36,13 @@ module DbSchema 
     | 
|
| 
       36 
36 
     | 
    
         
             
                  end
         
     | 
| 
       37 
37 
     | 
    
         | 
| 
       38 
38 
     | 
    
         
             
                  def has_index_on?(*field_names)
         
     | 
| 
       39 
     | 
    
         
            -
                     
     | 
| 
      
 39 
     | 
    
         
            +
                    indexes.any? do |index|
         
     | 
| 
       40 
40 
     | 
    
         
             
                      index.columns.none?(&:expression?) && index.columns.map(&:name) == field_names
         
     | 
| 
       41 
41 
     | 
    
         
             
                    end
         
     | 
| 
       42 
42 
     | 
    
         
             
                  end
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
44 
     | 
    
         
             
                  def has_unique_index_on?(*field_names)
         
     | 
| 
       45 
     | 
    
         
            -
                     
     | 
| 
      
 45 
     | 
    
         
            +
                    indexes.any? do |index|
         
     | 
| 
       46 
46 
     | 
    
         
             
                      index.unique? && index.columns.none?(&:expression?) && index.columns.map(&:name) == field_names
         
     | 
| 
       47 
47 
     | 
    
         
             
                    end
         
     | 
| 
       48 
48 
     | 
    
         
             
                  end
         
     | 
| 
         @@ -71,7 +71,7 @@ module DbSchema 
     | 
|
| 
       71 
71 
     | 
    
         
             
                    Table.new(
         
     | 
| 
       72 
72 
     | 
    
         
             
                      new_name,
         
     | 
| 
       73 
73 
     | 
    
         
             
                      fields:       fields,
         
     | 
| 
       74 
     | 
    
         
            -
                       
     | 
| 
      
 74 
     | 
    
         
            +
                      indexes:      indexes,
         
     | 
| 
       75 
75 
     | 
    
         
             
                      checks:       checks,
         
     | 
| 
       76 
76 
     | 
    
         
             
                      foreign_keys: foreign_keys
         
     | 
| 
       77 
77 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -81,27 +81,37 @@ module DbSchema 
     | 
|
| 
       81 
81 
     | 
    
         
             
                    Table.new(
         
     | 
| 
       82 
82 
     | 
    
         
             
                      name,
         
     | 
| 
       83 
83 
     | 
    
         
             
                      fields:       new_fields,
         
     | 
| 
       84 
     | 
    
         
            -
                       
     | 
| 
      
 84 
     | 
    
         
            +
                      indexes:      indexes,
         
     | 
| 
       85 
85 
     | 
    
         
             
                      checks:       checks,
         
     | 
| 
       86 
86 
     | 
    
         
             
                      foreign_keys: foreign_keys
         
     | 
| 
       87 
87 
     | 
    
         
             
                    )
         
     | 
| 
       88 
88 
     | 
    
         
             
                  end
         
     | 
| 
       89 
89 
     | 
    
         | 
| 
       90 
     | 
    
         
            -
                  def  
     | 
| 
      
 90 
     | 
    
         
            +
                  def with_indexes(new_indexes)
         
     | 
| 
       91 
91 
     | 
    
         
             
                    Table.new(
         
     | 
| 
       92 
92 
     | 
    
         
             
                      name,
         
     | 
| 
       93 
93 
     | 
    
         
             
                      fields:       fields,
         
     | 
| 
       94 
     | 
    
         
            -
                       
     | 
| 
      
 94 
     | 
    
         
            +
                      indexes:      new_indexes,
         
     | 
| 
       95 
95 
     | 
    
         
             
                      checks:       checks,
         
     | 
| 
       96 
96 
     | 
    
         
             
                      foreign_keys: foreign_keys
         
     | 
| 
       97 
97 
     | 
    
         
             
                    )
         
     | 
| 
       98 
98 
     | 
    
         
             
                  end
         
     | 
| 
       99 
99 
     | 
    
         | 
| 
      
 100 
     | 
    
         
            +
                  def with_checks(new_checks)
         
     | 
| 
      
 101 
     | 
    
         
            +
                    Table.new(
         
     | 
| 
      
 102 
     | 
    
         
            +
                      name,
         
     | 
| 
      
 103 
     | 
    
         
            +
                      fields:       fields,
         
     | 
| 
      
 104 
     | 
    
         
            +
                      indexes:      indexes,
         
     | 
| 
      
 105 
     | 
    
         
            +
                      checks:       new_checks,
         
     | 
| 
      
 106 
     | 
    
         
            +
                      foreign_keys: foreign_keys
         
     | 
| 
      
 107 
     | 
    
         
            +
                    )
         
     | 
| 
      
 108 
     | 
    
         
            +
                  end
         
     | 
| 
      
 109 
     | 
    
         
            +
             
     | 
| 
       100 
110 
     | 
    
         
             
                  def with_foreign_keys(new_foreign_keys)
         
     | 
| 
       101 
111 
     | 
    
         
             
                    Table.new(
         
     | 
| 
       102 
112 
     | 
    
         
             
                      name,
         
     | 
| 
       103 
113 
     | 
    
         
             
                      fields:       fields,
         
     | 
| 
       104 
     | 
    
         
            -
                       
     | 
| 
      
 114 
     | 
    
         
            +
                      indexes:      indexes,
         
     | 
| 
       105 
115 
     | 
    
         
             
                      checks:       checks,
         
     | 
| 
       106 
116 
     | 
    
         
             
                      foreign_keys: new_foreign_keys
         
     | 
| 
       107 
117 
     | 
    
         
             
                    )
         
     | 
| 
         @@ -111,7 +121,7 @@ module DbSchema 
     | 
|
| 
       111 
121 
     | 
    
         
             
                class NullTable < Table
         
     | 
| 
       112 
122 
     | 
    
         
             
                  def initialize
         
     | 
| 
       113 
123 
     | 
    
         
             
                    @fields       = []
         
     | 
| 
       114 
     | 
    
         
            -
                    @ 
     | 
| 
      
 124 
     | 
    
         
            +
                    @indexes      = []
         
     | 
| 
       115 
125 
     | 
    
         
             
                    @checks       = []
         
     | 
| 
       116 
126 
     | 
    
         
             
                    @foreign_keys = []
         
     | 
| 
       117 
127 
     | 
    
         
             
                  end
         
     | 
    
        data/lib/db_schema/dsl.rb
    CHANGED
    
    | 
         @@ -17,7 +17,7 @@ module DbSchema 
     | 
|
| 
       17 
17 
     | 
    
         
             
                  @schema.tables << Definitions::Table.new(
         
     | 
| 
       18 
18 
     | 
    
         
             
                    name,
         
     | 
| 
       19 
19 
     | 
    
         
             
                    fields:       table_yielder.fields,
         
     | 
| 
       20 
     | 
    
         
            -
                     
     | 
| 
      
 20 
     | 
    
         
            +
                    indexes:      table_yielder.indexes,
         
     | 
| 
       21 
21 
     | 
    
         
             
                    checks:       table_yielder.checks,
         
     | 
| 
       22 
22 
     | 
    
         
             
                    foreign_keys: table_yielder.foreign_keys
         
     | 
| 
       23 
23 
     | 
    
         
             
                  )
         
     | 
| 
         @@ -64,7 +64,7 @@ module DbSchema 
     | 
|
| 
       64 
64 
     | 
    
         
             
                  end
         
     | 
| 
       65 
65 
     | 
    
         | 
| 
       66 
66 
     | 
    
         
             
                  def index(*columns, **index_options)
         
     | 
| 
       67 
     | 
    
         
            -
                     
     | 
| 
      
 67 
     | 
    
         
            +
                    indexes << TableYielder.build_index(
         
     | 
| 
       68 
68 
     | 
    
         
             
                      columns,
         
     | 
| 
       69 
69 
     | 
    
         
             
                      table_name: table_name,
         
     | 
| 
       70 
70 
     | 
    
         
             
                      **index_options
         
     | 
| 
         @@ -105,8 +105,8 @@ module DbSchema 
     | 
|
| 
       105 
105 
     | 
    
         
             
                    @fields ||= []
         
     | 
| 
       106 
106 
     | 
    
         
             
                  end
         
     | 
| 
       107 
107 
     | 
    
         | 
| 
       108 
     | 
    
         
            -
                  def  
     | 
| 
       109 
     | 
    
         
            -
                    @ 
     | 
| 
      
 108 
     | 
    
         
            +
                  def indexes
         
     | 
| 
      
 109 
     | 
    
         
            +
                    @indexes ||= []
         
     | 
| 
       110 
110 
     | 
    
         
             
                  end
         
     | 
| 
       111 
111 
     | 
    
         | 
| 
       112 
112 
     | 
    
         
             
                  def checks
         
     | 
    
        data/lib/db_schema/migrator.rb
    CHANGED
    
    
    
        data/lib/db_schema/normalizer.rb
    CHANGED
    
    | 
         @@ -16,7 +16,7 @@ module DbSchema 
     | 
|
| 
       16 
16 
     | 
    
         | 
| 
       17 
17 
     | 
    
         
             
                    schema.tables = schema.tables.map do |table|
         
     | 
| 
       18 
18 
     | 
    
         
             
                      if table.has_expressions?
         
     | 
| 
       19 
     | 
    
         
            -
                        Table.new(table, hash, connection).normalized_table
         
     | 
| 
      
 19 
     | 
    
         
            +
                        Table.new(table, hash, schema.enums.map(&:name), connection).normalized_table
         
     | 
| 
       20 
20 
     | 
    
         
             
                      else
         
     | 
| 
       21 
21 
     | 
    
         
             
                        table
         
     | 
| 
       22 
22 
     | 
    
         
             
                      end
         
     | 
| 
         @@ -50,7 +50,7 @@ module DbSchema 
     | 
|
| 
       50 
50 
     | 
    
         
             
                def hash
         
     | 
| 
       51 
51 
     | 
    
         
             
                  @hash ||= begin
         
     | 
| 
       52 
52 
     | 
    
         
             
                    names = schema.tables.flat_map do |table|
         
     | 
| 
       53 
     | 
    
         
            -
                      [table.name] + table.fields.map(&:name) + table. 
     | 
| 
      
 53 
     | 
    
         
            +
                      [table.name] + table.fields.map(&:name) + table.indexes.map(&:name) + table.checks.map(&:name)
         
     | 
| 
       54 
54 
     | 
    
         
             
                    end
         
     | 
| 
       55 
55 
     | 
    
         | 
| 
       56 
56 
     | 
    
         
             
                    Digest::MD5.hexdigest(names.join(','))[0..9]
         
     | 
| 
         @@ -58,11 +58,12 @@ module DbSchema 
     | 
|
| 
       58 
58 
     | 
    
         
             
                end
         
     | 
| 
       59 
59 
     | 
    
         | 
| 
       60 
60 
     | 
    
         
             
                class Table
         
     | 
| 
       61 
     | 
    
         
            -
                  attr_reader :table, :hash, :connection
         
     | 
| 
      
 61 
     | 
    
         
            +
                  attr_reader :table, :hash, :enum_names, :connection
         
     | 
| 
       62 
62 
     | 
    
         | 
| 
       63 
     | 
    
         
            -
                  def initialize(table, hash, connection)
         
     | 
| 
      
 63 
     | 
    
         
            +
                  def initialize(table, hash, enum_names, connection)
         
     | 
| 
       64 
64 
     | 
    
         
             
                    @table      = table
         
     | 
| 
       65 
65 
     | 
    
         
             
                    @hash       = hash
         
     | 
| 
      
 66 
     | 
    
         
            +
                    @enum_names = enum_names
         
     | 
| 
       66 
67 
     | 
    
         
             
                    @connection = connection
         
     | 
| 
       67 
68 
     | 
    
         
             
                  end
         
     | 
| 
       68 
69 
     | 
    
         | 
| 
         @@ -76,7 +77,8 @@ module DbSchema 
     | 
|
| 
       76 
77 
     | 
    
         
             
                    operation = Operations::CreateTable.new(
         
     | 
| 
       77 
78 
     | 
    
         
             
                      table.with_name(temporary_table_name)
         
     | 
| 
       78 
79 
     | 
    
         
             
                        .with_fields(rename_types(table.fields))
         
     | 
| 
       79 
     | 
    
         
            -
                        . 
     | 
| 
      
 80 
     | 
    
         
            +
                        .with_indexes(rename_indexes(table.indexes))
         
     | 
| 
      
 81 
     | 
    
         
            +
                        .with_checks(rename_types_in_checks(table.checks))
         
     | 
| 
       80 
82 
     | 
    
         
             
                    )
         
     | 
| 
       81 
83 
     | 
    
         | 
| 
       82 
84 
     | 
    
         
             
                    Runner.new([operation], connection).run!
         
     | 
| 
         @@ -87,43 +89,72 @@ module DbSchema 
     | 
|
| 
       87 
89 
     | 
    
         | 
| 
       88 
90 
     | 
    
         
             
                    temporary_table.with_name(table.name)
         
     | 
| 
       89 
91 
     | 
    
         
             
                      .with_fields(rename_types_back(temporary_table.fields))
         
     | 
| 
       90 
     | 
    
         
            -
                      . 
     | 
| 
      
 92 
     | 
    
         
            +
                      .with_indexes(rename_indexes_back(temporary_table.indexes))
         
     | 
| 
      
 93 
     | 
    
         
            +
                      .with_checks(rename_types_in_checks_back(temporary_table.checks))
         
     | 
| 
       91 
94 
     | 
    
         
             
                      .with_foreign_keys(table.foreign_keys)
         
     | 
| 
       92 
95 
     | 
    
         
             
                  end
         
     | 
| 
       93 
96 
     | 
    
         | 
| 
       94 
97 
     | 
    
         
             
                  def rename_types(fields)
         
     | 
| 
       95 
98 
     | 
    
         
             
                    fields.map do |field|
         
     | 
| 
      
 99 
     | 
    
         
            +
                      new_default = if field.default_is_expression?
         
     | 
| 
      
 100 
     | 
    
         
            +
                        rename_all_types_in(field.default.to_s).to_sym
         
     | 
| 
      
 101 
     | 
    
         
            +
                      else
         
     | 
| 
      
 102 
     | 
    
         
            +
                        field.default
         
     | 
| 
      
 103 
     | 
    
         
            +
                      end
         
     | 
| 
      
 104 
     | 
    
         
            +
             
     | 
| 
       96 
105 
     | 
    
         
             
                      if field.custom?
         
     | 
| 
       97 
106 
     | 
    
         
             
                        field.with_type(append_hash(field.type))
         
     | 
| 
       98 
107 
     | 
    
         
             
                      elsif field.array? && field.custom_element_type?
         
     | 
| 
       99 
108 
     | 
    
         
             
                        field.with_attribute(:element_type, append_hash(field.element_type.type).to_sym)
         
     | 
| 
       100 
109 
     | 
    
         
             
                      else
         
     | 
| 
       101 
110 
     | 
    
         
             
                        field
         
     | 
| 
       102 
     | 
    
         
            -
                      end
         
     | 
| 
      
 111 
     | 
    
         
            +
                      end.with_default(new_default)
         
     | 
| 
       103 
112 
     | 
    
         
             
                    end
         
     | 
| 
       104 
113 
     | 
    
         
             
                  end
         
     | 
| 
       105 
114 
     | 
    
         | 
| 
       106 
115 
     | 
    
         
             
                  def rename_types_back(fields)
         
     | 
| 
       107 
116 
     | 
    
         
             
                    fields.map do |field|
         
     | 
| 
      
 117 
     | 
    
         
            +
                      new_default = if field.default_is_expression?
         
     | 
| 
      
 118 
     | 
    
         
            +
                        rename_all_types_back_in(field.default.to_s).to_sym
         
     | 
| 
      
 119 
     | 
    
         
            +
                      else
         
     | 
| 
      
 120 
     | 
    
         
            +
                        field.default
         
     | 
| 
      
 121 
     | 
    
         
            +
                      end
         
     | 
| 
      
 122 
     | 
    
         
            +
             
     | 
| 
       108 
123 
     | 
    
         
             
                      if field.custom?
         
     | 
| 
       109 
124 
     | 
    
         
             
                        field.with_type(remove_hash(field.type))
         
     | 
| 
       110 
125 
     | 
    
         
             
                      elsif field.array? && field.custom_element_type?
         
     | 
| 
       111 
126 
     | 
    
         
             
                        field.with_attribute(:element_type, remove_hash(field.element_type.type).to_sym)
         
     | 
| 
       112 
127 
     | 
    
         
             
                      else
         
     | 
| 
       113 
128 
     | 
    
         
             
                        field
         
     | 
| 
       114 
     | 
    
         
            -
                      end
         
     | 
| 
      
 129 
     | 
    
         
            +
                      end.with_default(new_default)
         
     | 
| 
      
 130 
     | 
    
         
            +
                    end
         
     | 
| 
      
 131 
     | 
    
         
            +
                  end
         
     | 
| 
      
 132 
     | 
    
         
            +
             
     | 
| 
      
 133 
     | 
    
         
            +
                  def rename_indexes(indexes)
         
     | 
| 
      
 134 
     | 
    
         
            +
                    indexes.map do |index|
         
     | 
| 
      
 135 
     | 
    
         
            +
                      index
         
     | 
| 
      
 136 
     | 
    
         
            +
                        .with_name(append_hash(index.name))
         
     | 
| 
      
 137 
     | 
    
         
            +
                        .with_condition(rename_all_types_in(index.condition))
         
     | 
| 
       115 
138 
     | 
    
         
             
                    end
         
     | 
| 
       116 
139 
     | 
    
         
             
                  end
         
     | 
| 
       117 
140 
     | 
    
         | 
| 
       118 
     | 
    
         
            -
                  def  
     | 
| 
       119 
     | 
    
         
            -
                     
     | 
| 
       120 
     | 
    
         
            -
                      index 
     | 
| 
      
 141 
     | 
    
         
            +
                  def rename_indexes_back(indexes)
         
     | 
| 
      
 142 
     | 
    
         
            +
                    indexes.map do |index|
         
     | 
| 
      
 143 
     | 
    
         
            +
                      index
         
     | 
| 
      
 144 
     | 
    
         
            +
                        .with_name(remove_hash(index.name))
         
     | 
| 
      
 145 
     | 
    
         
            +
                        .with_condition(rename_all_types_back_in(index.condition))
         
     | 
| 
       121 
146 
     | 
    
         
             
                    end
         
     | 
| 
       122 
147 
     | 
    
         
             
                  end
         
     | 
| 
       123 
148 
     | 
    
         | 
| 
       124 
     | 
    
         
            -
                  def  
     | 
| 
       125 
     | 
    
         
            -
                     
     | 
| 
       126 
     | 
    
         
            -
                       
     | 
| 
      
 149 
     | 
    
         
            +
                  def rename_types_in_checks(checks)
         
     | 
| 
      
 150 
     | 
    
         
            +
                    checks.map do |check|
         
     | 
| 
      
 151 
     | 
    
         
            +
                      check.with_condition(rename_all_types_in(check.condition))
         
     | 
| 
      
 152 
     | 
    
         
            +
                    end
         
     | 
| 
      
 153 
     | 
    
         
            +
                  end
         
     | 
| 
      
 154 
     | 
    
         
            +
             
     | 
| 
      
 155 
     | 
    
         
            +
                  def rename_types_in_checks_back(checks)
         
     | 
| 
      
 156 
     | 
    
         
            +
                    checks.map do |check|
         
     | 
| 
      
 157 
     | 
    
         
            +
                      check.with_condition(rename_all_types_back_in(check.condition))
         
     | 
| 
       127 
158 
     | 
    
         
             
                    end
         
     | 
| 
       128 
159 
     | 
    
         
             
                  end
         
     | 
| 
       129 
160 
     | 
    
         | 
| 
         @@ -138,6 +169,28 @@ module DbSchema 
     | 
|
| 
       138 
169 
     | 
    
         
             
                  def remove_hash(name)
         
     | 
| 
       139 
170 
     | 
    
         
             
                    name.to_s.sub(/_#{Regexp.escape(hash)}$/, '').to_sym
         
     | 
| 
       140 
171 
     | 
    
         
             
                  end
         
     | 
| 
      
 172 
     | 
    
         
            +
             
     | 
| 
      
 173 
     | 
    
         
            +
                  def rename_all_types_in(string)
         
     | 
| 
      
 174 
     | 
    
         
            +
                    return string unless string.is_a?(String)
         
     | 
| 
      
 175 
     | 
    
         
            +
             
     | 
| 
      
 176 
     | 
    
         
            +
                    enum_renaming.reduce(string) do |new_string, (from, to)|
         
     | 
| 
      
 177 
     | 
    
         
            +
                      new_string.gsub(from, to)
         
     | 
| 
      
 178 
     | 
    
         
            +
                    end
         
     | 
| 
      
 179 
     | 
    
         
            +
                  end
         
     | 
| 
      
 180 
     | 
    
         
            +
             
     | 
| 
      
 181 
     | 
    
         
            +
                  def rename_all_types_back_in(string)
         
     | 
| 
      
 182 
     | 
    
         
            +
                    return string unless string.is_a?(String)
         
     | 
| 
      
 183 
     | 
    
         
            +
             
     | 
| 
      
 184 
     | 
    
         
            +
                    enum_renaming.invert.reduce(string) do |new_string, (from, to)|
         
     | 
| 
      
 185 
     | 
    
         
            +
                      new_string.gsub(from, to)
         
     | 
| 
      
 186 
     | 
    
         
            +
                    end
         
     | 
| 
      
 187 
     | 
    
         
            +
                  end
         
     | 
| 
      
 188 
     | 
    
         
            +
             
     | 
| 
      
 189 
     | 
    
         
            +
                  def enum_renaming
         
     | 
| 
      
 190 
     | 
    
         
            +
                    enum_names.reduce({}) do |hash, enum_name|
         
     | 
| 
      
 191 
     | 
    
         
            +
                      hash.merge("::#{enum_name}" => "::#{append_hash(enum_name)}")
         
     | 
| 
      
 192 
     | 
    
         
            +
                    end
         
     | 
| 
      
 193 
     | 
    
         
            +
                  end
         
     | 
| 
       141 
194 
     | 
    
         
             
                end
         
     | 
| 
       142 
195 
     | 
    
         
             
              end
         
     | 
| 
       143 
196 
     | 
    
         
             
            end
         
     | 
    
        data/lib/db_schema/reader.rb
    CHANGED
    
    | 
         @@ -78,7 +78,7 @@ SELECT conname AS name, 
     | 
|
| 
       78 
78 
     | 
    
         
             
               AND contype = 'c'
         
     | 
| 
       79 
79 
     | 
    
         
             
                  SQL
         
     | 
| 
       80 
80 
     | 
    
         | 
| 
       81 
     | 
    
         
            -
                   
     | 
| 
      
 81 
     | 
    
         
            +
                  INDEXES_QUERY = <<-SQL.freeze
         
     | 
| 
       82 
82 
     | 
    
         
             
               SELECT relname AS name,
         
     | 
| 
       83 
83 
     | 
    
         
             
                      indkey AS column_positions,
         
     | 
| 
       84 
84 
     | 
    
         
             
                      indisunique AS unique,
         
     | 
| 
         @@ -102,7 +102,7 @@ LEFT JOIN pg_am 
     | 
|
| 
       102 
102 
     | 
    
         
             
              GROUP BY name, column_positions, indisunique, index_options, condition, index_type, index_oid
         
     | 
| 
       103 
103 
     | 
    
         
             
                  SQL
         
     | 
| 
       104 
104 
     | 
    
         | 
| 
       105 
     | 
    
         
            -
                   
     | 
| 
      
 105 
     | 
    
         
            +
                  EXPRESSION_INDEXES_QUERY = <<-SQL.freeze
         
     | 
| 
       106 
106 
     | 
    
         
             
                WITH index_ids AS (SELECT unnest(?) AS index_id),
         
     | 
| 
       107 
107 
     | 
    
         
             
                     elements AS (SELECT unnest(?) AS element)
         
     | 
| 
       108 
108 
     | 
    
         
             
              SELECT index_id,
         
     | 
| 
         @@ -142,7 +142,7 @@ SELECT extname 
     | 
|
| 
       142 
142 
     | 
    
         
             
                        build_field(column_data, primary_key: column_data[:name] == primary_key_name)
         
     | 
| 
       143 
143 
     | 
    
         
             
                      end
         
     | 
| 
       144 
144 
     | 
    
         | 
| 
       145 
     | 
    
         
            -
                       
     | 
| 
      
 145 
     | 
    
         
            +
                      indexes = indexes_data_for(table_name, connection).map do |index_data|
         
     | 
| 
       146 
146 
     | 
    
         
             
                        Definitions::Index.new(index_data)
         
     | 
| 
       147 
147 
     | 
    
         
             
                      end.sort_by(&:name)
         
     | 
| 
       148 
148 
     | 
    
         | 
| 
         @@ -160,21 +160,21 @@ SELECT extname 
     | 
|
| 
       160 
160 
     | 
    
         
             
                      Definitions::Table.new(
         
     | 
| 
       161 
161 
     | 
    
         
             
                        table_name,
         
     | 
| 
       162 
162 
     | 
    
         
             
                        fields:       fields,
         
     | 
| 
       163 
     | 
    
         
            -
                         
     | 
| 
      
 163 
     | 
    
         
            +
                        indexes:      indexes,
         
     | 
| 
       164 
164 
     | 
    
         
             
                        checks:       checks,
         
     | 
| 
       165 
165 
     | 
    
         
             
                        foreign_keys: foreign_keys
         
     | 
| 
       166 
166 
     | 
    
         
             
                      )
         
     | 
| 
       167 
167 
     | 
    
         
             
                    end
         
     | 
| 
       168 
168 
     | 
    
         | 
| 
       169 
     | 
    
         
            -
                    def  
     | 
| 
      
 169 
     | 
    
         
            +
                    def indexes_data_for(table_name, connection)
         
     | 
| 
       170 
170 
     | 
    
         
             
                      column_names = connection[COLUMN_NAMES_QUERY, table_name.to_s].reduce({}) do |names, column|
         
     | 
| 
       171 
171 
     | 
    
         
             
                        names.merge(column[:pos] => column[:name].to_sym)
         
     | 
| 
       172 
172 
     | 
    
         
             
                      end
         
     | 
| 
       173 
173 
     | 
    
         | 
| 
       174 
     | 
    
         
            -
                       
     | 
| 
       175 
     | 
    
         
            -
                      expressions_data = index_expressions_data( 
     | 
| 
      
 174 
     | 
    
         
            +
                      indexes_data     = connection[INDEXES_QUERY, table_name.to_s].to_a
         
     | 
| 
      
 175 
     | 
    
         
            +
                      expressions_data = index_expressions_data(indexes_data, connection)
         
     | 
| 
       176 
176 
     | 
    
         | 
| 
       177 
     | 
    
         
            -
                       
     | 
| 
      
 177 
     | 
    
         
            +
                      indexes_data.map do |index|
         
     | 
| 
       178 
178 
     | 
    
         
             
                        positions = index[:column_positions].split(' ').map(&:to_i)
         
     | 
| 
       179 
179 
     | 
    
         
             
                        options   = index[:index_options].split(' ').map(&:to_i)
         
     | 
| 
       180 
180 
     | 
    
         | 
| 
         @@ -227,10 +227,10 @@ SELECT extname 
     | 
|
| 
       227 
227 
     | 
    
         
             
                    end
         
     | 
| 
       228 
228 
     | 
    
         | 
| 
       229 
229 
     | 
    
         
             
                  private
         
     | 
| 
       230 
     | 
    
         
            -
                    def index_expressions_data( 
     | 
| 
      
 230 
     | 
    
         
            +
                    def index_expressions_data(indexes_data, connection)
         
     | 
| 
       231 
231 
     | 
    
         
             
                      all_positions, max_position = {}, 0
         
     | 
| 
       232 
232 
     | 
    
         | 
| 
       233 
     | 
    
         
            -
                       
     | 
| 
      
 233 
     | 
    
         
            +
                      indexes_data.each do |index_data|
         
     | 
| 
       234 
234 
     | 
    
         
             
                        positions = index_data[:column_positions].split(' ').map(&:to_i)
         
     | 
| 
       235 
235 
     | 
    
         
             
                        expression_positions = positions.each_index.select { |i| positions[i].zero? }
         
     | 
| 
       236 
236 
     | 
    
         | 
| 
         @@ -242,7 +242,7 @@ SELECT extname 
     | 
|
| 
       242 
242 
     | 
    
         | 
| 
       243 
243 
     | 
    
         
             
                      if all_positions.any?
         
     | 
| 
       244 
244 
     | 
    
         
             
                        connection[
         
     | 
| 
       245 
     | 
    
         
            -
                           
     | 
| 
      
 245 
     | 
    
         
            +
                          EXPRESSION_INDEXES_QUERY,
         
     | 
| 
       246 
246 
     | 
    
         
             
                          Sequel.pg_array(all_positions.keys),
         
     | 
| 
       247 
247 
     | 
    
         
             
                          Sequel.pg_array((1..max_position.succ).to_a)
         
     | 
| 
       248 
248 
     | 
    
         
             
                        ].each_with_object({}) do |index_data, indexes_data|
         
     | 
    
        data/lib/db_schema/runner.rb
    CHANGED
    
    
    
        data/lib/db_schema/utils.rb
    CHANGED
    
    | 
         @@ -44,6 +44,12 @@ module DbSchema 
     | 
|
| 
       44 
44 
     | 
    
         
             
                    end
         
     | 
| 
       45 
45 
     | 
    
         
             
                  end
         
     | 
| 
       46 
46 
     | 
    
         | 
| 
      
 47 
     | 
    
         
            +
                  def to_hash(array, attribute)
         
     | 
| 
      
 48 
     | 
    
         
            +
                    array.reduce({}) do |hash, object|
         
     | 
| 
      
 49 
     | 
    
         
            +
                      hash.merge(object.public_send(attribute) => object)
         
     | 
| 
      
 50 
     | 
    
         
            +
                    end
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
       47 
53 
     | 
    
         
             
                  def sort_by_class(array, sorted_classes)
         
     | 
| 
       48 
54 
     | 
    
         
             
                    sorted_classes.flat_map do |klass|
         
     | 
| 
       49 
55 
     | 
    
         
             
                      array.select { |object| object.is_a?(klass) }
         
     | 
    
        data/lib/db_schema/validator.rb
    CHANGED
    
    | 
         @@ -41,7 +41,7 @@ module DbSchema 
     | 
|
| 
       41 
41 
     | 
    
         | 
| 
       42 
42 
     | 
    
         
             
                      field_names = table.fields.map(&:name)
         
     | 
| 
       43 
43 
     | 
    
         | 
| 
       44 
     | 
    
         
            -
                      table. 
     | 
| 
      
 44 
     | 
    
         
            +
                      table.indexes.each do |index|
         
     | 
| 
       45 
45 
     | 
    
         
             
                        index.columns.reject(&:expression?).map(&:name).each do |field_name|
         
     | 
| 
       46 
46 
     | 
    
         
             
                          unless field_names.include?(field_name)
         
     | 
| 
       47 
47 
     | 
    
         
             
                            error_message = %(Index "#{index.name}" refers to a missing field "#{table.name}.#{field_name}")
         
     | 
    
        data/lib/db_schema/version.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | 
         @@ -1,14 +1,14 @@ 
     | 
|
| 
       1 
1 
     | 
    
         
             
            --- !ruby/object:Gem::Specification
         
     | 
| 
       2 
2 
     | 
    
         
             
            name: db_schema
         
     | 
| 
       3 
3 
     | 
    
         
             
            version: !ruby/object:Gem::Version
         
     | 
| 
       4 
     | 
    
         
            -
              version:  
     | 
| 
      
 4 
     | 
    
         
            +
              version: 0.3.1
         
     | 
| 
       5 
5 
     | 
    
         
             
            platform: ruby
         
     | 
| 
       6 
6 
     | 
    
         
             
            authors:
         
     | 
| 
       7 
7 
     | 
    
         
             
            - Vsevolod Romashov
         
     | 
| 
       8 
8 
     | 
    
         
             
            autorequire: 
         
     | 
| 
       9 
9 
     | 
    
         
             
            bindir: exe
         
     | 
| 
       10 
10 
     | 
    
         
             
            cert_chain: []
         
     | 
| 
       11 
     | 
    
         
            -
            date: 2017- 
     | 
| 
      
 11 
     | 
    
         
            +
            date: 2017-11-25 00:00:00.000000000 Z
         
     | 
| 
       12 
12 
     | 
    
         
             
            dependencies:
         
     | 
| 
       13 
13 
     | 
    
         
             
            - !ruby/object:Gem::Dependency
         
     | 
| 
       14 
14 
     | 
    
         
             
              name: sequel
         
     | 
| 
         @@ -234,7 +234,7 @@ files: 
     | 
|
| 
       234 
234 
     | 
    
         
             
            - lib/db_schema/utils.rb
         
     | 
| 
       235 
235 
     | 
    
         
             
            - lib/db_schema/validator.rb
         
     | 
| 
       236 
236 
     | 
    
         
             
            - lib/db_schema/version.rb
         
     | 
| 
       237 
     | 
    
         
            -
            homepage: https://github.com/ 
     | 
| 
      
 237 
     | 
    
         
            +
            homepage: https://github.com/db-schema/core
         
     | 
| 
       238 
238 
     | 
    
         
             
            licenses:
         
     | 
| 
       239 
239 
     | 
    
         
             
            - MIT
         
     | 
| 
       240 
240 
     | 
    
         
             
            metadata: {}
         
     |