From 581b47194f82f3d56c4e2c6522c3eea4431ef138 Mon Sep 17 00:00:00 2001 From: Jonathan Bartlett Date: Fri, 22 Apr 2022 09:36:54 -0500 Subject: [PATCH 1/2] Make the column more similar to PostgreSQL uuid support if the :nil_invalid_values options is set --- lib/mysql-binuuid/type.rb | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/lib/mysql-binuuid/type.rb b/lib/mysql-binuuid/type.rb index 8273fa6..ac6489b 100644 --- a/lib/mysql-binuuid/type.rb +++ b/lib/mysql-binuuid/type.rb @@ -2,6 +2,12 @@ module MySQLBinUUID class InvalidUUID < StandardError; end class Type < ActiveModel::Type::Binary + attr_accessor :options + + def initialize(opts = {}) + self.options = opts + end + def type :uuid end @@ -34,6 +40,7 @@ def serialize(value) # does not explicity escape the Binary data type. escaping is implicit as # the Binary data type always converts its value to a hex string. unless valid_undashed_uuid?(undashed_uuid) + return nil if options[:nil_invalid_values] raise MySQLBinUUID::InvalidUUID, "#{value} is not a valid UUID" end From 0e6a4b304d17487c10ab0ec9090642ce53d08b5e Mon Sep 17 00:00:00 2001 From: Jonathan Bartlett Date: Fri, 22 Apr 2022 09:43:03 -0500 Subject: [PATCH 2/2] Doc changes --- README.md | 54 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/README.md b/README.md index a2c11b5..1d97d91 100644 --- a/README.md +++ b/README.md @@ -133,6 +133,60 @@ Migrating shouldn't be that difficult though. `UUIDTools::UUID` implements by default. But it's good to be aware of this in case you're running into weirdness. +## Invalid Values + +By default, this will raise an exception if an invalid value is attempted to be stored. +However, the PostgreSQL driver nils invalid UUID values. +To get PG-friendly behavior, pass in the option `:nil_invalid_values => true` when creating the attribute. + +Additionally, as a convenience function, you can add the following to `ApplicationRecord` to get parity between PG and MySQL: + +``` +def self.uuid_fields(*fields) + if ActiveRecord::Base.connection.adapter_name.downcase.starts_with?("mysql") + fields.each do |fld| + attribute fld, MySQLBinUUID::Type.new(:nil_invalid_values => true) + end + end +end +``` + +Then, in your models, just begin them by marking the fields which are UUIDs. + +## UUID Type in migrations + +To get a UUID type in a migration, add the following to your initializers (Rails 7 specific): + +``` +require "active_record" +require "active_record/connection_adapters/mysql2_adapter" +require "active_record/type_caster/map" + +module ActiveRecord + module ConnectionAdapters + module MySQL + module ColumnMethods + def uuid(*args, **options) + # http://dba.stackexchange.com/questions/904/mysql-data-type-for-128-bit-integers + # http://dev.mysql.com/doc/refman/5.7/en/binary-varbinary.html + args.each { |name| column(name, "varbinary(16)", **options.merge(limit: 16)) } + end + end + module SchemaStatements + def type_to_sql(type, limit: nil, precision: nil, scale: nil, size: limit_to_size(limit, type), unsigned: nil, **) + if type.to_s == "uuid" + return "varbinary(16)" + else + super + end + end + end + end + end +end + +ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter::NATIVE_DATABASE_TYPES[:uuid] = {:name => "varbinary", :limit => 16} +``` # Known issues * With Rails 5.0 in combination with uniqueness validations, ActiveRecord