diff --git a/app/models/concerns/searchable.rb b/app/models/concerns/searchable.rb index 0d0e11c7e..6ce1e2aba 100644 --- a/app/models/concerns/searchable.rb +++ b/app/models/concerns/searchable.rb @@ -2,7 +2,7 @@ module Searchable extend ActiveSupport::Concern def self.search_index_table_name(account_id) - "search_index_#{account_id.to_s.hash.abs % 16}" + "search_index_#{account_id.hash.abs % 16}" end included do @@ -21,7 +21,8 @@ module Searchable uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) self.class.connection.execute self.class.sanitize_sql([ - "INSERT INTO #{table_name} (searchable_type, searchable_id, card_id, board_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?)", + "INSERT INTO #{table_name} (id, searchable_type, searchable_id, card_id, board_id, title, content, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)", + uuid_type.serialize(ActiveRecord::Type::Uuid.generate), self.class.name, uuid_type.serialize(id), uuid_type.serialize(search_card_id), diff --git a/config/initializers/uuid_primary_keys.rb b/config/initializers/uuid_primary_keys.rb index fb60c737f..01a1625fa 100644 --- a/config/initializers/uuid_primary_keys.rb +++ b/config/initializers/uuid_primary_keys.rb @@ -1,6 +1,6 @@ # Automatically use UUID type for all binary(16) columns ActiveSupport.on_load(:active_record) do - ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.class_eval do + module MysqlUuidAdapter def lookup_cast_type(sql_type) if sql_type == "varbinary(16)" ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) @@ -9,10 +9,9 @@ ActiveSupport.on_load(:active_record) do end end - # Override quote to handle binary UUID values properly def quote(value) if value.is_a?(String) && value.encoding == Encoding::BINARY && value.bytesize == 16 - # Quote binary UUID as hex literal for MySQL + # Convert binary UUIDs to hex literals to avoid encoding conflicts in SQL strings "X'#{value.unpack1('H*')}'" else super @@ -20,6 +19,8 @@ ActiveSupport.on_load(:active_record) do end end + ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.prepend(MysqlUuidAdapter) + # Fix schema dumper to include limit for binary columns module SchemaDumperBinaryLimit def prepare_column_options(column) @@ -33,43 +34,37 @@ ActiveSupport.on_load(:active_record) do end ActiveRecord::ConnectionAdapters::MySQL::SchemaDumper.prepend(SchemaDumperBinaryLimit) -end -# Automatically convert :uuid to binary(16) for primary keys and columns -module ActiveRecord - module ConnectionAdapters - class TableDefinition - alias_method :original_set_primary_key, :set_primary_key - - def set_primary_key(table_name, id, primary_key, **options) - # Convert :uuid to :binary with limit 16 - if id == :uuid - id = :binary - options[:limit] = 16 - elsif id.is_a?(Hash) && id[:type] == :uuid - id[:type] = :binary - id[:limit] = 16 - end - - original_set_primary_key(table_name, id, primary_key, **options) + # Automatically convert :uuid to binary(16) for primary keys and columns + module TableDefinitionUuidSupport + def set_primary_key(table_name, id, primary_key, **options) + # Convert :uuid to :binary with limit 16 + if id == :uuid + id = :binary + options[:limit] = 16 + elsif id.is_a?(Hash) && id[:type] == :uuid + id[:type] = :binary + id[:limit] = 16 end - alias_method :original_column, :column + super + end - def column(name, type, **options) - # Convert :uuid to :binary with limit 16 for regular columns too - if type == :uuid - type = :binary - options[:limit] = 16 - end - - original_column(name, type, **options) + def column(name, type, **options) + # Convert :uuid to :binary with limit 16 for regular columns too + if type == :uuid + type = :binary + options[:limit] = 16 end - # Define uuid as a column type method (like string, integer, etc.) - def uuid(name, **options) - column(name, :uuid, **options) - end + super + end + + # Define uuid as a column type method (like string, integer, etc.) + def uuid(name, **options) + column(name, :uuid, **options) end end + + ActiveRecord::ConnectionAdapters::TableDefinition.prepend(TableDefinitionUuidSupport) end diff --git a/db/migrate/20251112093037_create_search_indices.rb b/db/migrate/20251112093037_create_search_indices.rb index cbad229b3..441284590 100644 --- a/db/migrate/20251112093037_create_search_indices.rb +++ b/db/migrate/20251112093037_create_search_indices.rb @@ -1,7 +1,7 @@ class CreateSearchIndices < ActiveRecord::Migration[8.2] def up 16.times do |i| - create_table "search_index_#{i}".to_sym do |t| + create_table "search_index_#{i}".to_sym, id: :uuid, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci" do |t| t.string :searchable_type, null: false t.uuid :searchable_id, null: false t.uuid :card_id, null: false diff --git a/db/schema.rb b/db/schema.rb index 106a7325c..8800b4984 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -375,7 +375,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["reacter_id"], name: "index_reactions_on_reacter_id" end - create_table "search_index_0", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_0", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -387,7 +387,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si0_type_id", unique: true end - create_table "search_index_1", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_1", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -399,7 +399,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si1_type_id", unique: true end - create_table "search_index_10", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_10", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -411,7 +411,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si10_type_id", unique: true end - create_table "search_index_11", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_11", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -423,7 +423,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si11_type_id", unique: true end - create_table "search_index_12", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_12", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -435,7 +435,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si12_type_id", unique: true end - create_table "search_index_13", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_13", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -447,7 +447,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si13_type_id", unique: true end - create_table "search_index_14", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_14", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -459,7 +459,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si14_type_id", unique: true end - create_table "search_index_15", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_15", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -471,7 +471,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si15_type_id", unique: true end - create_table "search_index_2", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_2", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -483,7 +483,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si2_type_id", unique: true end - create_table "search_index_3", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_3", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -495,7 +495,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si3_type_id", unique: true end - create_table "search_index_4", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_4", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -507,7 +507,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si4_type_id", unique: true end - create_table "search_index_5", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_5", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -519,7 +519,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si5_type_id", unique: true end - create_table "search_index_6", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_6", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -531,7 +531,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si6_type_id", unique: true end - create_table "search_index_7", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_7", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -543,7 +543,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si7_type_id", unique: true end - create_table "search_index_8", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_8", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content" @@ -555,7 +555,7 @@ ActiveRecord::Schema[8.2].define(version: 2025_11_12_184932) do t.index ["searchable_type", "searchable_id"], name: "idx_si8_type_id", unique: true end - create_table "search_index_9", charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| + create_table "search_index_9", id: { type: :binary, limit: 16 }, charset: "utf8mb4", collation: "utf8mb4_0900_ai_ci", force: :cascade do |t| t.binary "board_id", null: false, limit: 16 t.binary "card_id", null: false, limit: 16 t.text "content"