activerecord-import 0.15.0 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.travis.yml +4 -1
- data/CHANGELOG.md +23 -0
- data/Gemfile +1 -0
- data/benchmarks/README +2 -2
- data/lib/activerecord-import/adapters/abstract_adapter.rb +4 -10
- data/lib/activerecord-import/adapters/mysql_adapter.rb +14 -5
- data/lib/activerecord-import/adapters/postgresql_adapter.rb +21 -9
- data/lib/activerecord-import/adapters/sqlite3_adapter.rb +22 -6
- data/lib/activerecord-import/import.rb +56 -30
- data/lib/activerecord-import/version.rb +1 -1
- data/test/import_test.rb +115 -19
- data/test/models/alarm.rb +2 -0
- data/test/models/dictionary.rb +2 -0
- data/test/models/vendor.rb +7 -0
- data/test/models/widget.rb +16 -0
- data/test/postgis/import_test.rb +4 -0
- data/test/schema/generic_schema.rb +14 -2
- data/test/schema/jdbcpostgresql_schema.rb +1 -0
- data/test/schema/postgis_schema.rb +1 -0
- data/test/schema/postgresql_schema.rb +41 -0
- data/test/sqlite3/import_test.rb +15 -0
- data/test/support/factories.rb +1 -0
- data/test/support/mysql/import_examples.rb +1 -0
- data/test/support/postgresql/import_examples.rb +91 -0
- data/test/support/shared_examples/on_duplicate_key_ignore.rb +25 -0
- data/test/support/shared_examples/recursive_import.rb +25 -7
- data/test/test_helper.rb +2 -0
- metadata +17 -4
    
        checksums.yaml
    CHANGED
    
    | @@ -1,7 +1,7 @@ | |
| 1 1 | 
             
            ---
         | 
| 2 2 | 
             
            SHA1:
         | 
| 3 | 
            -
              metadata.gz:  | 
| 4 | 
            -
              data.tar.gz:  | 
| 3 | 
            +
              metadata.gz: 64717eaa9cb0c49761ad29e6aa358c87e96b7166
         | 
| 4 | 
            +
              data.tar.gz: 879ac9c11ee4a9a62988c97608ad195a1052893d
         | 
| 5 5 | 
             
            SHA512:
         | 
| 6 | 
            -
              metadata.gz:  | 
| 7 | 
            -
              data.tar.gz:  | 
| 6 | 
            +
              metadata.gz: 7e06cfe24bc207291c4a4c8737f8c3d6309218ccde2ca3246089f45d7f36c85f6fa7a63abe5758934dd6c9e939422c40395880b7ba950e10db7b96fa38b071af
         | 
| 7 | 
            +
              data.tar.gz: 6ce0fc279753ab340269eefa2d64be18c40d2bed279f3cda27ba4057cb428582a207c5637da5a7c5222cffa77d2a43a0030578c87ebe05f76c81ad098da3fa21
         | 
    
        data/.travis.yml
    CHANGED
    
    | @@ -27,10 +27,13 @@ matrix: | |
| 27 27 | 
             
            before_script:
         | 
| 28 28 | 
             
              - mysql -e 'create database activerecord_import_test;'
         | 
| 29 29 | 
             
              - psql -c 'create database activerecord_import_test;' -U postgres
         | 
| 30 | 
            -
              - psql  | 
| 30 | 
            +
              - psql activerecord_import_test -c 'create extension if not exists hstore;' -U postgres
         | 
| 31 | 
            +
              - psql -c 'create extension if not exists postgis;' -U postgres
         | 
| 32 | 
            +
              - psql -c 'create extension if not exists "uuid-ossp";' -U postgres
         | 
| 31 33 | 
             
              - cp test/travis/database.yml test/database.yml
         | 
| 32 34 |  | 
| 33 35 | 
             
            addons:
         | 
| 36 | 
            +
              postgresql: "9.4"
         | 
| 34 37 | 
             
              apt:
         | 
| 35 38 | 
             
                sources:
         | 
| 36 39 | 
             
                  - travis-ci/sqlite3
         | 
    
        data/CHANGELOG.md
    CHANGED
    
    | @@ -1,3 +1,26 @@ | |
| 1 | 
            +
            ## Changes in 0.16.0
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            ### New Features
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            * Add partial index upsert support for PostgreSQL. Thanks to @luislew via \#305
         | 
| 6 | 
            +
            * Add UUID primary key support for PostgreSQL. Thanks to @jkowens via
         | 
| 7 | 
            +
              \#312
         | 
| 8 | 
            +
            * Add store accessor support for JSON, JSON, and HSTORE data types.
         | 
| 9 | 
            +
              Thanks to @jkowens via \#322
         | 
| 10 | 
            +
            * Log warning if database does not support :on_duplicate_key_update.
         | 
| 11 | 
            +
              Thanks to @jkowens vi \#324
         | 
| 12 | 
            +
            * Add option :on_duplicate_key_ignore for MySQL and SQLite. Thanks to
         | 
| 13 | 
            +
              @jkowens via \#326
         | 
| 14 | 
            +
             | 
| 15 | 
            +
            ### Fixes
         | 
| 16 | 
            +
             | 
| 17 | 
            +
            * Fixes issue with recursive import using same primary key for all models.
         | 
| 18 | 
            +
              Thanks to @chopraanmol1 via \#309
         | 
| 19 | 
            +
            * Fixes issue importing from STI subclass with polymorphic associations.
         | 
| 20 | 
            +
              Thanks to @JNajera via \#314
         | 
| 21 | 
            +
            * Fixes issue setting returned IDs to wrong models when some fail validation. Also fixes issue with SQLite returning wrong IDs. Thanks to @mizukami234 via \#315
         | 
| 22 | 
            +
             | 
| 23 | 
            +
             | 
| 1 24 | 
             
            ## Changes in 0.15.0
         | 
| 2 25 |  | 
| 3 26 | 
             
            ### New Features
         | 
    
        data/Gemfile
    CHANGED
    
    
    
        data/benchmarks/README
    CHANGED
    
    | @@ -15,10 +15,10 @@ See "ruby benchmark.rb -h" for the complete listing of options. | |
| 15 15 | 
             
            EXAMPLES
         | 
| 16 16 | 
             
            --------
         | 
| 17 17 | 
             
            To output to html format:
         | 
| 18 | 
            -
              ruby benchmark.rb --adapter= | 
| 18 | 
            +
              ruby benchmark.rb --adapter=mysql2 --to-html=results.html
         | 
| 19 19 |  | 
| 20 20 | 
             
            To output to csv format:
         | 
| 21 | 
            -
              ruby benchmark.rb --adapter= | 
| 21 | 
            +
              ruby benchmark.rb --adapter=mysql2 --to-csv=results.csv
         | 
| 22 22 |  | 
| 23 23 | 
             
            LIMITATIONS
         | 
| 24 24 | 
             
            -----------
         | 
| @@ -23,7 +23,6 @@ module ActiveRecord::Import::AbstractAdapter | |
| 23 23 | 
             
                  sql = []
         | 
| 24 24 | 
             
                  sql << options[:pre_sql] if options[:pre_sql]
         | 
| 25 25 | 
             
                  sql << options[:command] if options[:command]
         | 
| 26 | 
            -
                  sql << "IGNORE" if options[:ignore]
         | 
| 27 26 |  | 
| 28 27 | 
             
                  # add keywords like IGNORE or DELAYED
         | 
| 29 28 | 
             
                  if options[:keywords].is_a?(Array)
         | 
| @@ -45,15 +44,10 @@ module ActiveRecord::Import::AbstractAdapter | |
| 45 44 | 
             
                def post_sql_statements( table_name, options ) # :nodoc:
         | 
| 46 45 | 
             
                  post_sql_statements = []
         | 
| 47 46 |  | 
| 48 | 
            -
                  if supports_on_duplicate_key_update?
         | 
| 49 | 
            -
                     | 
| 50 | 
            -
             | 
| 51 | 
            -
             | 
| 52 | 
            -
                        post_sql_statements << sql_for_on_duplicate_key_ignore( table_name, options[:on_duplicate_key_ignore] )
         | 
| 53 | 
            -
                      end
         | 
| 54 | 
            -
                    elsif options[:on_duplicate_key_update]
         | 
| 55 | 
            -
                      post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] )
         | 
| 56 | 
            -
                    end
         | 
| 47 | 
            +
                  if supports_on_duplicate_key_update? && options[:on_duplicate_key_update]
         | 
| 48 | 
            +
                    post_sql_statements << sql_for_on_duplicate_key_update( table_name, options[:on_duplicate_key_update] )
         | 
| 49 | 
            +
                  elsif options[:on_duplicate_key_update]
         | 
| 50 | 
            +
                    logger.warn "Ignoring on_duplicate_key_update because it is not supported by the database."
         | 
| 57 51 | 
             
                  end
         | 
| 58 52 |  | 
| 59 53 | 
             
                  # custom user post_sql
         | 
| @@ -39,10 +39,13 @@ module ActiveRecord::Import::MysqlAdapter | |
| 39 39 | 
             
                  value_sets = ::ActiveRecord::Import::ValueSetsBytesParser.parse(values,
         | 
| 40 40 | 
             
                    reserved_bytes: sql_size,
         | 
| 41 41 | 
             
                    max_bytes: max)
         | 
| 42 | 
            -
             | 
| 43 | 
            -
             | 
| 44 | 
            -
                     | 
| 45 | 
            -
             | 
| 42 | 
            +
             | 
| 43 | 
            +
                  transaction(requires_new: true) do
         | 
| 44 | 
            +
                    value_sets.each do |value_set|
         | 
| 45 | 
            +
                      number_of_inserts += 1
         | 
| 46 | 
            +
                      sql2insert = base_sql + value_set.join( ',' ) + post_sql
         | 
| 47 | 
            +
                      insert( sql2insert, *args )
         | 
| 48 | 
            +
                    end
         | 
| 46 49 | 
             
                  end
         | 
| 47 50 | 
             
                end
         | 
| 48 51 |  | 
| @@ -60,6 +63,12 @@ module ActiveRecord::Import::MysqlAdapter | |
| 60 63 | 
             
                end
         | 
| 61 64 | 
             
              end
         | 
| 62 65 |  | 
| 66 | 
            +
              def pre_sql_statements( options)
         | 
| 67 | 
            +
                sql = []
         | 
| 68 | 
            +
                sql << "IGNORE" if options[:ignore] || options[:on_duplicate_key_ignore]
         | 
| 69 | 
            +
                sql + super
         | 
| 70 | 
            +
              end
         | 
| 71 | 
            +
             | 
| 63 72 | 
             
              # Add a column to be updated on duplicate key update
         | 
| 64 73 | 
             
              def add_column_for_on_duplicate_key_update( column, options = {} ) # :nodoc:
         | 
| 65 74 | 
             
                if options.include?(:on_duplicate_key_update)
         | 
| @@ -68,7 +77,7 @@ module ActiveRecord::Import::MysqlAdapter | |
| 68 77 | 
             
                  when Array then columns << column.to_sym unless columns.include?(column.to_sym)
         | 
| 69 78 | 
             
                  when Hash then columns[column.to_sym] = column.to_sym
         | 
| 70 79 | 
             
                  end
         | 
| 71 | 
            -
                 | 
| 80 | 
            +
                elsif !options[:ignore] && !options[:on_duplicate_key_ignore]
         | 
| 72 81 | 
             
                  options[:on_duplicate_key_update] = [column.to_sym]
         | 
| 73 82 | 
             
                end
         | 
| 74 83 | 
             
              end
         | 
| @@ -31,11 +31,24 @@ module ActiveRecord::Import::PostgreSQLAdapter | |
| 31 31 | 
             
              end
         | 
| 32 32 |  | 
| 33 33 | 
             
              def post_sql_statements( table_name, options ) # :nodoc:
         | 
| 34 | 
            -
                 | 
| 35 | 
            -
             | 
| 36 | 
            -
                 | 
| 37 | 
            -
                   | 
| 34 | 
            +
                sql = []
         | 
| 35 | 
            +
             | 
| 36 | 
            +
                if supports_on_duplicate_key_update?
         | 
| 37 | 
            +
                  # Options :recursive and :on_duplicate_key_ignore are mutually exclusive
         | 
| 38 | 
            +
                  if (options[:ignore] || options[:on_duplicate_key_ignore]) && !options[:on_duplicate_key_update] && !options[:recursive]
         | 
| 39 | 
            +
                    sql << sql_for_on_duplicate_key_ignore( table_name, options[:on_duplicate_key_ignore] )
         | 
| 40 | 
            +
                  end
         | 
| 41 | 
            +
                elsif options[:on_duplicate_key_ignore] && !options[:on_duplicate_key_update]
         | 
| 42 | 
            +
                  logger.warn "Ignoring on_duplicate_key_ignore because it is not supported by the database."
         | 
| 43 | 
            +
                end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
                sql += super(table_name, options)
         | 
| 46 | 
            +
             | 
| 47 | 
            +
                unless options[:no_returning] || options[:primary_key].blank?
         | 
| 48 | 
            +
                  sql << "RETURNING #{options[:primary_key]}"
         | 
| 38 49 | 
             
                end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                sql
         | 
| 39 52 | 
             
              end
         | 
| 40 53 |  | 
| 41 54 | 
             
              # Add a column to be updated on duplicate key update
         | 
| @@ -113,10 +126,13 @@ module ActiveRecord::Import::PostgreSQLAdapter | |
| 113 126 | 
             
              def sql_for_conflict_target( args = {} )
         | 
| 114 127 | 
             
                constraint_name = args[:constraint_name]
         | 
| 115 128 | 
             
                conflict_target = args[:conflict_target]
         | 
| 129 | 
            +
                index_predicate = args[:index_predicate]
         | 
| 116 130 | 
             
                if constraint_name.present?
         | 
| 117 131 | 
             
                  "ON CONSTRAINT #{constraint_name} "
         | 
| 118 132 | 
             
                elsif conflict_target.present?
         | 
| 119 | 
            -
                  '(' << Array( conflict_target ).reject( &:empty? ).join( ', ' ) << ') '
         | 
| 133 | 
            +
                  '(' << Array( conflict_target ).reject( &:empty? ).join( ', ' ) << ') '.tap do |sql|
         | 
| 134 | 
            +
                    sql << "WHERE #{index_predicate} " if index_predicate
         | 
| 135 | 
            +
                  end
         | 
| 120 136 | 
             
                end
         | 
| 121 137 | 
             
              end
         | 
| 122 138 |  | 
| @@ -134,10 +150,6 @@ module ActiveRecord::Import::PostgreSQLAdapter | |
| 134 150 | 
             
                current_version >= MIN_VERSION_FOR_UPSERT
         | 
| 135 151 | 
             
              end
         | 
| 136 152 |  | 
| 137 | 
            -
              def supports_on_duplicate_key_ignore?(current_version = postgresql_version)
         | 
| 138 | 
            -
                supports_on_duplicate_key_update?(current_version)
         | 
| 139 | 
            -
              end
         | 
| 140 | 
            -
             | 
| 141 153 | 
             
              def support_setting_primary_key_of_imported_objects?
         | 
| 142 154 | 
             
                true
         | 
| 143 155 | 
             
              end
         | 
| @@ -30,21 +30,37 @@ module ActiveRecord::Import::SQLite3Adapter | |
| 30 30 | 
             
                value_sets = ::ActiveRecord::Import::ValueSetsRecordsParser.parse(values,
         | 
| 31 31 | 
             
                  max_records: SQLITE_LIMIT_COMPOUND_SELECT)
         | 
| 32 32 |  | 
| 33 | 
            -
                 | 
| 34 | 
            -
                   | 
| 35 | 
            -
             | 
| 36 | 
            -
             | 
| 37 | 
            -
             | 
| 38 | 
            -
             | 
| 33 | 
            +
                transaction(requires_new: true) do
         | 
| 34 | 
            +
                  value_sets.each do |value_set|
         | 
| 35 | 
            +
                    number_of_inserts += 1
         | 
| 36 | 
            +
                    sql2insert = base_sql + value_set.join( ',' ) + post_sql
         | 
| 37 | 
            +
                    last_insert_id = insert( sql2insert, *args )
         | 
| 38 | 
            +
                    first_insert_id = last_insert_id - affected_rows + 1
         | 
| 39 | 
            +
                    ids.concat((first_insert_id..last_insert_id).to_a)
         | 
| 40 | 
            +
                  end
         | 
| 39 41 | 
             
                end
         | 
| 40 42 |  | 
| 41 43 | 
             
                [number_of_inserts, ids]
         | 
| 42 44 | 
             
              end
         | 
| 43 45 |  | 
| 46 | 
            +
              def pre_sql_statements( options)
         | 
| 47 | 
            +
                sql = []
         | 
| 48 | 
            +
                # Options :recursive and :on_duplicate_key_ignore are mutually exclusive
         | 
| 49 | 
            +
                if (options[:ignore] || options[:on_duplicate_key_ignore]) && !options[:recursive]
         | 
| 50 | 
            +
                  sql << "OR IGNORE"
         | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
                sql + super
         | 
| 53 | 
            +
              end
         | 
| 54 | 
            +
             | 
| 44 55 | 
             
              def next_value_for_sequence(sequence_name)
         | 
| 45 56 | 
             
                %{nextval('#{sequence_name}')}
         | 
| 46 57 | 
             
              end
         | 
| 47 58 |  | 
| 59 | 
            +
              def affected_rows
         | 
| 60 | 
            +
                result = execute('SELECT changes();')
         | 
| 61 | 
            +
                result.first[0]
         | 
| 62 | 
            +
              end
         | 
| 63 | 
            +
             | 
| 48 64 | 
             
              def support_setting_primary_key_of_imported_objects?
         | 
| 49 65 | 
             
                true
         | 
| 50 66 | 
             
              end
         | 
| @@ -171,10 +171,11 @@ class ActiveRecord::Base | |
| 171 171 | 
             
                # == Options
         | 
| 172 172 | 
             
                # * +validate+ - true|false, tells import whether or not to use
         | 
| 173 173 | 
             
                #    ActiveRecord validations. Validations are enforced by default.
         | 
| 174 | 
            -
                # * +ignore+ - true|false,  | 
| 175 | 
            -
                # | 
| 176 | 
            -
                #  | 
| 177 | 
            -
                #     | 
| 174 | 
            +
                # * +ignore+ - true|false, an alias for on_duplicate_key_ignore.
         | 
| 175 | 
            +
                # * +on_duplicate_key_ignore+ - true|false, tells import to discard
         | 
| 176 | 
            +
                #    records that contain duplicate keys. For Postgres 9.5+ it adds
         | 
| 177 | 
            +
                #    ON CONFLICT DO NOTHING, for MySQL it uses INSERT IGNORE, and for
         | 
| 178 | 
            +
                #    SQLite it uses INSERT OR IGNORE. Cannot be enabled on a
         | 
| 178 179 | 
             
                #    recursive import.
         | 
| 179 180 | 
             
                # * +on_duplicate_key_update+ - an Array or Hash, tells import to
         | 
| 180 181 | 
             
                #    use MySQL's ON DUPLICATE KEY UPDATE or Postgres 9.5+ ON CONFLICT
         | 
| @@ -246,7 +247,8 @@ class ActiveRecord::Base | |
| 246 247 | 
             
                # == On Duplicate Key Update (Postgres 9.5+)
         | 
| 247 248 | 
             
                #
         | 
| 248 249 | 
             
                # The :on_duplicate_key_update option can be an Array or a Hash with up to
         | 
| 249 | 
            -
                #  | 
| 250 | 
            +
                # three attributes, :conflict_target (and optionally :index_predicate) or
         | 
| 251 | 
            +
                # :constraint_name, and :columns.
         | 
| 250 252 | 
             
                #
         | 
| 251 253 | 
             
                # ==== Using an Array
         | 
| 252 254 | 
             
                #
         | 
| @@ -260,10 +262,11 @@ class ActiveRecord::Base | |
| 260 262 | 
             
                #
         | 
| 261 263 | 
             
                # ====  Using a Hash
         | 
| 262 264 | 
             
                #
         | 
| 263 | 
            -
                # The : | 
| 264 | 
            -
                # :conflict_target  | 
| 265 | 
            -
                #  | 
| 266 | 
            -
                #  | 
| 265 | 
            +
                # The :on_duplicate_key_update option can be a hash with up to three
         | 
| 266 | 
            +
                # attributes, :conflict_target (and optionally :index_predicate) or
         | 
| 267 | 
            +
                # :constraint_name, and :columns. Unlike MySQL, Postgres requires the
         | 
| 268 | 
            +
                # conflicting constraint to be explicitly specified. Using this option
         | 
| 269 | 
            +
                # allows you to specify a constraint other than the primary key.
         | 
| 267 270 | 
             
                #
         | 
| 268 271 | 
             
                # ====== :conflict_target
         | 
| 269 272 | 
             
                #
         | 
| @@ -273,7 +276,16 @@ class ActiveRecord::Base | |
| 273 276 | 
             
                # but it is the preferred method of identifying a constraint. It will
         | 
| 274 277 | 
             
                # default to the primary key. Below is an example:
         | 
| 275 278 | 
             
                #
         | 
| 276 | 
            -
                #   BlogPost.import columns, values, on_duplicate_key_update: { conflict_target: [:author_id, :slug], columns: [ :date_modified ] }
         | 
| 279 | 
            +
                #   BlogPost.import columns, values, on_duplicate_key_update: { conflict_target: [ :author_id, :slug ], columns: [ :date_modified ] }
         | 
| 280 | 
            +
                #
         | 
| 281 | 
            +
                # ====== :index_predicate
         | 
| 282 | 
            +
                #
         | 
| 283 | 
            +
                # The :index_predicate attribute optionally specifies a WHERE condition
         | 
| 284 | 
            +
                # on :conflict_target, which is required for matching against partial
         | 
| 285 | 
            +
                # indexes. This attribute is ignored if :constraint_name is included.
         | 
| 286 | 
            +
                # Below is an example:
         | 
| 287 | 
            +
                #
         | 
| 288 | 
            +
                #   BlogPost.import columns, values, on_duplicate_key_update: { conflict_target: [ :author_id, :slug ], index_predicate: 'status <> 0', columns: [ :date_modified ] }
         | 
| 277 289 | 
             
                #
         | 
| 278 290 | 
             
                # ====== :constraint_name
         | 
| 279 291 | 
             
                #
         | 
| @@ -307,7 +319,7 @@ class ActiveRecord::Base | |
| 307 319 | 
             
                # This returns an object which responds to +failed_instances+ and +num_inserts+.
         | 
| 308 320 | 
             
                # * failed_instances - an array of objects that fails validation and were not committed to the database. An empty array if no validation is performed.
         | 
| 309 321 | 
             
                # * num_inserts - the number of insert statements it took to import the data
         | 
| 310 | 
            -
                # * ids - the primary keys of the imported ids | 
| 322 | 
            +
                # * ids - the primary keys of the imported ids if the adapter supports it, otherwise an empty array.
         | 
| 311 323 | 
             
                def import(*args)
         | 
| 312 324 | 
             
                  if args.first.is_a?( Array ) && args.first.first.is_a?(ActiveRecord::Base)
         | 
| 313 325 | 
             
                    options = {}
         | 
| @@ -332,11 +344,13 @@ class ActiveRecord::Base | |
| 332 344 | 
             
                end
         | 
| 333 345 |  | 
| 334 346 | 
             
                def import_helper( *args )
         | 
| 335 | 
            -
                  options = { validate: true, timestamps: true | 
| 347 | 
            +
                  options = { validate: true, timestamps: true }
         | 
| 336 348 | 
             
                  options.merge!( args.pop ) if args.last.is_a? Hash
         | 
| 349 | 
            +
                  # making sure that current model's primary key is used
         | 
| 350 | 
            +
                  options[:primary_key] = primary_key
         | 
| 337 351 |  | 
| 338 352 | 
             
                  # Don't modify incoming arguments
         | 
| 339 | 
            -
                  if options[:on_duplicate_key_update]
         | 
| 353 | 
            +
                  if options[:on_duplicate_key_update] && options[:on_duplicate_key_update].duplicable?
         | 
| 340 354 | 
             
                    options[:on_duplicate_key_update] = options[:on_duplicate_key_update].dup
         | 
| 341 355 | 
             
                  end
         | 
| 342 356 |  | 
| @@ -347,17 +361,27 @@ class ActiveRecord::Base | |
| 347 361 | 
             
                  if args.last.is_a?( Array ) && args.last.first.is_a?(ActiveRecord::Base)
         | 
| 348 362 | 
             
                    if args.length == 2
         | 
| 349 363 | 
             
                      models = args.last
         | 
| 350 | 
            -
                      column_names = args.first
         | 
| 364 | 
            +
                      column_names = args.first.dup
         | 
| 351 365 | 
             
                    else
         | 
| 352 366 | 
             
                      models = args.first
         | 
| 353 367 | 
             
                      column_names = self.column_names.dup
         | 
| 354 368 | 
             
                    end
         | 
| 355 369 |  | 
| 370 | 
            +
                    if column_names.include?(primary_key) && columns_hash[primary_key].type == :uuid
         | 
| 371 | 
            +
                      column_names.delete(primary_key)
         | 
| 372 | 
            +
                    end
         | 
| 373 | 
            +
             | 
| 374 | 
            +
                    stored_attrs = respond_to?(:stored_attributes) ? stored_attributes : {}
         | 
| 375 | 
            +
             | 
| 356 376 | 
             
                    array_of_attributes = models.map do |model|
         | 
| 357 377 | 
             
                      # this next line breaks sqlite.so with a segmentation fault
         | 
| 358 378 | 
             
                      # if model.new_record? || options[:on_duplicate_key_update]
         | 
| 359 379 | 
             
                      column_names.map do |name|
         | 
| 360 | 
            -
                         | 
| 380 | 
            +
                        if stored_attrs.any? && stored_attrs.key?(name.to_sym)
         | 
| 381 | 
            +
                          model.read_attribute(name.to_s)
         | 
| 382 | 
            +
                        else
         | 
| 383 | 
            +
                          model.read_attribute_before_type_cast(name.to_s)
         | 
| 384 | 
            +
                        end
         | 
| 361 385 | 
             
                      end
         | 
| 362 386 | 
             
                      # end
         | 
| 363 387 | 
             
                    end
         | 
| @@ -367,14 +391,14 @@ class ActiveRecord::Base | |
| 367 391 | 
             
                    # supports 2-element array and array
         | 
| 368 392 | 
             
                  elsif args.size == 2 && args.first.is_a?( Array ) && args.last.is_a?( Array )
         | 
| 369 393 | 
             
                    column_names, array_of_attributes = args
         | 
| 394 | 
            +
             | 
| 395 | 
            +
                    # dup the passed args so we don't modify unintentionally
         | 
| 396 | 
            +
                    column_names = column_names.dup
         | 
| 370 397 | 
             
                    array_of_attributes = array_of_attributes.map(&:dup)
         | 
| 371 398 | 
             
                  else
         | 
| 372 399 | 
             
                    raise ArgumentError, "Invalid arguments!"
         | 
| 373 400 | 
             
                  end
         | 
| 374 401 |  | 
| 375 | 
            -
                  # dup the passed in array so we don't modify it unintentionally
         | 
| 376 | 
            -
                  column_names = column_names.dup
         | 
| 377 | 
            -
             | 
| 378 402 | 
             
                  # Force the primary key col into the insert if it's not
         | 
| 379 403 | 
             
                  # on the list and we are using a sequence and stuff a nil
         | 
| 380 404 | 
             
                  # value for it into each row so the sequencer will fire later
         | 
| @@ -394,11 +418,7 @@ class ActiveRecord::Base | |
| 394 418 | 
             
                        models.each_with_index do |model, i|
         | 
| 395 419 | 
             
                          model = model.dup if options[:recursive]
         | 
| 396 420 | 
             
                          next if model.valid?(options[:validate_with_context])
         | 
| 397 | 
            -
                          if options[:raise_error] | 
| 398 | 
            -
                            model.send(:raise_validation_error)
         | 
| 399 | 
            -
                          elsif options[:raise_error] # Rails 3.2, 4.0, 4.1 and 4.2
         | 
| 400 | 
            -
                            model.send(:raise_record_invalid)
         | 
| 401 | 
            -
                          end
         | 
| 421 | 
            +
                          raise(ActiveRecord::RecordInvalid, model) if options[:raise_error]
         | 
| 402 422 | 
             
                          array_of_attributes[i] = nil
         | 
| 403 423 | 
             
                          failed << model
         | 
| 404 424 | 
             
                        end
         | 
| @@ -418,7 +438,7 @@ class ActiveRecord::Base | |
| 418 438 | 
             
                  return_obj.num_inserts = 0 if return_obj.num_inserts.nil?
         | 
| 419 439 |  | 
| 420 440 | 
             
                  # if we have ids, then set the id on the models and mark the models as clean.
         | 
| 421 | 
            -
                  if support_setting_primary_key_of_imported_objects?
         | 
| 441 | 
            +
                  if models && support_setting_primary_key_of_imported_objects?
         | 
| 422 442 | 
             
                    set_ids_and_mark_clean(models, return_obj)
         | 
| 423 443 |  | 
| 424 444 | 
             
                    # if there are auto-save associations on the models we imported that are new, import them as well
         | 
| @@ -502,7 +522,9 @@ class ActiveRecord::Base | |
| 502 522 | 
             
                  end
         | 
| 503 523 |  | 
| 504 524 | 
             
                  columns_sql = "(#{column_names.map { |name| connection.quote_column_name(name) }.join(',')})"
         | 
| 505 | 
            -
                   | 
| 525 | 
            +
                  pre_sql_statements = connection.pre_sql_statements( options )
         | 
| 526 | 
            +
                  insert_sql = ['INSERT', pre_sql_statements, "INTO #{quoted_table_name} #{columns_sql} VALUES "]
         | 
| 527 | 
            +
                  insert_sql = insert_sql.flatten.join(' ')
         | 
| 506 528 | 
             
                  values_sql = values_sql_for_columns_and_attributes(columns, array_of_attributes)
         | 
| 507 529 |  | 
| 508 530 | 
             
                  number_inserted = 0
         | 
| @@ -521,9 +543,11 @@ class ActiveRecord::Base | |
| 521 543 | 
             
                      ids += result[1]
         | 
| 522 544 | 
             
                    end
         | 
| 523 545 | 
             
                  else
         | 
| 524 | 
            -
                     | 
| 525 | 
            -
                       | 
| 526 | 
            -
             | 
| 546 | 
            +
                    transaction(requires_new: true) do
         | 
| 547 | 
            +
                      values_sql.each do |values|
         | 
| 548 | 
            +
                        ids << connection.insert(insert_sql + values)
         | 
| 549 | 
            +
                        number_inserted += 1
         | 
| 550 | 
            +
                      end
         | 
| 527 551 | 
             
                    end
         | 
| 528 552 | 
             
                  end
         | 
| 529 553 | 
             
                  [number_inserted, ids]
         | 
| @@ -533,9 +557,10 @@ class ActiveRecord::Base | |
| 533 557 |  | 
| 534 558 | 
             
                def set_ids_and_mark_clean(models, import_result)
         | 
| 535 559 | 
             
                  return if models.nil?
         | 
| 560 | 
            +
                  models -= import_result.failed_instances
         | 
| 536 561 | 
             
                  import_result.ids.each_with_index do |id, index|
         | 
| 537 562 | 
             
                    model = models[index]
         | 
| 538 | 
            -
                    model.id = id | 
| 563 | 
            +
                    model.id = id
         | 
| 539 564 | 
             
                    if model.respond_to?(:clear_changes_information) # Rails 4.0 and higher
         | 
| 540 565 | 
             
                      model.clear_changes_information
         | 
| 541 566 | 
             
                    else # Rails 3.2
         | 
| @@ -550,6 +575,7 @@ class ActiveRecord::Base | |
| 550 575 | 
             
                  # notes:
         | 
| 551 576 | 
             
                  #    does not handle associations that reference themselves
         | 
| 552 577 | 
             
                  #    should probably take a hash to associations to follow.
         | 
| 578 | 
            +
                  return if models.nil?
         | 
| 553 579 | 
             
                  associated_objects_by_class = {}
         | 
| 554 580 | 
             
                  models.each { |model| find_associated_objects_for_import(associated_objects_by_class, model) }
         | 
| 555 581 |  | 
| @@ -585,7 +611,7 @@ class ActiveRecord::Base | |
| 585 611 | 
             
                      child.public_send("#{association_reflection.foreign_key}=", model.id)
         | 
| 586 612 | 
             
                      # For polymorphic associations
         | 
| 587 613 | 
             
                      association_reflection.type.try do |type|
         | 
| 588 | 
            -
                        child.public_send("#{type}=", model.class.name)
         | 
| 614 | 
            +
                        child.public_send("#{type}=", model.class.base_class.name)
         | 
| 589 615 | 
             
                      end
         | 
| 590 616 | 
             
                    end
         | 
| 591 617 | 
             
                    associated_objects_by_class[model.class.name][association_reflection.name].concat changed_objects
         | 
    
        data/test/import_test.rb
    CHANGED
    
    | @@ -44,6 +44,25 @@ describe "#import" do | |
| 44 44 | 
             
                    end
         | 
| 45 45 | 
             
                  end
         | 
| 46 46 | 
             
                end
         | 
| 47 | 
            +
             | 
| 48 | 
            +
                context "that have no primary key" do
         | 
| 49 | 
            +
                  it "should import models successfully" do
         | 
| 50 | 
            +
                    assert_difference "Rule.count", +3 do
         | 
| 51 | 
            +
                      Rule.import Build(3, :rules)
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
                  end
         | 
| 54 | 
            +
                end
         | 
| 55 | 
            +
              end
         | 
| 56 | 
            +
             | 
| 57 | 
            +
              describe "with STI models" do
         | 
| 58 | 
            +
                it "should import models successfully" do
         | 
| 59 | 
            +
                  dictionaries = [Dictionary.new(author_name: "Noah Webster", title: "Webster's Dictionary")]
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                  assert_difference "Dictionary.count", +1 do
         | 
| 62 | 
            +
                    Dictionary.import dictionaries
         | 
| 63 | 
            +
                  end
         | 
| 64 | 
            +
                  assert_equal "Dictionary", Dictionary.first.type
         | 
| 65 | 
            +
                end
         | 
| 47 66 | 
             
              end
         | 
| 48 67 |  | 
| 49 68 | 
             
              context "with :validation option" do
         | 
| @@ -51,6 +70,8 @@ describe "#import" do | |
| 51 70 | 
             
                let(:valid_values) { [["LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
         | 
| 52 71 | 
             
                let(:valid_values_with_context) { [[1111, "Jerry Carter"], [2222, "Chad Fowler"]] }
         | 
| 53 72 | 
             
                let(:invalid_values) { [["The RSpec Book", ""], ["Agile+UX", ""]] }
         | 
| 73 | 
            +
                let(:valid_models) { valid_values.map { |title, author_name| Topic.new(title: title, author_name: author_name) } }
         | 
| 74 | 
            +
                let(:invalid_models) { invalid_values.map { |title, author_name| Topic.new(title: title, author_name: author_name) } }
         | 
| 54 75 |  | 
| 55 76 | 
             
                context "with validation checks turned off" do
         | 
| 56 77 | 
             
                  it "should import valid data" do
         | 
| @@ -103,6 +124,16 @@ describe "#import" do | |
| 103 124 | 
             
                    results.failed_instances.each { |e| assert_kind_of Topic, e }
         | 
| 104 125 | 
             
                  end
         | 
| 105 126 |  | 
| 127 | 
            +
                  it "should set ids in valid models if adapter supports setting primary key of imported objects" do
         | 
| 128 | 
            +
                    if ActiveRecord::Base.support_setting_primary_key_of_imported_objects?
         | 
| 129 | 
            +
                      Topic.import (invalid_models + valid_models), validate: true
         | 
| 130 | 
            +
                      assert_nil invalid_models[0].id
         | 
| 131 | 
            +
                      assert_nil invalid_models[1].id
         | 
| 132 | 
            +
                      assert_equal valid_models[0].id, Topic.all[0].id
         | 
| 133 | 
            +
                      assert_equal valid_models[1].id, Topic.all[1].id
         | 
| 134 | 
            +
                    end
         | 
| 135 | 
            +
                  end
         | 
| 136 | 
            +
             | 
| 106 137 | 
             
                  it "should import valid data when mixed with invalid data" do
         | 
| 107 138 | 
             
                    assert_difference "Topic.count", +2 do
         | 
| 108 139 | 
             
                      Topic.import columns, valid_values + invalid_values, validate: true
         | 
| @@ -112,6 +143,18 @@ describe "#import" do | |
| 112 143 | 
             
                end
         | 
| 113 144 | 
             
              end
         | 
| 114 145 |  | 
| 146 | 
            +
              context "without :validation option" do
         | 
| 147 | 
            +
                let(:columns) { %w(title author_name) }
         | 
| 148 | 
            +
                let(:invalid_values) { [["The RSpec Book", ""], ["Agile+UX", ""]] }
         | 
| 149 | 
            +
             | 
| 150 | 
            +
                it "should not import invalid data" do
         | 
| 151 | 
            +
                  assert_no_difference "Topic.count" do
         | 
| 152 | 
            +
                    result = Topic.import columns, invalid_values
         | 
| 153 | 
            +
                    assert_equal 2, result.failed_instances.size
         | 
| 154 | 
            +
                  end
         | 
| 155 | 
            +
                end
         | 
| 156 | 
            +
              end
         | 
| 157 | 
            +
             | 
| 115 158 | 
             
              context "with :all_or_none option" do
         | 
| 116 159 | 
             
                let(:columns) { %w(title author_name) }
         | 
| 117 160 | 
             
                let(:valid_values) { [["LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
         | 
| @@ -281,6 +324,12 @@ describe "#import" do | |
| 281 324 | 
             
                  Topic.import [:id, :author_name, :title], [[99, "Bob Jones", "Topic 99"]]
         | 
| 282 325 | 
             
                  assert_equal 99, Topic.last.id
         | 
| 283 326 | 
             
                end
         | 
| 327 | 
            +
             | 
| 328 | 
            +
                it "ignores the recursive option" do
         | 
| 329 | 
            +
                  assert_difference "Topic.count", +1 do
         | 
| 330 | 
            +
                    Topic.import [:author_name, :title], [["David Chelimsky", "The RSpec Book"]], recursive: true
         | 
| 331 | 
            +
                  end
         | 
| 332 | 
            +
                end
         | 
| 284 333 | 
             
              end
         | 
| 285 334 |  | 
| 286 335 | 
             
              context "ActiveRecord timestamps" do
         | 
| @@ -485,43 +534,90 @@ describe "#import" do | |
| 485 534 | 
             
              end
         | 
| 486 535 |  | 
| 487 536 | 
             
              describe "importing serialized fields" do
         | 
| 488 | 
            -
                it "imports values for serialized fields" do
         | 
| 537 | 
            +
                it "imports values for serialized Hash fields" do
         | 
| 489 538 | 
             
                  assert_difference "Widget.unscoped.count", +1 do
         | 
| 490 539 | 
             
                    Widget.import [:w_id, :data], [[1, { a: :b }]]
         | 
| 491 540 | 
             
                  end
         | 
| 492 541 | 
             
                  assert_equal({ a: :b }, Widget.find_by_w_id(1).data)
         | 
| 493 542 | 
             
                end
         | 
| 494 543 |  | 
| 495 | 
            -
                 | 
| 496 | 
            -
                   | 
| 497 | 
            -
             | 
| 498 | 
            -
             | 
| 499 | 
            -
             | 
| 544 | 
            +
                it "imports values for serialized fields" do
         | 
| 545 | 
            +
                  assert_difference "Widget.unscoped.count", +1 do
         | 
| 546 | 
            +
                    Widget.import [:w_id, :unspecified_data], [[1, { a: :b }]]
         | 
| 547 | 
            +
                  end
         | 
| 548 | 
            +
                  assert_equal({ a: :b }, Widget.find_by_w_id(1).unspecified_data)
         | 
| 549 | 
            +
                end
         | 
| 550 | 
            +
             | 
| 551 | 
            +
                it "imports values for custom coder" do
         | 
| 552 | 
            +
                  assert_difference "Widget.unscoped.count", +1 do
         | 
| 553 | 
            +
                    Widget.import [:w_id, :custom_data], [[1, { a: :b }]]
         | 
| 554 | 
            +
                  end
         | 
| 555 | 
            +
                  assert_equal({ a: :b }, Widget.find_by_w_id(1).custom_data)
         | 
| 556 | 
            +
                end
         | 
| 557 | 
            +
             | 
| 558 | 
            +
                let(:data) { { a: :b } }
         | 
| 559 | 
            +
                it "imports values for serialized JSON fields" do
         | 
| 560 | 
            +
                  assert_difference "Widget.unscoped.count", +1 do
         | 
| 561 | 
            +
                    Widget.import [:w_id, :json_data], [[9, data]]
         | 
| 562 | 
            +
                  end
         | 
| 563 | 
            +
                  assert_equal(data.as_json, Widget.find_by_w_id(9).json_data)
         | 
| 564 | 
            +
                end
         | 
| 565 | 
            +
             | 
| 566 | 
            +
                context "with a store" do
         | 
| 567 | 
            +
                  it "imports serialized attributes set using accessors" do
         | 
| 568 | 
            +
                    vendors = [Vendor.new(name: 'Vendor 1', color: 'blue')]
         | 
| 569 | 
            +
                    assert_difference "Vendor.count", +1 do
         | 
| 570 | 
            +
                      Vendor.import vendors
         | 
| 500 571 | 
             
                    end
         | 
| 501 | 
            -
                    assert_equal( | 
| 572 | 
            +
                    assert_equal('blue', Vendor.first.color)
         | 
| 502 573 | 
             
                  end
         | 
| 503 574 | 
             
                end
         | 
| 504 575 | 
             
              end
         | 
| 505 576 |  | 
| 506 577 | 
             
              describe "#import!" do
         | 
| 507 | 
            -
                 | 
| 508 | 
            -
             | 
| 509 | 
            -
             | 
| 578 | 
            +
                context "with an array of unsaved model instances" do
         | 
| 579 | 
            +
                  let(:topics) { Build(2, :topics) }
         | 
| 580 | 
            +
                  let(:invalid_topics) { Build(2, :invalid_topics) }
         | 
| 581 | 
            +
             | 
| 582 | 
            +
                  context "with invalid data" do
         | 
| 583 | 
            +
                    it "should raise ActiveRecord::RecordInvalid" do
         | 
| 584 | 
            +
                      assert_no_difference "Topic.count" do
         | 
| 585 | 
            +
                        assert_raise ActiveRecord::RecordInvalid do
         | 
| 586 | 
            +
                          Topic.import! invalid_topics
         | 
| 587 | 
            +
                        end
         | 
| 588 | 
            +
                      end
         | 
| 589 | 
            +
                    end
         | 
| 590 | 
            +
                  end
         | 
| 510 591 |  | 
| 511 | 
            -
             | 
| 512 | 
            -
             | 
| 513 | 
            -
             | 
| 514 | 
            -
             | 
| 515 | 
            -
                        Topic.import! columns, invalid_values
         | 
| 592 | 
            +
                  context "with valid data" do
         | 
| 593 | 
            +
                    it "should import data" do
         | 
| 594 | 
            +
                      assert_difference "Topic.count", +2 do
         | 
| 595 | 
            +
                        Topic.import! topics
         | 
| 516 596 | 
             
                      end
         | 
| 517 597 | 
             
                    end
         | 
| 518 598 | 
             
                  end
         | 
| 519 599 | 
             
                end
         | 
| 520 600 |  | 
| 521 | 
            -
                context "with  | 
| 522 | 
            -
                   | 
| 523 | 
            -
             | 
| 524 | 
            -
             | 
| 601 | 
            +
                context "with array of columns and array of values" do
         | 
| 602 | 
            +
                  let(:columns) { %w(title author_name) }
         | 
| 603 | 
            +
                  let(:valid_values) { [["LDAP", "Jerry Carter"], ["Rails Recipes", "Chad Fowler"]] }
         | 
| 604 | 
            +
                  let(:invalid_values) { [["Rails Recipes", "Chad Fowler"], ["The RSpec Book", ""], ["Agile+UX", ""]] }
         | 
| 605 | 
            +
             | 
| 606 | 
            +
                  context "with invalid data" do
         | 
| 607 | 
            +
                    it "should raise ActiveRecord::RecordInvalid" do
         | 
| 608 | 
            +
                      assert_no_difference "Topic.count" do
         | 
| 609 | 
            +
                        assert_raise ActiveRecord::RecordInvalid do
         | 
| 610 | 
            +
                          Topic.import! columns, invalid_values
         | 
| 611 | 
            +
                        end
         | 
| 612 | 
            +
                      end
         | 
| 613 | 
            +
                    end
         | 
| 614 | 
            +
                  end
         | 
| 615 | 
            +
             | 
| 616 | 
            +
                  context "with valid data" do
         | 
| 617 | 
            +
                    it "should import data" do
         | 
| 618 | 
            +
                      assert_difference "Topic.count", +2 do
         | 
| 619 | 
            +
                        Topic.import! columns, valid_values
         | 
| 620 | 
            +
                      end
         | 
| 525 621 | 
             
                    end
         | 
| 526 622 | 
             
                  end
         | 
| 527 623 | 
             
                end
         | 
    
        data/test/models/widget.rb
    CHANGED
    
    | @@ -1,3 +1,17 @@ | |
| 1 | 
            +
            class CustomCoder
         | 
| 2 | 
            +
              def load(value)
         | 
| 3 | 
            +
                if value.nil?
         | 
| 4 | 
            +
                  {}
         | 
| 5 | 
            +
                else
         | 
| 6 | 
            +
                  YAML.load(value)
         | 
| 7 | 
            +
                end
         | 
| 8 | 
            +
              end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
              def dump(value)
         | 
| 11 | 
            +
                YAML.dump(value)
         | 
| 12 | 
            +
              end
         | 
| 13 | 
            +
            end
         | 
| 14 | 
            +
             | 
| 1 15 | 
             
            class Widget < ActiveRecord::Base
         | 
| 2 16 | 
             
              self.primary_key = :w_id
         | 
| 3 17 |  | 
| @@ -5,4 +19,6 @@ class Widget < ActiveRecord::Base | |
| 5 19 |  | 
| 6 20 | 
             
              serialize :data, Hash
         | 
| 7 21 | 
             
              serialize :json_data, JSON
         | 
| 22 | 
            +
              serialize :unspecified_data
         | 
| 23 | 
            +
              serialize :custom_data, CustomCoder.new
         | 
| 8 24 | 
             
            end
         | 
    
        data/test/postgis/import_test.rb
    CHANGED
    
    | @@ -2,3 +2,7 @@ require File.expand_path(File.dirname(__FILE__) + '/../test_helper') | |
| 2 2 | 
             
            require File.expand_path(File.dirname(__FILE__) + '/../support/postgresql/import_examples')
         | 
| 3 3 |  | 
| 4 4 | 
             
            should_support_postgresql_import_functionality
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
         | 
| 7 | 
            +
              should_support_postgresql_upsert_functionality
         | 
| 8 | 
            +
            end
         | 
| @@ -64,6 +64,7 @@ ActiveRecord::Schema.define do | |
| 64 64 | 
             
                t.integer :topic_id
         | 
| 65 65 | 
             
                t.boolean :for_sale, default: true
         | 
| 66 66 | 
             
                t.integer :status, default: 0
         | 
| 67 | 
            +
                t.string :type
         | 
| 67 68 | 
             
              end
         | 
| 68 69 |  | 
| 69 70 | 
             
              create_table :chapters, force: :cascade do |t|
         | 
| @@ -73,7 +74,7 @@ ActiveRecord::Schema.define do | |
| 73 74 | 
             
                t.datetime :updated_at
         | 
| 74 75 | 
             
              end
         | 
| 75 76 |  | 
| 76 | 
            -
              create_table :end_notes, force: :cascade do |t|
         | 
| 77 | 
            +
              create_table :end_notes, primary_key: :end_note_id, force: :cascade do |t|
         | 
| 77 78 | 
             
                t.string :note
         | 
| 78 79 | 
             
                t.integer :book_id, null: false
         | 
| 79 80 | 
             
                t.datetime :created_at
         | 
| @@ -115,6 +116,8 @@ ActiveRecord::Schema.define do | |
| 115 116 | 
             
                t.boolean :active, default: false
         | 
| 116 117 | 
             
                t.text :data
         | 
| 117 118 | 
             
                t.text :json_data
         | 
| 119 | 
            +
                t.text :unspecified_data
         | 
| 120 | 
            +
                t.text :custom_data
         | 
| 118 121 | 
             
              end
         | 
| 119 122 |  | 
| 120 123 | 
             
              create_table :promotions, primary_key: :promotion_id, force: :cascade do |t|
         | 
| @@ -131,7 +134,8 @@ ActiveRecord::Schema.define do | |
| 131 134 | 
             
                t.string :discountable_type
         | 
| 132 135 | 
             
              end
         | 
| 133 136 |  | 
| 134 | 
            -
              create_table :rules, force: :cascade do |t|
         | 
| 137 | 
            +
              create_table :rules, id: false, force: :cascade do |t|
         | 
| 138 | 
            +
                t.integer :id
         | 
| 135 139 | 
             
                t.string :condition_text
         | 
| 136 140 | 
             
                t.integer :question_id
         | 
| 137 141 | 
             
              end
         | 
| @@ -139,4 +143,12 @@ ActiveRecord::Schema.define do | |
| 139 143 | 
             
              create_table :questions, force: :cascade do |t|
         | 
| 140 144 | 
             
                t.string :body
         | 
| 141 145 | 
             
              end
         | 
| 146 | 
            +
             | 
| 147 | 
            +
              create_table :vendors, force: :cascade do |t|
         | 
| 148 | 
            +
                t.string :name, null: true
         | 
| 149 | 
            +
                t.text :preferences
         | 
| 150 | 
            +
                t.text :data
         | 
| 151 | 
            +
                t.text :config
         | 
| 152 | 
            +
                t.text :settings
         | 
| 153 | 
            +
              end
         | 
| 142 154 | 
             
            end
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/postgresql_schema')
         | 
| @@ -0,0 +1 @@ | |
| 1 | 
            +
            require File.expand_path(File.dirname(__FILE__) + '/postgresql_schema')
         | 
| @@ -0,0 +1,41 @@ | |
| 1 | 
            +
            ActiveRecord::Schema.define do
         | 
| 2 | 
            +
              execute('CREATE extension IF NOT EXISTS "hstore";')
         | 
| 3 | 
            +
              execute('CREATE extension IF NOT EXISTS "uuid-ossp";')
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              create_table :vendors, id: :uuid, force: :cascade do |t|
         | 
| 6 | 
            +
                t.string :name, null: true
         | 
| 7 | 
            +
                t.text :preferences
         | 
| 8 | 
            +
             | 
| 9 | 
            +
                if t.respond_to?(:json)
         | 
| 10 | 
            +
                  t.json :data
         | 
| 11 | 
            +
                else
         | 
| 12 | 
            +
                  t.text :data
         | 
| 13 | 
            +
                end
         | 
| 14 | 
            +
             | 
| 15 | 
            +
                if t.respond_to?(:hstore)
         | 
| 16 | 
            +
                  t.hstore :config
         | 
| 17 | 
            +
                else
         | 
| 18 | 
            +
                  t.text :config
         | 
| 19 | 
            +
                end
         | 
| 20 | 
            +
             | 
| 21 | 
            +
                if t.respond_to?(:jsonb)
         | 
| 22 | 
            +
                  t.jsonb :settings
         | 
| 23 | 
            +
                else
         | 
| 24 | 
            +
                  t.text :settings
         | 
| 25 | 
            +
                end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
                t.datetime :created_at
         | 
| 28 | 
            +
                t.datetime :updated_at
         | 
| 29 | 
            +
              end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
              create_table :alarms, force: true do |t|
         | 
| 32 | 
            +
                t.column :device_id, :integer, null: false
         | 
| 33 | 
            +
                t.column :alarm_type, :integer, null: false
         | 
| 34 | 
            +
                t.column :status, :integer, null: false
         | 
| 35 | 
            +
                t.column :metadata, :text
         | 
| 36 | 
            +
                t.datetime :created_at
         | 
| 37 | 
            +
                t.datetime :updated_at
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
             | 
| 40 | 
            +
              add_index :alarms, [:device_id, :alarm_type], unique: true, where: 'status <> 0'
         | 
| 41 | 
            +
            end
         | 
    
        data/test/sqlite3/import_test.rb
    CHANGED
    
    | @@ -1,6 +1,7 @@ | |
| 1 1 | 
             
            require File.expand_path(File.dirname(__FILE__) + '/../test_helper')
         | 
| 2 2 |  | 
| 3 3 | 
             
            should_support_recursive_import
         | 
| 4 | 
            +
            should_support_on_duplicate_key_ignore
         | 
| 4 5 |  | 
| 5 6 | 
             
            describe "#supports_imports?" do
         | 
| 6 7 | 
             
              context "and SQLite is 3.7.11 or higher" do
         | 
| @@ -49,4 +50,18 @@ describe "#import" do | |
| 49 50 | 
             
                  assert_equal 2500, Topic.count, "Failed to insert all records. Make sure you have a supported version of SQLite3 (3.7.11 or higher) installed"
         | 
| 50 51 | 
             
                end
         | 
| 51 52 | 
             
              end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
              context "with :on_duplicate_key_update" do
         | 
| 55 | 
            +
                let(:topics) { Build(1, :topics) }
         | 
| 56 | 
            +
             | 
| 57 | 
            +
                it "should log a warning message" do
         | 
| 58 | 
            +
                  log = StringIO.new
         | 
| 59 | 
            +
                  logger = Logger.new(log)
         | 
| 60 | 
            +
                  logger.level = Logger::WARN
         | 
| 61 | 
            +
                  ActiveRecord::Base.connection.stubs(:logger).returns(logger)
         | 
| 62 | 
            +
             | 
| 63 | 
            +
                  Topic.import topics, on_duplicate_key_update: true
         | 
| 64 | 
            +
                  assert_match(/Ignoring on_duplicate_key_update/, log.string)
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
              end
         | 
| 52 67 | 
             
            end
         | 
    
        data/test/support/factories.rb
    CHANGED
    
    
| @@ -4,6 +4,7 @@ def should_support_mysql_import_functionality | |
| 4 4 | 
             
              ActiveRecord::Base.connection.execute "set sql_mode='STRICT_ALL_TABLES'"
         | 
| 5 5 |  | 
| 6 6 | 
             
              should_support_basic_on_duplicate_key_update
         | 
| 7 | 
            +
              should_support_on_duplicate_key_ignore
         | 
| 7 8 |  | 
| 8 9 | 
             
              describe "#import" do
         | 
| 9 10 | 
             
                context "with :on_duplicate_key_update and validation checks turned off" do
         | 
| @@ -58,10 +58,57 @@ def should_support_postgresql_import_functionality | |
| 58 58 | 
             
                  end
         | 
| 59 59 | 
             
                end
         | 
| 60 60 | 
             
              end
         | 
| 61 | 
            +
             | 
| 62 | 
            +
              describe "with a uuid primary key" do
         | 
| 63 | 
            +
                let(:vendor) { Vendor.new(name: "foo") }
         | 
| 64 | 
            +
                let(:vendors) { [vendor] }
         | 
| 65 | 
            +
             | 
| 66 | 
            +
                it "creates records" do
         | 
| 67 | 
            +
                  assert_difference "Vendor.count", +1 do
         | 
| 68 | 
            +
                    Vendor.import vendors
         | 
| 69 | 
            +
                  end
         | 
| 70 | 
            +
                end
         | 
| 71 | 
            +
             | 
| 72 | 
            +
                it "assigns an id to the model objects" do
         | 
| 73 | 
            +
                  Vendor.import vendors
         | 
| 74 | 
            +
                  assert_not_nil vendor.id
         | 
| 75 | 
            +
                end
         | 
| 76 | 
            +
              end
         | 
| 77 | 
            +
             | 
| 78 | 
            +
              describe "with store accessor fields" do
         | 
| 79 | 
            +
                if ENV['AR_VERSION'].to_f >= 4.0
         | 
| 80 | 
            +
                  it "imports values for json fields" do
         | 
| 81 | 
            +
                    vendors = [Vendor.new(name: 'Vendor 1', size: 100)]
         | 
| 82 | 
            +
                    assert_difference "Vendor.count", +1 do
         | 
| 83 | 
            +
                      Vendor.import vendors
         | 
| 84 | 
            +
                    end
         | 
| 85 | 
            +
                    assert_equal(100, Vendor.first.size)
         | 
| 86 | 
            +
                  end
         | 
| 87 | 
            +
             | 
| 88 | 
            +
                  it "imports values for hstore fields" do
         | 
| 89 | 
            +
                    vendors = [Vendor.new(name: 'Vendor 1', contact: 'John Smith')]
         | 
| 90 | 
            +
                    assert_difference "Vendor.count", +1 do
         | 
| 91 | 
            +
                      Vendor.import vendors
         | 
| 92 | 
            +
                    end
         | 
| 93 | 
            +
                    assert_equal('John Smith', Vendor.first.contact)
         | 
| 94 | 
            +
                  end
         | 
| 95 | 
            +
                end
         | 
| 96 | 
            +
             | 
| 97 | 
            +
                if ENV['AR_VERSION'].to_f >= 4.2
         | 
| 98 | 
            +
                  it "imports values for jsonb fields" do
         | 
| 99 | 
            +
                    vendors = [Vendor.new(name: 'Vendor 1', charge_code: '12345')]
         | 
| 100 | 
            +
                    assert_difference "Vendor.count", +1 do
         | 
| 101 | 
            +
                      Vendor.import vendors
         | 
| 102 | 
            +
                    end
         | 
| 103 | 
            +
                    assert_equal('12345', Vendor.first.charge_code)
         | 
| 104 | 
            +
                  end
         | 
| 105 | 
            +
                end
         | 
| 106 | 
            +
              end
         | 
| 61 107 | 
             
            end
         | 
| 62 108 |  | 
| 63 109 | 
             
            def should_support_postgresql_upsert_functionality
         | 
| 64 110 | 
             
              should_support_basic_on_duplicate_key_update
         | 
| 111 | 
            +
              should_support_on_duplicate_key_ignore
         | 
| 65 112 |  | 
| 66 113 | 
             
              describe "#import" do
         | 
| 67 114 | 
             
                extend ActiveSupport::TestCase::ImportAssertions
         | 
| @@ -151,6 +198,50 @@ def should_support_postgresql_upsert_functionality | |
| 151 198 | 
             
                      end
         | 
| 152 199 | 
             
                    end
         | 
| 153 200 |  | 
| 201 | 
            +
                    context 'with :index_predicate' do
         | 
| 202 | 
            +
                      let(:columns) { %w( id device_id alarm_type status metadata ) }
         | 
| 203 | 
            +
                      let(:values) { [[99, 17, 1, 1, 'foo']] }
         | 
| 204 | 
            +
                      let(:updated_values) { [[99, 17, 1, 2, 'bar']] }
         | 
| 205 | 
            +
             | 
| 206 | 
            +
                      macro(:perform_import) do |*opts|
         | 
| 207 | 
            +
                        Alarm.import columns, updated_values, opts.extract_options!.merge(on_duplicate_key_update: { conflict_target: [:device_id, :alarm_type], index_predicate: 'status <> 0', columns: [:status] }, validate: false)
         | 
| 208 | 
            +
                      end
         | 
| 209 | 
            +
             | 
| 210 | 
            +
                      macro(:updated_alarm) { Alarm.find(@alarm.id) }
         | 
| 211 | 
            +
             | 
| 212 | 
            +
                      setup do
         | 
| 213 | 
            +
                        Alarm.import columns, values, validate: false
         | 
| 214 | 
            +
                        @alarm = Alarm.find 99
         | 
| 215 | 
            +
                      end
         | 
| 216 | 
            +
             | 
| 217 | 
            +
                      context 'supports on duplicate key update for partial indexes' do
         | 
| 218 | 
            +
                        it 'should not update created_at timestamp columns' do
         | 
| 219 | 
            +
                          Timecop.freeze Chronic.parse("5 minutes from now") do
         | 
| 220 | 
            +
                            perform_import
         | 
| 221 | 
            +
                            assert_in_delta @alarm.created_at.to_i, updated_alarm.created_at.to_i, 1
         | 
| 222 | 
            +
                          end
         | 
| 223 | 
            +
                        end
         | 
| 224 | 
            +
             | 
| 225 | 
            +
                        it 'should update updated_at timestamp columns' do
         | 
| 226 | 
            +
                          time = Chronic.parse("5 minutes from now")
         | 
| 227 | 
            +
                          Timecop.freeze time do
         | 
| 228 | 
            +
                            perform_import
         | 
| 229 | 
            +
                            assert_in_delta time.to_i, updated_alarm.updated_at.to_i, 1
         | 
| 230 | 
            +
                          end
         | 
| 231 | 
            +
                        end
         | 
| 232 | 
            +
             | 
| 233 | 
            +
                        it 'should not update fields not mentioned' do
         | 
| 234 | 
            +
                          perform_import
         | 
| 235 | 
            +
                          assert_equal 'foo', updated_alarm.metadata
         | 
| 236 | 
            +
                        end
         | 
| 237 | 
            +
             | 
| 238 | 
            +
                        it 'should update fields mentioned with hash mappings' do
         | 
| 239 | 
            +
                          perform_import
         | 
| 240 | 
            +
                          assert_equal 2, updated_alarm.status
         | 
| 241 | 
            +
                        end
         | 
| 242 | 
            +
                      end
         | 
| 243 | 
            +
                    end
         | 
| 244 | 
            +
             | 
| 154 245 | 
             
                    context "with :constraint_name" do
         | 
| 155 246 | 
             
                      let(:columns) { %w( id title author_name author_email_address parent_id ) }
         | 
| 156 247 | 
             
                      let(:values) { [[100, "Book", "John Doe", "john@doe.com", 17]] }
         | 
| @@ -0,0 +1,25 @@ | |
| 1 | 
            +
            def should_support_on_duplicate_key_ignore
         | 
| 2 | 
            +
              describe "#import" do
         | 
| 3 | 
            +
                extend ActiveSupport::TestCase::ImportAssertions
         | 
| 4 | 
            +
                let(:topic) { Topic.create!(title: "Book", author_name: "John Doe") }
         | 
| 5 | 
            +
                let(:topics) { [topic] }
         | 
| 6 | 
            +
             | 
| 7 | 
            +
                context "with :on_duplicate_key_ignore" do
         | 
| 8 | 
            +
                  it "should skip duplicates and continue import" do
         | 
| 9 | 
            +
                    topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
         | 
| 10 | 
            +
                    assert_difference "Topic.count", +1 do
         | 
| 11 | 
            +
                      Topic.import topics, on_duplicate_key_ignore: true, validate: false
         | 
| 12 | 
            +
                    end
         | 
| 13 | 
            +
                  end
         | 
| 14 | 
            +
                end
         | 
| 15 | 
            +
             | 
| 16 | 
            +
                context "with :ignore" do
         | 
| 17 | 
            +
                  it "should skip duplicates and continue import" do
         | 
| 18 | 
            +
                    topics << Topic.new(title: "Book 2", author_name: "Jane Doe")
         | 
| 19 | 
            +
                    assert_difference "Topic.count", +1 do
         | 
| 20 | 
            +
                      Topic.import topics, ignore: true, validate: false
         | 
| 21 | 
            +
                    end
         | 
| 22 | 
            +
                  end
         | 
| 23 | 
            +
                end
         | 
| 24 | 
            +
              end
         | 
| 25 | 
            +
            end
         | 
| @@ -48,6 +48,22 @@ def should_support_recursive_import | |
| 48 48 | 
             
                  end
         | 
| 49 49 | 
             
                end
         | 
| 50 50 |  | 
| 51 | 
            +
                it 'imports polymorphic associations from subclass' do
         | 
| 52 | 
            +
                  discounts = Array.new(1) { |i| Discount.new(amount: i) }
         | 
| 53 | 
            +
                  dictionaries = Array.new(1) { |i| Dictionary.new(author_name: "Author ##{i}", title: "Book ##{i}") }
         | 
| 54 | 
            +
                  dictionaries.each do |dictionary|
         | 
| 55 | 
            +
                    dictionary.discounts << discounts
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                  Dictionary.import dictionaries, recursive: true
         | 
| 58 | 
            +
                  assert_equal 1, Dictionary.last.discounts.count
         | 
| 59 | 
            +
                  dictionaries.each do |dictionary|
         | 
| 60 | 
            +
                    dictionary.discounts.each do |discount|
         | 
| 61 | 
            +
                      assert_not_nil discount.discountable_id
         | 
| 62 | 
            +
                      assert_equal 'Book', discount.discountable_type
         | 
| 63 | 
            +
                    end
         | 
| 64 | 
            +
                  end
         | 
| 65 | 
            +
                end
         | 
| 66 | 
            +
             | 
| 51 67 | 
             
                [{ recursive: false }, {}].each do |import_options|
         | 
| 52 68 | 
             
                  it "skips recursion for #{import_options}" do
         | 
| 53 69 | 
             
                    assert_difference "Book.count", 0 do
         | 
| @@ -106,14 +122,16 @@ def should_support_recursive_import | |
| 106 122 |  | 
| 107 123 | 
             
                # If adapter supports on_duplicate_key_update, it is only applied to top level models so that SQL with invalid
         | 
| 108 124 | 
             
                # columns, keys, etc isn't generated for child associations when doing recursive import
         | 
| 109 | 
            -
                 | 
| 110 | 
            -
                   | 
| 125 | 
            +
                if ActiveRecord::Base.connection.supports_on_duplicate_key_update?
         | 
| 126 | 
            +
                  describe "on_duplicate_key_update" do
         | 
| 127 | 
            +
                    let(:new_topics) { Build(1, :topic_with_book) }
         | 
| 111 128 |  | 
| 112 | 
            -
             | 
| 113 | 
            -
             | 
| 114 | 
            -
             | 
| 115 | 
            -
             | 
| 116 | 
            -
             | 
| 129 | 
            +
                    it "imports objects with associations" do
         | 
| 130 | 
            +
                      assert_difference "Topic.count", +1 do
         | 
| 131 | 
            +
                        Topic.import new_topics, recursive: true, on_duplicate_key_update: [:updated_at], validate: false
         | 
| 132 | 
            +
                        new_topics.each do |topic|
         | 
| 133 | 
            +
                          assert_not_nil topic.id
         | 
| 134 | 
            +
                        end
         | 
| 117 135 | 
             
                      end
         | 
| 118 136 | 
             
                    end
         | 
| 119 137 | 
             
                  end
         | 
    
        data/test/test_helper.rb
    CHANGED
    
    
    
        metadata
    CHANGED
    
    | @@ -1,14 +1,14 @@ | |
| 1 1 | 
             
            --- !ruby/object:Gem::Specification
         | 
| 2 2 | 
             
            name: activerecord-import
         | 
| 3 3 | 
             
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            -
              version: 0. | 
| 4 | 
            +
              version: 0.16.0
         | 
| 5 5 | 
             
            platform: ruby
         | 
| 6 6 | 
             
            authors:
         | 
| 7 7 | 
             
            - Zach Dennis
         | 
| 8 8 | 
             
            autorequire: 
         | 
| 9 9 | 
             
            bindir: bin
         | 
| 10 10 | 
             
            cert_chain: []
         | 
| 11 | 
            -
            date: 2016- | 
| 11 | 
            +
            date: 2016-10-10 00:00:00.000000000 Z
         | 
| 12 12 | 
             
            dependencies:
         | 
| 13 13 | 
             
            - !ruby/object:Gem::Dependency
         | 
| 14 14 | 
             
              name: activerecord
         | 
| @@ -111,8 +111,10 @@ files: | |
| 111 111 | 
             
            - test/import_test.rb
         | 
| 112 112 | 
             
            - test/jdbcmysql/import_test.rb
         | 
| 113 113 | 
             
            - test/jdbcpostgresql/import_test.rb
         | 
| 114 | 
            +
            - test/models/alarm.rb
         | 
| 114 115 | 
             
            - test/models/book.rb
         | 
| 115 116 | 
             
            - test/models/chapter.rb
         | 
| 117 | 
            +
            - test/models/dictionary.rb
         | 
| 116 118 | 
             
            - test/models/discount.rb
         | 
| 117 119 | 
             
            - test/models/end_note.rb
         | 
| 118 120 | 
             
            - test/models/group.rb
         | 
| @@ -120,6 +122,7 @@ files: | |
| 120 122 | 
             
            - test/models/question.rb
         | 
| 121 123 | 
             
            - test/models/rule.rb
         | 
| 122 124 | 
             
            - test/models/topic.rb
         | 
| 125 | 
            +
            - test/models/vendor.rb
         | 
| 123 126 | 
             
            - test/models/widget.rb
         | 
| 124 127 | 
             
            - test/mysql2/import_test.rb
         | 
| 125 128 | 
             
            - test/mysql2_makara/import_test.rb
         | 
| @@ -127,7 +130,10 @@ files: | |
| 127 130 | 
             
            - test/postgis/import_test.rb
         | 
| 128 131 | 
             
            - test/postgresql/import_test.rb
         | 
| 129 132 | 
             
            - test/schema/generic_schema.rb
         | 
| 133 | 
            +
            - test/schema/jdbcpostgresql_schema.rb
         | 
| 130 134 | 
             
            - test/schema/mysql_schema.rb
         | 
| 135 | 
            +
            - test/schema/postgis_schema.rb
         | 
| 136 | 
            +
            - test/schema/postgresql_schema.rb
         | 
| 131 137 | 
             
            - test/schema/version.rb
         | 
| 132 138 | 
             
            - test/sqlite3/import_test.rb
         | 
| 133 139 | 
             
            - test/support/active_support/test_case_extensions.rb
         | 
| @@ -136,6 +142,7 @@ files: | |
| 136 142 | 
             
            - test/support/generate.rb
         | 
| 137 143 | 
             
            - test/support/mysql/import_examples.rb
         | 
| 138 144 | 
             
            - test/support/postgresql/import_examples.rb
         | 
| 145 | 
            +
            - test/support/shared_examples/on_duplicate_key_ignore.rb
         | 
| 139 146 | 
             
            - test/support/shared_examples/on_duplicate_key_update.rb
         | 
| 140 147 | 
             
            - test/support/shared_examples/recursive_import.rb
         | 
| 141 148 | 
             
            - test/synchronize_test.rb
         | 
| @@ -163,7 +170,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement | |
| 163 170 | 
             
                  version: '0'
         | 
| 164 171 | 
             
            requirements: []
         | 
| 165 172 | 
             
            rubyforge_project: 
         | 
| 166 | 
            -
            rubygems_version: 2. | 
| 173 | 
            +
            rubygems_version: 2.4.8
         | 
| 167 174 | 
             
            signing_key: 
         | 
| 168 175 | 
             
            specification_version: 4
         | 
| 169 176 | 
             
            summary: Bulk-loading extension for ActiveRecord
         | 
| @@ -183,8 +190,10 @@ test_files: | |
| 183 190 | 
             
            - test/import_test.rb
         | 
| 184 191 | 
             
            - test/jdbcmysql/import_test.rb
         | 
| 185 192 | 
             
            - test/jdbcpostgresql/import_test.rb
         | 
| 193 | 
            +
            - test/models/alarm.rb
         | 
| 186 194 | 
             
            - test/models/book.rb
         | 
| 187 195 | 
             
            - test/models/chapter.rb
         | 
| 196 | 
            +
            - test/models/dictionary.rb
         | 
| 188 197 | 
             
            - test/models/discount.rb
         | 
| 189 198 | 
             
            - test/models/end_note.rb
         | 
| 190 199 | 
             
            - test/models/group.rb
         | 
| @@ -192,6 +201,7 @@ test_files: | |
| 192 201 | 
             
            - test/models/question.rb
         | 
| 193 202 | 
             
            - test/models/rule.rb
         | 
| 194 203 | 
             
            - test/models/topic.rb
         | 
| 204 | 
            +
            - test/models/vendor.rb
         | 
| 195 205 | 
             
            - test/models/widget.rb
         | 
| 196 206 | 
             
            - test/mysql2/import_test.rb
         | 
| 197 207 | 
             
            - test/mysql2_makara/import_test.rb
         | 
| @@ -199,7 +209,10 @@ test_files: | |
| 199 209 | 
             
            - test/postgis/import_test.rb
         | 
| 200 210 | 
             
            - test/postgresql/import_test.rb
         | 
| 201 211 | 
             
            - test/schema/generic_schema.rb
         | 
| 212 | 
            +
            - test/schema/jdbcpostgresql_schema.rb
         | 
| 202 213 | 
             
            - test/schema/mysql_schema.rb
         | 
| 214 | 
            +
            - test/schema/postgis_schema.rb
         | 
| 215 | 
            +
            - test/schema/postgresql_schema.rb
         | 
| 203 216 | 
             
            - test/schema/version.rb
         | 
| 204 217 | 
             
            - test/sqlite3/import_test.rb
         | 
| 205 218 | 
             
            - test/support/active_support/test_case_extensions.rb
         | 
| @@ -208,6 +221,7 @@ test_files: | |
| 208 221 | 
             
            - test/support/generate.rb
         | 
| 209 222 | 
             
            - test/support/mysql/import_examples.rb
         | 
| 210 223 | 
             
            - test/support/postgresql/import_examples.rb
         | 
| 224 | 
            +
            - test/support/shared_examples/on_duplicate_key_ignore.rb
         | 
| 211 225 | 
             
            - test/support/shared_examples/on_duplicate_key_update.rb
         | 
| 212 226 | 
             
            - test/support/shared_examples/recursive_import.rb
         | 
| 213 227 | 
             
            - test/synchronize_test.rb
         | 
| @@ -215,4 +229,3 @@ test_files: | |
| 215 229 | 
             
            - test/travis/database.yml
         | 
| 216 230 | 
             
            - test/value_sets_bytes_parser_test.rb
         | 
| 217 231 | 
             
            - test/value_sets_records_parser_test.rb
         | 
| 218 | 
            -
            has_rdoc: 
         |