diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a58a6db68f..b92d34bec2 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -55,7 +55,7 @@ jobs: steps: - name: Install system packages timeout-minutes: 10 - run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libsqlite3-0 libvips curl ffmpeg + run: sudo apt-get update && sudo apt-get install --no-install-recommends -y libmysqlclient-dev libsqlite3-0 libvips curl ffmpeg - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: diff --git a/Brewfile b/Brewfile index ce2b67921c..5f1e5c71fd 100644 --- a/Brewfile +++ b/Brewfile @@ -1,4 +1,5 @@ brew "imagemagick" +brew "mysql-client" brew "openslide" brew "vips" brew "gitleaks" diff --git a/Dockerfile b/Dockerfile index 93460dabe2..1425517d23 100644 --- a/Dockerfile +++ b/Dockerfile @@ -16,7 +16,7 @@ WORKDIR /rails # Install base packages RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y ca-certificates curl libjemalloc2 libvips sqlite3 libssl-dev && \ + apt-get install --no-install-recommends -y ca-certificates curl libjemalloc2 libmariadb3 libvips sqlite3 libssl-dev && \ ln -s /usr/lib/$(uname -m)-linux-gnu/libjemalloc.so.2 /usr/local/lib/libjemalloc.so && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives @@ -32,7 +32,7 @@ FROM base AS build # Install packages needed to build gems RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y build-essential git libyaml-dev pkg-config && \ + apt-get install --no-install-recommends -y build-essential default-libmysqlclient-dev git libyaml-dev pkg-config && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Install application gems diff --git a/Gemfile b/Gemfile index 20989cfd19..10792a8251 100644 --- a/Gemfile +++ b/Gemfile @@ -23,7 +23,7 @@ gem "solid_cache", "~> 1.0" gem "solid_queue", "~> 1.7" gem "sqlite3", ">= 2.0" gem "thruster", require: false -gem "trilogy", "~> 2.13" +gem "mysql2", "~> 0.5" gem "tzinfo-data" # Bundled zone data; the base image's tzdata package lags IANA # Features diff --git a/Gemfile.lock b/Gemfile.lock index 1ba6b2406f..0bd866cdf7 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -308,6 +308,8 @@ GEM mocha (3.1.0) ruby2_keywords (>= 0.0.5) msgpack (1.8.5) + mysql2 (0.5.7) + bigdecimal net-http-persistent (4.0.8) connection_pool (>= 2.2.4, < 4) net-imap (0.6.6) @@ -323,7 +325,8 @@ GEM net-ssh (>= 5.0.0, < 8.0.0) net-smtp (0.5.1) net-protocol - net-ssh (7.3.3) + net-ssh (7.3.8) + logger nio4r (2.7.5) nokogiri (1.19.4-aarch64-linux-gnu) racc (~> 1.4) @@ -490,8 +493,6 @@ GEM thruster (0.1.26-x86_64-darwin) thruster (0.1.26-x86_64-linux) timeout (0.6.1) - trilogy (2.13.1) - bigdecimal tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -553,6 +554,7 @@ DEPENDENCIES mission_control-jobs mittens mocha + mysql2 (~> 0.5) net-http-persistent platform_agent propshaft @@ -573,7 +575,6 @@ DEPENDENCIES stimulus-rails surfguard! thruster - trilogy (~> 2.13) turbo-rails! tzinfo-data useragent! @@ -680,6 +681,7 @@ CHECKSUMS mittens (0.3.4) sha256=baa8e35c24bc06d6ed5c51633c800e8b6108ebcbd8efb1782d7146923797b383 mocha (3.1.0) sha256=75f42d69ebfb1f10b32489dff8f8431d37a418120ecdfc07afe3bc183d4e1d56 msgpack (1.8.5) sha256=97419eaa7b35dfe3e462c0e6c1a3dfdcbfc8756605c09e46e4c6daef127517ba + mysql2 (0.5.7) sha256=ba09ede515a0ae8a7192040a1b778c0fb0f025fa5877e9be895cd325fa5e9d7b net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6 net-imap (0.6.6) sha256=96aa4ee50df3060203e649efc341f53480b791d49e150f2fdebf68beb141a8df net-pop (0.1.2) sha256=848b4e982013c15b2f0382792268763b748cce91c9e91e36b0f27ed26420dff3 @@ -687,7 +689,7 @@ CHECKSUMS net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d net-sftp (4.0.0) sha256=65bb91c859c2f93b09826757af11b69af931a3a9155050f50d1b06d384526364 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 - net-ssh (7.3.3) sha256=831def58b2c51dcef66ec00d29397d4f210de89c19fe78f95873ca30f386e86a + net-ssh (7.3.8) sha256=16bb1fccc4d9a580decdeea8ddb98e8544df29b7e6ac8cb94d4b379cfe4bee9f nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af @@ -767,7 +769,6 @@ CHECKSUMS thruster (0.1.26-x86_64-darwin) sha256=c7f7f0cbefd8030ea03bf88f6a988f595a0618a35a1a0d358e729c6898eb5b6a thruster (0.1.26-x86_64-linux) sha256=3117a6ee430663f845a0457699fe9a05232dcc6c396e2cc83504de5a223c60e8 timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb - trilogy (2.13.1) sha256=fbfb17c439b347e4240be70b80facf0f6f9c417b34c528179ff065b3d53635c9 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f turbo-rails (2.0.23) tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b diff --git a/Gemfile.saas.lock b/Gemfile.saas.lock index 796a4afacd..f55a9c74d8 100644 --- a/Gemfile.saas.lock +++ b/Gemfile.saas.lock @@ -428,6 +428,8 @@ GEM ruby2_keywords (>= 0.0.5) msgpack (1.8.5) multi_json (1.19.1) + mysql2 (0.5.7) + bigdecimal net-http (0.9.1) uri (>= 0.11.1) net-http-persistent (4.0.8) @@ -445,7 +447,8 @@ GEM net-ssh (>= 5.0.0, < 8.0.0) net-smtp (0.5.1) net-protocol - net-ssh (7.3.3) + net-ssh (7.3.8) + logger nio4r (2.7.5) nokogiri (1.19.4-aarch64-linux-gnu) racc (~> 1.4) @@ -668,8 +671,6 @@ GEM thruster (0.1.26-x86_64-darwin) thruster (0.1.26-x86_64-linux) timeout (0.6.1) - trilogy (2.13.1) - bigdecimal tsort (0.2.0) tzinfo (2.0.6) concurrent-ruby (~> 1.0) @@ -771,6 +772,7 @@ DEPENDENCIES mission_control-jobs mittens mocha + mysql2 (~> 0.5) net-http-persistent platform_agent prometheus-client-mmap (~> 1.3) @@ -796,7 +798,6 @@ DEPENDENCIES stimulus-rails surfguard! thruster - trilogy (~> 2.13) turbo-rails! tzinfo-data useragent! @@ -934,6 +935,7 @@ CHECKSUMS mocha (3.1.0) sha256=75f42d69ebfb1f10b32489dff8f8431d37a418120ecdfc07afe3bc183d4e1d56 msgpack (1.8.5) sha256=97419eaa7b35dfe3e462c0e6c1a3dfdcbfc8756605c09e46e4c6daef127517ba multi_json (1.19.1) sha256=7aefeff8f2c854bf739931a238e4aea64592845e0c0395c8a7d2eea7fdd631b7 + mysql2 (0.5.7) sha256=ba09ede515a0ae8a7192040a1b778c0fb0f025fa5877e9be895cd325fa5e9d7b net-http (0.9.1) sha256=25ba0b67c63e89df626ed8fac771d0ad24ad151a858af2cc8e6a716ca4336996 net-http-persistent (4.0.8) sha256=ef3de8319d691537b329053fae3a33195f8b070bbbfae8bf1a58c796081960e6 net-imap (0.6.6) sha256=96aa4ee50df3060203e649efc341f53480b791d49e150f2fdebf68beb141a8df @@ -942,7 +944,7 @@ CHECKSUMS net-scp (4.1.0) sha256=a99b0b92a1e5d360b0de4ffbf2dc0c91531502d3d4f56c28b0139a7c093d1a5d net-sftp (4.0.0) sha256=65bb91c859c2f93b09826757af11b69af931a3a9155050f50d1b06d384526364 net-smtp (0.5.1) sha256=ed96a0af63c524fceb4b29b0d352195c30d82dd916a42f03c62a3a70e5b70736 - net-ssh (7.3.3) sha256=831def58b2c51dcef66ec00d29397d4f210de89c19fe78f95873ca30f386e86a + net-ssh (7.3.8) sha256=16bb1fccc4d9a580decdeea8ddb98e8544df29b7e6ac8cb94d4b379cfe4bee9f nio4r (2.7.5) sha256=6c90168e48fb5f8e768419c93abb94ba2b892a1d0602cb06eef16d8b7df1dca1 nokogiri (1.19.4-aarch64-linux-gnu) sha256=1269fb644a6de405057a53dd5c762b1209b43ca7424f839454d3dbc677c31a8f nokogiri (1.19.4-aarch64-linux-musl) sha256=35c65b9ce72b3bb03207bdbe7067915019dc18c1b9b59139684bd6690fdd01af @@ -1040,7 +1042,6 @@ CHECKSUMS thruster (0.1.26-x86_64-darwin) sha256=c7f7f0cbefd8030ea03bf88f6a988f595a0618a35a1a0d358e729c6898eb5b6a thruster (0.1.26-x86_64-linux) sha256=3117a6ee430663f845a0457699fe9a05232dcc6c396e2cc83504de5a223c60e8 timeout (0.6.1) sha256=78f57368a7e7bbadec56971f78a3f5ecbcfb59b7fcbb0a3ed6ddc08a5094accb - trilogy (2.13.1) sha256=fbfb17c439b347e4240be70b80facf0f6f9c417b34c528179ff065b3d53635c9 tsort (0.2.0) sha256=9650a793f6859a43b6641671278f79cfead60ac714148aabe4e3f0060480089f turbo-rails (2.0.23) tzinfo (2.0.6) sha256=8daf828cc77bcf7d63b0e3bdb6caa47e2272dcfaf4fbfe46f8c3a9df087a829b diff --git a/app/models/board/accessible.rb b/app/models/board/accessible.rb index 984e9ec94a..3000510279 100644 --- a/app/models/board/accessible.rb +++ b/app/models/board/accessible.rb @@ -85,7 +85,7 @@ def notifications_for_user(user) # # Notification->Event->Mention->Card and Notification->Event->Mention->Comment->Card are # handled by destroying mentions_for_user. - uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + uuid_type = ActiveRecord::Type.lookup(:uuid, adapter: :mysql2) board_id_binary = uuid_type.serialize(id) user.notifications diff --git a/app/models/search/record.rb b/app/models/search/record.rb index 52ab6cd80f..124778107d 100644 --- a/app/models/search/record.rb +++ b/app/models/search/record.rb @@ -1,5 +1,5 @@ class Search::Record < ApplicationRecord - if connection.adapter_name == "Trilogy" + if connection.adapter_name == "Mysql2" self.abstract_class = true # MySQL uses sharded tables; shard models set their own table_name else self.table_name = "search_records" diff --git a/config/database.mysql.yml b/config/database.mysql.yml index 2f72f6e989..a42f15a815 100644 --- a/config/database.mysql.yml +++ b/config/database.mysql.yml @@ -1,5 +1,5 @@ default: &default - adapter: trilogy + adapter: mysql2 host: <%= ENV.fetch("MYSQL_HOST", "127.0.0.1") %> port: <%= ENV.fetch("MYSQL_PORT", "3306") %> username: <%= ENV.fetch("MYSQL_USER", "root") %> diff --git a/config/initializers/mysql_ssl_mode.rb b/config/initializers/mysql_ssl_mode.rb new file mode 100644 index 0000000000..1d7f470b12 --- /dev/null +++ b/config/initializers/mysql_ssl_mode.rb @@ -0,0 +1,5 @@ +# mysql2 built against MariaDB Connector/C, as in the Docker image, ignores verify_ca and +# connects without checking the server certificate. +if ENV["MYSQL_SSL_MODE"].to_s.casecmp?("verify_ca") + raise "MYSQL_SSL_MODE=verify_ca is not supported. Use verify_identity, which checks the server certificate and its host name." +end diff --git a/config/initializers/uuid_primary_keys.rb b/config/initializers/uuid_primary_keys.rb index 4b4bc8fdf7..7131d9c56d 100644 --- a/config/initializers/uuid_primary_keys.rb +++ b/config/initializers/uuid_primary_keys.rb @@ -35,7 +35,7 @@ module MysqlUuidAdapter # Override lookup_cast_type to recognize binary(16) as UUID type def lookup_cast_type(sql_type) if sql_type == "binary(16)" - ActiveRecord::Type.lookup(:uuid, adapter: :trilogy) + ActiveRecord::Type.lookup(:uuid, adapter: :mysql2) else super end @@ -116,7 +116,7 @@ def uuid(name, **options) ActiveRecord::ConnectionAdapters::TableDefinition.prepend(TableDefinitionUuidSupport) end -ActiveSupport.on_load(:active_record_trilogyadapter) do +ActiveSupport.on_load(:active_record_mysql2adapter) do ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.prepend(MysqlUuidAdapter) ActiveRecord::ConnectionAdapters::MySQL::SchemaDumper.prepend(SchemaDumperUuidType) end diff --git a/lib/rails_ext/active_record_sql_redaction.rb b/lib/rails_ext/active_record_sql_redaction.rb index 74d2d01be2..06d21cdd70 100644 --- a/lib/rails_ext/active_record_sql_redaction.rb +++ b/lib/rails_ext/active_record_sql_redaction.rb @@ -1,4 +1,4 @@ -# Trilogy runs without prepared statements, so a value reaches the SQL text as a quoted literal. The +# MySQL runs without prepared statements, so a value reaches the SQL text as a quoted literal. The # sql.active_record notification carries that text to every consumer, and with Rails.event in debug mode # (config/application.rb) it reaches the log: in SaaS mode, the structured log line for the request or # job, and from there Loki. Sentry breadcrumbs and spans read it too, and a database error can quote a @@ -72,7 +72,7 @@ def mentions_redacted_table?(text) def filter_error(error) filtered = \ if server_error_code(error) - [ error.error_code, FILTERED_ERROR ].join(": ") + [ error.error_number, FILTERED_ERROR ].join(": ") else redact_error_message(error.message) end @@ -89,7 +89,7 @@ def redact_error_message(message) private def server_error_code(error) - error.respond_to?(:error_code) && error.error_code + error.respond_to?(:error_number) && error.error_number end def reference_to(table) @@ -123,7 +123,7 @@ module ActiveRecordSqlRedaction::ErrorTranslation private def translate_exception_class(native_error, sql, binds) if ActiveRecordSqlRedaction.redacted_statement?(sql) && \ - ActiveRecordSqlRedaction::UNFILTERED_ERROR_CODES.exclude?(native_error.try(:error_code)) + ActiveRecordSqlRedaction::UNFILTERED_ERROR_CODES.exclude?(native_error.try(:error_number)) ActiveRecordSqlRedaction.filter_error(native_error) end diff --git a/lib/rails_ext/active_record_trilogy_quote_string.rb b/lib/rails_ext/active_record_trilogy_quote_string.rb deleted file mode 100644 index 9447fae7a5..0000000000 --- a/lib/rails_ext/active_record_trilogy_quote_string.rb +++ /dev/null @@ -1,14 +0,0 @@ -# Match mysql2 behavior -module ActiveRecordTrilogyQuoteString - def quote_string(string) - unless string.encoding == Encoding::UTF_8 || string.encoding == Encoding::BINARY || @config[:encoding] == "binary" - string = string.encode(Encoding::UTF_8) - end - - super - end -end - -ActiveSupport.on_load :active_record_trilogyadapter do - prepend ActiveRecordTrilogyQuoteString -end diff --git a/lib/rails_ext/active_record_uuid_type.rb b/lib/rails_ext/active_record_uuid_type.rb index f2004f2493..e3357505af 100644 --- a/lib/rails_ext/active_record_uuid_type.rb +++ b/lib/rails_ext/active_record_uuid_type.rb @@ -41,6 +41,6 @@ def cast(value) end end -# Register the UUID type for Trilogy (MySQL) and SQLite3 adapters -ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :trilogy) +# Register the UUID type for the Mysql2 and SQLite3 adapters +ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :mysql2) ActiveRecord::Type.register(:uuid, ActiveRecord::Type::Uuid, adapter: :sqlite3) diff --git a/saas/Dockerfile b/saas/Dockerfile index 9f6e75e13b..9dc962f7b8 100644 --- a/saas/Dockerfile +++ b/saas/Dockerfile @@ -25,7 +25,7 @@ FROM base AS build # Install packages needed to build gems RUN apt-get update -qq && \ - apt-get install -y --no-install-recommends -y build-essential pkg-config git libvips libyaml-dev libssl-dev && \ + apt-get install -y --no-install-recommends -y build-essential default-libmysqlclient-dev pkg-config git libvips libyaml-dev libssl-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # Install application gems @@ -63,7 +63,7 @@ FROM base # library (ActiveStorage::VIPS_AVAILABLE=false), and config/initializers/vips.rb only demands it # when no cell is registered. RUN apt-get update -qq && \ - apt-get install --no-install-recommends -y libsqlite3-0 sqlite3 libjemalloc-dev && \ + apt-get install --no-install-recommends -y libmariadb3 libsqlite3-0 sqlite3 libjemalloc-dev && \ rm -rf /var/lib/apt/lists /var/cache/apt/archives # The eMudhra intermediate CAs' private keys leaked in October 2026, and they chain to these two roots. diff --git a/saas/config/database.yml b/saas/config/database.yml index eff904609b..51f918d587 100644 --- a/saas/config/database.yml +++ b/saas/config/database.yml @@ -33,9 +33,10 @@ %> default: &default - adapter: trilogy + adapter: mysql2 host: <%= ENV.fetch "FIZZY_DB_HOST", "127.0.0.1" %> port: <%= ENV.fetch "FIZZY_DB_PORT", 3306 %> + username: root # mysql2 falls back to the OS user; production names its own pool: 50 timeout: 5000 <% unless ENV["MIGRATE"].present? %> diff --git a/saas/test/integration/structured_log_redaction_test.rb b/saas/test/integration/structured_log_redaction_test.rb index 3e7f492f9d..998b5e5891 100644 --- a/saas/test/integration/structured_log_redaction_test.rb +++ b/saas/test/integration/structured_log_redaction_test.rb @@ -118,7 +118,7 @@ def perform(identity) ActiveRecord::Base.cache { 2.times { assert MagicLink.find_by(code: CODE) } } assert_raises(ActiveRecord::RecordNotUnique) { identities(:kevin).magic_links.new(code: CODE, expires_at: 1.minute.from_now).save!(validate: false) } transaction.finish - Sentry.capture_exception(ActiveRecord::RecordNotUnique.new("Trilogy::ProtocolError: 1062: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'")) + Sentry.capture_exception(ActiveRecord::RecordNotUnique.new("Mysql2::Error: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'")) assert sentry_events.many? assert_no_secret CODE, sentry_events.map { |event| event.to_h.to_json } @@ -139,7 +139,7 @@ def perform(identity) unclosed = Sentry::Breadcrumb.new(category: "sql.active_record", data: { sql: "SELECT * FROM magic_links WHERE purpose = '0' AND code = '#{CODE}" }) assert_no_secret CODE, [ config.before_breadcrumb.call(unclosed, {}).data[:sql] ] - error = ActiveRecord::RecordNotUnique.new("Trilogy::ProtocolError: 1062: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'") + error = ActiveRecord::RecordNotUnique.new("Mysql2::Error: Duplicate entry '#{CODE}' for key 'magic_links.index_magic_links_on_code'") event = Sentry::ErrorEvent.new(configuration: config).tap { |event| event.add_exception_interface(error, mechanism: Sentry::Mechanism.new) } assert_no_secret CODE, [ config.before_send.call(event, {}).exception.values.map(&:value).inspect ] diff --git a/test/initializers/mysql_ssl_mode_test.rb b/test/initializers/mysql_ssl_mode_test.rb new file mode 100644 index 0000000000..5b5be80992 --- /dev/null +++ b/test/initializers/mysql_ssl_mode_test.rb @@ -0,0 +1,31 @@ +require "test_helper" + +class MysqlSslModeTest < ActiveSupport::TestCase + test "verify_ca is refused" do + %w[ verify_ca VERIFY_CA ].each do |mode| + with_mysql_ssl_mode(mode) do + error = assert_raises(RuntimeError) { load_initializer } + assert_match "verify_identity", error.message + end + end + end + + test "other modes are allowed" do + [ nil, "disabled", "required", "verify_identity" ].each do |mode| + with_mysql_ssl_mode(mode) { assert_nothing_raised { load_initializer } } + end + end + + private + def with_mysql_ssl_mode(mode) + original = ENV["MYSQL_SSL_MODE"] + ENV["MYSQL_SSL_MODE"] = mode + yield + ensure + ENV["MYSQL_SSL_MODE"] = original + end + + def load_initializer + load Rails.root.join("config/initializers/mysql_ssl_mode.rb").to_s + end +end diff --git a/test/lib/database_adapter_test.rb b/test/lib/database_adapter_test.rb new file mode 100644 index 0000000000..e3aa2ccf61 --- /dev/null +++ b/test/lib/database_adapter_test.rb @@ -0,0 +1,23 @@ +require "test_helper" + +class DatabaseAdapterTest < ActiveSupport::TestCase + unless Fizzy.db_adapter.sqlite? + test "MySQL connects with mysql2" do + assert_equal "Mysql2", connection.adapter_name + end + + test "MySQL connects in utf8mb4" do + assert_equal "utf8mb4", connection.select_value("SELECT @@character_set_connection") + assert_equal "🗂️", connection.select_value("SELECT #{connection.quote("🗂️")}") + end + + test "MySQL reads binary(16) columns as UUIDs" do + assert_equal :uuid, Card.columns_hash["id"].type + end + end + + private + def connection + ActiveRecord::Base.connection + end +end diff --git a/test/lib/rails_ext/active_record_sql_redaction_test.rb b/test/lib/rails_ext/active_record_sql_redaction_test.rb index 8ca4c5692e..e95b9cd02a 100644 --- a/test/lib/rails_ext/active_record_sql_redaction_test.rb +++ b/test/lib/rails_ext/active_record_sql_redaction_test.rb @@ -175,6 +175,18 @@ class ActiveRecordSqlRedactionTest < ActiveSupport::TestCase assert_no_secret CODE, seen.join("\n") end + unless Fizzy.db_adapter.sqlite? + test "a MySQL server error keeps only its code" do + identities(:kevin).magic_links.create!(code: CODE) + + error = assert_raises ActiveRecord::RecordNotUnique do + identities(:kevin).magic_links.new(code: CODE, expires_at: 1.minute.from_now).save!(validate: false) + end + + assert_equal "1062: #{ActiveRecordSqlRedaction::FILTERED_ERROR}", error.cause.message + end + end + test "an invalid statement's error, which can quote the statement, carries no value" do error = assert_raises ActiveRecord::StatementInvalid do MagicLink.connection.select_all("SELECT * FROM magic_links WHERE code = '#{CODE}' INVALID")