sequel 4.26.0 → 4.27.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +30 -0
- data/bin/sequel +8 -0
- data/doc/opening_databases.rdoc +3 -1
- data/doc/postgresql.rdoc +13 -0
- data/doc/release_notes/4.27.0.txt +78 -0
- data/doc/thread_safety.rdoc +1 -1
- data/lib/sequel/adapters/jdbc/postgresql.rb +1 -1
- data/lib/sequel/adapters/postgres.rb +4 -0
- data/lib/sequel/adapters/shared/postgres.rb +1 -1
- data/lib/sequel/core.rb +8 -18
- data/lib/sequel/database/query.rb +1 -1
- data/lib/sequel/database/schema_generator.rb +17 -4
- data/lib/sequel/dataset/actions.rb +38 -9
- data/lib/sequel/extensions/pg_json_ops.rb +58 -5
- data/lib/sequel/extensions/schema_dumper.rb +12 -1
- data/lib/sequel/model/base.rb +30 -14
- data/lib/sequel/plugins/active_model.rb +7 -0
- data/lib/sequel/plugins/before_after_save.rb +48 -0
- data/lib/sequel/plugins/defaults_setter.rb +8 -4
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/postgres_spec.rb +10 -0
- data/spec/bin_spec.rb +4 -0
- data/spec/core/dataset_spec.rb +34 -0
- data/spec/core/schema_generator_spec.rb +13 -0
- data/spec/core/schema_spec.rb +17 -0
- data/spec/extensions/active_model_spec.rb +70 -108
- data/spec/extensions/before_after_save_spec.rb +40 -0
- data/spec/extensions/pg_json_ops_spec.rb +46 -0
- data/spec/extensions/schema_dumper_spec.rb +18 -0
- data/spec/integration/dataset_test.rb +14 -0
- data/spec/integration/schema_test.rb +6 -0
- data/spec/model/class_dataset_methods_spec.rb +4 -0
- data/spec/model/model_spec.rb +1 -1
- metadata +7 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c8e0006e87c72a9cee83298b0fab32a7ccea74ef
|
4
|
+
data.tar.gz: 46a222bb7aa35ce30d2a931c56c0cc49d8fd1b3a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: db00224bfcada026d014fbe777cee84849e8786a33519258197a371e9f48970c8a37a1e45e05462f90e0c9e89fb638dcfd1c5442855f1d6b02e7a79b2a21901d
|
7
|
+
data.tar.gz: 79bbb7ad3a536b02bd7242c2fd2b6f08b2772a5b44ba5acdca66224fe93c009e093861ec9e61b877b8349d77ae3d2c5350edcae41cfd6f10827984be0e77ffde
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,33 @@
|
|
1
|
+
=== 4.27.0 (2015-10-01)
|
2
|
+
|
3
|
+
* Don't stub Sequel.synchronize on MRI (YorickPeterse) (#1083)
|
4
|
+
|
5
|
+
* Make bin/sequel warn if given arguments that it doesn't use (jeremyevans)
|
6
|
+
|
7
|
+
* Fix the order of referenced composite keys returned by Database#foreign_key_list on PostgreSQL (jeremyevans) (#1081)
|
8
|
+
|
9
|
+
* Recognize another disconnect error in the jdbc/postgresql adapter (jeremyevans)
|
10
|
+
|
11
|
+
* In the active model plugin, make Model#persisted? return false if the transaction used for creation is rolled back (jeremyevans) (#1076)
|
12
|
+
|
13
|
+
* Use primary_key :keep_order option in the schema dumper if the auto incrementing column is not the first column in the table (jeremyevans)
|
14
|
+
|
15
|
+
* Set :auto_increment option correctly in the schema parser when the auto incrementing column is not the first column in the table (jeremyevans)
|
16
|
+
|
17
|
+
* Support :keep_order option to primary_key in schema generator, to not automatically make the primary key the first column (jeremyevans)
|
18
|
+
|
19
|
+
* Add new jsonb/json functions and operators supported in PostgreSQL 9.5+ (jeremyevans)
|
20
|
+
|
21
|
+
* Add before_after_save plugin, for refreshing created objects and resetting modified flag before calling after_create/update/save hooks (jeremyevans)
|
22
|
+
|
23
|
+
* Add Dataset#single_record! and #single_value! which don't require cloning the receiver (jeremyevans)
|
24
|
+
|
25
|
+
* Dataset#with_sql_single_value now works correctly for model datasets (jeremyevans)
|
26
|
+
|
27
|
+
* Optimize Dataset#single_value and #with_sql_single_value to not create an unnecessary array (jeremyevans)
|
28
|
+
|
29
|
+
* Make postgres adapter work with postgres-pr 0.7.0 (jeremyevans) (#1074)
|
30
|
+
|
1
31
|
=== 4.26.0 (2015-09-01)
|
2
32
|
|
3
33
|
* Make Dataset#== not consider frozen status in determining equality (jeremyevans)
|
data/bin/sequel
CHANGED
@@ -116,6 +116,9 @@ error_proc = lambda do |msg|
|
|
116
116
|
$stderr.puts(msg)
|
117
117
|
exit 1
|
118
118
|
end
|
119
|
+
extra_proc = lambda do
|
120
|
+
$stderr.puts("Warning: last #{ARGV.length} arguments ignored") unless ARGV.empty?
|
121
|
+
end
|
119
122
|
|
120
123
|
error_proc["Error: Must specify -m if using -M"] if migrate_ver && !migrate_dir
|
121
124
|
error_proc["Error: Cannot specify #{exclusive_options.map{|v| "-#{v}"}.join(' and ')} together"] if exclusive_options.length > 1
|
@@ -151,16 +154,19 @@ begin
|
|
151
154
|
DB = connect_proc[db]
|
152
155
|
load_dirs.each{|d| d.is_a?(Array) ? require(d.first) : Dir["#{d}/**/*.rb"].each{|f| load(f)}}
|
153
156
|
if migrate_dir
|
157
|
+
extra_proc.call
|
154
158
|
Sequel.extension :migration, :core_extensions
|
155
159
|
Sequel::Migrator.apply(DB, migrate_dir, migrate_ver)
|
156
160
|
exit
|
157
161
|
end
|
158
162
|
if dump_migration
|
163
|
+
extra_proc.call
|
159
164
|
DB.extension :schema_dumper
|
160
165
|
puts DB.dump_schema_migration(:same_db=>dump_migration==:same_db)
|
161
166
|
exit
|
162
167
|
end
|
163
168
|
if dump_schema
|
169
|
+
extra_proc.call
|
164
170
|
DB.extension :schema_caching
|
165
171
|
DB.tables.each{|t| DB.schema(Sequel::SQL::Identifier.new(t))}
|
166
172
|
DB.dump_schema_cache(dump_schema)
|
@@ -172,6 +178,7 @@ begin
|
|
172
178
|
|
173
179
|
db2 = ARGV.shift
|
174
180
|
error_proc["Error: Must specify database connection string or path to yaml file as second argument for database you want to copy to"] if db2.nil? || db2.empty?
|
181
|
+
extra_proc.call
|
175
182
|
start_time = Time.now
|
176
183
|
TO_DB = connect_proc[db2]
|
177
184
|
same_db = DB.database_type==TO_DB.database_type
|
@@ -225,6 +232,7 @@ begin
|
|
225
232
|
exit
|
226
233
|
end
|
227
234
|
if code
|
235
|
+
extra_proc.call
|
228
236
|
eval(code)
|
229
237
|
exit
|
230
238
|
end
|
data/doc/opening_databases.rdoc
CHANGED
@@ -309,7 +309,9 @@ The Sequel postgres adapter works with the pg, postgres, and postgres-pr ruby li
|
|
309
309
|
The pg library is the best supported, as it supports real bound variables and prepared statements.
|
310
310
|
If the pg library is being used, Sequel will also attempt to load the sequel_pg library, which is
|
311
311
|
a C extension that optimizes performance when Sequel is used with pg. All users of Sequel who
|
312
|
-
use pg are encouraged to install sequel_pg.
|
312
|
+
use pg are encouraged to install sequel_pg. For users who want to use postgres-pr to avoid issues
|
313
|
+
with C extensions, it is recommended to use jeremyevans-postgres-pr, which fixes many issues in
|
314
|
+
the upstream postgres-pr gem, and is regularly tested with Sequel.
|
313
315
|
|
314
316
|
The following additional options are supported:
|
315
317
|
|
data/doc/postgresql.rdoc
CHANGED
@@ -185,6 +185,19 @@ When returning is used, instead of returning the number of rows affected (for up
|
|
185
185
|
or the serial primary key value (for insert), it will return an array of hashes with the
|
186
186
|
returned results.
|
187
187
|
|
188
|
+
=== VALUES Support
|
189
|
+
|
190
|
+
Sequel offers support for the +VALUES+ statement using <tt>Database#values</tt>:
|
191
|
+
|
192
|
+
DB.values([[1,2],[2,3],[3,4]])
|
193
|
+
# VALUES (1, 2), (2, 3), (3, 4)
|
194
|
+
|
195
|
+
DB.values([[1,2],[2,3],[3,4]]).order(2, 1)
|
196
|
+
# VALUES (1, 2), (2, 3), (3, 4) ORDER BY 2, 1
|
197
|
+
|
198
|
+
DB.values([[1,2],[2,3],[3,4]]).order(2, 1).limit(1,2)
|
199
|
+
# VALUES (1, 2), (2, 3), (3, 4) ORDER BY 2, 1 LIMIT 1 OFFSET 2
|
200
|
+
|
188
201
|
=== INSERT ON CONFLICT Support
|
189
202
|
|
190
203
|
Starting with PostgreSQL 9.5, you can do an upsert or ignore unique or exclusion constraint
|
@@ -0,0 +1,78 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A before_after_save plugin has been added, which for newly
|
4
|
+
created objects refreshes the object before calling after_create,
|
5
|
+
and resets the modified flag before calling after_update.
|
6
|
+
Previously, these actions were not taken until after after_save
|
7
|
+
was called. This will be the default behavior in Sequel 5.
|
8
|
+
|
9
|
+
* In create_table blocks, primary_key now supports a :keep_order
|
10
|
+
option, which will not change the order in which the primary key
|
11
|
+
is added. Without this option, Sequel's historical behavior of
|
12
|
+
making the primary key column the first column is used.
|
13
|
+
|
14
|
+
DB.create_table(:foo) do
|
15
|
+
Integer :a
|
16
|
+
primary_key :b, :keep_order=>true
|
17
|
+
end
|
18
|
+
# CREATE TABLE foo
|
19
|
+
# (a integer, b integer PRIMARY KEY AUTOINCREMENT)
|
20
|
+
|
21
|
+
The schema dumper now uses this option if necessary, allowing it
|
22
|
+
to correctly dump tables where the primary key column is not the
|
23
|
+
first column.
|
24
|
+
|
25
|
+
* Dataset#single_record! and #single_value! have been added. These
|
26
|
+
are faster versions of #single_record and #single_value that
|
27
|
+
don't require cloning the dataset. If you are sure the dataset
|
28
|
+
will only return a single row or a single value, you can use
|
29
|
+
these methods for better performance.
|
30
|
+
|
31
|
+
* The new jsonb and json functions added in PostgreSQL 9.5 are now
|
32
|
+
supported by the pg_json_ops extension.
|
33
|
+
|
34
|
+
Sequel.pg_jsonb_op(:metadata).set(%w'a b', [1,2,3])
|
35
|
+
# jsonb_set("metadata", ARRAY['a','b'], '[1,2,3]'::jsonb, true)
|
36
|
+
|
37
|
+
= Other Improvements
|
38
|
+
|
39
|
+
* Sequel.synchronize is no longer a stub on MRI. Testing has shown
|
40
|
+
that relying on the global interpreter lock to protect
|
41
|
+
multi-threaded access to hashes is not safe in all environments,
|
42
|
+
so Sequel now uses a mutex on MRI just as it does on other ruby
|
43
|
+
interpreters.
|
44
|
+
|
45
|
+
* Database#schema now sets the :auto_increment option correctly for
|
46
|
+
auto incrementing primary keys if they are not the first column
|
47
|
+
in the table.
|
48
|
+
|
49
|
+
* Dataset#single_value and #with_sql_single_value are now slightly
|
50
|
+
faster by avoiding an array allocation.
|
51
|
+
|
52
|
+
* Model datasets can now use #with_sql_single_value and return a
|
53
|
+
single value, instead of an array in [:column_name, value] format.
|
54
|
+
|
55
|
+
* Model#persisted? in the active_model plugin will now return false
|
56
|
+
if the transaction that inserts the row for the object is rolled
|
57
|
+
back.
|
58
|
+
|
59
|
+
* bin/sequel now warns if additional arguments are passed that it
|
60
|
+
ignores. In Sequel 5, bin/sequel will raise an error in these
|
61
|
+
cases.
|
62
|
+
|
63
|
+
* Database#foreign_key_list on PostgreSQL now returns referenced
|
64
|
+
composite keys in the correct order.
|
65
|
+
|
66
|
+
* The postgres adapter now works with postgres-pr 0.7.0. Note that
|
67
|
+
postgres adapter users that want a pure-ruby driver are encouraged
|
68
|
+
to use jeremyevans-postgres-pr as that has many additional bugfixes
|
69
|
+
and is the version tested with Sequel on a regular basis.
|
70
|
+
|
71
|
+
* The jdbc/postgresql adapter now recognizes an additional disconnect
|
72
|
+
error.
|
73
|
+
|
74
|
+
= Backwards Compatibility
|
75
|
+
|
76
|
+
* Users who were relying on #with_sql_single_value returning an array
|
77
|
+
instead of a single value for model datasets need to update their
|
78
|
+
code.
|
data/doc/thread_safety.rdoc
CHANGED
@@ -4,7 +4,7 @@ Most Sequel usage (and all common Sequel usage) is thread safe by default. Spec
|
|
4
4
|
|
5
5
|
== Connection Pool
|
6
6
|
|
7
|
-
In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block
|
7
|
+
In order to allow multiple threads to operate on the same database at the same time, Sequel uses a connection pool. The connection pool is designed so that a thread uses a connection for the minimum amount of time, returning the connection to the pool as soon as it is done using the connection. If a thread requests a connection and the pool does not have an available connection, a new connection will be created. If the maximum number of connections in the pool has already been reached, the thread will block until a connection is available or the connection pool timeout has elapsed (in which case a PoolTimeout error will be raised).
|
8
8
|
|
9
9
|
== Exceptions
|
10
10
|
|
@@ -135,7 +135,7 @@ module Sequel
|
|
135
135
|
end
|
136
136
|
|
137
137
|
def disconnect_error?(exception, opts)
|
138
|
-
super || exception.message =~ /\
|
138
|
+
super || exception.message =~ /\A(This connection has been closed\.|FATAL: terminating connection due to administrator command|An I\/O error occurred while sending to the backend\.)\z/
|
139
139
|
end
|
140
140
|
|
141
141
|
# Use setNull for nil arguments as the default behavior of setString
|
@@ -2,6 +2,10 @@ Sequel.require 'adapters/shared/postgres'
|
|
2
2
|
|
3
3
|
begin
|
4
4
|
require 'pg'
|
5
|
+
|
6
|
+
# Work around postgres-pr 0.7.0+ which ships with a pg.rb file
|
7
|
+
raise LoadError unless defined?(PGconn::CONNECTION_OK)
|
8
|
+
|
5
9
|
SEQUEL_POSTGRES_USES_PG = true
|
6
10
|
rescue LoadError => e
|
7
11
|
SEQUEL_POSTGRES_USES_PG = false
|
@@ -308,7 +308,7 @@ module Sequel
|
|
308
308
|
ref_ds = base_ds.
|
309
309
|
join(:pg_class___cl2, :oid=>:co__confrelid).
|
310
310
|
join(:pg_attribute___att2, :attrelid=>:oid, :attnum=>SQL::Function.new(:ANY, :co__confkey)).
|
311
|
-
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:
|
311
|
+
order(:co__conname, SQL::CaseExpression.new(range.map{|x| [SQL::Subscript.new(:co__confkey, [x]), x]}, 32, :att2__attnum)).
|
312
312
|
select(:co__conname___name, :cl2__relname___table, :att2__attname___refcolumn)
|
313
313
|
|
314
314
|
# If a schema is given, we only search in that schema, and the returned :table
|
data/lib/sequel/core.rb
CHANGED
@@ -293,24 +293,14 @@ module Sequel
|
|
293
293
|
end
|
294
294
|
end
|
295
295
|
|
296
|
-
|
297
|
-
|
298
|
-
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
def self.synchronize(&block)
|
305
|
-
@single_threaded ? yield : @data_mutex.synchronize(&block)
|
306
|
-
end
|
307
|
-
# :nocov:
|
308
|
-
else
|
309
|
-
# Yield directly to the block. You don't need to synchronize
|
310
|
-
# access on MRI because the GVL makes certain methods atomic.
|
311
|
-
def self.synchronize
|
312
|
-
yield
|
313
|
-
end
|
296
|
+
# Mutex used to protect mutable data structures
|
297
|
+
@data_mutex = Mutex.new
|
298
|
+
|
299
|
+
# Unless in single threaded mode, protects access to any mutable
|
300
|
+
# global data structure in Sequel.
|
301
|
+
# Uses a non-reentrant mutex, so calling code should be careful.
|
302
|
+
def self.synchronize(&block)
|
303
|
+
@single_threaded ? yield : @data_mutex.synchronize(&block)
|
314
304
|
end
|
315
305
|
|
316
306
|
# Uses a transaction on all given databases with the given options. This:
|
@@ -236,22 +236,35 @@ module Sequel
|
|
236
236
|
#
|
237
237
|
# If an array of column symbols is used, you can specify the :name option
|
238
238
|
# to name the constraint.
|
239
|
+
#
|
240
|
+
# Options:
|
241
|
+
# :keep_order :: For non-composite primary keys, respects the existing order of
|
242
|
+
# columns, overriding the default behavior of making the primary
|
243
|
+
# key the first column.
|
239
244
|
#
|
240
245
|
# Examples:
|
241
246
|
# primary_key(:id)
|
247
|
+
# primary_key(:id, Bigint)
|
248
|
+
# primary_key(:id, Bigint, :keep_order=>true)
|
242
249
|
# primary_key([:street_number, :house_number], :name=>:some constraint_name)
|
243
250
|
def primary_key(name, *args)
|
244
251
|
return composite_primary_key(name, *args) if name.is_a?(Array)
|
245
|
-
|
252
|
+
column = @db.serial_primary_key_options.merge({:name => name})
|
246
253
|
|
247
254
|
if opts = args.pop
|
248
255
|
opts = {:type => opts} unless opts.is_a?(Hash)
|
249
256
|
if type = args.pop
|
250
|
-
opts.merge
|
257
|
+
opts = opts.merge(:type => type)
|
251
258
|
end
|
252
|
-
|
259
|
+
column.merge!(opts)
|
260
|
+
end
|
261
|
+
|
262
|
+
@primary_key = column
|
263
|
+
if column[:keep_order]
|
264
|
+
columns << column
|
265
|
+
else
|
266
|
+
columns.unshift(column)
|
253
267
|
end
|
254
|
-
@primary_key
|
255
268
|
end
|
256
269
|
|
257
270
|
# The name of the primary key for this generator, if it has a primary key.
|
@@ -12,7 +12,7 @@ module Sequel
|
|
12
12
|
<< [] all avg count columns columns! delete each
|
13
13
|
empty? fetch_rows first first! get import insert interval last
|
14
14
|
map max min multi_insert paged_each range select_hash select_hash_groups select_map select_order_map
|
15
|
-
single_record single_value sum to_hash to_hash_groups truncate update
|
15
|
+
single_record single_record! single_value single_value! sum to_hash to_hash_groups truncate update
|
16
16
|
METHS
|
17
17
|
|
18
18
|
# Inserts the given argument into the database. Returns self so it
|
@@ -637,22 +637,51 @@ module Sequel
|
|
637
637
|
_select_map(column, true, &block)
|
638
638
|
end
|
639
639
|
|
640
|
-
#
|
641
|
-
# has no records. Users should probably use +first+ instead of
|
642
|
-
# this method.
|
640
|
+
# Limits the dataset to one record, and returns the first record in the dataset,
|
641
|
+
# or nil if the dataset has no records. Users should probably use +first+ instead of
|
642
|
+
# this method. Example:
|
643
|
+
#
|
644
|
+
# DB[:test].single_record # SELECT * FROM test LIMIT 1
|
645
|
+
# # => {:column_name=>'value'}
|
643
646
|
def single_record
|
644
|
-
clone(:limit=>1).
|
645
|
-
|
647
|
+
clone(:limit=>1).single_record!
|
648
|
+
end
|
649
|
+
|
650
|
+
# Returns the first record in dataset, without limiting the dataset. Returns nil if
|
651
|
+
# the dataset has no records. Users should probably use +first+ instead of this method.
|
652
|
+
# This should only be used if you know the dataset is already limited to a single record.
|
653
|
+
# This method may be desirable to use for performance reasons, as it does not clone the
|
654
|
+
# receiver. Example:
|
655
|
+
#
|
656
|
+
# DB[:test].single_record! # SELECT * FROM test
|
657
|
+
# # => {:column_name=>'value'}
|
658
|
+
def single_record!
|
659
|
+
with_sql_first(select_sql)
|
646
660
|
end
|
647
661
|
|
648
662
|
# Returns the first value of the first record in the dataset.
|
649
663
|
# Returns nil if dataset is empty. Users should generally use
|
650
|
-
# +get+ instead of this method.
|
664
|
+
# +get+ instead of this method. Example:
|
665
|
+
#
|
666
|
+
# DB[:test].single_value # SELECT * FROM test LIMIT 1
|
667
|
+
# # => 'value'
|
651
668
|
def single_value
|
652
669
|
if r = ungraphed.naked.single_record
|
653
|
-
r.
|
670
|
+
r.each{|_, v| return v}
|
654
671
|
end
|
655
672
|
end
|
673
|
+
|
674
|
+
# Returns the first value of the first record in the dataset, without limiting the dataset.
|
675
|
+
# Returns nil if the dataset is empty. Users should generally use +get+ instead of this
|
676
|
+
# method. Should not be used on graphed datasets or datasets that have row_procs that
|
677
|
+
# don't return hashes. This method may be desirable to use for performance reasons, as
|
678
|
+
# it does not clone the receiver.
|
679
|
+
#
|
680
|
+
# DB[:test].single_value! # SELECT * FROM test
|
681
|
+
# # => 'value'
|
682
|
+
def single_value!
|
683
|
+
with_sql_single_value(select_sql)
|
684
|
+
end
|
656
685
|
|
657
686
|
# Returns the sum for the given column/expression.
|
658
687
|
# Uses a virtual row block if no column is given.
|
@@ -818,7 +847,7 @@ module Sequel
|
|
818
847
|
# only a single value. See with_sql_each.
|
819
848
|
def with_sql_single_value(sql)
|
820
849
|
if r = with_sql_first(sql)
|
821
|
-
r.
|
850
|
+
r.each{|_, v| return v}
|
822
851
|
end
|
823
852
|
end
|
824
853
|
|
@@ -50,6 +50,7 @@
|
|
50
50
|
# j.each_text # json_each_text(json_column)
|
51
51
|
# j.keys # json_object_keys(json_column)
|
52
52
|
# j.typeof # json_typeof(json_column)
|
53
|
+
# j.strip_nulls # json_strip_nulls(json_column)
|
53
54
|
#
|
54
55
|
# j.populate(:a) # json_populate_record(:a, json_column)
|
55
56
|
# j.populate_set(:a) # json_populate_recordset(:a, json_column)
|
@@ -58,11 +59,16 @@
|
|
58
59
|
#
|
59
60
|
# There are additional methods are are only supported on JSONBOp instances:
|
60
61
|
#
|
61
|
-
# j
|
62
|
-
# j.
|
63
|
-
# j.
|
64
|
-
# j.
|
65
|
-
# j.
|
62
|
+
# j - 1 # (jsonb_column - 1)
|
63
|
+
# j.concat(:h) # (jsonb_column || h)
|
64
|
+
# j.contain_all(:a) # (jsonb_column ?& a)
|
65
|
+
# j.contain_any(:a) # (jsonb_column ?| a)
|
66
|
+
# j.contains(:h) # (jsonb_column @> h)
|
67
|
+
# j.contained_by(:h) # (jsonb_column <@ h)
|
68
|
+
# j.delete_path(%w'0 a') # (jsonb_column #- ARRAY['0','a'])
|
69
|
+
# j.has_key?('a') # (jsonb_column ? 'a')
|
70
|
+
# j.pretty # jsonb_pretty(jsonb_column)
|
71
|
+
# j.set(%w'0 a', :h) # jsonb_set(jsonb_column, ARRAY['0','a'], h, true)
|
66
72
|
#
|
67
73
|
# If you are also using the pg_json extension, you should load it before
|
68
74
|
# loading this extension. Doing so will allow you to use the #op method on
|
@@ -192,6 +198,13 @@ module Sequel
|
|
192
198
|
SQL::Function.new(function_name(:populate_recordset), arg, self)
|
193
199
|
end
|
194
200
|
|
201
|
+
# Returns a json value stripped of all internal null values.
|
202
|
+
#
|
203
|
+
# json_op.strip_nulls # json_strip_nulls(json)
|
204
|
+
def strip_nulls
|
205
|
+
self.class.new(function(:strip_nulls))
|
206
|
+
end
|
207
|
+
|
195
208
|
# Builds arbitrary record from json object. You need to define the
|
196
209
|
# structure of the record using #as on the resulting object:
|
197
210
|
#
|
@@ -266,12 +279,30 @@ module Sequel
|
|
266
279
|
#
|
267
280
|
# jsonb_op = Sequel.pg_jsonb(:jsonb)
|
268
281
|
class JSONBOp < JSONBaseOp
|
282
|
+
CONCAT = ["(".freeze, " || ".freeze, ")".freeze].freeze
|
269
283
|
CONTAIN_ALL = ["(".freeze, " ?& ".freeze, ")".freeze].freeze
|
270
284
|
CONTAIN_ANY = ["(".freeze, " ?| ".freeze, ")".freeze].freeze
|
271
285
|
CONTAINS = ["(".freeze, " @> ".freeze, ")".freeze].freeze
|
272
286
|
CONTAINED_BY = ["(".freeze, " <@ ".freeze, ")".freeze].freeze
|
287
|
+
DELETE_PATH = ["(".freeze, " #- ".freeze, ")".freeze].freeze
|
273
288
|
HAS_KEY = ["(".freeze, " ? ".freeze, ")".freeze].freeze
|
274
289
|
|
290
|
+
# jsonb expression for deletion of the given argument from the
|
291
|
+
# current jsonb.
|
292
|
+
#
|
293
|
+
# jsonb_op - "a" # (jsonb - 'a')
|
294
|
+
def -(other)
|
295
|
+
self.class.new(super)
|
296
|
+
end
|
297
|
+
|
298
|
+
# jsonb expression for concatenation of the given jsonb into
|
299
|
+
# the current jsonb.
|
300
|
+
#
|
301
|
+
# jsonb_op.concat(:h) # (jsonb || h)
|
302
|
+
def concat(other)
|
303
|
+
json_op(CONCAT, wrap_input_jsonb(other))
|
304
|
+
end
|
305
|
+
|
275
306
|
# Check if the receiver contains all of the keys in the given array:
|
276
307
|
#
|
277
308
|
# jsonb_op.contain_all(:a) # (jsonb ?& a)
|
@@ -300,6 +331,13 @@ module Sequel
|
|
300
331
|
bool_op(CONTAINED_BY, wrap_input_jsonb(other))
|
301
332
|
end
|
302
333
|
|
334
|
+
# Check if the other jsonb contains all entries in the receiver:
|
335
|
+
#
|
336
|
+
# jsonb_op.delete_path(:h) # (jsonb #- h)
|
337
|
+
def delete_path(other)
|
338
|
+
json_op(DELETE_PATH, wrap_input_array(other))
|
339
|
+
end
|
340
|
+
|
303
341
|
# Check if the receiver contains the given key:
|
304
342
|
#
|
305
343
|
# jsonb_op.has_key?('a') # (jsonb ? 'a')
|
@@ -313,6 +351,21 @@ module Sequel
|
|
313
351
|
self
|
314
352
|
end
|
315
353
|
|
354
|
+
# Returns a json value for the object at the given path.
|
355
|
+
#
|
356
|
+
# jsonb_op.pretty # jsonb_pretty(jsonb)
|
357
|
+
def pretty
|
358
|
+
Sequel::SQL::StringExpression.new(:NOOP, function(:pretty))
|
359
|
+
end
|
360
|
+
|
361
|
+
# Returns a json value for the object at the given path.
|
362
|
+
#
|
363
|
+
# jsonb_op.set(['a', 'b'], h) # jsonb_set(jsonb, ARRAY['a', 'b'], h, true)
|
364
|
+
# jsonb_op.set(['a', 'b'], h, false) # jsonb_set(jsonb, ARRAY['a', 'b'], h, false)
|
365
|
+
def set(path, other, create_missing=true)
|
366
|
+
self.class.new(function(:set, wrap_input_array(path), wrap_input_jsonb(other), create_missing))
|
367
|
+
end
|
368
|
+
|
316
369
|
private
|
317
370
|
|
318
371
|
# Return a placeholder literal with the given str and args, wrapped
|