torque-postgresql 4.0.1 → 4.1.1
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/Rakefile +52 -0
- data/lib/generators/torque/skill_generator.rb +17 -0
- data/lib/generators/torque/templates/trigger.sql.erb +3 -0
- data/lib/generators/torque/trigger_generator.rb +13 -0
- data/lib/torque/postgresql/adapter/database_statements.rb +98 -7
- data/lib/torque/postgresql/adapter/inheritance_statements.rb +297 -0
- data/lib/torque/postgresql/adapter/oid/array.rb +13 -0
- data/lib/torque/postgresql/adapter/oid/box.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/circle.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/composite.rb +117 -0
- data/lib/torque/postgresql/adapter/oid/enum.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/enum_set.rb +2 -2
- data/lib/torque/postgresql/adapter/oid/interval.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/line.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/lquery.rb +70 -0
- data/lib/torque/postgresql/adapter/oid/ltree.rb +111 -0
- data/lib/torque/postgresql/adapter/oid/segment.rb +1 -1
- data/lib/torque/postgresql/adapter/oid/struct.rb +150 -0
- data/lib/torque/postgresql/adapter/oid/struct_list.rb +52 -0
- data/lib/torque/postgresql/adapter/oid/struct_set.rb +38 -0
- data/lib/torque/postgresql/adapter/oid.rb +13 -0
- data/lib/torque/postgresql/adapter/quoting.rb +11 -2
- data/lib/torque/postgresql/adapter/schema_definitions.rb +51 -0
- data/lib/torque/postgresql/adapter/schema_dumper.rb +27 -0
- data/lib/torque/postgresql/adapter/schema_statements.rb +207 -0
- data/lib/torque/postgresql/adapter.rb +2 -5
- data/lib/torque/postgresql/arel/nodes.rb +114 -1
- data/lib/torque/postgresql/arel/visitors.rb +65 -8
- data/lib/torque/postgresql/associations/join_dependency.rb +55 -0
- data/lib/torque/postgresql/associations.rb +3 -0
- data/lib/torque/postgresql/attributes/base.rb +97 -0
- data/lib/torque/postgresql/attributes/composite.rb +98 -0
- data/lib/torque/postgresql/attributes/enum.rb +13 -7
- data/lib/torque/postgresql/attributes/lquery.rb +120 -0
- data/lib/torque/postgresql/attributes/ltree.rb +121 -0
- data/lib/torque/postgresql/attributes/simple_enum.rb +72 -0
- data/lib/torque/postgresql/attributes/struct.rb +137 -0
- data/lib/torque/postgresql/base.rb +26 -15
- data/lib/torque/postgresql/config.rb +100 -15
- data/lib/torque/postgresql/geometry_builder.rb +4 -10
- data/lib/torque/postgresql/inheritance/expander.rb +66 -0
- data/lib/torque/postgresql/inheritance/record.rb +52 -0
- data/lib/torque/postgresql/inheritance.rb +141 -68
- data/lib/torque/postgresql/migration/command_recorder.rb +76 -0
- data/lib/torque/postgresql/predicate_builder/arel_attribute_handler.rb +1 -1
- data/lib/torque/postgresql/predicate_builder/array_handler.rb +16 -8
- data/lib/torque/postgresql/predicate_builder/composite_handler.rb +122 -0
- data/lib/torque/postgresql/predicate_builder/ltree_handler.rb +112 -0
- data/lib/torque/postgresql/predicate_builder/struct_handler.rb +88 -0
- data/lib/torque/postgresql/predicate_builder.rb +53 -0
- data/lib/torque/postgresql/predicate_table.rb +61 -0
- data/lib/torque/postgresql/railtie.rb +52 -0
- data/lib/torque/postgresql/relation/auxiliary_statement.rb +7 -3
- data/lib/torque/postgresql/relation/buckets.rb +2 -1
- data/lib/torque/postgresql/relation/cross_join.rb +193 -0
- data/lib/torque/postgresql/relation/inheritance.rb +145 -28
- data/lib/torque/postgresql/relation/join_series.rb +1 -0
- data/lib/torque/postgresql/relation/merger.rb +30 -5
- data/lib/torque/postgresql/relation/segments.rb +140 -0
- data/lib/torque/postgresql/relation.rb +32 -12
- data/lib/torque/postgresql/schema_cache.rb +1 -0
- data/lib/torque/postgresql/validations.rb +29 -0
- data/lib/torque/postgresql/version.rb +1 -1
- data/lib/torque/postgresql/versioned_commands/command_migration.rb +24 -1
- data/lib/torque/postgresql/versioned_commands.rb +39 -0
- data/lib/torque/postgresql.rb +4 -0
- data/sig/generators/torque/function_generator.rbs +9 -0
- data/sig/generators/torque/skill_generator.rbs +7 -0
- data/sig/generators/torque/trigger_generator.rbs +9 -0
- data/sig/generators/torque/type_generator.rbs +9 -0
- data/sig/generators/torque/view_generator.rbs +9 -0
- data/sig/manifest.yaml +7 -0
- data/sig/torque/postgresql/adapter/database_statements.rbs +42 -0
- data/sig/torque/postgresql/adapter/inheritance_statements.rbs +36 -0
- data/sig/torque/postgresql/adapter/oid/array.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/box.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/circle.rbs +33 -0
- data/sig/torque/postgresql/adapter/oid/composite.rbs +35 -0
- data/sig/torque/postgresql/adapter/oid/enum.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/enum_set.rbs +19 -0
- data/sig/torque/postgresql/adapter/oid/interval.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/line.rbs +33 -0
- data/sig/torque/postgresql/adapter/oid/lquery.rbs +21 -0
- data/sig/torque/postgresql/adapter/oid/ltree.rbs +32 -0
- data/sig/torque/postgresql/adapter/oid/range.rbs +28 -0
- data/sig/torque/postgresql/adapter/oid/segment.rbs +38 -0
- data/sig/torque/postgresql/adapter/oid/struct.rbs +41 -0
- data/sig/torque/postgresql/adapter/oid/struct_list.rbs +20 -0
- data/sig/torque/postgresql/adapter/oid/struct_set.rbs +18 -0
- data/sig/torque/postgresql/adapter/oid.rbs +9 -0
- data/sig/torque/postgresql/adapter/quoting.rbs +20 -0
- data/sig/torque/postgresql/adapter/schema_creation.rbs +13 -0
- data/sig/torque/postgresql/adapter/schema_definitions.rbs +55 -0
- data/sig/torque/postgresql/adapter/schema_dumper.rbs +37 -0
- data/sig/torque/postgresql/adapter/schema_overrides.rbs +46 -0
- data/sig/torque/postgresql/adapter/schema_statements.rbs +50 -0
- data/sig/torque/postgresql/adapter.rbs +24 -0
- data/sig/torque/postgresql/arel/infix_operation.rbs +79 -0
- data/sig/torque/postgresql/arel/join_source.rbs +19 -0
- data/sig/torque/postgresql/arel/nodes.rbs +83 -0
- data/sig/torque/postgresql/arel/operations.rbs +28 -0
- data/sig/torque/postgresql/arel/select_manager.rbs +15 -0
- data/sig/torque/postgresql/arel/visitors.rbs +30 -0
- data/sig/torque/postgresql/arel.rbs +6 -0
- data/sig/torque/postgresql/associations/association_scope.rbs +28 -0
- data/sig/torque/postgresql/associations/belongs_to_many_association.rbs +51 -0
- data/sig/torque/postgresql/associations/builder/belongs_to_many.rbs +24 -0
- data/sig/torque/postgresql/associations/builder/has_many.rbs +15 -0
- data/sig/torque/postgresql/associations/builder.rbs +8 -0
- data/sig/torque/postgresql/associations/foreign_association.rbs +16 -0
- data/sig/torque/postgresql/associations/join_dependency.rbs +16 -0
- data/sig/torque/postgresql/associations/preloader/association.rbs +32 -0
- data/sig/torque/postgresql/associations/preloader/loader_query.rbs +20 -0
- data/sig/torque/postgresql/associations/preloader.rbs +8 -0
- data/sig/torque/postgresql/associations.rbs +17 -0
- data/sig/torque/postgresql/attributes/base.rbs +37 -0
- data/sig/torque/postgresql/attributes/builder/enum.rbs +38 -0
- data/sig/torque/postgresql/attributes/builder/full_text_search.rbs +27 -0
- data/sig/torque/postgresql/attributes/builder/period.rbs +106 -0
- data/sig/torque/postgresql/attributes/builder.rbs +19 -0
- data/sig/torque/postgresql/attributes/composite.rbs +28 -0
- data/sig/torque/postgresql/attributes/enum.rbs +69 -0
- data/sig/torque/postgresql/attributes/enum_set.rbs +75 -0
- data/sig/torque/postgresql/attributes/full_text_search.rbs +13 -0
- data/sig/torque/postgresql/attributes/lazy.rbs +17 -0
- data/sig/torque/postgresql/attributes/lquery.rbs +48 -0
- data/sig/torque/postgresql/attributes/ltree.rbs +46 -0
- data/sig/torque/postgresql/attributes/period.rbs +13 -0
- data/sig/torque/postgresql/attributes/simple_enum.rbs +39 -0
- data/sig/torque/postgresql/attributes/struct.rbs +38 -0
- data/sig/torque/postgresql/attributes.rbs +6 -0
- data/sig/torque/postgresql/autosave_association.rbs +16 -0
- data/sig/torque/postgresql/auxiliary_statement/recursive.rbs +22 -0
- data/sig/torque/postgresql/auxiliary_statement/settings.rbs +63 -0
- data/sig/torque/postgresql/auxiliary_statement.rbs +57 -0
- data/sig/torque/postgresql/base.rbs +41 -0
- data/sig/torque/postgresql/collector.rbs +7 -0
- data/sig/torque/postgresql/config.rbs +9 -0
- data/sig/torque/postgresql/function.rbs +21 -0
- data/sig/torque/postgresql/geometry_builder.rbs +20 -0
- data/sig/torque/postgresql/i18n.rbs +15 -0
- data/sig/torque/postgresql/inheritance/expander.rbs +23 -0
- data/sig/torque/postgresql/inheritance/record.rbs +20 -0
- data/sig/torque/postgresql/inheritance.rbs +47 -0
- data/sig/torque/postgresql/insert_all.rbs +22 -0
- data/sig/torque/postgresql/migration/command_recorder.rbs +30 -0
- data/sig/torque/postgresql/migration.rbs +6 -0
- data/sig/torque/postgresql/predicate_builder/arel_attribute_handler.rbs +15 -0
- data/sig/torque/postgresql/predicate_builder/array_handler.rbs +26 -0
- data/sig/torque/postgresql/predicate_builder/composite_handler.rbs +26 -0
- data/sig/torque/postgresql/predicate_builder/enumerator_lazy_handler.rbs +18 -0
- data/sig/torque/postgresql/predicate_builder/ltree_handler.rbs +36 -0
- data/sig/torque/postgresql/predicate_builder/regexp_handler.rbs +16 -0
- data/sig/torque/postgresql/predicate_builder/struct_handler.rbs +24 -0
- data/sig/torque/postgresql/predicate_builder.rbs +23 -0
- data/sig/torque/postgresql/predicate_table.rbs +24 -0
- data/sig/torque/postgresql/railtie.rbs +10 -0
- data/sig/torque/postgresql/reflection/abstract_reflection.rbs +22 -0
- data/sig/torque/postgresql/reflection/association_reflection.rbs +18 -0
- data/sig/torque/postgresql/reflection/belongs_to_many_reflection.rbs +30 -0
- data/sig/torque/postgresql/reflection/has_many_reflection.rbs +14 -0
- data/sig/torque/postgresql/reflection/runtime_reflection.rbs +18 -0
- data/sig/torque/postgresql/reflection/through_reflection.rbs +14 -0
- data/sig/torque/postgresql/reflection.rbs +11 -0
- data/sig/torque/postgresql/relation/auxiliary_statement.rbs +29 -0
- data/sig/torque/postgresql/relation/buckets.rbs +35 -0
- data/sig/torque/postgresql/relation/cross_join.rbs +63 -0
- data/sig/torque/postgresql/relation/distinct_on.rbs +16 -0
- data/sig/torque/postgresql/relation/inheritance.rbs +48 -0
- data/sig/torque/postgresql/relation/join_series.rbs +28 -0
- data/sig/torque/postgresql/relation/merger.rbs +23 -0
- data/sig/torque/postgresql/relation/segments.rbs +27 -0
- data/sig/torque/postgresql/relation.rbs +48 -0
- data/sig/torque/postgresql/schema_cache/bound_schema_reflection.rbs +14 -0
- data/sig/torque/postgresql/schema_cache/inheritance.rbs +12 -0
- data/sig/torque/postgresql/schema_cache/schema_reflection.rbs +14 -0
- data/sig/torque/postgresql/schema_cache.rbs +43 -0
- data/sig/torque/postgresql/table_name.rbs +20 -0
- data/sig/torque/postgresql/validations.rbs +13 -0
- data/sig/torque/postgresql/version.rbs +5 -0
- data/sig/torque/postgresql/versioned_commands/command_migration.rbs +54 -0
- data/sig/torque/postgresql/versioned_commands/generator.rbs +17 -0
- data/sig/torque/postgresql/versioned_commands/migration_context.rbs +30 -0
- data/sig/torque/postgresql/versioned_commands/migrator.rbs +19 -0
- data/sig/torque/postgresql/versioned_commands/schema_table.rbs +23 -0
- data/sig/torque/postgresql/versioned_commands.rbs +25 -0
- data/sig/torque/postgresql.rbs +26 -0
- data/skills/torque-postgresql/SKILL.md +70 -0
- data/spec/fixtures/migrations/20250101000008_create_trigger_audit_users_v1.sql +3 -0
- data/spec/initialize.rb +29 -2
- data/spec/mocks/cache_query.rb +13 -1
- data/spec/models/author.rb +1 -1
- data/spec/models/comment.rb +2 -0
- data/spec/models/place.rb +2 -0
- data/spec/models/profile.rb +31 -0
- data/spec/rbs_helper.rb +8 -0
- data/spec/schema.rb +35 -4
- data/spec/spec_helper.rb +1 -0
- data/spec/tests/arel_spec.rb +3 -3
- data/spec/tests/auxiliary_statement_spec.rb +12 -0
- data/spec/tests/buckets_spec.rb +122 -0
- data/spec/tests/composite_spec.rb +867 -0
- data/spec/tests/cross_join_spec.rb +185 -0
- data/spec/tests/distinct_on_spec.rb +5 -0
- data/spec/tests/enum_set_spec.rb +4 -0
- data/spec/tests/enum_spec.rb +20 -4
- data/spec/tests/geometric_builder_spec.rb +16 -5
- data/spec/tests/has_many_spec.rb +2 -1
- data/spec/tests/interval_spec.rb +6 -0
- data/spec/tests/ltree_spec.rb +886 -0
- data/spec/tests/predicate_builder_spec.rb +120 -0
- data/spec/tests/relation_spec.rb +25 -69
- data/spec/tests/segments_spec.rb +264 -0
- data/spec/tests/struct_spec.rb +1170 -0
- data/spec/tests/table_inheritance_spec.rb +1037 -56
- data/spec/tests/versioned_commands_spec.rb +145 -0
- metadata +198 -2
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
require 'spec_helper'
|
|
2
|
+
|
|
3
|
+
RSpec.describe 'CrossJoin' do
|
|
4
|
+
let(:table) { Profile.arel_table }
|
|
5
|
+
let(:snippets) { Torque::PostgreSQL::FN.unnest(table[:snippets]) }
|
|
6
|
+
|
|
7
|
+
context 'on model' do
|
|
8
|
+
it 'has its method' do
|
|
9
|
+
expect(Profile).to respond_to(:cross_join)
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
it 'returns a relation when using the method' do
|
|
13
|
+
expect(Profile.cross_join(snippets, as: :doc)).to be_a(ActiveRecord::Relation)
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
context 'on relation' do
|
|
18
|
+
subject { Profile.unscoped }
|
|
19
|
+
|
|
20
|
+
it 'has its method' do
|
|
21
|
+
expect(subject).to respond_to(:cross_join)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
it 'joins an arel function without naming its columns' do
|
|
25
|
+
expect(subject.cross_join(snippets, as: :doc).to_sql).to \
|
|
26
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST("profiles"."snippets") "doc"')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
it 'names the columns when they are given' do
|
|
30
|
+
expect(subject.cross_join(snippets, as: :doc, columns: %i[value]).to_sql).to \
|
|
31
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST("profiles"."snippets") "doc"("value")')
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
it 'does not write LATERAL for a function' do
|
|
35
|
+
expect(subject.cross_join(snippets, as: :doc).to_sql).not_to include('LATERAL')
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
it 'does not write ROWS FROM without an ordinality' do
|
|
39
|
+
expect(subject.cross_join(snippets, as: :doc).to_sql).not_to include('ROWS FROM')
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
it 'raises for a source it cannot join' do
|
|
43
|
+
expect { subject.cross_join('whatever', as: :doc) }.to \
|
|
44
|
+
raise_error(ArgumentError, /Unable to join/)
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
context 'modes' do
|
|
49
|
+
subject { Profile.unscoped }
|
|
50
|
+
|
|
51
|
+
it 'writes a left join against a true condition' do
|
|
52
|
+
expect(subject.cross_join(snippets, as: :doc, mode: :left).to_sql).to \
|
|
53
|
+
eql('SELECT "profiles".* FROM "profiles" LEFT OUTER JOIN UNNEST("profiles"."snippets") "doc" ON TRUE')
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
it 'raises for a mode postgres cannot combine with a lateral reference' do
|
|
57
|
+
expect { subject.cross_join(snippets, as: :doc, mode: :right) }.to \
|
|
58
|
+
raise_error(ArgumentError, /not implemented as a join type/)
|
|
59
|
+
end
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
context 'rows' do
|
|
63
|
+
subject { Profile.unscoped }
|
|
64
|
+
|
|
65
|
+
it 'turns a list of documents into one array per column' do
|
|
66
|
+
result = subject.cross_join([{ id: 1, label: 'a' }, { id: 2, label: 'b' }], as: :v)
|
|
67
|
+
expect(result.to_sql).to \
|
|
68
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST(ARRAY[1, 2], ARRAY[\'a\', \'b\']) "v"("id", "label")')
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
it 'accepts plain rows when the columns are named' do
|
|
72
|
+
result = subject.cross_join([[1, 'a']], as: :v, columns: %i[id label])
|
|
73
|
+
expect(result.to_sql).to \
|
|
74
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST(ARRAY[1], ARRAY[\'a\']) "v"("id", "label")')
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
it 'reads the documents by name, in any order' do
|
|
78
|
+
result = subject.cross_join([{ id: 1, label: 'a' }, { label: 'b', id: 2 }], as: :v)
|
|
79
|
+
expect(result.to_sql).to \
|
|
80
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST(ARRAY[1, 2], ARRAY[\'a\', \'b\']) "v"("id", "label")')
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
it 'does not care whether the keys are strings or symbols' do
|
|
84
|
+
result = subject.cross_join([{ 'id' => 1 }], as: :v, columns: %i[id])
|
|
85
|
+
expect(result.to_sql).to include('UNNEST(ARRAY[1]) "v"("id")')
|
|
86
|
+
end
|
|
87
|
+
|
|
88
|
+
it 'raises when the columns cannot be named' do
|
|
89
|
+
expect { subject.cross_join([[1, 'a']], as: :v) }.to \
|
|
90
|
+
raise_error(ArgumentError, /Unable to name the columns/)
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
it 'raises for an empty list' do
|
|
94
|
+
expect { subject.cross_join([], as: :v) }.to \
|
|
95
|
+
raise_error(ArgumentError, /empty list/)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
it 'multiplies the rows of the query' do
|
|
99
|
+
Profile.create!(name: 'one')
|
|
100
|
+
Profile.create!(name: 'two')
|
|
101
|
+
|
|
102
|
+
expect(subject.cross_join([{ n: 1 }, { n: 2 }, { n: 3 }], as: :v).count).to be_eql(6)
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
it 'keeps the values typed' do
|
|
106
|
+
Profile.create!(name: 'one')
|
|
107
|
+
result = subject.cross_join([{ n: 10 }], as: :v).pluck(Arel.sql('"v"."n"'))
|
|
108
|
+
|
|
109
|
+
expect(result).to be_eql([10])
|
|
110
|
+
end
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
context 'a relation' do
|
|
114
|
+
subject { Profile.unscoped }
|
|
115
|
+
|
|
116
|
+
it 'is written as a lateral subquery' do
|
|
117
|
+
other = Profile.select(:name).limit(1)
|
|
118
|
+
expect(subject.cross_join(other, as: :other, columns: %i[name]).to_sql).to \
|
|
119
|
+
include('CROSS JOIN LATERAL (SELECT "profiles"."name" FROM "profiles" LIMIT 1) "other"("name")')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
it 'keeps the bindings of both queries apart' do
|
|
123
|
+
Profile.create!(name: 'aaa')
|
|
124
|
+
Profile.create!(name: 'bbb')
|
|
125
|
+
|
|
126
|
+
other = Profile.where(name: 'aaa').select(:name).limit(1)
|
|
127
|
+
result = subject.where(name: 'bbb').cross_join(other, as: :other, columns: %i[name])
|
|
128
|
+
|
|
129
|
+
expect(result.pluck(:name, Arel.sql('"other"."name"'))).to be_eql([%w[bbb aaa]])
|
|
130
|
+
end
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
context 'with_ordinality' do
|
|
134
|
+
subject { Profile.unscoped }
|
|
135
|
+
|
|
136
|
+
it 'uses the configured name' do
|
|
137
|
+
expect(subject.cross_join(snippets, as: :doc, columns: %i[value], with_ordinality: true).to_sql).to \
|
|
138
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST("profiles"."snippets")' \
|
|
139
|
+
' WITH ORDINALITY "doc"("value", "ordinality")')
|
|
140
|
+
end
|
|
141
|
+
|
|
142
|
+
it 'accepts a name of its own' do
|
|
143
|
+
expect(subject.cross_join(snippets, as: :doc, columns: %i[value], with_ordinality: :idx).to_sql).to \
|
|
144
|
+
eql('SELECT "profiles".* FROM "profiles" CROSS JOIN UNNEST("profiles"."snippets")' \
|
|
145
|
+
' WITH ORDINALITY "doc"("value", "idx")')
|
|
146
|
+
end
|
|
147
|
+
|
|
148
|
+
it 'leaves the list out when the columns are unknown' do
|
|
149
|
+
result = subject.cross_join(snippets, as: :doc, with_ordinality: true).to_sql
|
|
150
|
+
expect(result).to end_with('WITH ORDINALITY "doc"')
|
|
151
|
+
end
|
|
152
|
+
|
|
153
|
+
it 'refuses a name of its own when the columns are unknown' do
|
|
154
|
+
expect { subject.cross_join(snippets, as: :doc, with_ordinality: :idx) }.to \
|
|
155
|
+
raise_error(ArgumentError, /columns of the source are unknown/)
|
|
156
|
+
end
|
|
157
|
+
|
|
158
|
+
it 'counts from one, in the order of the list' do
|
|
159
|
+
Profile.create!(name: 'one')
|
|
160
|
+
result = subject.cross_join([{ n: 'a' }, { n: 'b' }, { n: 'c' }], as: :v, with_ordinality: true)
|
|
161
|
+
|
|
162
|
+
expect(result.pluck(Arel.sql('"v"."ordinality"'))).to be_eql([1, 2, 3])
|
|
163
|
+
end
|
|
164
|
+
end
|
|
165
|
+
|
|
166
|
+
context 'scoping' do
|
|
167
|
+
subject { Profile.unscoped.cross_join(snippets, as: :doc) }
|
|
168
|
+
|
|
169
|
+
it 'is removed by unscope' do
|
|
170
|
+
expect(subject.unscope(:cross_joins).to_sql).to eql('SELECT "profiles".* FROM "profiles"')
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
it 'is removed by except' do
|
|
174
|
+
expect(subject.except(:cross_joins).to_sql).to eql('SELECT "profiles".* FROM "profiles"')
|
|
175
|
+
end
|
|
176
|
+
|
|
177
|
+
it 'is carried over by merge' do
|
|
178
|
+
expect(Profile.all.merge(subject).to_sql).to include('CROSS JOIN UNNEST')
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
it 'is not removed by except(:joins)' do
|
|
182
|
+
expect(subject.except(:joins).to_sql).to include('CROSS JOIN UNNEST')
|
|
183
|
+
end
|
|
184
|
+
end
|
|
185
|
+
end
|
|
@@ -39,6 +39,11 @@ RSpec.describe 'DistinctOn' do
|
|
|
39
39
|
eql('SELECT DISTINCT ON ( "authors"."name", "authors"."age" ) "posts".* FROM "posts"')
|
|
40
40
|
end
|
|
41
41
|
|
|
42
|
+
it 'is able to do with a part of a column' do
|
|
43
|
+
expect(Profile.distinct_on(settings: :theme).to_sql).to \
|
|
44
|
+
eql(%{SELECT DISTINCT ON ( ("profiles"."settings" #>> ARRAY['theme']) ) "profiles".* FROM "profiles"})
|
|
45
|
+
end
|
|
46
|
+
|
|
42
47
|
it 'raises with invalid relation' do
|
|
43
48
|
expect { subject.distinct_on(supervisors: :name).to_sql }.to \
|
|
44
49
|
raise_error(ArgumentError, /Relation for/)
|
data/spec/tests/enum_set_spec.rb
CHANGED
|
@@ -227,6 +227,10 @@ RSpec.describe 'Enum' do
|
|
|
227
227
|
expect(subject.serialize(enum.B | enum.C)).to be_eql('{B,C}')
|
|
228
228
|
expect(subject.serialize(3)).to be_eql('{A,B}')
|
|
229
229
|
end
|
|
230
|
+
|
|
231
|
+
it 'keeps an invalid value for the database to reject' do
|
|
232
|
+
expect(subject.serialize(%w[A E])).to be_eql('{A,E}')
|
|
233
|
+
end
|
|
230
234
|
end
|
|
231
235
|
|
|
232
236
|
context 'on cast' do
|
data/spec/tests/enum_spec.rb
CHANGED
|
@@ -98,6 +98,13 @@ RSpec.describe 'Enum' do
|
|
|
98
98
|
expect(subject.values).to be_eql(values)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
it 'loads the values without a permanent connection' do
|
|
102
|
+
expect(ActiveRecord::Base).not_to receive(:connection)
|
|
103
|
+
|
|
104
|
+
subject.remove_instance_variable(:@values) if subject.instance_variable_defined?(:@values)
|
|
105
|
+
expect(subject.values).to be_eql(values)
|
|
106
|
+
end
|
|
107
|
+
|
|
101
108
|
it 'can return a sample value' do
|
|
102
109
|
expect(Enum).to respond_to(:sample)
|
|
103
110
|
expect(Enum::ContentStatus).to respond_to(:sample)
|
|
@@ -291,8 +298,13 @@ RSpec.describe 'Enum' do
|
|
|
291
298
|
context 'on serialize' do
|
|
292
299
|
it 'returns nil' do
|
|
293
300
|
expect(subject.serialize(nil)).to be_nil
|
|
294
|
-
|
|
295
|
-
|
|
301
|
+
end
|
|
302
|
+
|
|
303
|
+
it 'keeps an invalid value for the database to reject' do
|
|
304
|
+
expect(subject.serialize('test')).to be_eql('test')
|
|
305
|
+
expect(subject.serialize(15)).to be_eql('15')
|
|
306
|
+
expect { User.create!(name: 'Rick', role: 'bogus') }
|
|
307
|
+
.to raise_error(ActiveRecord::StatementInvalid, /invalid input value for enum/)
|
|
296
308
|
end
|
|
297
309
|
|
|
298
310
|
it 'returns as string' do
|
|
@@ -306,12 +318,16 @@ RSpec.describe 'Enum' do
|
|
|
306
318
|
expect(subject.cast(nil)).to be_nil
|
|
307
319
|
end
|
|
308
320
|
|
|
309
|
-
it 'accepts
|
|
321
|
+
it 'accepts blank values as nil' do
|
|
310
322
|
expect(subject.cast(false)).to be_nil
|
|
311
|
-
expect(subject.cast(true)).to be_nil
|
|
312
323
|
expect(subject.cast([])).to be_nil
|
|
313
324
|
end
|
|
314
325
|
|
|
326
|
+
it 'keeps invalid values for the database to reject' do
|
|
327
|
+
expect(subject.cast(true)).to be(true)
|
|
328
|
+
expect(subject.cast('bogus')).to be_eql('bogus')
|
|
329
|
+
end
|
|
330
|
+
|
|
315
331
|
it 'accepts string' do
|
|
316
332
|
value = subject.cast('created')
|
|
317
333
|
expect(value).to be_a(enum)
|
|
@@ -73,13 +73,23 @@ RSpec.describe 'Geometries' do
|
|
|
73
73
|
expect(config_class).to receive(:new).with(1.0, 2.0, 3.0, 4.0).and_return(1)
|
|
74
74
|
expect(instance.cast('1.0, 2.0, 3.0, 4.0')).to be_eql(1)
|
|
75
75
|
|
|
76
|
-
expect
|
|
76
|
+
expect(config_class).to receive(:new).with(6.0).and_return(9)
|
|
77
|
+
expect(instance.cast(['6 6 6'])).to be_eql(9)
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
it 'does not change the given value' do
|
|
81
|
+
value = '(1, 2, 3, 4)'
|
|
82
|
+
expect(config_class).to receive(:new).with(1, 2, 3, 4).twice.and_return(1)
|
|
83
|
+
expect(instance.cast(value)).to be_eql(1)
|
|
84
|
+
expect(value).to be_eql('(1, 2, 3, 4)')
|
|
85
|
+
expect(instance.cast(value.freeze)).to be_eql(1)
|
|
77
86
|
end
|
|
78
87
|
|
|
79
88
|
it 'accepts hash values' do
|
|
80
89
|
expect(instance.cast({})).to be_nil
|
|
81
90
|
|
|
82
|
-
expect
|
|
91
|
+
expect(config_class).to receive(:new).with(1, 2).and_return(9)
|
|
92
|
+
expect(instance.cast({ 'a' => 1, 'b' => 2 })).to be_eql(9)
|
|
83
93
|
|
|
84
94
|
expect(config_class).to receive(:new).with(1, 2, 3, 4).and_return(4)
|
|
85
95
|
expect(instance.cast({ 'a' => 1, 'b' => 2 , 'c' => 3, 'd' => 4})).to be_eql(4)
|
|
@@ -103,7 +113,8 @@ RSpec.describe 'Geometries' do
|
|
|
103
113
|
|
|
104
114
|
expect(instance.cast([])).to be_nil
|
|
105
115
|
|
|
106
|
-
expect
|
|
116
|
+
expect(config_class).to receive(:new).with(6, 5, 4).and_return(9)
|
|
117
|
+
expect(instance.cast([6, 5, 4])).to be_eql(9)
|
|
107
118
|
end
|
|
108
119
|
end
|
|
109
120
|
|
|
@@ -121,13 +132,13 @@ RSpec.describe 'Geometries' do
|
|
|
121
132
|
end
|
|
122
133
|
|
|
123
134
|
it 'accepts hash value' do
|
|
124
|
-
expect
|
|
135
|
+
expect(instance.serialize({a: 1, b: 2, c: 3})).to be_eql('(1, 2, <3, {}>)')
|
|
125
136
|
expect(instance.serialize({a: 1, b: 2, c: 3, d: 4})).to be_eql('(1, 2, <3, {4}>)')
|
|
126
137
|
expect(instance.serialize({a: 1, b: 2, c: 3, d: 4, e: 5, f: 6})).to be_eql('(1, 2, <3, {4}>)')
|
|
127
138
|
end
|
|
128
139
|
|
|
129
140
|
it 'accepts array value' do
|
|
130
|
-
expect
|
|
141
|
+
expect(instance.serialize([6, 5, 4])).to be_eql('(6, 5, <4, {}>)')
|
|
131
142
|
expect(instance.serialize([1, 2, 3, 4])).to be_eql('(1, 2, <3, {4}>)')
|
|
132
143
|
expect(instance.serialize([5, 4, 3, 2, 1, 0])).to be_eql('(5, 4, <3, {2}>)')
|
|
133
144
|
end
|
data/spec/tests/has_many_spec.rb
CHANGED
|
@@ -204,7 +204,8 @@ RSpec.describe 'HasMany' do
|
|
|
204
204
|
end
|
|
205
205
|
|
|
206
206
|
context 'with query constraint' do
|
|
207
|
-
|
|
207
|
+
# why: Rails 8.1.4+ skips preloading for owners whose composite key has a nil part
|
|
208
|
+
let(:activity) { Activity.create!(author: FactoryBot.create(:author)) }
|
|
208
209
|
|
|
209
210
|
before do
|
|
210
211
|
skip('Only Rails 7.1 onwards') unless Post.respond_to?(:query_constraints)
|
data/spec/tests/interval_spec.rb
CHANGED
|
@@ -98,6 +98,12 @@ RSpec.describe 'Interval' do
|
|
|
98
98
|
expect(value).to eq(reference)
|
|
99
99
|
end
|
|
100
100
|
|
|
101
|
+
it 'does not change the given array' do
|
|
102
|
+
value = [1, nil, 2]
|
|
103
|
+
subject.cast(value)
|
|
104
|
+
expect(value).to be_eql([1, nil, 2])
|
|
105
|
+
end
|
|
106
|
+
|
|
101
107
|
it 'accepts array with Y-M-D H:M:S format' do
|
|
102
108
|
value = subject.cast([1, 2, 3, 4, 5, 6])
|
|
103
109
|
expect(value).to be_a(ActiveSupport::Duration)
|