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.
- checksums.yaml +4 -4
- data/lib/generators/torque/function_generator.rb +13 -0
- data/lib/generators/torque/templates/function.sql.erb +4 -0
- data/lib/generators/torque/templates/type.sql.erb +2 -0
- data/lib/generators/torque/templates/view.sql.erb +3 -0
- data/lib/generators/torque/type_generator.rb +13 -0
- data/lib/generators/torque/view_generator.rb +16 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +111 -94
- data/lib/torque/postgresql/adapter/oid/array.rb +17 -0
- data/lib/torque/postgresql/adapter/oid/line.rb +2 -6
- data/lib/torque/postgresql/adapter/oid/range.rb +4 -4
- data/lib/torque/postgresql/adapter/oid.rb +1 -23
- data/lib/torque/postgresql/adapter/quoting.rb +13 -7
- data/lib/torque/postgresql/adapter/schema_creation.rb +7 -28
- data/lib/torque/postgresql/adapter/schema_definitions.rb +58 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +136 -34
- data/lib/torque/postgresql/adapter/schema_overrides.rb +45 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +109 -49
- data/lib/torque/postgresql/arel/infix_operation.rb +15 -28
- data/lib/torque/postgresql/arel/nodes.rb +16 -2
- data/lib/torque/postgresql/arel/operations.rb +7 -1
- data/lib/torque/postgresql/arel/visitors.rb +7 -9
- data/lib/torque/postgresql/associations/association_scope.rb +23 -31
- data/lib/torque/postgresql/associations/belongs_to_many_association.rb +25 -0
- data/lib/torque/postgresql/associations/builder/belongs_to_many.rb +16 -0
- data/lib/torque/postgresql/attributes/builder/enum.rb +12 -9
- data/lib/torque/postgresql/attributes/builder/full_text_search.rb +109 -0
- data/lib/torque/postgresql/attributes/builder/period.rb +21 -21
- data/lib/torque/postgresql/attributes/builder.rb +49 -11
- data/lib/torque/postgresql/attributes/enum.rb +7 -7
- data/lib/torque/postgresql/attributes/enum_set.rb +7 -7
- data/lib/torque/postgresql/attributes/full_text_search.rb +19 -0
- data/lib/torque/postgresql/attributes/period.rb +2 -2
- data/lib/torque/postgresql/attributes.rb +0 -4
- data/lib/torque/postgresql/auxiliary_statement/recursive.rb +3 -3
- data/lib/torque/postgresql/base.rb +5 -11
- data/lib/torque/postgresql/collector.rb +1 -1
- data/lib/torque/postgresql/config.rb +129 -5
- data/lib/torque/postgresql/function.rb +94 -0
- data/lib/torque/postgresql/inheritance.rb +52 -36
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +33 -0
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +47 -0
- data/lib/torque/postgresql/predicate_builder/enumerator_lazy_handler.rb +37 -0
- data/lib/torque/postgresql/predicate_builder/regexp_handler.rb +21 -0
- data/lib/torque/postgresql/predicate_builder.rb +35 -0
- data/lib/torque/postgresql/railtie.rb +137 -30
- data/lib/torque/postgresql/reflection/abstract_reflection.rb +12 -44
- data/lib/torque/postgresql/reflection/belongs_to_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/has_many_reflection.rb +4 -0
- data/lib/torque/postgresql/reflection/runtime_reflection.rb +1 -1
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -2
- data/lib/torque/postgresql/relation/buckets.rb +124 -0
- data/lib/torque/postgresql/relation/distinct_on.rb +7 -2
- data/lib/torque/postgresql/relation/inheritance.rb +22 -15
- data/lib/torque/postgresql/relation/join_series.rb +112 -0
- data/lib/torque/postgresql/relation/merger.rb +17 -3
- data/lib/torque/postgresql/relation.rb +24 -38
- data/lib/torque/postgresql/schema_cache.rb +6 -12
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql/versioned_commands/command_migration.rb +146 -0
- data/lib/torque/postgresql/versioned_commands/generator.rb +57 -0
- data/lib/torque/postgresql/versioned_commands/migration_context.rb +83 -0
- data/lib/torque/postgresql/versioned_commands/migrator.rb +39 -0
- data/lib/torque/postgresql/versioned_commands/schema_table.rb +101 -0
- data/lib/torque/postgresql/versioned_commands.rb +161 -0
- data/lib/torque/postgresql.rb +2 -1
- data/spec/fixtures/migrations/20250101000001_create_users.rb +0 -0
- data/spec/fixtures/migrations/20250101000002_create_function_count_users_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000003_create_internal_users.rb +0 -0
- data/spec/fixtures/migrations/20250101000004_update_function_count_users_v2.sql +0 -0
- data/spec/fixtures/migrations/20250101000005_create_view_all_users_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000006_create_type_user_id_v1.sql +0 -0
- data/spec/fixtures/migrations/20250101000007_remove_function_count_users_v2.sql +0 -0
- data/spec/initialize.rb +67 -0
- data/spec/mocks/cache_query.rb +21 -21
- data/spec/mocks/create_table.rb +6 -26
- data/spec/schema.rb +17 -12
- data/spec/spec_helper.rb +11 -2
- data/spec/tests/arel_spec.rb +32 -7
- data/spec/tests/auxiliary_statement_spec.rb +3 -3
- data/spec/tests/belongs_to_many_spec.rb +72 -5
- data/spec/tests/enum_set_spec.rb +12 -11
- data/spec/tests/enum_spec.rb +4 -2
- data/spec/tests/full_text_seach_test.rb +280 -0
- data/spec/tests/function_spec.rb +42 -0
- data/spec/tests/has_many_spec.rb +21 -8
- data/spec/tests/interval_spec.rb +1 -7
- data/spec/tests/period_spec.rb +61 -61
- data/spec/tests/predicate_builder_spec.rb +132 -0
- data/spec/tests/relation_spec.rb +229 -0
- data/spec/tests/schema_spec.rb +6 -9
- data/spec/tests/table_inheritance_spec.rb +25 -26
- data/spec/tests/versioned_commands_spec.rb +513 -0
- 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
|
data/lib/torque/postgresql.rb
CHANGED
@@ -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)
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
data/spec/initialize.rb
ADDED
@@ -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
|
data/spec/mocks/cache_query.rb
CHANGED
@@ -1,33 +1,33 @@
|
|
1
1
|
module Mocks
|
2
2
|
module CacheQuery
|
3
|
-
|
4
|
-
|
5
|
-
|
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
|
-
|
7
|
+
map = cache.instance_variable_get(:@map)
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
block.call
|
10
|
+
result = map.keys.first
|
12
11
|
|
13
|
-
|
14
|
-
|
12
|
+
cache.instance_variable_set(:@enabled, false)
|
13
|
+
map.delete(result)
|
15
14
|
|
16
|
-
|
17
|
-
|
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
|
-
|
27
|
-
|
18
|
+
def get_query_with_binds(&block)
|
19
|
+
result = nil
|
28
20
|
|
29
|
-
|
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
|
data/spec/mocks/create_table.rb
CHANGED
@@ -1,34 +1,14 @@
|
|
1
1
|
module Mocks
|
2
2
|
module CreateTable
|
3
3
|
def mock_create_table
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
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
|
-
|
31
|
-
|
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 =
|
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
|
96
|
-
t.string
|
97
|
-
t.interval
|
98
|
-
t.enum
|
99
|
-
t.
|
100
|
-
t.
|
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
|
109
|
-
t.integer
|
110
|
-
t.string
|
111
|
-
t.text
|
112
|
-
t.enum
|
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
|
-
|
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
|
-
|
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
|
data/spec/tests/arel_spec.rb
CHANGED
@@ -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 |(
|
25
|
-
klass_name =
|
23
|
+
].each do |(operation, value, quoted_value)|
|
24
|
+
klass_name = operation.to_s.camelize
|
26
25
|
|
27
|
-
context "##{
|
26
|
+
context "##{operation}" do
|
27
|
+
let(:operator) { instance.operator }
|
28
28
|
let(:instance) do
|
29
|
-
attribute.public_send(
|
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\" #{
|
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
|
-
|
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",
|
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",
|
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",
|
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"'
|