simple_query 0.5.0 → 0.6.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/README.md +428 -185
- data/lib/simple_query/builder.rb +115 -23
- data/lib/simple_query/clauses/aggregation_clause.rb +8 -5
- data/lib/simple_query/clauses/group_having_clause.rb +6 -6
- data/lib/simple_query/clauses/set_clause.rb +4 -3
- data/lib/simple_query/clauses/where_clause.rb +34 -9
- data/lib/simple_query/stream/mysql_stream.rb +9 -4
- data/lib/simple_query/stream/postgres_stream.rb +45 -11
- data/lib/simple_query/version.rb +1 -1
- data/lib/simple_query.rb +10 -4
- metadata +6 -6
data/lib/simple_query/builder.rb
CHANGED
|
@@ -12,18 +12,18 @@ module SimpleQuery
|
|
|
12
12
|
@arel_table = @model.arel_table
|
|
13
13
|
|
|
14
14
|
@selects = []
|
|
15
|
-
@wheres = WhereClause.new(@arel_table)
|
|
15
|
+
@wheres = WhereClause.new(@arel_table, @model)
|
|
16
16
|
@joins = JoinClause.new
|
|
17
|
-
@group_having = GroupHavingClause.new(@arel_table)
|
|
17
|
+
@group_having = GroupHavingClause.new(@arel_table, @model)
|
|
18
18
|
@orders = OrderClause.new(@arel_table)
|
|
19
19
|
@limits = LimitOffsetClause.new
|
|
20
20
|
@distinct_flag = DistinctClause.new
|
|
21
|
-
@aggregations = AggregationClause.new(@arel_table)
|
|
21
|
+
@aggregations = AggregationClause.new(@arel_table, @model)
|
|
22
22
|
|
|
23
23
|
@query_cache = {}
|
|
24
24
|
@query_built = false
|
|
25
25
|
@read_model_class = nil
|
|
26
|
-
@
|
|
26
|
+
@result_structs = {}
|
|
27
27
|
end
|
|
28
28
|
|
|
29
29
|
def select(*fields)
|
|
@@ -172,7 +172,8 @@ module SimpleQuery
|
|
|
172
172
|
# Method to get last/bottom record by column
|
|
173
173
|
def last_by(column, alias_name: nil)
|
|
174
174
|
alias_name ||= "last_#{column}"
|
|
175
|
-
custom_aggregation("LAST_VALUE(#{resolve_column_name(column)}) OVER (ORDER BY #{resolve_column_name(column)}
|
|
175
|
+
custom_aggregation("LAST_VALUE(#{resolve_column_name(column)}) OVER (ORDER BY #{resolve_column_name(column)} " \
|
|
176
|
+
"ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING)",
|
|
176
177
|
alias_name)
|
|
177
178
|
end
|
|
178
179
|
|
|
@@ -191,37 +192,44 @@ module SimpleQuery
|
|
|
191
192
|
end
|
|
192
193
|
|
|
193
194
|
def bulk_update(set:)
|
|
194
|
-
|
|
195
|
-
set_sql = SetClause.new(set).to_sql
|
|
195
|
+
validate_bulk_update_scope!
|
|
196
196
|
|
|
197
|
+
set_sql = SetClause.new(set, @model).to_sql
|
|
197
198
|
raise ArgumentError, "No columns to update" if set_sql.empty?
|
|
198
199
|
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
200
|
+
update = Arel::UpdateManager.new
|
|
201
|
+
update.table(@arel_table)
|
|
202
|
+
update.set(Arel.sql(set_sql))
|
|
203
|
+
condition = @wheres.to_arel
|
|
204
|
+
update.where(condition) if condition
|
|
202
205
|
|
|
203
|
-
|
|
206
|
+
connection.execute(compile_sql(update))
|
|
204
207
|
end
|
|
205
208
|
|
|
206
209
|
def stream_each(batch_size: 1000, &block)
|
|
207
|
-
|
|
210
|
+
validate_stream_batch_size!(batch_size)
|
|
211
|
+
|
|
212
|
+
adapter_name = connection.adapter_name
|
|
213
|
+
adapter = adapter_name.downcase
|
|
208
214
|
if adapter.include?("postgres")
|
|
209
215
|
stream_each_postgres(batch_size, &block)
|
|
210
216
|
elsif adapter.include?("mysql")
|
|
211
217
|
stream_each_mysql(&block)
|
|
212
218
|
else
|
|
213
|
-
|
|
219
|
+
message = "stream_each is only implemented for PostgreSQL and MySQL adapters " \
|
|
220
|
+
"(current adapter: #{adapter_name})"
|
|
221
|
+
raise UnsupportedAdapterError, message
|
|
214
222
|
end
|
|
215
223
|
end
|
|
216
224
|
|
|
217
225
|
def execute
|
|
218
|
-
records =
|
|
226
|
+
records = connection.select_all(cached_sql)
|
|
219
227
|
build_result_objects_from_rows(records)
|
|
220
228
|
end
|
|
221
229
|
|
|
222
230
|
def lazy_execute
|
|
223
231
|
Enumerator.new do |yielder|
|
|
224
|
-
records =
|
|
232
|
+
records = connection.select_all(cached_sql)
|
|
225
233
|
if @read_model_class
|
|
226
234
|
build_read_models_enumerator(records, yielder)
|
|
227
235
|
else
|
|
@@ -256,6 +264,22 @@ module SimpleQuery
|
|
|
256
264
|
|
|
257
265
|
private
|
|
258
266
|
|
|
267
|
+
def validate_stream_batch_size!(batch_size)
|
|
268
|
+
return if batch_size.is_a?(Integer) && batch_size.positive?
|
|
269
|
+
|
|
270
|
+
raise ArgumentError, "stream_each batch_size must be a positive Integer"
|
|
271
|
+
end
|
|
272
|
+
|
|
273
|
+
def validate_bulk_update_scope!
|
|
274
|
+
unsupported = []
|
|
275
|
+
unsupported << "joins" if @joins.joins.any?
|
|
276
|
+
unsupported << "limit/offset" if @limits.limit_value || @limits.offset_value
|
|
277
|
+
unsupported << "group/having" if @group_having.group_fields.any? || @group_having.having_conditions.any?
|
|
278
|
+
return if unsupported.empty?
|
|
279
|
+
|
|
280
|
+
raise ArgumentError, "bulk_update only supports where conditions (found #{unsupported.join(", ")})"
|
|
281
|
+
end
|
|
282
|
+
|
|
259
283
|
def build_select_expressions
|
|
260
284
|
expressions = []
|
|
261
285
|
|
|
@@ -273,11 +297,13 @@ module SimpleQuery
|
|
|
273
297
|
expressions
|
|
274
298
|
end
|
|
275
299
|
|
|
276
|
-
def
|
|
277
|
-
|
|
278
|
-
|
|
300
|
+
def connection
|
|
301
|
+
@model.connection
|
|
302
|
+
end
|
|
279
303
|
|
|
280
|
-
|
|
304
|
+
# Compiles with the model's own connection, inlining any bind values.
|
|
305
|
+
def compile_sql(arel)
|
|
306
|
+
connection.unprepared_statement { connection.to_sql(arel) }
|
|
281
307
|
end
|
|
282
308
|
|
|
283
309
|
def reset_query
|
|
@@ -299,7 +325,7 @@ module SimpleQuery
|
|
|
299
325
|
@aggregations.aggregations
|
|
300
326
|
]
|
|
301
327
|
|
|
302
|
-
@query_cache[key] ||= build_query
|
|
328
|
+
@query_cache[key] ||= compile_sql(build_query)
|
|
303
329
|
end
|
|
304
330
|
|
|
305
331
|
def build_result_objects_from_rows(records)
|
|
@@ -340,7 +366,8 @@ module SimpleQuery
|
|
|
340
366
|
end
|
|
341
367
|
|
|
342
368
|
def result_struct(columns)
|
|
343
|
-
|
|
369
|
+
struct_key = columns.map(&:to_s)
|
|
370
|
+
@result_structs[struct_key] ||= Struct.new(*struct_key.map(&:to_sym))
|
|
344
371
|
end
|
|
345
372
|
|
|
346
373
|
def apply_distinct
|
|
@@ -392,9 +419,10 @@ module SimpleQuery
|
|
|
392
419
|
end
|
|
393
420
|
end
|
|
394
421
|
|
|
395
|
-
def method_missing(method_name, *args, &block)
|
|
422
|
+
def method_missing(method_name, *args, **kwargs, &block)
|
|
396
423
|
if (scope_block = find_scope(method_name))
|
|
397
|
-
|
|
424
|
+
validate_scope_arity!(method_name, scope_block, scope_arguments_for_validation(scope_block, args, kwargs))
|
|
425
|
+
instance_exec(*args, **kwargs, &scope_block)
|
|
398
426
|
self
|
|
399
427
|
else
|
|
400
428
|
super
|
|
@@ -410,5 +438,69 @@ module SimpleQuery
|
|
|
410
438
|
|
|
411
439
|
model._simple_scopes[method_name.to_sym]
|
|
412
440
|
end
|
|
441
|
+
|
|
442
|
+
def validate_scope_arity!(scope_name, scope_block, args)
|
|
443
|
+
range = scope_argument_range(scope_block)
|
|
444
|
+
provided = args.length
|
|
445
|
+
return if scope_arity_matches?(range, provided)
|
|
446
|
+
|
|
447
|
+
raise ArgumentError,
|
|
448
|
+
"simple_scope :#{scope_name} expected #{scope_arity_description(range)}, provided #{provided}"
|
|
449
|
+
end
|
|
450
|
+
|
|
451
|
+
def scope_arguments_for_validation(scope_block, args, kwargs)
|
|
452
|
+
return args if kwargs.empty? || scope_uses_keyword_arguments?(scope_block)
|
|
453
|
+
|
|
454
|
+
args + [kwargs]
|
|
455
|
+
end
|
|
456
|
+
|
|
457
|
+
def scope_argument_range(scope_block)
|
|
458
|
+
# Keyword argument arity has Ruby-version-specific edge cases; let Ruby raise its native error.
|
|
459
|
+
return nil if scope_uses_keyword_arguments?(scope_block)
|
|
460
|
+
|
|
461
|
+
minimum = minimum_scope_arguments(scope_block)
|
|
462
|
+
maximum = maximum_scope_arguments(scope_block)
|
|
463
|
+
|
|
464
|
+
[minimum, maximum]
|
|
465
|
+
end
|
|
466
|
+
|
|
467
|
+
def scope_uses_keyword_arguments?(scope_block)
|
|
468
|
+
scope_block.parameters.any? { |kind, _name| [:key, :keyreq, :keyrest].include?(kind) }
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def minimum_scope_arguments(scope_block)
|
|
472
|
+
arity = scope_block.arity
|
|
473
|
+
return arity if arity >= 0
|
|
474
|
+
|
|
475
|
+
-arity - 1
|
|
476
|
+
end
|
|
477
|
+
|
|
478
|
+
def maximum_scope_arguments(scope_block)
|
|
479
|
+
parameters = scope_block.parameters
|
|
480
|
+
return nil if parameters.any? { |kind, _name| kind == :rest }
|
|
481
|
+
|
|
482
|
+
parameters.count { |kind, _name| [:req, :opt].include?(kind) }
|
|
483
|
+
end
|
|
484
|
+
|
|
485
|
+
def scope_arity_matches?(range, provided)
|
|
486
|
+
return true if range.nil?
|
|
487
|
+
|
|
488
|
+
minimum, maximum = range
|
|
489
|
+
return provided >= minimum if maximum.nil?
|
|
490
|
+
|
|
491
|
+
provided.between?(minimum, maximum)
|
|
492
|
+
end
|
|
493
|
+
|
|
494
|
+
def scope_arity_description(range)
|
|
495
|
+
minimum, maximum = range
|
|
496
|
+
return "at least #{argument_count_description(minimum)}" if maximum.nil?
|
|
497
|
+
return "exactly #{argument_count_description(minimum)}" if minimum == maximum
|
|
498
|
+
|
|
499
|
+
"#{minimum} to #{argument_count_description(maximum)}"
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
def argument_count_description(count)
|
|
503
|
+
"#{count} #{count == 1 ? "argument" : "arguments"}"
|
|
504
|
+
end
|
|
413
505
|
end
|
|
414
506
|
end
|
|
@@ -4,8 +4,9 @@ module SimpleQuery
|
|
|
4
4
|
class AggregationClause
|
|
5
5
|
attr_reader :aggregations
|
|
6
6
|
|
|
7
|
-
def initialize(table)
|
|
7
|
+
def initialize(table, model = ActiveRecord::Base)
|
|
8
8
|
@table = table
|
|
9
|
+
@model = model
|
|
9
10
|
@aggregations = []
|
|
10
11
|
end
|
|
11
12
|
|
|
@@ -91,15 +92,17 @@ module SimpleQuery
|
|
|
91
92
|
alias_name ||= "group_concat_#{sanitize_alias(column)}"
|
|
92
93
|
|
|
93
94
|
# Use database-specific group concatenation
|
|
94
|
-
|
|
95
|
+
connection = @model.connection
|
|
96
|
+
adapter = connection.adapter_name.downcase
|
|
97
|
+
quoted_separator = connection.quote(separator.to_s)
|
|
95
98
|
|
|
96
99
|
expression = case adapter
|
|
97
100
|
when /mysql/
|
|
98
|
-
"GROUP_CONCAT(#{column_expr} SEPARATOR
|
|
101
|
+
"GROUP_CONCAT(#{column_expr} SEPARATOR #{quoted_separator})"
|
|
99
102
|
when /postgres/
|
|
100
|
-
"STRING_AGG(#{column_expr}::text,
|
|
103
|
+
"STRING_AGG(#{column_expr}::text, #{quoted_separator})"
|
|
101
104
|
when /sqlite/
|
|
102
|
-
"GROUP_CONCAT(#{column_expr},
|
|
105
|
+
"GROUP_CONCAT(#{column_expr}, #{quoted_separator})"
|
|
103
106
|
else
|
|
104
107
|
# Fallback for other databases
|
|
105
108
|
"GROUP_CONCAT(#{column_expr})"
|
|
@@ -4,8 +4,9 @@ module SimpleQuery
|
|
|
4
4
|
class GroupHavingClause
|
|
5
5
|
attr_reader :group_fields, :having_conditions
|
|
6
6
|
|
|
7
|
-
def initialize(table)
|
|
7
|
+
def initialize(table, model = ActiveRecord::Base)
|
|
8
8
|
@table = table
|
|
9
|
+
@model = model
|
|
9
10
|
@group_fields = []
|
|
10
11
|
@having_conditions = []
|
|
11
12
|
end
|
|
@@ -15,15 +16,14 @@ module SimpleQuery
|
|
|
15
16
|
end
|
|
16
17
|
|
|
17
18
|
def add_having(condition)
|
|
18
|
-
@
|
|
19
|
+
clause = WhereClause.new(@table, @model)
|
|
20
|
+
clause.add(condition)
|
|
21
|
+
@having_conditions.concat(clause.conditions)
|
|
19
22
|
end
|
|
20
23
|
|
|
21
24
|
def apply_to(query)
|
|
22
25
|
@group_fields.each { |g| query.group(g) }
|
|
23
|
-
if @having_conditions.any?
|
|
24
|
-
combined = @having_conditions.inject { |c, a| c.and(a) }
|
|
25
|
-
query.having(combined)
|
|
26
|
-
end
|
|
26
|
+
query.having(WhereClause.combine(@having_conditions)) if @having_conditions.any?
|
|
27
27
|
query
|
|
28
28
|
end
|
|
29
29
|
end
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
module SimpleQuery
|
|
4
4
|
class SetClause
|
|
5
|
-
def initialize(set_hash)
|
|
5
|
+
def initialize(set_hash, model = ActiveRecord::Base)
|
|
6
6
|
@set_hash = set_hash
|
|
7
|
+
@model = model
|
|
7
8
|
end
|
|
8
9
|
|
|
9
10
|
def to_sql
|
|
@@ -15,11 +16,11 @@ module SimpleQuery
|
|
|
15
16
|
private
|
|
16
17
|
|
|
17
18
|
def quote_column(col)
|
|
18
|
-
|
|
19
|
+
@model.connection.quote_column_name(col)
|
|
19
20
|
end
|
|
20
21
|
|
|
21
22
|
def quote_value(val)
|
|
22
|
-
|
|
23
|
+
@model.connection.quote(val)
|
|
23
24
|
end
|
|
24
25
|
end
|
|
25
26
|
end
|
|
@@ -4,8 +4,16 @@ module SimpleQuery
|
|
|
4
4
|
class WhereClause
|
|
5
5
|
attr_reader :conditions
|
|
6
6
|
|
|
7
|
-
def
|
|
7
|
+
def self.combine(conditions)
|
|
8
|
+
return nil if conditions.empty?
|
|
9
|
+
return conditions.first if conditions.one?
|
|
10
|
+
|
|
11
|
+
Arel::Nodes::And.new(conditions.map { |c| c.is_a?(Arel::Nodes::Grouping) ? c : Arel::Nodes::Grouping.new(c) })
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def initialize(table, model = ActiveRecord::Base)
|
|
8
15
|
@table = table
|
|
16
|
+
@model = model
|
|
9
17
|
@conditions = []
|
|
10
18
|
end
|
|
11
19
|
|
|
@@ -15,11 +23,7 @@ module SimpleQuery
|
|
|
15
23
|
end
|
|
16
24
|
|
|
17
25
|
def to_arel
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
@conditions.inject do |combined, current|
|
|
21
|
-
combined.and(current)
|
|
22
|
-
end
|
|
26
|
+
self.class.combine(@conditions)
|
|
23
27
|
end
|
|
24
28
|
|
|
25
29
|
private
|
|
@@ -27,15 +31,36 @@ module SimpleQuery
|
|
|
27
31
|
def parse_condition(condition)
|
|
28
32
|
case condition
|
|
29
33
|
when Hash
|
|
30
|
-
condition.map { |field, value|
|
|
31
|
-
when Arel::Nodes::Node
|
|
34
|
+
condition.map { |field, value| hash_predicate(field, value) }
|
|
35
|
+
when Arel::Nodes::Node, Arel::Attributes::Attribute
|
|
32
36
|
[condition]
|
|
33
37
|
when Array
|
|
34
|
-
sanitized_sql =
|
|
38
|
+
sanitized_sql = @model.send(:sanitize_sql_array, condition)
|
|
35
39
|
[Arel.sql(sanitized_sql)]
|
|
36
40
|
else
|
|
37
41
|
[Arel.sql(condition.to_s)]
|
|
38
42
|
end
|
|
39
43
|
end
|
|
44
|
+
|
|
45
|
+
def hash_predicate(field, value)
|
|
46
|
+
attribute = @table[field]
|
|
47
|
+
return attribute.eq(value) if force_equality?(field, value)
|
|
48
|
+
|
|
49
|
+
case value
|
|
50
|
+
when Array
|
|
51
|
+
values = value.compact
|
|
52
|
+
predicate = attribute.in(values)
|
|
53
|
+
values.size == value.size ? predicate : predicate.or(attribute.eq(nil))
|
|
54
|
+
when Range
|
|
55
|
+
attribute.between(value)
|
|
56
|
+
else
|
|
57
|
+
attribute.eq(value)
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# PostgreSQL array/range columns compare arrays and ranges by equality, like ActiveRecord does.
|
|
62
|
+
def force_equality?(field, value)
|
|
63
|
+
@table.able_to_type_cast? && @table.type_for_attribute(field.to_s).force_equality?(value)
|
|
64
|
+
end
|
|
40
65
|
end
|
|
41
66
|
end
|
|
@@ -6,12 +6,17 @@ module SimpleQuery
|
|
|
6
6
|
def stream_each_mysql(&block)
|
|
7
7
|
select_sql = cached_sql
|
|
8
8
|
|
|
9
|
-
raw_conn =
|
|
9
|
+
raw_conn = connection.raw_connection
|
|
10
10
|
|
|
11
11
|
result = raw_conn.query(select_sql, stream: true, cache_rows: false, as: :hash)
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
12
|
+
begin
|
|
13
|
+
result.each do |mysql_row|
|
|
14
|
+
record = build_row_object_mysql(mysql_row)
|
|
15
|
+
block.call(record)
|
|
16
|
+
end
|
|
17
|
+
ensure
|
|
18
|
+
# Discards unread rows so the connection can run the next query after an error or break.
|
|
19
|
+
result.free
|
|
15
20
|
end
|
|
16
21
|
end
|
|
17
22
|
|
|
@@ -3,17 +3,27 @@
|
|
|
3
3
|
module SimpleQuery
|
|
4
4
|
module Stream
|
|
5
5
|
module PostgresStream
|
|
6
|
+
PQTRANS_IDLE = 0 # PG::PQTRANS_IDLE
|
|
7
|
+
|
|
6
8
|
# rubocop:disable Metrics/MethodLength
|
|
7
9
|
def stream_each_postgres(batch_size, &block)
|
|
10
|
+
validate_postgres_stream_batch_size!(batch_size)
|
|
11
|
+
|
|
8
12
|
select_sql = cached_sql
|
|
9
13
|
|
|
10
|
-
conn =
|
|
14
|
+
conn = connection.raw_connection
|
|
11
15
|
cursor_name = "simple_query_cursor_#{object_id}"
|
|
12
16
|
|
|
17
|
+
# Cursors need a transaction. Reuse the caller's one instead of committing it.
|
|
18
|
+
own_transaction = conn.transaction_status == PQTRANS_IDLE
|
|
19
|
+
cursor_declared = false
|
|
20
|
+
cursor_close_started = false
|
|
21
|
+
completed = false
|
|
22
|
+
|
|
13
23
|
begin
|
|
14
|
-
conn.exec("BEGIN")
|
|
15
|
-
|
|
16
|
-
|
|
24
|
+
conn.exec("BEGIN") if own_transaction
|
|
25
|
+
conn.exec("DECLARE #{cursor_name} NO SCROLL CURSOR FOR #{select_sql}")
|
|
26
|
+
cursor_declared = true
|
|
17
27
|
|
|
18
28
|
loop do
|
|
19
29
|
res = conn.exec("FETCH #{batch_size} FROM #{cursor_name}")
|
|
@@ -25,21 +35,45 @@ module SimpleQuery
|
|
|
25
35
|
end
|
|
26
36
|
end
|
|
27
37
|
|
|
38
|
+
cursor_close_started = true
|
|
28
39
|
conn.exec("CLOSE #{cursor_name}")
|
|
29
|
-
conn.exec("COMMIT")
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
40
|
+
conn.exec("COMMIT") if own_transaction
|
|
41
|
+
completed = true
|
|
42
|
+
ensure
|
|
43
|
+
# Also runs when the caller leaves the block early with break/return.
|
|
44
|
+
unless completed
|
|
45
|
+
abort_postgres_stream(conn, cursor_name, close_cursor: cursor_declared && !cursor_close_started,
|
|
46
|
+
rollback: own_transaction)
|
|
35
47
|
end
|
|
36
|
-
raise e
|
|
37
48
|
end
|
|
38
49
|
end
|
|
39
50
|
# rubocop:enable Metrics/MethodLength
|
|
40
51
|
|
|
41
52
|
private
|
|
42
53
|
|
|
54
|
+
def validate_postgres_stream_batch_size!(batch_size)
|
|
55
|
+
return if batch_size.is_a?(Integer) && batch_size.positive?
|
|
56
|
+
|
|
57
|
+
raise ArgumentError, "stream_each batch_size must be a positive Integer"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def abort_postgres_stream(conn, cursor_name, close_cursor:, rollback:)
|
|
61
|
+
close_postgres_stream_cursor(conn, cursor_name) if close_cursor
|
|
62
|
+
rollback_postgres_stream_transaction(conn) if rollback
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
def close_postgres_stream_cursor(conn, cursor_name)
|
|
66
|
+
conn.exec("CLOSE #{cursor_name}")
|
|
67
|
+
rescue StandardError
|
|
68
|
+
nil
|
|
69
|
+
end
|
|
70
|
+
|
|
71
|
+
def rollback_postgres_stream_transaction(conn)
|
|
72
|
+
conn.exec("ROLLBACK")
|
|
73
|
+
rescue StandardError
|
|
74
|
+
nil
|
|
75
|
+
end
|
|
76
|
+
|
|
43
77
|
def build_row_object(pg_row)
|
|
44
78
|
if @read_model_class
|
|
45
79
|
obj = @read_model_class.allocate
|
data/lib/simple_query/version.rb
CHANGED
data/lib/simple_query.rb
CHANGED
|
@@ -19,6 +19,8 @@ require_relative "simple_query/clauses/aggregation_clause"
|
|
|
19
19
|
module SimpleQuery
|
|
20
20
|
extend ActiveSupport::Concern
|
|
21
21
|
|
|
22
|
+
class UnsupportedAdapterError < StandardError; end
|
|
23
|
+
|
|
22
24
|
class Configuration
|
|
23
25
|
attr_accessor :auto_include_ar
|
|
24
26
|
|
|
@@ -46,12 +48,16 @@ module SimpleQuery
|
|
|
46
48
|
end
|
|
47
49
|
|
|
48
50
|
def simple_scope(name, body = nil, &block)
|
|
49
|
-
raise ArgumentError, "Pass either a proc/lambda or a block, not both" if body && block_given?
|
|
51
|
+
raise ArgumentError, "Pass either a proc/lambda or a block, not both" if !body.nil? && block_given?
|
|
52
|
+
|
|
53
|
+
scope_body = body.nil? ? block : body
|
|
54
|
+
raise ArgumentError, "You must provide a block or a proc" if scope_body.nil?
|
|
55
|
+
raise ArgumentError, "simple_scope body must respond to #to_proc" unless scope_body.respond_to?(:to_proc)
|
|
50
56
|
|
|
51
|
-
|
|
52
|
-
raise ArgumentError, "
|
|
57
|
+
scope_proc = scope_body.to_proc
|
|
58
|
+
raise ArgumentError, "simple_scope body #to_proc must return a Proc" unless scope_proc.is_a?(Proc)
|
|
53
59
|
|
|
54
|
-
_simple_scopes[name.to_sym] =
|
|
60
|
+
_simple_scopes[name.to_sym] = scope_proc
|
|
55
61
|
end
|
|
56
62
|
end
|
|
57
63
|
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: simple_query
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.6.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Alex Kholodniak
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-10-11 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: activerecord
|
|
@@ -17,9 +17,9 @@ dependencies:
|
|
|
17
17
|
- - ">="
|
|
18
18
|
- !ruby/object:Gem::Version
|
|
19
19
|
version: '7.0'
|
|
20
|
-
- - "
|
|
20
|
+
- - "<"
|
|
21
21
|
- !ruby/object:Gem::Version
|
|
22
|
-
version: '8.
|
|
22
|
+
version: '8.2'
|
|
23
23
|
type: :runtime
|
|
24
24
|
prerelease: false
|
|
25
25
|
version_requirements: !ruby/object:Gem::Requirement
|
|
@@ -27,9 +27,9 @@ dependencies:
|
|
|
27
27
|
- - ">="
|
|
28
28
|
- !ruby/object:Gem::Version
|
|
29
29
|
version: '7.0'
|
|
30
|
-
- - "
|
|
30
|
+
- - "<"
|
|
31
31
|
- !ruby/object:Gem::Version
|
|
32
|
-
version: '8.
|
|
32
|
+
version: '8.2'
|
|
33
33
|
description: SimpleQuery provides a flexible and performant way to construct complex
|
|
34
34
|
database queries in Ruby on Rails applications. It offers an intuitive interface
|
|
35
35
|
for building queries with joins, conditions, and aggregations, while potentially
|