torque-postgresql 3.4.1 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (94) hide show
  1. checksums.yaml +4 -4
  2. data/lib/generators/torque/function_generator.rb +13 -0
  3. data/lib/generators/torque/templates/function.sql.erb +4 -0
  4. data/lib/generators/torque/templates/type.sql.erb +2 -0
  5. data/lib/generators/torque/templates/view.sql.erb +3 -0
  6. data/lib/generators/torque/type_generator.rb +13 -0
  7. data/lib/generators/torque/view_generator.rb +16 -0
  8. data/lib/torque/postgresql/adapter/database_statements.rb +111 -94
  9. data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
  10. data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
  11. data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
  12. data/lib/torque/postgresql/adapter/oid.rb +1 -23
  13. data/lib/torque/postgresql/adapter/quoting.rb +13 -7
  14. data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
  15. data/lib/torque/postgresql/adapter/schema_definitions.rb +58 -0
  16. data/lib/torque/postgresql/adapter/schema_dumper.rb +136 -34
  17. data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
  18. data/lib/torque/postgresql/adapter/schema_statements.rb +109 -49
  19. data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
  20. data/lib/torque/postgresql/arel/nodes.rb +16 -2
  21. data/lib/torque/postgresql/arel/operations.rb +7 -1
  22. data/lib/torque/postgresql/arel/visitors.rb +7 -9
  23. data/lib/torque/postgresql/associations/association_scope.rb +23 -31
  24. data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
  25. data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
  26. data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
  27. data/lib/torque/postgresql/attributes/builder/full_text_search.rb +109 -0
  28. data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
  29. data/lib/torque/postgresql/attributes/builder.rb +49 -11
  30. data/lib/torque/postgresql/attributes/enum.rb +7 -7
  31. data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
  32. data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
  33. data/lib/torque/postgresql/attributes/period.rb +2 -2
  34. data/lib/torque/postgresql/attributes.rb +0 -4
  35. data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
  36. data/lib/torque/postgresql/base.rb +5 -11
  37. data/lib/torque/postgresql/collector.rb +1 -1
  38. data/lib/torque/postgresql/config.rb +129 -5
  39. data/lib/torque/postgresql/function.rb +94 -0
  40. data/lib/torque/postgresql/inheritance.rb +52 -36
  41. data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
  42. data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
  43. data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
  44. data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
  45. data/lib/torque/postgresql/predicate_builder.rb +35 -0
  46. data/lib/torque/postgresql/railtie.rb +137 -30
  47. data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
  48. data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
  49. data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
  50. data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
  51. data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -2
  52. data/lib/torque/postgresql/relation/buckets.rb +124 -0
  53. data/lib/torque/postgresql/relation/distinct_on.rb +7 -2
  54. data/lib/torque/postgresql/relation/inheritance.rb +22 -15
  55. data/lib/torque/postgresql/relation/join_series.rb +112 -0
  56. data/lib/torque/postgresql/relation/merger.rb +17 -3
  57. data/lib/torque/postgresql/relation.rb +24 -38
  58. data/lib/torque/postgresql/schema_cache.rb +6 -12
  59. data/lib/torque/postgresql/version.rb +1 -1
  60. data/lib/torque/postgresql/versioned_commands/command_migration.rb +146 -0
  61. data/lib/torque/postgresql/versioned_commands/generator.rb +57 -0
  62. data/lib/torque/postgresql/versioned_commands/migration_context.rb +83 -0
  63. data/lib/torque/postgresql/versioned_commands/migrator.rb +39 -0
  64. data/lib/torque/postgresql/versioned_commands/schema_table.rb +101 -0
  65. data/lib/torque/postgresql/versioned_commands.rb +161 -0
  66. data/lib/torque/postgresql.rb +2 -1
  67. data/spec/fixtures/migrations/20250101000001_create_users.rb +0 -0
  68. data/spec/fixtures/migrations/20250101000002_create_function_count_users_v1.sql +0 -0
  69. data/spec/fixtures/migrations/20250101000003_create_internal_users.rb +0 -0
  70. data/spec/fixtures/migrations/20250101000004_update_function_count_users_v2.sql +0 -0
  71. data/spec/fixtures/migrations/20250101000005_create_view_all_users_v1.sql +0 -0
  72. data/spec/fixtures/migrations/20250101000006_create_type_user_id_v1.sql +0 -0
  73. data/spec/fixtures/migrations/20250101000007_remove_function_count_users_v2.sql +0 -0
  74. data/spec/initialize.rb +67 -0
  75. data/spec/mocks/cache_query.rb +21 -21
  76. data/spec/mocks/create_table.rb +6 -26
  77. data/spec/schema.rb +17 -12
  78. data/spec/spec_helper.rb +11 -2
  79. data/spec/tests/arel_spec.rb +32 -7
  80. data/spec/tests/auxiliary_statement_spec.rb +3 -3
  81. data/spec/tests/belongs_to_many_spec.rb +72 -5
  82. data/spec/tests/enum_set_spec.rb +12 -11
  83. data/spec/tests/enum_spec.rb +4 -2
  84. data/spec/tests/full_text_seach_test.rb +280 -0
  85. data/spec/tests/function_spec.rb +42 -0
  86. data/spec/tests/has_many_spec.rb +21 -8
  87. data/spec/tests/interval_spec.rb +1 -7
  88. data/spec/tests/period_spec.rb +61 -61
  89. data/spec/tests/predicate_builder_spec.rb +132 -0
  90. data/spec/tests/relation_spec.rb +229 -0
  91. data/spec/tests/schema_spec.rb +6 -9
  92. data/spec/tests/table_inheritance_spec.rb +25 -26
  93. data/spec/tests/versioned_commands_spec.rb +513 -0
  94. metadata +64 -39
@@ -0,0 +1,161 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative 'versioned_commands/command_migration'
4
+ require_relative 'versioned_commands/migration_context'
5
+ require_relative 'versioned_commands/migrator'
6
+ require_relative 'versioned_commands/schema_table'
7
+
8
+ module Torque
9
+ module PostgreSQL
10
+ # Takes advantage of Rails migrations to create other sorts of
11
+ # objects/commands that can also be versioned. Everything migrated will
12
+ # still live within Migrations borders (i.e., the schema_migrations), but
13
+ # the way they are handled and registered in the schema dumper is completely
14
+ # different
15
+ module VersionedCommands
16
+ RAILS_APP = defined?(Rails.application.paths)
17
+ NAME_MATCH = '"?((?:[_a-z0-9]+"?\."?)?[_a-z0-9]+)"?'
18
+
19
+ class << self
20
+ # Check if the type is current enabled
21
+ def valid_type?(type)
22
+ PostgreSQL.config.versioned_commands.types.include?(type.to_sym)
23
+ end
24
+
25
+ # Run the internal validations for the given type and content
26
+ def validate!(type, content, name)
27
+ method_name = :"validate_#{type}!"
28
+ return send(method_name, content, name) if valid_type?(type)
29
+ raise ArgumentError, "Unknown versioned command type: #{type}"
30
+ end
31
+
32
+ # Get the content of the command based on the type, name, and version
33
+ def fetch_command(dirs, type, name, version)
34
+ paths = Array.wrap(dirs).map { |d| "#{d}/**/*_#{type}_#{name}_v#{version}.sql" }
35
+ files = Dir[*paths]
36
+ return File.read(files.first) if files.one?
37
+
38
+ raise ArgumentError, <<~MSG.squish if files.none?
39
+ No previous version found for #{type} #{name}
40
+ of version v#{version}.
41
+ MSG
42
+
43
+ raise ArgumentError, <<~MSG.squish if files.many?
44
+ Multiple files found for #{type} #{name}
45
+ of version v#{version}.
46
+ MSG
47
+ end
48
+
49
+ # The regexp is dynamic due to the list of available types
50
+ def filename_regexp
51
+ @filename_regexp ||= begin
52
+ types = PostgreSQL.config.versioned_commands.types
53
+ Regexp.new([
54
+ "\\A([0-9]+)_",
55
+ "(create|update|remove)_",
56
+ "(#{types.join('|')})_",
57
+ "([_a-z0-9]*)",
58
+ "_v([0-9]+)",
59
+ "\\.?([_a-z0-9]*)?",
60
+ "\\.sql\\z",
61
+ ].join)
62
+ end
63
+ end
64
+
65
+ private
66
+
67
+ # Validate that the content of the command is correct
68
+ def validate_function!(content, name)
69
+ result = content.scan(Regexp.new([
70
+ '^\s*CREATE\s+(OR\s+REPLACE)?\s*',
71
+ "FUNCTION\\s+#{NAME_MATCH}",
72
+ ].join, 'mi'))
73
+
74
+ names = result.map(&:last).compact.uniq(&:downcase)
75
+ raise ArgumentError, <<~MSG.squish if names.size > 1
76
+ Multiple functions definition found.
77
+ MSG
78
+
79
+ raise ArgumentError, <<~MSG.squish unless result.all?(&:first)
80
+ 'OR REPLACE' is required for proper migration support.
81
+ MSG
82
+
83
+ fn_name = names.first.downcase.sub('.', '_')
84
+ raise ArgumentError, <<~MSG.squish if fn_name != name.downcase
85
+ Function name must match file name.
86
+ MSG
87
+ end
88
+
89
+ # Validate that the content of the command is correct
90
+ def validate_type!(content, name)
91
+ creates = content.scan(Regexp.new(['^\s*CREATE\s+TYPE\s+', NAME_MATCH].join, 'mi'))
92
+ drops = content.scan(Regexp.new([
93
+ '^\s*DROP\s+TYPE\s+(IF\s+EXISTS)?\s*',
94
+ NAME_MATCH,
95
+ ].join, 'mi'))
96
+
97
+ raise ArgumentError, <<~MSG.squish if creates.size > 1
98
+ More than one type definition found.
99
+ MSG
100
+
101
+ raise ArgumentError, <<~MSG.squish if drops.size > 1
102
+ More than one type drop found.
103
+ MSG
104
+
105
+ raise ArgumentError, <<~MSG.squish if drops.empty?
106
+ 'DROP TYPE' is required for proper migration support.
107
+ MSG
108
+
109
+ create_name = creates.first.last.downcase
110
+ raise ArgumentError, <<~MSG.squish if drops.first.last.downcase != create_name
111
+ Drop does not match create.
112
+ MSG
113
+
114
+ create_name = create_name.sub('.', '_')
115
+ raise ArgumentError, <<~MSG.squish if create_name != name.downcase
116
+ Type name must match file name.
117
+ MSG
118
+ end
119
+
120
+ # Validate that the content of the command is correct
121
+ def validate_view!(content, name)
122
+ result = content.scan(Regexp.new([
123
+ '^\s*CREATE\s+(OR\s+REPLACE)?\s*',
124
+ '((?:TEMP|TEMPORARY|MATERIALIZED)\s+)?',
125
+ '(?:RECURSIVE\s+)?',
126
+ "VIEW\\s+#{NAME_MATCH}",
127
+ ].join, 'mi'))
128
+
129
+ raise ArgumentError, <<~MSG.squish if result.empty?
130
+ Missing or invalid view definition.
131
+ MSG
132
+
133
+ raise ArgumentError, <<~MSG.squish if result.size > 1
134
+ More than one view definition found.
135
+ MSG
136
+
137
+ with_replace, opt, view_name = result.first
138
+ if opt&.strip == 'MATERIALIZED'
139
+ raise ArgumentError, <<~MSG.squish if with_replace.present?
140
+ Materialized view does not support 'OR REPLACE'.
141
+ MSG
142
+
143
+ with_drop = "DROP MATERIALIZED VIEW IF EXISTS #{view_name};"
144
+ raise ArgumentError, <<~MSG.squish unless content.include?(with_drop)
145
+ 'DROP MATERIALIZED VIEW IF EXISTS' is required for proper migration support.
146
+ MSG
147
+ else
148
+ raise ArgumentError, <<~MSG.squish if with_replace.blank?
149
+ 'OR REPLACE' is required for proper migration support.
150
+ MSG
151
+ end
152
+
153
+ view_name = view_name.downcase.sub('.', '_')
154
+ raise ArgumentError, <<~MSG.squish if view_name != name.downcase
155
+ View name must match file name.
156
+ MSG
157
+ end
158
+ end
159
+ end
160
+ end
161
+ end
@@ -12,6 +12,7 @@ require 'torque/postgresql/config'
12
12
  require 'torque/postgresql/version'
13
13
  require 'torque/postgresql/collector'
14
14
  require 'torque/postgresql/geometry_builder'
15
+ require 'torque/postgresql/predicate_builder'
15
16
 
16
17
  require 'torque/postgresql/i18n'
17
18
  require 'torque/postgresql/arel'
@@ -19,7 +20,6 @@ require 'torque/postgresql/adapter'
19
20
  require 'torque/postgresql/associations'
20
21
  require 'torque/postgresql/attributes'
21
22
  require 'torque/postgresql/autosave_association'
22
- require 'torque/postgresql/auxiliary_statement'
23
23
  require 'torque/postgresql/inheritance'
24
24
  require 'torque/postgresql/base' # Needs to be after inheritance
25
25
  require 'torque/postgresql/insert_all'
@@ -28,5 +28,6 @@ require 'torque/postgresql/relation'
28
28
  require 'torque/postgresql/reflection'
29
29
  require 'torque/postgresql/schema_cache'
30
30
  require 'torque/postgresql/table_name'
31
+ require 'torque/postgresql/function'
31
32
 
32
33
  require 'torque/postgresql/railtie' if defined?(Rails)
@@ -0,0 +1,67 @@
1
+ require_relative '../lib/torque/postgresql/auxiliary_statement'
2
+
3
+ require_relative '../lib/torque/postgresql/adapter/schema_overrides'
4
+
5
+ require_relative '../lib/torque/postgresql/adapter/oid/box'
6
+ require_relative '../lib/torque/postgresql/adapter/oid/circle'
7
+ require_relative '../lib/torque/postgresql/adapter/oid/enum'
8
+ require_relative '../lib/torque/postgresql/adapter/oid/enum_set'
9
+ require_relative '../lib/torque/postgresql/adapter/oid/interval'
10
+ require_relative '../lib/torque/postgresql/adapter/oid/line'
11
+ require_relative '../lib/torque/postgresql/adapter/oid/segment'
12
+
13
+ require_relative '../lib/torque/postgresql/attributes/enum'
14
+ require_relative '../lib/torque/postgresql/attributes/enum_set'
15
+ require_relative '../lib/torque/postgresql/attributes/period'
16
+ require_relative '../lib/torque/postgresql/attributes/full_text_search'
17
+
18
+ require_relative '../lib/torque/postgresql/relation/auxiliary_statement'
19
+ require_relative '../lib/torque/postgresql/relation/join_series'
20
+ require_relative '../lib/torque/postgresql/relation/buckets'
21
+
22
+ require_relative '../lib/torque/postgresql/versioned_commands'
23
+
24
+ module Torque
25
+ module PostgreSQL
26
+ ActiveRecord::Base.belongs_to_many_required_by_default = false
27
+
28
+ Attributes::Enum.include_on(ActiveRecord::Base)
29
+ Attributes::EnumSet.include_on(ActiveRecord::Base)
30
+ Attributes::Period.include_on(ActiveRecord::Base)
31
+ Attributes::FullTextSearch.include_on(ActiveRecord::Base)
32
+
33
+ Relation.include(Relation::AuxiliaryStatement)
34
+ Relation.include(Relation::JoinSeries)
35
+ Relation.include(Relation::Buckets)
36
+
37
+ config.versioned_commands.enabled = true
38
+ ActiveRecord::Schema::Definition.include(Adapter::Definition)
39
+
40
+ ::Object.const_set('TorqueCTE', AuxiliaryStatement)
41
+ ::Object.const_set('TorqueRecursiveCTE', AuxiliaryStatement::Recursive)
42
+
43
+ config.enum.namespace = ::Object.const_set('Enum', Module.new)
44
+ config.enum.namespace.define_singleton_method(:const_missing) do |name|
45
+ Attributes::Enum.lookup(name)
46
+ end
47
+
48
+ config.enum.namespace.define_singleton_method(:sample) do |name|
49
+ Attributes::Enum.lookup(name).sample
50
+ end
51
+
52
+ ar_type = ActiveRecord::Type
53
+ ar_type.register(:enum, Adapter::OID::Enum, adapter: :postgresql)
54
+ ar_type.register(:enum_set, Adapter::OID::EnumSet, adapter: :postgresql)
55
+
56
+ ar_type.register(:box, Adapter::OID::Box, adapter: :postgresql)
57
+ ar_type.register(:circle, Adapter::OID::Circle, adapter: :postgresql)
58
+ ar_type.register(:line, Adapter::OID::Line, adapter: :postgresql)
59
+ ar_type.register(:segment, Adapter::OID::Segment, adapter: :postgresql)
60
+
61
+ ar_type.register(:interval, Adapter::OID::Interval, adapter: :postgresql)
62
+
63
+ Arel.build_operations(config.arel.infix_operators)
64
+
65
+ ActiveRecord::Base.connection.torque_load_additional_types
66
+ end
67
+ end
@@ -1,33 +1,33 @@
1
1
  module Mocks
2
2
  module CacheQuery
3
- if Torque::PostgreSQL::AR720
4
- def get_last_executed_query(&block)
5
- cache = ActiveRecord::Base.connection.query_cache
6
- cache.instance_variable_set(:@enabled, true)
3
+ def get_last_executed_query(&block)
4
+ cache = ActiveRecord::Base.connection.query_cache
5
+ cache.instance_variable_set(:@enabled, true)
7
6
 
8
- map = cache.instance_variable_get(:@map)
7
+ map = cache.instance_variable_get(:@map)
9
8
 
10
- block.call
11
- result = map.keys.first
9
+ block.call
10
+ result = map.keys.first
12
11
 
13
- cache.instance_variable_set(:@enabled, false)
14
- map.delete(result)
12
+ cache.instance_variable_set(:@enabled, false)
13
+ map.delete(result)
15
14
 
16
- result
17
- end
18
- else
19
- def get_last_executed_query(&block)
20
- conn = ActiveRecord::Base.connection
21
- conn.instance_variable_set(:@query_cache_enabled, true)
22
-
23
- block.call
24
- result = conn.query_cache.keys.first
15
+ result
16
+ end
25
17
 
26
- conn.instance_variable_set(:@query_cache_enabled, false)
27
- conn.instance_variable_get(:@query_cache).delete(result)
18
+ def get_query_with_binds(&block)
19
+ result = nil
28
20
 
29
- result
21
+ original_method = ActiveRecord::Base.connection.method(:raw_execute)
22
+ original_method.receiver.define_singleton_method(:raw_execute) do |*args, **xargs, &block|
23
+ result ||= [args.first, args.third]
24
+ super(*args, **xargs, &block)
30
25
  end
26
+
27
+ block.call
28
+ original_method.receiver.define_singleton_method(:raw_execute, &original_method.to_proc)
29
+
30
+ result
31
31
  end
32
32
  end
33
33
  end
@@ -1,34 +1,14 @@
1
1
  module Mocks
2
2
  module CreateTable
3
3
  def mock_create_table
4
- path = ActiveRecord::Base.connection.method(:create_table).super_method.source_location[0]
5
-
6
- before :all do
7
- ActiveRecord::ConnectionAdapters::SchemaStatements.send(:define_method, :create_table) do |table_name, **options, &block|
8
- td = create_table_definition(table_name, **options)
9
-
10
- # Does things as the same as schema statements
11
- if options[:id] != false && !options[:as]
12
- pk = options.fetch(:primary_key) do
13
- ActiveRecord::Base.get_primary_key table_name.to_s.singularize
14
- end
15
-
16
- if pk.is_a?(Array)
17
- td.primary_keys pk
18
- else
19
- td.primary_key pk, options.fetch(:id, :primary_key), **options
20
- end
21
- end
22
-
23
- block.call(td) if block.present?
24
-
25
- # Now generate the SQL and return it
26
- schema_creation.accept td
4
+ around do |example|
5
+ original_method = ActiveRecord::Base.connection.method(:log)
6
+ original_method.receiver.define_singleton_method(:log) do |sql, *, **, &block|
7
+ sql
27
8
  end
28
- end
29
9
 
30
- after :all do
31
- load path
10
+ example.run
11
+ original_method.receiver.define_singleton_method(:log, &original_method.to_proc)
32
12
  end
33
13
  end
34
14
  end
data/spec/schema.rb CHANGED
@@ -10,7 +10,7 @@
10
10
  #
11
11
  # It's strongly recommended that you check this file into your version control system.
12
12
 
13
- version = 4
13
+ version = 6
14
14
 
15
15
  return if ActiveRecord::Migrator.current_version == version
16
16
  ActiveRecord::Schema.define(version: version) do
@@ -60,6 +60,7 @@ ActiveRecord::Schema.define(version: version) do
60
60
  t.enum "type", enum_type: :types
61
61
  t.enum "conflicts", enum_type: :conflicts, array: true
62
62
  t.jsonb "metadata"
63
+ # t.column "pieces", :int4multirange
63
64
  t.datetime "created_at", null: false
64
65
  t.datetime "updated_at", null: false
65
66
  end
@@ -92,12 +93,14 @@ ActiveRecord::Schema.define(version: version) do
92
93
  end
93
94
 
94
95
  create_table "courses", force: :cascade do |t|
95
- t.integer "category_id"
96
- t.string "title", null: false
97
- t.interval "duration"
98
- t.enum "types", enum_type: :types, array: true
99
- t.datetime "created_at", null: false
100
- t.datetime "updated_at", null: false
96
+ t.integer "category_id"
97
+ t.string "title", null: false
98
+ t.interval "duration"
99
+ t.enum "types", enum_type: :types, array: true
100
+ t.search_language "lang", null: false, default: 'english'
101
+ t.search_vector "search_vector", columns: :title, language: :lang
102
+ t.datetime "created_at", null: false
103
+ t.datetime "updated_at", null: false
101
104
  end
102
105
 
103
106
  create_table "images", force: :cascade, id: false do |t|
@@ -105,11 +108,12 @@ ActiveRecord::Schema.define(version: version) do
105
108
  end
106
109
 
107
110
  create_table "posts", force: :cascade do |t|
108
- t.integer "author_id"
109
- t.integer "activity_id"
110
- t.string "title"
111
- t.text "content"
112
- t.enum "status", enum_type: :content_status
111
+ t.integer "author_id"
112
+ t.integer "activity_id"
113
+ t.string "title"
114
+ t.text "content"
115
+ t.enum "status", enum_type: :content_status
116
+ t.search_vector "search_vector", columns: %i[title content]
113
117
  t.index ["author_id"], name: "index_posts_on_author_id", using: :btree
114
118
  end
115
119
 
@@ -123,6 +127,7 @@ ActiveRecord::Schema.define(version: version) do
123
127
  create_table "users", force: :cascade do |t|
124
128
  t.string "name", null: false
125
129
  t.enum "role", enum_type: :roles, default: :visitor
130
+ t.integer "age"
126
131
  t.datetime "created_at", null: false
127
132
  t.datetime "updated_at", null: false
128
133
  end
data/spec/spec_helper.rb CHANGED
@@ -4,7 +4,12 @@ require 'factory_bot'
4
4
  require 'dotenv'
5
5
  require 'faker'
6
6
  require 'rspec'
7
- require 'byebug'
7
+
8
+ begin
9
+ require 'debug/prelude'
10
+ rescue LoadError
11
+ # No debugger available, skip
12
+ end
8
13
 
9
14
  Dotenv.load
10
15
 
@@ -21,7 +26,11 @@ cleaner = ->() do
21
26
  cache.instance_variable_set(:@inheritance_associations, {})
22
27
  end
23
28
 
24
- load File.join('schema.rb')
29
+ # Load all the files that are optional and managed by Railtie
30
+ require_relative 'initialize'
31
+
32
+ # This needs to come after loading all optional features
33
+ require_relative 'schema'
25
34
  Dir.glob(File.join('spec', '{models,factories,mocks}', '**', '*.rb')) do |file|
26
35
  require file[5..-4]
27
36
  end
@@ -2,7 +2,6 @@ require 'spec_helper'
2
2
 
3
3
  RSpec.describe 'Arel' do
4
4
  context 'on inflix operation' do
5
- let(:list) { Torque::PostgreSQL::Arel::INFLIX_OPERATION }
6
5
  let(:collector) { ::Arel::Collectors::SQLString }
7
6
  let(:attribute) { ::Arel::Table.new('a')['sample'] }
8
7
  let(:conn) { ActiveRecord::Base.connection }
@@ -21,12 +20,13 @@ RSpec.describe 'Arel' do
21
20
  [:doesnt_right_extend, ::Arel.sql('numrange(5, 6)'), 'numrange(5, 6)'],
22
21
  [:doesnt_left_extend, ::Arel.sql('numrange(7, 8)'), 'numrange(7, 8)'],
23
22
  [:adjacent_to, ::Arel.sql('numrange(9, 0)'), 'numrange(9, 0)'],
24
- ].each do |(operator, value, quoted_value)|
25
- klass_name = operator.to_s.camelize
23
+ ].each do |(operation, value, quoted_value)|
24
+ klass_name = operation.to_s.camelize
26
25
 
27
- context "##{operator}" do
26
+ context "##{operation}" do
27
+ let(:operator) { instance.operator }
28
28
  let(:instance) do
29
- attribute.public_send(operator, value.is_a?(Array) ? ::Arel.array(value) : value)
29
+ attribute.public_send(operation, value.is_a?(Array) ? ::Arel.array(value) : value)
30
30
  end
31
31
 
32
32
  context 'for attribute' do
@@ -41,7 +41,7 @@ RSpec.describe 'Arel' do
41
41
  let(:result) { visitor.accept(instance, collector.new).value }
42
42
 
43
43
  it 'returns a formatted operation' do
44
- expect(result).to be_eql("\"a\".\"sample\" #{list[klass_name]} #{quoted_value}")
44
+ expect(result).to be_eql("\"a\".\"sample\" #{operator} #{quoted_value}")
45
45
  end
46
46
  end
47
47
  end
@@ -123,9 +123,34 @@ RSpec.describe 'Arel' do
123
123
  quoted = ::Arel::Nodes::build_quoted([1])
124
124
  casted = ::Arel::Nodes::build_quoted(1, attribute)
125
125
 
126
+ expect(attribute.pg_cast('text').to_sql).to be_eql('"a"."sample"::text')
127
+ expect(quoted.pg_cast('bigint', true).to_sql).to be_eql('ARRAY[1]::bigint[]')
128
+ expect(casted.pg_cast('string').to_sql).to be_eql("1::string")
129
+ end
130
+
131
+ it 'provides proper support to cast methods' do
132
+ attribute = ::Arel::Table.new('a')['sample']
133
+ quoted = ::Arel::Nodes::build_quoted([1])
134
+ casted = ::Arel::Nodes::build_quoted(1)
135
+
126
136
  expect(attribute.cast('text').to_sql).to be_eql('"a"."sample"::text')
127
137
  expect(quoted.cast('bigint', true).to_sql).to be_eql('ARRAY[1]::bigint[]')
128
- expect(casted.cast('string').to_sql).to be_eql("1::string")
138
+
139
+ changed_result = ActiveRecord.gem_version >= Gem::Version.new('8.0.2')
140
+ changed_result = changed_result ? 'CAST(1 AS string)' : '1::string'
141
+ expect(casted.pg_cast('string').to_sql).to be_eql("1::string")
142
+ end
143
+
144
+ it 'properly works combined on a query' do
145
+ condition = Video.arel_table[:tag_ids].contains([1,2]).cast(:bigint, :array)
146
+ query = Video.all.where(condition).to_sql
147
+
148
+ expect(query).to include('WHERE "videos"."tag_ids" @> ARRAY[1, 2]::bigint[]')
149
+
150
+ condition = QuestionSelect.arel_table[:options].overlaps(%w[a b]).cast(:string, :array)
151
+ query = QuestionSelect.all.where(condition).to_sql
152
+
153
+ expect(query).to include('"options" && ARRAY[\'a\', \'b\']::string[]')
129
154
  end
130
155
  end
131
156
  end
@@ -587,7 +587,7 @@ RSpec.describe 'AuxiliaryStatement' do
587
587
  result << ' FROM "categories"'
588
588
  result << ' WHERE "categories"."parent_id" IS NULL'
589
589
  result << ' UNION'
590
- result << ' SELECT "categories"."id", "categories"."parent_id", array_append("all_categories"."path", "categories"."id"::varchar) AS path'
590
+ result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("all_categories"."path", "categories"."id"::varchar) AS path'
591
591
  result << ' FROM "categories", "all_categories"'
592
592
  result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
593
593
  result << ' ) SELECT "courses".* FROM "courses" INNER JOIN "all_categories"'
@@ -607,7 +607,7 @@ RSpec.describe 'AuxiliaryStatement' do
607
607
  result << ' FROM "categories"'
608
608
  result << ' WHERE "categories"."parent_id" IS NULL'
609
609
  result << ' UNION'
610
- result << ' SELECT "categories"."id", "categories"."parent_id", array_append("all_categories"."p", "categories"."name"::varchar) AS p'
610
+ result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("all_categories"."p", "categories"."name"::varchar) AS p'
611
611
  result << ' FROM "categories", "all_categories"'
612
612
  result << ' WHERE "categories"."parent_id" = "all_categories"."id"'
613
613
  result << ' ) SELECT "courses".*, "all_categories"."p" AS category_path FROM "courses" INNER JOIN "all_categories"'
@@ -889,7 +889,7 @@ RSpec.describe 'AuxiliaryStatement' do
889
889
  result << ' FROM "categories"'
890
890
  result << ' WHERE "categories"."parent_id" IS NULL'
891
891
  result << ' UNION'
892
- result << ' SELECT "categories"."id", "categories"."parent_id", array_append("category"."a", "categories"."b"::varchar) AS a'
892
+ result << ' SELECT "categories"."id", "categories"."parent_id", ARRAY_APPEND("category"."a", "categories"."b"::varchar) AS a'
893
893
  result << ' FROM "categories", "category"'
894
894
  result << ' WHERE "categories"."parent_id" = "category"."id"'
895
895
  result << ' ) SELECT "courses".*, "category"."a" AS c FROM "courses" INNER JOIN "category"'