moneta 1.3.0 → 1.4.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 +26 -8
- data/CHANGES +6 -0
- data/CONTRIBUTORS +2 -1
- data/Gemfile +7 -5
- data/README.md +2 -6
- data/feature_matrix.yaml +0 -10
- data/lib/moneta.rb +9 -9
- data/lib/moneta/adapters/mongo.rb +256 -7
- data/lib/moneta/adapters/redis.rb +5 -1
- data/lib/moneta/adapters/sequel.rb +45 -464
- data/lib/moneta/adapters/sequel/mysql.rb +66 -0
- data/lib/moneta/adapters/sequel/postgres.rb +80 -0
- data/lib/moneta/adapters/sequel/postgres_hstore.rb +240 -0
- data/lib/moneta/adapters/sequel/sqlite.rb +57 -0
- data/lib/moneta/adapters/sqlite.rb +7 -7
- data/lib/moneta/create_support.rb +21 -0
- data/lib/moneta/dbm_adapter.rb +31 -0
- data/lib/moneta/{mixins.rb → defaults.rb} +1 -302
- data/lib/moneta/each_key_support.rb +27 -0
- data/lib/moneta/expires_support.rb +60 -0
- data/lib/moneta/hash_adapter.rb +68 -0
- data/lib/moneta/increment_support.rb +16 -0
- data/lib/moneta/nil_values.rb +35 -0
- data/lib/moneta/option_support.rb +51 -0
- data/lib/moneta/transformer/helper/bson.rb +5 -15
- data/lib/moneta/version.rb +1 -1
- data/lib/rack/cache/moneta.rb +14 -15
- data/moneta.gemspec +7 -9
- data/script/benchmarks +1 -2
- data/script/contributors +11 -6
- data/spec/active_support/cache_moneta_store_spec.rb +27 -29
- data/spec/features/concurrent_increment.rb +2 -3
- data/spec/features/create_expires.rb +15 -15
- data/spec/features/default_expires.rb +11 -12
- data/spec/features/expires.rb +215 -210
- data/spec/helper.rb +16 -33
- data/spec/moneta/adapters/mongo/adapter_mongo_spec.rb +16 -1
- data/spec/moneta/adapters/mongo/adapter_mongo_with_default_expires_spec.rb +1 -1
- data/spec/moneta/adapters/mongo/standard_mongo_spec.rb +1 -1
- data/spec/moneta/adapters/sequel/adapter_sequel_spec.rb +7 -34
- data/spec/moneta/adapters/sequel/helper.rb +37 -0
- data/spec/moneta/adapters/sequel/standard_sequel_spec.rb +4 -10
- data/spec/moneta/adapters/sequel/standard_sequel_with_expires_spec.rb +7 -8
- data/spec/moneta/proxies/shared/shared_unix_spec.rb +10 -0
- data/spec/restserver.rb +15 -0
- metadata +39 -58
- data/lib/moneta/adapters/mongo/base.rb +0 -103
- data/lib/moneta/adapters/mongo/moped.rb +0 -166
- data/lib/moneta/adapters/mongo/official.rb +0 -156
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_spec.rb +0 -26
- data/spec/moneta/adapters/mongo/adapter_mongo_moped_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/adapter_mongo_official_spec.rb +0 -27
- data/spec/moneta/adapters/mongo/adapter_mongo_official_with_default_expires_spec.rb +0 -14
- data/spec/moneta/adapters/mongo/standard_mongo_moped_spec.rb +0 -7
- data/spec/moneta/adapters/mongo/standard_mongo_official_spec.rb +0 -7
- data/spec/quality_spec.rb +0 -51
@@ -26,7 +26,11 @@ module Moneta
|
|
26
26
|
# number as a time to live in seconds.
|
27
27
|
def key?(key, options = {})
|
28
28
|
with_expiry_update(key, default: nil, **options) do
|
29
|
-
@backend.exists
|
29
|
+
if @backend.respond_to?(:exists?)
|
30
|
+
@backend.exists?(key)
|
31
|
+
else
|
32
|
+
@backend.exists(key)
|
33
|
+
end
|
30
34
|
end
|
31
35
|
end
|
32
36
|
|
@@ -7,6 +7,11 @@ module Moneta
|
|
7
7
|
class Sequel
|
8
8
|
include Defaults
|
9
9
|
|
10
|
+
autoload :MySQL, 'moneta/adapters/sequel/mysql'
|
11
|
+
autoload :Postgres, 'moneta/adapters/sequel/postgres'
|
12
|
+
autoload :PostgresHStore, 'moneta/adapters/sequel/postgres_hstore'
|
13
|
+
autoload :SQLite, 'moneta/adapters/sequel/sqlite'
|
14
|
+
|
10
15
|
supports :create, :increment, :each_key
|
11
16
|
attr_reader :backend, :key_column, :value_column
|
12
17
|
|
@@ -31,51 +36,20 @@ module Moneta
|
|
31
36
|
# possible to specify a separate connection to use for `#each_key`. Use
|
32
37
|
# in conjunction with Sequel's `:servers` option
|
33
38
|
# @option options All other options passed to `Sequel#connect`
|
34
|
-
def
|
39
|
+
def initialize(options = {})
|
35
40
|
extensions = options.delete(:extensions)
|
36
41
|
connection_validation_timeout = options.delete(:connection_validation_timeout)
|
37
42
|
optimize = options.delete(:optimize)
|
38
|
-
backend = options.delete(:backend) ||
|
39
|
-
|
40
|
-
raise ArgumentError, 'Option :db is required' unless db = options.delete(:db)
|
41
|
-
other_cols = [:table, :create_table, :key_column, :value_column, :hstore]
|
42
|
-
::Sequel.connect(db, options.reject { |k,| other_cols.member?(k) }).tap do |backend|
|
43
|
-
if extensions
|
44
|
-
raise ArgumentError, 'Option :extensions must be an Array' unless extensions.is_a?(Array)
|
45
|
-
extensions.map(&:to_sym).each(&backend.method(:extension))
|
46
|
-
end
|
47
|
-
|
48
|
-
if connection_validation_timeout
|
49
|
-
backend.pool.connection_validation_timeout = connection_validation_timeout
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
43
|
+
@backend = options.delete(:backend) ||
|
44
|
+
connect(extensions: extensions, connection_validation_timeout: connection_validation_timeout, **options)
|
53
45
|
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
if options[:hstore]
|
61
|
-
PostgresHStore.allocate
|
62
|
-
elsif matches = backend.get(::Sequel[:version].function).match(/PostgreSQL (\d+)\.(\d+)/)
|
63
|
-
# Our optimisations only work on Postgres 9.5+
|
64
|
-
major, minor = matches[1..2].map(&:to_i)
|
65
|
-
Postgres.allocate if major > 9 || (major == 9 && minor >= 5)
|
66
|
-
end
|
67
|
-
when :sqlite
|
68
|
-
SQLite.allocate
|
69
|
-
end
|
70
|
-
end || allocate
|
71
|
-
|
72
|
-
instance.instance_variable_set(:@backend, backend)
|
73
|
-
instance.send(:initialize, options)
|
74
|
-
instance
|
75
|
-
end
|
46
|
+
if hstore = options.delete(:hstore)
|
47
|
+
@row = hstore.to_s
|
48
|
+
extend Sequel::PostgresHStore
|
49
|
+
elsif optimize == nil || optimize
|
50
|
+
add_optimizations
|
51
|
+
end
|
76
52
|
|
77
|
-
# @api private
|
78
|
-
def initialize(options)
|
79
53
|
@table_name = (options.delete(:table) || :moneta).to_sym
|
80
54
|
@key_column = options.delete(:key_column) || :k
|
81
55
|
@value_column = options.delete(:value_column) || :v
|
@@ -227,6 +201,37 @@ module Moneta
|
|
227
201
|
|
228
202
|
protected
|
229
203
|
|
204
|
+
# @api private
|
205
|
+
def connect(db:, extensions: nil, connection_validation_timeout: nil, **options)
|
206
|
+
other_cols = [:table, :create_table, :key_column, :value_column, :hstore]
|
207
|
+
::Sequel.connect(db, options.reject { |k,| other_cols.member?(k) }).tap do |backend|
|
208
|
+
if extensions
|
209
|
+
raise ArgumentError, 'Option :extensions must be an Array' unless extensions.is_a?(Array)
|
210
|
+
extensions.map(&:to_sym).each(&backend.method(:extension))
|
211
|
+
end
|
212
|
+
|
213
|
+
if connection_validation_timeout
|
214
|
+
backend.pool.connection_validation_timeout = connection_validation_timeout
|
215
|
+
end
|
216
|
+
end
|
217
|
+
end
|
218
|
+
|
219
|
+
# @api private
|
220
|
+
def add_optimizations
|
221
|
+
case backend.database_type
|
222
|
+
when :mysql
|
223
|
+
extend Sequel::MySQL
|
224
|
+
when :postgres
|
225
|
+
if matches = backend.get(::Sequel[:version].function).match(/PostgreSQL (\d+)\.(\d+)/)
|
226
|
+
# Our optimisations only work on Postgres 9.5+
|
227
|
+
major, minor = matches[1..2].map(&:to_i)
|
228
|
+
extend Sequel::Postgres if major > 9 || (major == 9 && minor >= 5)
|
229
|
+
end
|
230
|
+
when :sqlite
|
231
|
+
extend Sequel::SQLite
|
232
|
+
end
|
233
|
+
end
|
234
|
+
|
230
235
|
def blob(str)
|
231
236
|
::Sequel.blob(str) unless str == nil
|
232
237
|
end
|
@@ -324,430 +329,6 @@ module Moneta
|
|
324
329
|
|
325
330
|
# @api private
|
326
331
|
class IncrementError < ::Sequel::DatabaseError; end
|
327
|
-
|
328
|
-
# @api private
|
329
|
-
class MySQL < Sequel
|
330
|
-
def store(key, value, options = {})
|
331
|
-
@store.call(key: key, value: blob(value))
|
332
|
-
value
|
333
|
-
end
|
334
|
-
|
335
|
-
def increment(key, amount = 1, options = {})
|
336
|
-
@backend.transaction do
|
337
|
-
# this creates a row-level lock even if there is no existing row (a
|
338
|
-
# "gap lock").
|
339
|
-
if row = @load_for_update.call(key: key)
|
340
|
-
# Integer() will raise an exception if the existing value cannot be parsed
|
341
|
-
amount += Integer(row[value_column])
|
342
|
-
@increment_update.call(key: key, value: amount)
|
343
|
-
else
|
344
|
-
@create.call(key: key, value: amount)
|
345
|
-
end
|
346
|
-
amount
|
347
|
-
end
|
348
|
-
rescue ::Sequel::SerializationFailure # Thrown on deadlock
|
349
|
-
tries ||= 0
|
350
|
-
(tries += 1) <= 3 ? retry : raise
|
351
|
-
end
|
352
|
-
|
353
|
-
def merge!(pairs, options = {}, &block)
|
354
|
-
@backend.transaction do
|
355
|
-
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
356
|
-
@table
|
357
|
-
.on_duplicate_key_update
|
358
|
-
.import([key_column, value_column], blob_pairs(pairs).to_a)
|
359
|
-
end
|
360
|
-
|
361
|
-
self
|
362
|
-
end
|
363
|
-
|
364
|
-
def each_key
|
365
|
-
return super unless block_given? && @each_key_server && @table.respond_to?(:stream)
|
366
|
-
# Order is not required when streaming
|
367
|
-
@table.server(@each_key_server).select(key_column).paged_each do |row|
|
368
|
-
yield row[key_column]
|
369
|
-
end
|
370
|
-
self
|
371
|
-
end
|
372
|
-
|
373
|
-
protected
|
374
|
-
|
375
|
-
def prepare_store
|
376
|
-
@store = @table
|
377
|
-
.on_duplicate_key_update
|
378
|
-
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
379
|
-
end
|
380
|
-
|
381
|
-
def prepare_increment
|
382
|
-
@increment_update = @table
|
383
|
-
.where(key_column => :$key)
|
384
|
-
.prepare(:update, statement_id(:increment_update), value_column => :$value)
|
385
|
-
super
|
386
|
-
end
|
387
|
-
end
|
388
|
-
|
389
|
-
# @api private
|
390
|
-
class Postgres < Sequel
|
391
|
-
def store(key, value, options = {})
|
392
|
-
@store.call(key: key, value: blob(value))
|
393
|
-
value
|
394
|
-
end
|
395
|
-
|
396
|
-
def increment(key, amount = 1, options = {})
|
397
|
-
result = @increment.call(key: key, value: blob(amount.to_s), amount: amount)
|
398
|
-
if row = result.first
|
399
|
-
row[value_column].to_i
|
400
|
-
end
|
401
|
-
end
|
402
|
-
|
403
|
-
def delete(key, options = {})
|
404
|
-
result = @delete.call(key: key)
|
405
|
-
if row = result.first
|
406
|
-
row[value_column]
|
407
|
-
end
|
408
|
-
end
|
409
|
-
|
410
|
-
def merge!(pairs, options = {}, &block)
|
411
|
-
@backend.transaction do
|
412
|
-
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
413
|
-
@table
|
414
|
-
.insert_conflict(target: key_column,
|
415
|
-
update: { value_column => ::Sequel[:excluded][value_column] })
|
416
|
-
.import([key_column, value_column], blob_pairs(pairs).to_a)
|
417
|
-
end
|
418
|
-
|
419
|
-
self
|
420
|
-
end
|
421
|
-
|
422
|
-
def each_key
|
423
|
-
return super unless block_given? && !@each_key_server && @table.respond_to?(:use_cursor)
|
424
|
-
# With a cursor, this will Just Work.
|
425
|
-
@table.select(key_column).paged_each do |row|
|
426
|
-
yield row[key_column]
|
427
|
-
end
|
428
|
-
self
|
429
|
-
end
|
430
|
-
|
431
|
-
protected
|
432
|
-
|
433
|
-
def prepare_store
|
434
|
-
@store = @table
|
435
|
-
.insert_conflict(target: key_column,
|
436
|
-
update: { value_column => ::Sequel[:excluded][value_column] })
|
437
|
-
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
438
|
-
end
|
439
|
-
|
440
|
-
def prepare_increment
|
441
|
-
update_expr = ::Sequel[:convert_to].function(
|
442
|
-
(::Sequel[:convert_from].function(
|
443
|
-
::Sequel[@table_name][value_column],
|
444
|
-
'UTF8'
|
445
|
-
).cast(Integer) + :$amount).cast(String),
|
446
|
-
'UTF8'
|
447
|
-
)
|
448
|
-
|
449
|
-
@increment = @table
|
450
|
-
.returning(value_column)
|
451
|
-
.insert_conflict(target: key_column, update: { value_column => update_expr })
|
452
|
-
.prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
453
|
-
end
|
454
|
-
|
455
|
-
def prepare_delete
|
456
|
-
@delete = @table
|
457
|
-
.returning(value_column)
|
458
|
-
.where(key_column => :$key)
|
459
|
-
.prepare(:delete, statement_id(:delete))
|
460
|
-
end
|
461
|
-
end
|
462
|
-
|
463
|
-
# @api private
|
464
|
-
class PostgresHStore < Sequel
|
465
|
-
def initialize(options)
|
466
|
-
@row = options.delete(:hstore).to_s
|
467
|
-
@backend.extension :pg_hstore
|
468
|
-
::Sequel.extension :pg_hstore_ops
|
469
|
-
@backend.extension :pg_array
|
470
|
-
super
|
471
|
-
end
|
472
|
-
|
473
|
-
def key?(key, options = {})
|
474
|
-
if @key
|
475
|
-
row = @key.call(row: @row, key: key) || false
|
476
|
-
row && row[:present]
|
477
|
-
else
|
478
|
-
@key_pl.get(key)
|
479
|
-
end
|
480
|
-
end
|
481
|
-
|
482
|
-
def store(key, value, options = {})
|
483
|
-
@backend.transaction do
|
484
|
-
create_row
|
485
|
-
@store.call(row: @row, pair: ::Sequel.hstore(key => value))
|
486
|
-
end
|
487
|
-
value
|
488
|
-
end
|
489
|
-
|
490
|
-
def load(key, options = {})
|
491
|
-
if row = @load.call(row: @row, key: key)
|
492
|
-
row[:value]
|
493
|
-
end
|
494
|
-
end
|
495
|
-
|
496
|
-
def delete(key, options = {})
|
497
|
-
@backend.transaction do
|
498
|
-
value = load(key, options)
|
499
|
-
@delete.call(row: @row, key: key)
|
500
|
-
value
|
501
|
-
end
|
502
|
-
end
|
503
|
-
|
504
|
-
def increment(key, amount = 1, options = {})
|
505
|
-
@backend.transaction do
|
506
|
-
create_row
|
507
|
-
if row = @increment.call(row: @row, key: key, amount: amount).first
|
508
|
-
row[:value].to_i
|
509
|
-
end
|
510
|
-
end
|
511
|
-
end
|
512
|
-
|
513
|
-
def create(key, value, options = {})
|
514
|
-
@backend.transaction do
|
515
|
-
create_row
|
516
|
-
1 ==
|
517
|
-
if @create
|
518
|
-
@create.call(row: @row, key: key, pair: ::Sequel.hstore(key => value))
|
519
|
-
else
|
520
|
-
@table
|
521
|
-
.where(key_column => @row)
|
522
|
-
.exclude(::Sequel[value_column].hstore.key?(key))
|
523
|
-
.update(value_column => ::Sequel[value_column].hstore.merge(key => value))
|
524
|
-
end
|
525
|
-
end
|
526
|
-
end
|
527
|
-
|
528
|
-
def clear(options = {})
|
529
|
-
@clear.call(row: @row)
|
530
|
-
self
|
531
|
-
end
|
532
|
-
|
533
|
-
def values_at(*keys, **options)
|
534
|
-
if row = @values_at.call(row: @row, keys: ::Sequel.pg_array(keys))
|
535
|
-
row[:values].to_a
|
536
|
-
else
|
537
|
-
[]
|
538
|
-
end
|
539
|
-
end
|
540
|
-
|
541
|
-
def slice(*keys, **options)
|
542
|
-
if row = @slice.call(row: @row, keys: ::Sequel.pg_array(keys))
|
543
|
-
row[:pairs].to_h
|
544
|
-
else
|
545
|
-
[]
|
546
|
-
end
|
547
|
-
end
|
548
|
-
|
549
|
-
def merge!(pairs, options = {}, &block)
|
550
|
-
@backend.transaction do
|
551
|
-
create_row
|
552
|
-
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
553
|
-
hash = Hash === pairs ? pairs : Hash[pairs.to_a]
|
554
|
-
@store.call(row: @row, pair: ::Sequel.hstore(hash))
|
555
|
-
end
|
556
|
-
|
557
|
-
self
|
558
|
-
end
|
559
|
-
|
560
|
-
def each_key
|
561
|
-
return enum_for(:each_key) { @size.call(row: @row)[:size] } unless block_given?
|
562
|
-
|
563
|
-
ds =
|
564
|
-
if @each_key_server
|
565
|
-
@table.server(@each_key_server)
|
566
|
-
else
|
567
|
-
@table
|
568
|
-
end
|
569
|
-
ds = ds.order(:skeys) unless @table.respond_to?(:use_cursor)
|
570
|
-
ds.where(key_column => @row)
|
571
|
-
.select(::Sequel[value_column].hstore.skeys)
|
572
|
-
.paged_each do |row|
|
573
|
-
yield row[:skeys]
|
574
|
-
end
|
575
|
-
self
|
576
|
-
end
|
577
|
-
|
578
|
-
protected
|
579
|
-
|
580
|
-
def create_row
|
581
|
-
@create_row.call(row: @row)
|
582
|
-
end
|
583
|
-
|
584
|
-
def create_table
|
585
|
-
key_column = self.key_column
|
586
|
-
value_column = self.value_column
|
587
|
-
|
588
|
-
@backend.create_table?(@table_name) do
|
589
|
-
column key_column, String, null: false, primary_key: true
|
590
|
-
column value_column, :hstore
|
591
|
-
index value_column, type: :gin
|
592
|
-
end
|
593
|
-
end
|
594
|
-
|
595
|
-
def slice_for_update(pairs)
|
596
|
-
keys = pairs.map { |k, _| k }.to_a
|
597
|
-
if row = @slice_for_update.call(row: @row, keys: ::Sequel.pg_array(keys))
|
598
|
-
row[:pairs].to_h
|
599
|
-
else
|
600
|
-
{}
|
601
|
-
end
|
602
|
-
end
|
603
|
-
|
604
|
-
def prepare_statements
|
605
|
-
super
|
606
|
-
prepare_create_row
|
607
|
-
prepare_clear
|
608
|
-
prepare_values_at
|
609
|
-
prepare_size
|
610
|
-
end
|
611
|
-
|
612
|
-
def prepare_create_row
|
613
|
-
@create_row = @table
|
614
|
-
.insert_ignore
|
615
|
-
.prepare(:insert, statement_id(:hstore_create_row), key_column => :$row, value_column => '')
|
616
|
-
end
|
617
|
-
|
618
|
-
def prepare_clear
|
619
|
-
@clear = @table.where(key_column => :$row).prepare(:update, statement_id(:hstore_clear), value_column => '')
|
620
|
-
end
|
621
|
-
|
622
|
-
def prepare_key
|
623
|
-
if defined?(JRUBY_VERSION)
|
624
|
-
@key_pl = ::Sequel::Dataset::PlaceholderLiteralizer.loader(@table) do |pl, ds|
|
625
|
-
ds.where(key_column => @row).select(::Sequel[value_column].hstore.key?(pl.arg))
|
626
|
-
end
|
627
|
-
else
|
628
|
-
@key = @table.where(key_column => :$row)
|
629
|
-
.select(::Sequel[value_column].hstore.key?(:$key).as(:present))
|
630
|
-
.prepare(:first, statement_id(:hstore_key))
|
631
|
-
end
|
632
|
-
end
|
633
|
-
|
634
|
-
def prepare_store
|
635
|
-
@store = @table
|
636
|
-
.where(key_column => :$row)
|
637
|
-
.prepare(:update, statement_id(:hstore_store), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
638
|
-
end
|
639
|
-
|
640
|
-
def prepare_increment
|
641
|
-
pair = ::Sequel[:hstore]
|
642
|
-
.function(:$key, (
|
643
|
-
::Sequel[:coalesce].function(::Sequel[value_column].hstore[:$key].cast(Integer), 0) +
|
644
|
-
:$amount
|
645
|
-
).cast(String))
|
646
|
-
|
647
|
-
@increment = @table
|
648
|
-
.returning(::Sequel[value_column].hstore[:$key].as(:value))
|
649
|
-
.where(key_column => :$row)
|
650
|
-
.prepare(:update, statement_id(:hstore_increment), value_column => ::Sequel.join([value_column, pair]))
|
651
|
-
end
|
652
|
-
|
653
|
-
def prepare_load
|
654
|
-
@load = @table.where(key_column => :$row)
|
655
|
-
.select(::Sequel[value_column].hstore[:$key].as(:value))
|
656
|
-
.prepare(:first, statement_id(:hstore_load))
|
657
|
-
end
|
658
|
-
|
659
|
-
def prepare_delete
|
660
|
-
@delete = @table.where(key_column => :$row)
|
661
|
-
.prepare(:update, statement_id(:hstore_delete), value_column => ::Sequel[value_column].hstore.delete(:$key))
|
662
|
-
end
|
663
|
-
|
664
|
-
def prepare_create
|
665
|
-
# Under JRuby we can't use a prepared statement for queries involving
|
666
|
-
# the hstore `?` (key?) operator. See
|
667
|
-
# https://stackoverflow.com/questions/11940401/escaping-hstore-contains-operators-in-a-jdbc-prepared-statement
|
668
|
-
return if defined?(JRUBY_VERSION)
|
669
|
-
@create = @table
|
670
|
-
.where(key_column => :$row)
|
671
|
-
.exclude(::Sequel[value_column].hstore.key?(:$key))
|
672
|
-
.prepare(:update, statement_id(:hstore_create), value_column => ::Sequel[value_column].hstore.merge(:$pair))
|
673
|
-
end
|
674
|
-
|
675
|
-
def prepare_values_at
|
676
|
-
@values_at = @table
|
677
|
-
.where(key_column => :$row)
|
678
|
-
.select(::Sequel[value_column].hstore[::Sequel.cast(:$keys, :"text[]")].as(:values))
|
679
|
-
.prepare(:first, statement_id(:hstore_values_at))
|
680
|
-
end
|
681
|
-
|
682
|
-
def prepare_slice
|
683
|
-
slice = @table
|
684
|
-
.where(key_column => :$row)
|
685
|
-
.select(::Sequel[value_column].hstore.slice(:$keys).as(:pairs))
|
686
|
-
@slice = slice.prepare(:first, statement_id(:hstore_slice))
|
687
|
-
@slice_for_update = slice.for_update.prepare(:first, statement_id(:hstore_slice_for_update))
|
688
|
-
end
|
689
|
-
|
690
|
-
def prepare_size
|
691
|
-
@size = @backend
|
692
|
-
.from(@table.where(key_column => :$row)
|
693
|
-
.select(::Sequel[value_column].hstore.each))
|
694
|
-
.select { count.function.*.as(:size) }
|
695
|
-
.prepare(:first, statement_id(:hstore_size))
|
696
|
-
end
|
697
|
-
end
|
698
|
-
|
699
|
-
# @api private
|
700
|
-
class SQLite < Sequel
|
701
|
-
def initialize(options)
|
702
|
-
@version = backend.get(::Sequel[:sqlite_version].function)
|
703
|
-
# See https://sqlite.org/lang_UPSERT.html
|
704
|
-
@can_upsert = ::Gem::Version.new(@version) >= ::Gem::Version.new('3.24.0')
|
705
|
-
super
|
706
|
-
end
|
707
|
-
|
708
|
-
def store(key, value, options = {})
|
709
|
-
@table.insert_conflict(:replace).insert(key_column => key, value_column => blob(value))
|
710
|
-
value
|
711
|
-
end
|
712
|
-
|
713
|
-
def increment(key, amount = 1, options = {})
|
714
|
-
return super unless @can_upsert
|
715
|
-
@backend.transaction do
|
716
|
-
@increment.call(key: key, value: blob(amount.to_s), amount: amount)
|
717
|
-
Integer(load(key))
|
718
|
-
end
|
719
|
-
end
|
720
|
-
|
721
|
-
def merge!(pairs, options = {}, &block)
|
722
|
-
@backend.transaction do
|
723
|
-
pairs = yield_merge_pairs(pairs, &block) if block_given?
|
724
|
-
@table.insert_conflict(:replace).import([key_column, value_column], blob_pairs(pairs).to_a)
|
725
|
-
end
|
726
|
-
|
727
|
-
self
|
728
|
-
end
|
729
|
-
|
730
|
-
protected
|
731
|
-
|
732
|
-
def prepare_store
|
733
|
-
@store = @table
|
734
|
-
.insert_conflict(:replace)
|
735
|
-
.prepare(:insert, statement_id(:store), key_column => :$key, value_column => :$value)
|
736
|
-
end
|
737
|
-
|
738
|
-
def prepare_increment
|
739
|
-
return super unless @can_upsert
|
740
|
-
update_expr = (::Sequel[value_column].cast(Integer) + :$amount).cast(:blob)
|
741
|
-
@increment = @table
|
742
|
-
.insert_conflict(
|
743
|
-
target: key_column,
|
744
|
-
update: { value_column => update_expr },
|
745
|
-
update_where: ::Sequel.|({ value_column => blob("0") },
|
746
|
-
{ ::Sequel.~(::Sequel[value_column].cast(Integer)) => 0 })
|
747
|
-
)
|
748
|
-
.prepare(:insert, statement_id(:increment), key_column => :$key, value_column => :$value)
|
749
|
-
end
|
750
|
-
end
|
751
332
|
end
|
752
333
|
end
|
753
334
|
end
|