mariadb_json_columns.rb 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061
  1. # Copyright (C) 2012-2024 Zammad Foundation, https://zammad-foundation.org/
  2. require 'active_record/connection_adapters/abstract_mysql_adapter'
  3. ActiveRecord::ConnectionAdapters::AbstractMysqlAdapter.class_eval do
  4. private
  5. alias_method :column_definitions_original, :column_definitions
  6. def column_definitions(table_name)
  7. result = column_definitions_original(table_name)
  8. if mariadb?
  9. result.each do |row|
  10. next if row[:Type] != 'longtext'
  11. next if !mariadb_column_json?(table_name, row[:Field])
  12. row[:Type] = 'json'
  13. end
  14. end
  15. result
  16. end
  17. # https://github.com/zammad/zammad/issues/4148
  18. # JSON columns in mariadb are listed as longtext, so we need to check
  19. # the constraint checks to find out if the column was created as json.
  20. # Based on this detection we will hack the type so rails will handle
  21. # values properly as json values.
  22. # INFORMATION_SCHEMA.CHECK_CONSTRAINTS is only support on > 10.4 mariadb
  23. # so we use object manager attributes table now to detect them.
  24. def mariadb_column_json?(table_name, field_name)
  25. field = quote(field_name)
  26. scope = quoted_scope(table_name)
  27. # As we are among other OSes supporting Ubuntu 20.04 LTS, which ships with
  28. # MariaDB 10.3, we need to check explicit for json (array) columns via
  29. # table name and field name.
  30. return true if mariadb_column_array?(table_name, field_name)
  31. # for older versions
  32. if database_version < '10.4' && %w[tickets users groups organizations].include?(table_name)
  33. class_name = table_name.classify
  34. execute_and_free("SELECT 1 FROM object_manager_attributes, object_lookups WHERE object_manager_attributes.object_lookup_id = object_lookups.id AND object_lookups.name = '#{class_name}' AND object_manager_attributes.name = #{field} AND object_manager_attributes.data_type IN ('multiselect', 'multi_tree_select') LIMIT 1") do |r|
  35. return r.to_a.present?
  36. end
  37. end
  38. execute_and_free("SELECT 1 FROM INFORMATION_SCHEMA.CHECK_CONSTRAINTS WHERE TABLE_NAME = #{scope[:name]} AND CONSTRAINT_SCHEMA = #{scope[:schema]} AND CONSTRAINT_NAME = #{field} AND CHECK_CLAUSE LIKE '%json_valid%'") do |r|
  39. return r.to_a.present?
  40. end
  41. end
  42. def mariadb_column_array?(table_name, field_name)
  43. column_array = {
  44. 'smime_certificates' => 'email_addresses',
  45. 'pgp_keys' => 'email_addresses',
  46. 'public_links' => 'screen',
  47. }
  48. column_array[table_name] == field_name
  49. end
  50. end