Set default :text type column limit

This commit is contained in:
2026-05-07 18:20:06 +02:00
parent ca7edb99e1
commit 76b95f7c30
2 changed files with 39 additions and 16 deletions

View File

@@ -21,7 +21,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_01_21_230456) do
end end
create_table "notes", force: :cascade do |t| create_table "notes", force: :cascade do |t|
t.text "text", null: false t.text "text", limit: 65535, null: false
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
end end
@@ -29,7 +29,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_01_21_230456) do
create_table "quantities", force: :cascade do |t| create_table "quantities", force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.string "name", limit: 31, null: false t.string "name", limit: 31, null: false
t.text "description" t.text "description", limit: 65535
t.integer "parent_id" t.integer "parent_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false
t.datetime "updated_at", null: false t.datetime "updated_at", null: false
@@ -60,7 +60,7 @@ ActiveRecord::Schema[8.1].define(version: 2025_01_21_230456) do
create_table "units", force: :cascade do |t| create_table "units", force: :cascade do |t|
t.integer "user_id" t.integer "user_id"
t.string "symbol", limit: 15, null: false t.string "symbol", limit: 15, null: false
t.text "description" t.text "description", limit: 65535
t.decimal "multiplier", precision: 30, scale: 15, default: "1.0", null: false t.decimal "multiplier", precision: 30, scale: 15, default: "1.0", null: false
t.integer "base_id" t.integer "base_id"
t.datetime "created_at", null: false t.datetime "created_at", null: false

View File

@@ -1,18 +1,40 @@
class DefaultSettingsStrategy < ActiveRecord::Migration::DefaultStrategy class DefaultSettingsStrategy < ActiveRecord::Migration::DefaultStrategy
# Without `:if_not_exists`/`:if_exists` options it's impossible to change COLUMN_DEFAULTS = {
# migration status once migration fails partially. If `up` migration creates # TODO: all types `null: false` ?
# some, but not all objects, its status is not updated from `down` to `up`. # TODO: references `foreign_key: {on_delete: :cascade}` ?
# Then it's impossible to migrate: a) up - due to `already exists` errors and # `timestamps` - Rails defaults to `null: false, precision: true`.
# b) down - due to migration status. # `limit:` for `text` - `text` can be theoretically up to 1GB or
# Using `force: :cascade` does nothing on MySQL, so `force:` is useless. # longer, so roughly unlimited for practical purposes. But:
# Adding `null: false` here somehow does not show up in schema files, be careful! # * the actual usable length depends on additional factors, like compile
# TODO: add_foreign_key {on_delete: :cascade} ? # time limits (`SQLITE_MAX_LENGTH` in SQLite), runtime settings
DEFAULTS = { # (`max_allowed_packet` in MySQL) and probably other,
# * Rails does not report limit for `text` column types, unless it is
# explicitly set.
# The decision is to always set safe limit and enforce it by validations, to
# avoid surprises (e.g. text truncation) when saving to dabatase.
text: {limit: 2**16 - 1}
}
COLUMN_DEFAULTS.default = {}
COLUMN_DEFAULTS.freeze
module ColumnSettingsStrategy
def column(name, type, **options)
super(name, type, **COLUMN_DEFAULTS[type].merge(options))
end
end
ActiveRecord::ConnectionAdapters::TableDefinition.prepend ColumnSettingsStrategy
# `force: :cascade` - does nothing on MySQL, so `force:` is useless.
# `if_not_exists: true`/`if_exists: true` - without these options it's impossible
# to change migration status once migration fails partially. If `up` migration
# creates some, but not all objects, its status is not updated from `down` to
# `up`. Then it's impossible to migrate: a) up - due to `already exists`
# errors and b) down - due to migration status.
MIGRATION_DEFAULTS = {
add_check_constraint: {if_not_exists: true}, add_check_constraint: {if_not_exists: true},
add_column: {if_not_exists: true}, add_column: {if_not_exists: true},
add_foreign_key: {if_not_exists: true}, add_foreign_key: {if_not_exists: true},
add_index: {if_not_exists: true, unique: true}, add_index: {if_not_exists: true, unique: true},
# Timestamps are by default `null: false, precision: true`.
add_timestamps: {if_not_exists: true}, add_timestamps: {if_not_exists: true},
create_table: {if_not_exists: true}, create_table: {if_not_exists: true},
drop_table: {if_exists: true}, drop_table: {if_exists: true},
@@ -22,11 +44,12 @@ class DefaultSettingsStrategy < ActiveRecord::Migration::DefaultStrategy
remove_index: {if_exists: true}, remove_index: {if_exists: true},
remove_timestamps: {if_exists: true}, remove_timestamps: {if_exists: true},
} }
DEFAULTS.default = {} MIGRATION_DEFAULTS.default = {}
DEFAULTS.freeze MIGRATION_DEFAULTS.freeze
def method_missing(method, *args, **kwargs, &) def method_missing(method, *args, **kwargs, &)
conflicts = kwargs.has_key?(:force) ? [:if_not_exists, :if_exists] : [] conflicts = kwargs.has_key?(:force) ? [:if_not_exists, :if_exists] : []
super(method, *args, **DEFAULTS[method.to_sym].except(*conflicts).merge(kwargs), &) defaults = MIGRATION_DEFAULTS[method.to_sym].except(*conflicts)
super(method, *args, **defaults.merge(kwargs), &)
end end
end end