dbee-active_record 2.0.4 → 2.2.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/.rubocop.yml +14 -13
- data/.ruby-version +1 -1
- data/.travis.yml +4 -10
- data/CHANGELOG.md +24 -0
- data/Guardfile +2 -1
- data/README.md +1 -1
- data/dbee-active_record.gemspec +21 -8
- data/exe/.gitkeep +0 -0
- data/lib/dbee/providers/active_record_provider.rb +3 -3
- data/lib/dbee/providers/active_record_provider/expression_builder.rb +96 -55
- data/lib/dbee/providers/active_record_provider/maker.rb +37 -0
- data/lib/dbee/providers/active_record_provider/{expression_builder/constraint_maker.rb → makers/constraint.rb} +12 -12
- data/lib/dbee/providers/active_record_provider/{expression_builder/order_maker.rb → makers/order.rb} +9 -9
- data/lib/dbee/providers/active_record_provider/makers/select.rb +81 -0
- data/lib/dbee/providers/active_record_provider/makers/where.rb +111 -0
- data/lib/dbee/providers/active_record_provider/version.rb +1 -1
- data/spec/db_helper.rb +134 -14
- data/spec/dbee/providers/active_record_provider/expression_builder_spec.rb +90 -0
- data/spec/dbee/providers/active_record_provider/makers/where_spec.rb +260 -0
- data/spec/dbee/providers/active_record_provider_spec.rb +112 -14
- data/spec/fixtures/active_record_snapshots/five_table_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/multiple_same_table_query_with_static_constraints.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_empty_query.yaml +11 -0
- data/spec/fixtures/active_record_snapshots/one_table_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_ascending_sort.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_descending_sort.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_filters.yaml +9 -8
- data/spec/fixtures/active_record_snapshots/one_table_query_with_limit.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/one_table_query_with_multiple_sorts.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/partitioner_example_1_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/partitioner_example_2_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/reverse_polymorphic_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/two_table_query.yaml +1 -0
- data/spec/fixtures/active_record_snapshots/two_table_query_with_aggregation.yaml +72 -0
- data/spec/fixtures/active_record_snapshots/two_table_query_with_pivoting.yaml +89 -0
- data/spec/fixtures/models.yaml +112 -84
- data/spec/spec_helper.rb +13 -2
- metadata +96 -28
- data/lib/dbee/providers/active_record_provider/expression_builder/select_maker.rb +0 -33
- data/lib/dbee/providers/active_record_provider/expression_builder/where_maker.rb +0 -68
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2019-present, Blue Marble Payroll, LLC
|
|
5
|
+
#
|
|
6
|
+
# This source code is licensed under the MIT license found in the
|
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require 'spec_helper'
|
|
11
|
+
require 'db_helper'
|
|
12
|
+
|
|
13
|
+
describe Dbee::Providers::ActiveRecordProvider::ExpressionBuilder do
|
|
14
|
+
let(:schema) { Dbee::Schema.new(models['Patients']) }
|
|
15
|
+
let(:alias_maker) { Dbee::Providers::ActiveRecordProvider::SafeAliasMaker.new }
|
|
16
|
+
|
|
17
|
+
let(:empty_query) { Dbee::Query.make(from: :patients) }
|
|
18
|
+
|
|
19
|
+
let(:id_and_average_query) do
|
|
20
|
+
Dbee::Query.make(
|
|
21
|
+
from: :patients,
|
|
22
|
+
fields: [
|
|
23
|
+
{
|
|
24
|
+
key_path: :id,
|
|
25
|
+
display: 'ID #'
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
key_path: 'patient_payments.amount',
|
|
29
|
+
display: 'Ave Payment',
|
|
30
|
+
aggregator: :ave
|
|
31
|
+
}
|
|
32
|
+
],
|
|
33
|
+
sorters: [
|
|
34
|
+
{
|
|
35
|
+
key_path: :id
|
|
36
|
+
}
|
|
37
|
+
],
|
|
38
|
+
filters: [
|
|
39
|
+
{
|
|
40
|
+
key_path: :id,
|
|
41
|
+
value: 123
|
|
42
|
+
}
|
|
43
|
+
]
|
|
44
|
+
)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
let(:first_and_count_query) do
|
|
48
|
+
Dbee::Query.make(
|
|
49
|
+
from: :patients,
|
|
50
|
+
fields: [
|
|
51
|
+
{
|
|
52
|
+
key_path: :first,
|
|
53
|
+
display: 'First Name'
|
|
54
|
+
},
|
|
55
|
+
{
|
|
56
|
+
key_path: 'patient_payments.amount',
|
|
57
|
+
display: 'Number of Payments',
|
|
58
|
+
aggregator: :count
|
|
59
|
+
}
|
|
60
|
+
]
|
|
61
|
+
)
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
subject { described_class.new(schema, alias_maker, alias_maker) }
|
|
65
|
+
|
|
66
|
+
before(:all) do
|
|
67
|
+
connect_to_db(:sqlite)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
describe '#to_sql' do
|
|
71
|
+
specify 'when called with no fields, then called with fields removes star select' do
|
|
72
|
+
expect(subject.to_sql(empty_query)).to include('*')
|
|
73
|
+
expect(subject.to_sql(id_and_average_query)).not_to include('*')
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
context 'with aggregation' do
|
|
77
|
+
it 'generates the same sql when called multiple times' do
|
|
78
|
+
first_sql = subject.to_sql(id_and_average_query)
|
|
79
|
+
second_sql = subject.to_sql(id_and_average_query)
|
|
80
|
+
|
|
81
|
+
expect(first_sql).to eq(second_sql)
|
|
82
|
+
|
|
83
|
+
third_sql = subject.to_sql(first_and_count_query)
|
|
84
|
+
fourth_sql = subject.to_sql(first_and_count_query)
|
|
85
|
+
|
|
86
|
+
expect(third_sql).to eq(fourth_sql)
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
end
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
#
|
|
4
|
+
# Copyright (c) 2020-present, Blue Marble Payroll, LLC
|
|
5
|
+
#
|
|
6
|
+
# This source code is licensed under the MIT license found in the
|
|
7
|
+
# LICENSE file in the root directory of this source tree.
|
|
8
|
+
#
|
|
9
|
+
|
|
10
|
+
require 'spec_helper'
|
|
11
|
+
require 'db_helper'
|
|
12
|
+
|
|
13
|
+
# rubocop:disable Layout/LineLength
|
|
14
|
+
# Disable the line length cop beause there are some lengthy 'expected' SQL strings in spec which
|
|
15
|
+
# cannot be shortened.
|
|
16
|
+
describe Dbee::Providers::ActiveRecordProvider::Makers::Where do
|
|
17
|
+
before(:all) { connect_to_db(:sqlite) }
|
|
18
|
+
|
|
19
|
+
let(:subject) { described_class.instance }
|
|
20
|
+
let(:column) { Arel::Table.new(:test)[:foo] }
|
|
21
|
+
|
|
22
|
+
describe 'equals' do
|
|
23
|
+
specify 'a string value' do
|
|
24
|
+
filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: 'bar')
|
|
25
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" = 'bar')
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
specify 'a null value' do
|
|
29
|
+
filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: nil)
|
|
30
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
specify 'a set with a null value' do
|
|
34
|
+
filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
35
|
+
expected = %q[("test"."foo" IS NULL OR "test"."foo" IN ('a', 'c'))]
|
|
36
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
specify 'a set without a null value' do
|
|
40
|
+
filter = Dbee::Query::Filters::Equals.new(key_path: :foo, value: %w[a b c])
|
|
41
|
+
expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" IN ('a', 'b', 'c')]
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
describe 'not equals' do
|
|
46
|
+
specify 'a string value' do
|
|
47
|
+
filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: 'bar')
|
|
48
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" != 'bar')
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
specify 'a null value' do
|
|
52
|
+
filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: nil)
|
|
53
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
specify 'a set with a null value' do
|
|
57
|
+
filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
58
|
+
expected = %q[("test"."foo" IS NOT NULL OR "test"."foo" NOT IN ('a', 'c'))]
|
|
59
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
specify 'a set without a null value' do
|
|
63
|
+
filter = Dbee::Query::Filters::NotEquals.new(key_path: :foo, value: %w[a b c])
|
|
64
|
+
expect(subject.make(filter, column).to_sql).to eq %q["test"."foo" NOT IN ('a', 'b', 'c')]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
describe 'contains' do
|
|
69
|
+
specify 'a string value' do
|
|
70
|
+
filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: 'bar')
|
|
71
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE '%bar%')
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
specify 'a null value' do
|
|
75
|
+
filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: nil)
|
|
76
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
specify 'a set with a null value' do
|
|
80
|
+
filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
81
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE '%a%') OR "test"."foo" LIKE '%c%')]
|
|
82
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
specify 'a set without a null value' do
|
|
86
|
+
filter = Dbee::Query::Filters::Contains.new(key_path: :foo, value: %w[a b c])
|
|
87
|
+
expected = %q[(("test"."foo" LIKE '%a%' OR "test"."foo" LIKE '%b%') OR "test"."foo" LIKE '%c%')]
|
|
88
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
89
|
+
end
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
describe 'not contains' do
|
|
93
|
+
specify 'a string value' do
|
|
94
|
+
filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: 'bar')
|
|
95
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE '%bar%')
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
specify 'a null value' do
|
|
99
|
+
filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: nil)
|
|
100
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
specify 'a set with a null value' do
|
|
104
|
+
filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
105
|
+
expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE '%a%') OR "test"."foo" NOT LIKE '%c%')]
|
|
106
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
specify 'a set without a null value' do
|
|
110
|
+
filter = Dbee::Query::Filters::NotContain.new(key_path: :foo, value: %w[a b c])
|
|
111
|
+
expected = %q[(("test"."foo" NOT LIKE '%a%' OR "test"."foo" NOT LIKE '%b%') OR "test"."foo" NOT LIKE '%c%')]
|
|
112
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
113
|
+
end
|
|
114
|
+
end
|
|
115
|
+
|
|
116
|
+
describe 'starts with' do
|
|
117
|
+
specify 'a string value' do
|
|
118
|
+
filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: 'bar')
|
|
119
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" LIKE 'bar%')
|
|
120
|
+
end
|
|
121
|
+
|
|
122
|
+
specify 'a null value' do
|
|
123
|
+
filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: nil)
|
|
124
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
specify 'a set with a null value' do
|
|
128
|
+
filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
129
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" LIKE 'a%') OR "test"."foo" LIKE 'c%')]
|
|
130
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
specify 'a set without a null value' do
|
|
134
|
+
filter = Dbee::Query::Filters::StartsWith.new(key_path: :foo, value: %w[a b c])
|
|
135
|
+
expected = %q[(("test"."foo" LIKE 'a%' OR "test"."foo" LIKE 'b%') OR "test"."foo" LIKE 'c%')]
|
|
136
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
137
|
+
end
|
|
138
|
+
end
|
|
139
|
+
|
|
140
|
+
describe 'not start with' do
|
|
141
|
+
specify 'a string value' do
|
|
142
|
+
filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: 'bar')
|
|
143
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" NOT LIKE 'bar%')
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
specify 'a null value' do
|
|
147
|
+
filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: nil)
|
|
148
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NOT NULL'
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
specify 'a set with a null value' do
|
|
152
|
+
filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
153
|
+
expected = %q[(("test"."foo" IS NOT NULL OR "test"."foo" NOT LIKE 'a%') OR "test"."foo" NOT LIKE 'c%')]
|
|
154
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
specify 'a set without a null value' do
|
|
158
|
+
filter = Dbee::Query::Filters::NotStartWith.new(key_path: :foo, value: %w[a b c])
|
|
159
|
+
expected = %q[(("test"."foo" NOT LIKE 'a%' OR "test"."foo" NOT LIKE 'b%') OR "test"."foo" NOT LIKE 'c%')]
|
|
160
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
|
|
164
|
+
describe 'greater than' do
|
|
165
|
+
specify 'a string value' do
|
|
166
|
+
filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: 'bar')
|
|
167
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" > 'bar')
|
|
168
|
+
end
|
|
169
|
+
|
|
170
|
+
specify 'a null value' do
|
|
171
|
+
filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: nil)
|
|
172
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
173
|
+
end
|
|
174
|
+
|
|
175
|
+
specify 'a set with a null value' do
|
|
176
|
+
filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
177
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" > 'a') OR "test"."foo" > 'c')]
|
|
178
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
179
|
+
end
|
|
180
|
+
|
|
181
|
+
specify 'a set without a null value' do
|
|
182
|
+
filter = Dbee::Query::Filters::GreaterThan.new(key_path: :foo, value: %w[a b c])
|
|
183
|
+
expected = %q[(("test"."foo" > 'a' OR "test"."foo" > 'b') OR "test"."foo" > 'c')]
|
|
184
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
185
|
+
end
|
|
186
|
+
end
|
|
187
|
+
|
|
188
|
+
describe 'greater than or equal to' do
|
|
189
|
+
specify 'a string value' do
|
|
190
|
+
filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: 'bar')
|
|
191
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" >= 'bar')
|
|
192
|
+
end
|
|
193
|
+
|
|
194
|
+
specify 'a null value' do
|
|
195
|
+
filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: nil)
|
|
196
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
specify 'a set with a null value' do
|
|
200
|
+
filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
201
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" >= 'a') OR "test"."foo" >= 'c')]
|
|
202
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
specify 'a set without a null value' do
|
|
206
|
+
filter = Dbee::Query::Filters::GreaterThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
|
|
207
|
+
expected = %q[(("test"."foo" >= 'a' OR "test"."foo" >= 'b') OR "test"."foo" >= 'c')]
|
|
208
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
209
|
+
end
|
|
210
|
+
end
|
|
211
|
+
|
|
212
|
+
describe 'less than' do
|
|
213
|
+
specify 'a string value' do
|
|
214
|
+
filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: 'bar')
|
|
215
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" < 'bar')
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
specify 'a null value' do
|
|
219
|
+
filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: nil)
|
|
220
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
221
|
+
end
|
|
222
|
+
|
|
223
|
+
specify 'a set with a null value' do
|
|
224
|
+
filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
225
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" < 'a') OR "test"."foo" < 'c')]
|
|
226
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
specify 'a set without a null value' do
|
|
230
|
+
filter = Dbee::Query::Filters::LessThan.new(key_path: :foo, value: %w[a b c])
|
|
231
|
+
expected = %q[(("test"."foo" < 'a' OR "test"."foo" < 'b') OR "test"."foo" < 'c')]
|
|
232
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
233
|
+
end
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
describe 'less than or equal to' do
|
|
237
|
+
specify 'a string value' do
|
|
238
|
+
filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: 'bar')
|
|
239
|
+
expect(subject.make(filter, column).to_sql).to eq %q("test"."foo" <= 'bar')
|
|
240
|
+
end
|
|
241
|
+
|
|
242
|
+
specify 'a null value' do
|
|
243
|
+
filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: nil)
|
|
244
|
+
expect(subject.make(filter, column).to_sql).to eq '"test"."foo" IS NULL'
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
specify 'a set with a null value' do
|
|
248
|
+
filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: ['a', nil, 'c'])
|
|
249
|
+
expected = %q[(("test"."foo" IS NULL OR "test"."foo" <= 'a') OR "test"."foo" <= 'c')]
|
|
250
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
specify 'a set without a null value' do
|
|
254
|
+
filter = Dbee::Query::Filters::LessThanOrEqualTo.new(key_path: :foo, value: %w[a b c])
|
|
255
|
+
expected = %q[(("test"."foo" <= 'a' OR "test"."foo" <= 'b') OR "test"."foo" <= 'c')]
|
|
256
|
+
expect(subject.make(filter, column).to_sql).to eq expected
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
260
|
+
# rubocop:enable Layout/LineLength
|
|
@@ -11,22 +11,19 @@ require 'spec_helper'
|
|
|
11
11
|
require 'db_helper'
|
|
12
12
|
|
|
13
13
|
describe Dbee::Providers::ActiveRecordProvider do
|
|
14
|
-
let(:models) { yaml_fixture('models.yaml') }
|
|
15
|
-
|
|
16
14
|
describe '#sql' do
|
|
17
15
|
before(:all) do
|
|
18
16
|
connect_to_db(:sqlite)
|
|
19
17
|
end
|
|
20
18
|
|
|
21
19
|
it 'errors when joining tables with no constraints' do
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
{ name: :logins }
|
|
26
|
-
]
|
|
20
|
+
schema_hash = {
|
|
21
|
+
users: { relationships: { logins: nil } },
|
|
22
|
+
logins: nil
|
|
27
23
|
}
|
|
28
24
|
|
|
29
25
|
query_hash = {
|
|
26
|
+
from: :users,
|
|
30
27
|
fields: [
|
|
31
28
|
{ key_path: 'id' },
|
|
32
29
|
{ key_path: 'logins.id' }
|
|
@@ -34,11 +31,11 @@ describe Dbee::Providers::ActiveRecordProvider do
|
|
|
34
31
|
}
|
|
35
32
|
|
|
36
33
|
query = Dbee::Query.make(query_hash)
|
|
37
|
-
|
|
34
|
+
schema = Dbee::Schema.new(schema_hash)
|
|
38
35
|
|
|
39
36
|
error_class = Dbee::Providers::ActiveRecordProvider::ExpressionBuilder::MissingConstraintError
|
|
40
37
|
|
|
41
|
-
expect { described_class.new.sql(
|
|
38
|
+
expect { described_class.new.sql(schema, query) }.to raise_error(error_class)
|
|
42
39
|
end
|
|
43
40
|
end
|
|
44
41
|
|
|
@@ -57,12 +54,13 @@ describe Dbee::Providers::ActiveRecordProvider do
|
|
|
57
54
|
yaml_fixture_files('active_record_snapshots').each_pair do |filename, snapshot|
|
|
58
55
|
specify File.basename(filename) do
|
|
59
56
|
model_name = snapshot['model_name']
|
|
57
|
+
|
|
60
58
|
query = Dbee::Query.make(snapshot['query'])
|
|
61
|
-
|
|
59
|
+
schema = Dbee::Schema.new(models[model_name])
|
|
62
60
|
|
|
63
61
|
expected_5_sql = snapshot[key].to_s.chomp.tr("\n", ' ')
|
|
64
62
|
expected_6_sql = expected_5_sql.gsub(' ', ' ').gsub("'t'", '1').gsub("'f'", '0')
|
|
65
|
-
actual_sql = described_class.new(readable: readable).sql(
|
|
63
|
+
actual_sql = described_class.new(readable: readable).sql(schema, query)
|
|
66
64
|
|
|
67
65
|
error_msg = <<~ERROR_MSG
|
|
68
66
|
Expected 5 SQL: #{expected_5_sql}
|
|
@@ -79,7 +77,7 @@ describe Dbee::Providers::ActiveRecordProvider do
|
|
|
79
77
|
end
|
|
80
78
|
end
|
|
81
79
|
|
|
82
|
-
context '
|
|
80
|
+
context 'Shallow SQL Execution' do
|
|
83
81
|
%w[sqlite].each do |dbms|
|
|
84
82
|
context dbms do
|
|
85
83
|
before(:all) do
|
|
@@ -95,9 +93,9 @@ describe Dbee::Providers::ActiveRecordProvider do
|
|
|
95
93
|
specify File.basename(filename) do
|
|
96
94
|
model_name = snapshot['model_name']
|
|
97
95
|
query = Dbee::Query.make(snapshot['query'])
|
|
98
|
-
|
|
96
|
+
schema = Dbee::Schema.new(models[model_name])
|
|
99
97
|
|
|
100
|
-
sql = described_class.new(readable: readable).sql(
|
|
98
|
+
sql = described_class.new(readable: readable).sql(schema, query)
|
|
101
99
|
|
|
102
100
|
expect { ActiveRecord::Base.connection.execute(sql) }.to_not raise_error
|
|
103
101
|
end
|
|
@@ -108,4 +106,104 @@ describe Dbee::Providers::ActiveRecordProvider do
|
|
|
108
106
|
end
|
|
109
107
|
end
|
|
110
108
|
end
|
|
109
|
+
|
|
110
|
+
describe 'Deep SQL execution' do
|
|
111
|
+
before(:all) do
|
|
112
|
+
connect_to_db(:sqlite)
|
|
113
|
+
load_schema
|
|
114
|
+
load_data
|
|
115
|
+
end
|
|
116
|
+
|
|
117
|
+
describe 'pivoting' do
|
|
118
|
+
let(:snapshot_path) do
|
|
119
|
+
%w[
|
|
120
|
+
spec
|
|
121
|
+
fixtures
|
|
122
|
+
active_record_snapshots
|
|
123
|
+
two_table_query_with_pivoting.yaml
|
|
124
|
+
]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
let(:snapshot) { yaml_file_read(*snapshot_path) }
|
|
128
|
+
let(:query) { Dbee::Query.make(snapshot['query']) }
|
|
129
|
+
let(:schema) { Dbee::Schema.new(models['Patients']) }
|
|
130
|
+
|
|
131
|
+
it 'pivots table rows into columns' do
|
|
132
|
+
sql = described_class.new.sql(schema, query)
|
|
133
|
+
|
|
134
|
+
results = ActiveRecord::Base.connection.execute(sql)
|
|
135
|
+
|
|
136
|
+
expect(results[0]).to include(
|
|
137
|
+
'First Name' => 'Bozo',
|
|
138
|
+
'Date of Birth' => '1904-04-04',
|
|
139
|
+
'Drivers License #' => '82-54-hut-hut-hike!',
|
|
140
|
+
'Demographic Notes' => 'The patient is funny!',
|
|
141
|
+
'Contact Notes' => 'Do not call this patient at night!'
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
expect(results[1]).to include(
|
|
145
|
+
'First Name' => 'Frank',
|
|
146
|
+
'Date of Birth' => nil,
|
|
147
|
+
'Drivers License #' => nil,
|
|
148
|
+
'Demographic Notes' => nil,
|
|
149
|
+
'Contact Notes' => nil
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
expect(results[2]).to include(
|
|
153
|
+
'First Name' => 'Bugs',
|
|
154
|
+
'Date of Birth' => '2040-01-01',
|
|
155
|
+
'Drivers License #' => nil,
|
|
156
|
+
'Demographic Notes' => nil,
|
|
157
|
+
'Contact Notes' => 'Call anytime!!'
|
|
158
|
+
)
|
|
159
|
+
end
|
|
160
|
+
end
|
|
161
|
+
|
|
162
|
+
describe 'aggregation' do
|
|
163
|
+
let(:snapshot_path) do
|
|
164
|
+
%w[
|
|
165
|
+
spec
|
|
166
|
+
fixtures
|
|
167
|
+
active_record_snapshots
|
|
168
|
+
two_table_query_with_aggregation.yaml
|
|
169
|
+
]
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
let(:snapshot) { yaml_file_read(*snapshot_path) }
|
|
173
|
+
let(:query) { Dbee::Query.make(snapshot['query']) }
|
|
174
|
+
let(:schema) { Dbee::Schema.new(models['Patients']) }
|
|
175
|
+
|
|
176
|
+
it 'executes correct SQL aggregate functions' do
|
|
177
|
+
sql = described_class.new.sql(schema, query)
|
|
178
|
+
results = ActiveRecord::Base.connection.execute(sql)
|
|
179
|
+
|
|
180
|
+
expect(results[0]).to include(
|
|
181
|
+
'First Name' => 'Bozo',
|
|
182
|
+
'Ave Payment' => 10,
|
|
183
|
+
'Number of Payments' => 3,
|
|
184
|
+
'Max Payment' => 15,
|
|
185
|
+
'Min Payment' => 5,
|
|
186
|
+
'Total Paid' => 30
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
expect(results[1]).to include(
|
|
190
|
+
'First Name' => 'Frank',
|
|
191
|
+
'Ave Payment' => 100,
|
|
192
|
+
'Number of Payments' => 2,
|
|
193
|
+
'Max Payment' => 150,
|
|
194
|
+
'Min Payment' => 50,
|
|
195
|
+
'Total Paid' => 200
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
expect(results[2]).to include(
|
|
199
|
+
'First Name' => 'Bugs',
|
|
200
|
+
'Ave Payment' => nil,
|
|
201
|
+
'Number of Payments' => 0,
|
|
202
|
+
'Max Payment' => nil,
|
|
203
|
+
'Min Payment' => nil,
|
|
204
|
+
'Total Paid' => nil
|
|
205
|
+
)
|
|
206
|
+
end
|
|
207
|
+
end
|
|
208
|
+
end
|
|
111
209
|
end
|