sequel 4.24.0 → 4.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +40 -0
- data/doc/association_basics.rdoc +2 -5
- data/doc/dataset_basics.rdoc +1 -1
- data/doc/postgresql.rdoc +47 -0
- data/doc/querying.rdoc +5 -0
- data/doc/release_notes/4.25.0.txt +181 -0
- data/lib/sequel/adapters/ibmdb.rb +0 -28
- data/lib/sequel/adapters/shared/db2.rb +31 -2
- data/lib/sequel/adapters/shared/mssql.rb +12 -12
- data/lib/sequel/adapters/shared/postgres.rb +102 -3
- data/lib/sequel/adapters/shared/sqlite.rb +1 -0
- data/lib/sequel/adapters/swift/sqlite.rb +12 -0
- data/lib/sequel/database/schema_generator.rb +4 -0
- data/lib/sequel/database/schema_methods.rb +3 -1
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +15 -7
- data/lib/sequel/dataset/query.rb +16 -2
- data/lib/sequel/dataset/sql.rb +19 -16
- data/lib/sequel/extensions/empty_array_consider_nulls.rb +35 -0
- data/lib/sequel/extensions/empty_array_ignore_nulls.rb +3 -34
- data/lib/sequel/extensions/pg_json_ops.rb +9 -1
- data/lib/sequel/extensions/query_literals.rb +1 -1
- data/lib/sequel/model/base.rb +7 -11
- data/lib/sequel/model/dataset_module.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +6 -0
- data/lib/sequel/plugins/dirty.rb +6 -1
- data/lib/sequel/plugins/inverted_subsets.rb +48 -0
- data/lib/sequel/plugins/serialization.rb +2 -0
- data/lib/sequel/plugins/singular_table_names.rb +31 -0
- data/lib/sequel/plugins/static_cache.rb +17 -0
- data/lib/sequel/sql.rb +1 -0
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +12 -0
- data/spec/adapters/mysql_spec.rb +1 -0
- data/spec/adapters/postgres_spec.rb +41 -1
- data/spec/core/database_spec.rb +1 -0
- data/spec/core/dataset_spec.rb +55 -7
- data/spec/core/expression_filters_spec.rb +18 -0
- data/spec/core/schema_spec.rb +10 -2
- data/spec/extensions/association_pks_spec.rb +12 -0
- data/spec/extensions/{empty_array_ignore_nulls_spec.rb → empty_array_consider_nulls_spec.rb} +7 -7
- data/spec/extensions/inverted_subsets_spec.rb +33 -0
- data/spec/extensions/query_literals_spec.rb +16 -0
- data/spec/extensions/serialization_spec.rb +21 -0
- data/spec/extensions/singular_table_names_spec.rb +22 -0
- data/spec/integration/dataset_test.rb +2 -1
- data/spec/integration/prepared_statement_test.rb +35 -1
- data/spec/model/associations_spec.rb +2 -2
- data/spec/model/base_spec.rb +13 -8
- data/spec/model/class_dataset_methods_spec.rb +1 -0
- metadata +10 -5
- data/lib/sequel/adapters/firebird.rb +0 -105
- data/lib/sequel/adapters/informix.rb +0 -68
@@ -625,6 +625,15 @@ module Sequel
|
|
625
625
|
end
|
626
626
|
end
|
627
627
|
|
628
|
+
# Literalize non-String collate options. This is because unquoted collatations
|
629
|
+
# are folded to lowercase, and PostgreSQL used mixed case or capitalized collations.
|
630
|
+
def column_definition_collate_sql(sql, column)
|
631
|
+
if collate = column[:collate]
|
632
|
+
collate = literal(collate) unless collate.is_a?(String)
|
633
|
+
sql << " COLLATE #{collate}"
|
634
|
+
end
|
635
|
+
end
|
636
|
+
|
628
637
|
# Handle PostgreSQL specific default format.
|
629
638
|
def column_schema_normalize_default(default, type)
|
630
639
|
if m = POSTGRES_DEFAULT_RE.match(default)
|
@@ -1200,7 +1209,7 @@ module Sequel
|
|
1200
1209
|
LOCK_MODES = ['ACCESS SHARE', 'ROW SHARE', 'ROW EXCLUSIVE', 'SHARE UPDATE EXCLUSIVE', 'SHARE', 'SHARE ROW EXCLUSIVE', 'EXCLUSIVE', 'ACCESS EXCLUSIVE'].each(&:freeze)
|
1201
1210
|
|
1202
1211
|
Dataset.def_sql_method(self, :delete, [['if server_version >= 90100', %w'with delete from using where returning'], ['else', %w'delete from using where returning']])
|
1203
|
-
Dataset.def_sql_method(self, :insert, [['if server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
1212
|
+
Dataset.def_sql_method(self, :insert, [['if server_version >= 90500', %w'with insert into columns values conflict returning'], ['elsif server_version >= 90100', %w'with insert into columns values returning'], ['else', %w'insert into columns values returning']])
|
1204
1213
|
Dataset.def_sql_method(self, :select, [['if opts[:values]', %w'values order limit'], ['elsif server_version >= 80400', %w'with select distinct columns from join where group having window compounds order limit lock'], ['else', %w'select distinct columns from join where group having compounds order limit lock']])
|
1205
1214
|
Dataset.def_sql_method(self, :update, [['if server_version >= 90100', %w'with update table set from where returning'], ['else', %w'update table set from where returning']])
|
1206
1215
|
|
@@ -1208,7 +1217,7 @@ module Sequel
|
|
1208
1217
|
module PreparedStatementMethods
|
1209
1218
|
# Override insert action to use RETURNING if the server supports it.
|
1210
1219
|
def run
|
1211
|
-
if @prepared_type == :insert
|
1220
|
+
if @prepared_type == :insert && (opts[:returning_pk] || !opts[:returning])
|
1212
1221
|
fetch_rows(prepared_sql){|r| return r.values.first}
|
1213
1222
|
else
|
1214
1223
|
super
|
@@ -1217,7 +1226,10 @@ module Sequel
|
|
1217
1226
|
|
1218
1227
|
def prepared_sql
|
1219
1228
|
return @prepared_sql if @prepared_sql
|
1220
|
-
|
1229
|
+
if @prepared_type == :insert && !opts[:returning]
|
1230
|
+
@opts[:returning] = insert_pk
|
1231
|
+
@opts[:returning_pk] = true
|
1232
|
+
end
|
1221
1233
|
super
|
1222
1234
|
@prepared_sql
|
1223
1235
|
end
|
@@ -1342,6 +1354,50 @@ module Sequel
|
|
1342
1354
|
end
|
1343
1355
|
end
|
1344
1356
|
|
1357
|
+
# Handle uniqueness violations when inserting, by updating the conflicting row, using
|
1358
|
+
# ON CONFLICT. With no options, uses ON CONFLICT DO NOTHING. Options:
|
1359
|
+
# :constraint :: An explicit constraint name, has precendence over :target.
|
1360
|
+
# :target :: The column name or expression to handle uniqueness violations on.
|
1361
|
+
# :update :: A hash of columns and values to set. Uses ON CONFLICT DO UPDATE.
|
1362
|
+
# :update_where :: A WHERE condition to use for the update.
|
1363
|
+
#
|
1364
|
+
# Examples:
|
1365
|
+
#
|
1366
|
+
# DB[:table].insert_conflict.insert(:a=>1, :b=>2)
|
1367
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1368
|
+
# # ON CONFLICT DO NOTHING
|
1369
|
+
#
|
1370
|
+
# DB[:table].insert_conflict(:constraint=>:table_a_uidx).insert(:a=>1, :b=>2)
|
1371
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1372
|
+
# # ON CONFLICT ON CONSTRAINT table_a_uidx DO NOTHING
|
1373
|
+
#
|
1374
|
+
# DB[:table].insert_conflict(:target=>:a).insert(:a=>1, :b=>2)
|
1375
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1376
|
+
# # ON CONFLICT (a) DO NOTHING
|
1377
|
+
#
|
1378
|
+
# DB[:table].insert_conflict(:target=>:a, :update=>{:b=>:excluded__b}).insert(:a=>1, :b=>2)
|
1379
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1380
|
+
# # ON CONFLICT (a) DO UPDATE SET b = excluded.b
|
1381
|
+
#
|
1382
|
+
# DB[:table].insert_conflict(:constraint=>:table_a_uidx,
|
1383
|
+
# :update=>{:b=>:excluded__b}, :update_where=>{:table__status_id=>1}).insert(:a=>1, :b=>2)
|
1384
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1385
|
+
# # ON CONFLICT ON CONSTRAINT table_a_uidx
|
1386
|
+
# # DO UPDATE SET b = excluded.b WHERE (table.status_id = 1)
|
1387
|
+
def insert_conflict(opts=OPTS)
|
1388
|
+
clone(:insert_conflict => opts)
|
1389
|
+
end
|
1390
|
+
|
1391
|
+
# Ignore uniqueness/exclusion violations when inserting, using ON CONFLICT DO NOTHING.
|
1392
|
+
# Exists mostly for compatibility to MySQL's insert_ignore. Example:
|
1393
|
+
#
|
1394
|
+
# DB[:table].insert_ignore.insert(:a=>1, :b=>2)
|
1395
|
+
# # INSERT INTO TABLE (a, b) VALUES (1, 2)
|
1396
|
+
# # ON CONFLICT DO NOTHING
|
1397
|
+
def insert_ignore
|
1398
|
+
insert_conflict
|
1399
|
+
end
|
1400
|
+
|
1345
1401
|
# Insert a record returning the record inserted. Always returns nil without
|
1346
1402
|
# inserting a query if disable_insert_returning is used.
|
1347
1403
|
def insert_select(*values)
|
@@ -1396,11 +1452,26 @@ module Sequel
|
|
1396
1452
|
true
|
1397
1453
|
end
|
1398
1454
|
|
1455
|
+
# PostgreSQL 9.5+ supports GROUP CUBE
|
1456
|
+
def supports_group_cube?
|
1457
|
+
server_version >= 90500
|
1458
|
+
end
|
1459
|
+
|
1460
|
+
# PostgreSQL 9.5+ supports GROUP ROLLUP
|
1461
|
+
def supports_group_rollup?
|
1462
|
+
server_version >= 90500
|
1463
|
+
end
|
1464
|
+
|
1399
1465
|
# True unless insert returning has been disabled for this dataset.
|
1400
1466
|
def supports_insert_select?
|
1401
1467
|
!@opts[:disable_insert_returning]
|
1402
1468
|
end
|
1403
1469
|
|
1470
|
+
# PostgreSQL 9.5+ supports the ON CONFLICT clause to INSERT.
|
1471
|
+
def supports_insert_conflict?
|
1472
|
+
server_version >= 90500
|
1473
|
+
end
|
1474
|
+
|
1404
1475
|
# PostgreSQL 9.3rc1+ supports lateral subqueries
|
1405
1476
|
def supports_lateral_subqueries?
|
1406
1477
|
server_version >= 90300
|
@@ -1503,6 +1574,34 @@ module Sequel
|
|
1503
1574
|
join_from_sql(:USING, sql)
|
1504
1575
|
end
|
1505
1576
|
|
1577
|
+
# Add ON CONFLICT clause if it should be used
|
1578
|
+
def insert_conflict_sql(sql)
|
1579
|
+
if opts = @opts[:insert_conflict]
|
1580
|
+
sql << " ON CONFLICT"
|
1581
|
+
|
1582
|
+
if target = opts[:constraint]
|
1583
|
+
sql << " ON CONSTRAINT "
|
1584
|
+
identifier_append(sql, target)
|
1585
|
+
elsif target = opts[:target]
|
1586
|
+
sql << ' ('
|
1587
|
+
identifier_append(sql, target)
|
1588
|
+
sql << ')'
|
1589
|
+
end
|
1590
|
+
|
1591
|
+
if values = opts[:update]
|
1592
|
+
sql << " DO UPDATE SET "
|
1593
|
+
update_sql_values_hash(sql, values)
|
1594
|
+
if where = opts[:update_where]
|
1595
|
+
sql << " WHERE "
|
1596
|
+
literal_append(sql, where)
|
1597
|
+
end
|
1598
|
+
else
|
1599
|
+
sql << " DO NOTHING"
|
1600
|
+
end
|
1601
|
+
|
1602
|
+
end
|
1603
|
+
end
|
1604
|
+
|
1506
1605
|
# Return the primary key to use for RETURNING in an INSERT statement
|
1507
1606
|
def insert_pk
|
1508
1607
|
if (f = opts[:from]) && !f.empty?
|
@@ -335,6 +335,7 @@ module Sequel
|
|
335
335
|
/\ACHECK constraint failed/ => CheckConstraintViolation,
|
336
336
|
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
337
337
|
/may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
|
338
|
+
/\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
|
338
339
|
}.freeze
|
339
340
|
def database_error_regexps
|
340
341
|
DATABASE_ERROR_REGEXPS
|
@@ -11,6 +11,18 @@ module Sequel
|
|
11
11
|
extend Sequel::Database::ResetIdentifierMangling
|
12
12
|
include Sequel::SQLite::DatabaseMethods
|
13
13
|
|
14
|
+
DATABASE_ERROR_REGEXPS = {
|
15
|
+
/\AUNIQUE constraint failed: / => UniqueConstraintViolation,
|
16
|
+
/\AFOREIGN KEY constraint failed/ => ForeignKeyConstraintViolation,
|
17
|
+
/\ACHECK constraint failed/ => CheckConstraintViolation,
|
18
|
+
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed/ => ConstraintViolation,
|
19
|
+
/may not be NULL\z|NOT NULL constraint failed: .+/ => NotNullConstraintViolation,
|
20
|
+
/\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
|
21
|
+
}.freeze
|
22
|
+
def database_error_regexps
|
23
|
+
DATABASE_ERROR_REGEXPS
|
24
|
+
end
|
25
|
+
|
14
26
|
# Set the correct pragmas on the connection.
|
15
27
|
def connect(opts)
|
16
28
|
c = super
|
@@ -81,6 +81,10 @@ module Sequel
|
|
81
81
|
#
|
82
82
|
# The following options are supported:
|
83
83
|
#
|
84
|
+
# :collate :: The collation to use for the column. For backwards compatibility,
|
85
|
+
# only symbols and string values are supported, and they are used verbatim.
|
86
|
+
# However, on PostgreSQL, symbols are literalized as regular identifiers,
|
87
|
+
# since unquoted collations are unlikely to be valid.
|
84
88
|
# :default :: The default value for the column.
|
85
89
|
# :deferrable :: For foreign key columns, this ensures referential integrity will work even if
|
86
90
|
# referencing table uses a foreign key value that does not
|
@@ -524,7 +524,9 @@ module Sequel
|
|
524
524
|
|
525
525
|
# Add collate SQL fragment to column creation SQL.
|
526
526
|
def column_definition_collate_sql(sql, column)
|
527
|
-
|
527
|
+
if collate = column[:collate]
|
528
|
+
sql << " COLLATE #{collate}"
|
529
|
+
end
|
528
530
|
end
|
529
531
|
|
530
532
|
# Add default SQL fragment to column creation SQL.
|
@@ -90,13 +90,19 @@ module Sequel
|
|
90
90
|
# The argument to supply to insert and update, which may use
|
91
91
|
# placeholders specified by prepared_args
|
92
92
|
attr_accessor :prepared_modify_values
|
93
|
-
|
93
|
+
|
94
94
|
# Sets the prepared_args to the given hash and runs the
|
95
95
|
# prepared statement.
|
96
96
|
def call(bind_vars={}, &block)
|
97
97
|
bind(bind_vars).run(&block)
|
98
98
|
end
|
99
99
|
|
100
|
+
# Raise an error if attempting to call prepare on an already
|
101
|
+
# prepared statement.
|
102
|
+
def prepare(*)
|
103
|
+
raise Error, "cannot prepare an already prepared statement"
|
104
|
+
end
|
105
|
+
|
100
106
|
# Send the columns to the original dataset, as calling it
|
101
107
|
# on the prepared statement can cause problems.
|
102
108
|
def columns
|
@@ -164,12 +170,14 @@ module Sequel
|
|
164
170
|
with_sql(prepared_sql).first
|
165
171
|
when :first
|
166
172
|
first
|
167
|
-
when :insert
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
+
when :insert, :update, :delete
|
174
|
+
if opts[:returning] && supports_returning?(@prepared_type)
|
175
|
+
returning_fetch_rows(prepared_sql)
|
176
|
+
elsif @prepared_type == :delete
|
177
|
+
delete
|
178
|
+
else
|
179
|
+
send(@prepared_type, *@prepared_modify_values)
|
180
|
+
end
|
173
181
|
when Array
|
174
182
|
case @prepared_type.at(0)
|
175
183
|
when :map, :to_hash, :to_hash_groups
|
data/lib/sequel/dataset/query.rb
CHANGED
@@ -34,7 +34,7 @@ module Sequel
|
|
34
34
|
# Methods that return modified datasets
|
35
35
|
QUERY_METHODS = (<<-METHS).split.map(&:to_sym) + JOIN_METHODS
|
36
36
|
add_graph_aliases and distinct except exclude exclude_having exclude_where
|
37
|
-
filter for_update from from_self graph grep group group_and_count group_by having intersect invert
|
37
|
+
filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
|
38
38
|
limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
|
39
39
|
reverse reverse_order select select_all select_append select_group select_more server
|
40
40
|
set_graph_aliases unfiltered ungraphed ungrouped union
|
@@ -314,6 +314,17 @@ module Sequel
|
|
314
314
|
select_group(*columns, &block).select_more(COUNT_OF_ALL_AS_COUNT)
|
315
315
|
end
|
316
316
|
|
317
|
+
# Returns a copy of the dataset with the given columns added to the list of
|
318
|
+
# existing columns to group on. If no existing columns are present this
|
319
|
+
# method simply sets the columns as the initial ones to group on.
|
320
|
+
#
|
321
|
+
# DB[:items].group_append(:b) # SELECT * FROM items GROUP BY b
|
322
|
+
# DB[:items].group(:a).group_append(:b) # SELECT * FROM items GROUP BY a, b
|
323
|
+
def group_append(*columns, &block)
|
324
|
+
columns = @opts[:group] + columns if @opts[:group]
|
325
|
+
group(*columns, &block)
|
326
|
+
end
|
327
|
+
|
317
328
|
# Adds the appropriate CUBE syntax to GROUP BY.
|
318
329
|
def group_cube
|
319
330
|
raise Error, "GROUP BY CUBE not supported on #{db.database_type}" unless supports_group_cube?
|
@@ -576,7 +587,10 @@ module Sequel
|
|
576
587
|
# A symbol may be used for database independent locking behavior, but
|
577
588
|
# all supported symbols have separate methods (e.g. for_update).
|
578
589
|
#
|
579
|
-
# DB[:items].lock_style('FOR SHARE NOWAIT')
|
590
|
+
# DB[:items].lock_style('FOR SHARE NOWAIT')
|
591
|
+
# # SELECT * FROM items FOR SHARE NOWAIT
|
592
|
+
# DB[:items].lock_style('FOR UPDATE OF table1 SKIP LOCKED')
|
593
|
+
# # SELECT * FROM items FOR UPDATE OF table1 SKIP LOCKED
|
580
594
|
def lock_style(style)
|
581
595
|
clone(:lock => style)
|
582
596
|
end
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -1010,8 +1010,7 @@ module Sequel
|
|
1010
1010
|
|
1011
1011
|
# An expression for how to handle an empty array lookup.
|
1012
1012
|
def empty_array_value(op, cols)
|
1013
|
-
|
1014
|
-
SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
|
1013
|
+
{1 => ((op == :IN) ? 0 : 1)}
|
1015
1014
|
end
|
1016
1015
|
|
1017
1016
|
# Format the timestamp based on the default_timestamp_format, with a couple
|
@@ -1527,27 +1526,31 @@ module Sequel
|
|
1527
1526
|
end
|
1528
1527
|
|
1529
1528
|
def update_set_sql(sql)
|
1530
|
-
values = opts[:values]
|
1531
1529
|
sql << SET
|
1530
|
+
values = @opts[:values]
|
1532
1531
|
if values.is_a?(Hash)
|
1533
|
-
|
1534
|
-
eq = EQUAL
|
1535
|
-
values.each do |k, v|
|
1536
|
-
sql << COMMA if c
|
1537
|
-
if k.is_a?(String) && !k.is_a?(LiteralString)
|
1538
|
-
quote_identifier_append(sql, k)
|
1539
|
-
else
|
1540
|
-
literal_append(sql, k)
|
1541
|
-
end
|
1542
|
-
sql << eq
|
1543
|
-
literal_append(sql, v)
|
1544
|
-
c ||= true
|
1545
|
-
end
|
1532
|
+
update_sql_values_hash(sql, values)
|
1546
1533
|
else
|
1547
1534
|
sql << values
|
1548
1535
|
end
|
1549
1536
|
end
|
1550
1537
|
|
1538
|
+
def update_sql_values_hash(sql, values)
|
1539
|
+
c = false
|
1540
|
+
eq = EQUAL
|
1541
|
+
values.each do |k, v|
|
1542
|
+
sql << COMMA if c
|
1543
|
+
if k.is_a?(String) && !k.is_a?(LiteralString)
|
1544
|
+
quote_identifier_append(sql, k)
|
1545
|
+
else
|
1546
|
+
literal_append(sql, k)
|
1547
|
+
end
|
1548
|
+
sql << eq
|
1549
|
+
literal_append(sql, v)
|
1550
|
+
c ||= true
|
1551
|
+
end
|
1552
|
+
end
|
1553
|
+
|
1551
1554
|
def update_update_sql(sql)
|
1552
1555
|
sql << UPDATE
|
1553
1556
|
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
# This changes Sequel's literalization of IN/NOT IN with an empty
|
2
|
+
# array value to consider NULL values if one of the referenced
|
3
|
+
# columns is NULL:
|
4
|
+
#
|
5
|
+
# DB[:test].where(:name=>[])
|
6
|
+
# # SELECT * FROM test WHERE (name != name)
|
7
|
+
# DB[:test].exclude(:name=>[])
|
8
|
+
# # SELECT * FROM test WHERE (name = name)
|
9
|
+
#
|
10
|
+
# The default Sequel behavior is to ignore NULLs, as the above
|
11
|
+
# query is not generally optimized well by databases.
|
12
|
+
#
|
13
|
+
# You can load this extension into specific datasets:
|
14
|
+
#
|
15
|
+
# ds = DB[:table]
|
16
|
+
# ds = ds.extension(:empty_array_consider_nulls)
|
17
|
+
#
|
18
|
+
# Or you can load it into all of a database's datasets, which
|
19
|
+
# is probably the desired behavior if you are using this extension:
|
20
|
+
#
|
21
|
+
# DB.extension(:empty_array_consider_nulls)
|
22
|
+
|
23
|
+
#
|
24
|
+
module Sequel
|
25
|
+
module EmptyArrayConsiderNulls
|
26
|
+
# Use a simple expression that is always true or false, never NULL.
|
27
|
+
def empty_array_value(op, cols)
|
28
|
+
c = Array(cols)
|
29
|
+
SQL::BooleanExpression.from_value_pairs(c.zip(c), :AND, op == :IN)
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
Dataset.register_extension(:empty_array_consider_nulls, EmptyArrayConsiderNulls)
|
35
|
+
end
|
@@ -1,34 +1,3 @@
|
|
1
|
-
# This
|
2
|
-
#
|
3
|
-
|
4
|
-
#
|
5
|
-
# DB[:test].where(:name=>[])
|
6
|
-
# # SELECT * FROM test WHERE (1 = 0)
|
7
|
-
# DB[:test].exclude(:name=>[])
|
8
|
-
# # SELECT * FROM test WHERE (1 = 1)
|
9
|
-
#
|
10
|
-
# The default Sequel behavior is to respect NULLs, so that when
|
11
|
-
# name is NULL, the expression returns NULL.
|
12
|
-
#
|
13
|
-
# You can load this extension into specific datasets:
|
14
|
-
#
|
15
|
-
# ds = DB[:table]
|
16
|
-
# ds = ds.extension(:empty_array_ignore_nulls)
|
17
|
-
#
|
18
|
-
# Or you can load it into all of a database's datasets, which
|
19
|
-
# is probably the desired behavior if you are using this extension:
|
20
|
-
#
|
21
|
-
# DB.extension(:empty_array_ignore_nulls)
|
22
|
-
|
23
|
-
#
|
24
|
-
module Sequel
|
25
|
-
module EmptyArrayIgnoreNulls
|
26
|
-
# Use a simple expression that is always true or false, never NULL.
|
27
|
-
def empty_array_value(op, cols)
|
28
|
-
{1 => ((op == :IN) ? 0 : 1)}
|
29
|
-
end
|
30
|
-
|
31
|
-
end
|
32
|
-
|
33
|
-
Dataset.register_extension(:empty_array_ignore_nulls, EmptyArrayIgnoreNulls)
|
34
|
-
end
|
1
|
+
# This only exists for backwards compatibility, as the behavior
|
2
|
+
# added by this extension is now the default Sequel behavior.
|
3
|
+
Sequel::Dataset.register_extension(:empty_array_ignore_nulls){}
|
@@ -34,7 +34,7 @@
|
|
34
34
|
# jb = :jsonb_column.pg_jsonb
|
35
35
|
#
|
36
36
|
# This creates a Sequel::Postgres::JSONOp or Sequel::Postgres::JSONBOp object that can be used
|
37
|
-
# for easier querying:
|
37
|
+
# for easier querying. The following methods are available for both JSONOp and JSONBOp instances:
|
38
38
|
#
|
39
39
|
# j[1] # (json_column -> 1)
|
40
40
|
# j[%w'a b'] # (json_column #> ARRAY['a','b'])
|
@@ -56,6 +56,14 @@
|
|
56
56
|
# j.to_record # json_to_record(json_column)
|
57
57
|
# j.to_recordset # json_to_recordset(json_column)
|
58
58
|
#
|
59
|
+
# There are additional methods are are only supported on JSONBOp instances:
|
60
|
+
#
|
61
|
+
# j.contain_all(:a) # (jsonb_column ?& a)
|
62
|
+
# j.contain_any(:a) # (jsonb_column ?| a)
|
63
|
+
# j.contains(:h) # (jsonb_column @> h)
|
64
|
+
# j.contained_by(:h) # (jsonb_column <@ h)
|
65
|
+
# j.has_key?('a') # (jsonb_column ? 'a')
|
66
|
+
#
|
59
67
|
# If you are also using the pg_json extension, you should load it before
|
60
68
|
# loading this extension. Doing so will allow you to use the #op method on
|
61
69
|
# JSONHash, JSONHarray, JSONBHash, and JSONBArray, allowing you to perform json/jsonb operations
|