Merge pull request #47425 from adrianna-chang-shopify/ac-fix-no-autoincrement-sqlite
Ensure `AUTOINCREMENT` declaration is preserved when altering SQLite tables
This commit is contained in:
commit
08d75b4140
@ -0,0 +1,41 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module ActiveRecord
|
||||
module ConnectionAdapters
|
||||
module SQLite3
|
||||
class Column < ConnectionAdapters::Column # :nodoc:
|
||||
def initialize(*, auto_increment: nil, **)
|
||||
super
|
||||
@auto_increment = auto_increment
|
||||
end
|
||||
|
||||
def auto_increment?
|
||||
@auto_increment
|
||||
end
|
||||
|
||||
def init_with(coder)
|
||||
@auto_increment = coder["auto_increment"]
|
||||
super
|
||||
end
|
||||
|
||||
def encode_with(coder)
|
||||
coder["auto_increment"] = @auto_increment
|
||||
super
|
||||
end
|
||||
|
||||
def ==(other)
|
||||
other.is_a?(Column) &&
|
||||
super &&
|
||||
auto_increment? == other.auto_increment?
|
||||
end
|
||||
alias :eql? :==
|
||||
|
||||
def hash
|
||||
Column.hash ^
|
||||
super.hash ^
|
||||
auto_increment?.hash
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
@ -145,7 +145,8 @@ def new_column_from_field(table_name, field)
|
||||
type_metadata,
|
||||
field["notnull"].to_i == 0,
|
||||
default_function,
|
||||
collation: field["collation"]
|
||||
collation: field["collation"],
|
||||
auto_increment: field["auto_increment"],
|
||||
)
|
||||
end
|
||||
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
require "active_record/connection_adapters/abstract_adapter"
|
||||
require "active_record/connection_adapters/statement_pool"
|
||||
require "active_record/connection_adapters/sqlite3/column"
|
||||
require "active_record/connection_adapters/sqlite3/explain_pretty_printer"
|
||||
require "active_record/connection_adapters/sqlite3/quoting"
|
||||
require "active_record/connection_adapters/sqlite3/database_statements"
|
||||
@ -527,13 +528,21 @@ def copy_table(from, to, options = {})
|
||||
default = type.deserialize(column.default)
|
||||
end
|
||||
|
||||
column_type = column.bigint? ? :bigint : column.type
|
||||
@definition.column(column_name, column_type,
|
||||
limit: column.limit, default: default,
|
||||
precision: column.precision, scale: column.scale,
|
||||
null: column.null, collation: column.collation,
|
||||
column_options = {
|
||||
limit: column.limit,
|
||||
precision: column.precision,
|
||||
scale: column.scale,
|
||||
null: column.null,
|
||||
collation: column.collation,
|
||||
primary_key: column_name == from_primary_key
|
||||
)
|
||||
}
|
||||
|
||||
unless column.auto_increment?
|
||||
column_options[:default] = default
|
||||
end
|
||||
|
||||
column_type = column.bigint? ? :bigint : column.type
|
||||
@definition.column(column_name, column_type, **column_options)
|
||||
end
|
||||
|
||||
yield @definition if block_given?
|
||||
@ -603,10 +612,12 @@ def translate_exception(exception, message:, sql:, binds:)
|
||||
end
|
||||
end
|
||||
|
||||
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i.freeze
|
||||
COLLATE_REGEX = /.*"(\w+)".*collate\s+"(\w+)".*/i
|
||||
PRIMARY_KEY_AUTOINCREMENT_REGEX = /.*"(\w+)".+PRIMARY KEY AUTOINCREMENT/i
|
||||
|
||||
def table_structure_with_collation(table_name, basic_structure)
|
||||
collation_hash = {}
|
||||
auto_increments = {}
|
||||
sql = <<~SQL
|
||||
SELECT sql FROM
|
||||
(SELECT * FROM sqlite_master UNION ALL
|
||||
@ -628,6 +639,7 @@ def table_structure_with_collation(table_name, basic_structure)
|
||||
# This regex will match the column name and collation type and will save
|
||||
# the value in $1 and $2 respectively.
|
||||
collation_hash[$1] = $2 if COLLATE_REGEX =~ column_string
|
||||
auto_increments[$1] = true if PRIMARY_KEY_AUTOINCREMENT_REGEX =~ column_string
|
||||
end
|
||||
|
||||
basic_structure.map do |column|
|
||||
@ -637,6 +649,10 @@ def table_structure_with_collation(table_name, basic_structure)
|
||||
column["collation"] = collation_hash[column_name]
|
||||
end
|
||||
|
||||
if auto_increments.has_key?(column_name)
|
||||
column["auto_increment"] = true
|
||||
end
|
||||
|
||||
column
|
||||
end
|
||||
else
|
||||
|
@ -561,6 +561,27 @@ def test_remove_column_preserves_index_options
|
||||
Barcode.reset_column_information
|
||||
end
|
||||
|
||||
def test_auto_increment_preserved_on_table_changes
|
||||
connection = Barcode.connection
|
||||
connection.create_table :barcodes, force: true do |t|
|
||||
t.string :code
|
||||
end
|
||||
|
||||
pk_column = connection.columns("barcodes").find { |col| col.name == "id" }
|
||||
sql = connection.exec_query("SELECT sql FROM sqlite_master WHERE tbl_name='barcodes'").rows.first.first
|
||||
|
||||
assert(pk_column.auto_increment?)
|
||||
assert(sql.match?("PRIMARY KEY AUTOINCREMENT"))
|
||||
|
||||
connection.change_column(:barcodes, :code, :integer)
|
||||
|
||||
pk_column = connection.columns("barcodes").find { |col| col.name == "id" }
|
||||
sql = connection.exec_query("SELECT sql FROM sqlite_master WHERE tbl_name='barcodes'").rows.first.first
|
||||
|
||||
assert(pk_column.auto_increment?)
|
||||
assert(sql.match?("PRIMARY KEY AUTOINCREMENT"))
|
||||
end
|
||||
|
||||
def test_supports_extensions
|
||||
assert_not @conn.supports_extensions?, "does not support extensions"
|
||||
end
|
||||
|
@ -262,6 +262,9 @@ def show
|
||||
RUBY
|
||||
|
||||
switch_env("DATABASE_URL", "mysql2://127.0.0.1:1") do
|
||||
# The existing schema cache dump will contain ActiveRecord::ConnectionAdapters::SQLite3::Column objects
|
||||
require "active_record/connection_adapters/sqlite3/column"
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
assert_nil ActiveRecord::Base.connection_pool.schema_cache
|
||||
@ -294,6 +297,9 @@ def show
|
||||
RUBY
|
||||
|
||||
switch_env("DATABASE_URL", "mysql2://127.0.0.1:1") do
|
||||
# The existing schema cache dump will contain ActiveRecord::ConnectionAdapters::SQLite3::Column objects
|
||||
require "active_record/connection_adapters/sqlite3/column"
|
||||
|
||||
require "#{app_path}/config/environment"
|
||||
|
||||
assert ActiveRecord::Base.connection_pool.schema_cache.data_sources("posts")
|
||||
|
Loading…
Reference in New Issue
Block a user