sequel 5.65.0 → 5.67.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/CHANGELOG +24 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/lib/sequel/adapters/shared/mssql.rb +32 -2
- data/lib/sequel/adapters/shared/oracle.rb +6 -1
- data/lib/sequel/adapters/shared/postgres.rb +12 -0
- data/lib/sequel/adapters/shared/sqlite.rb +13 -0
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/schema_generator.rb +1 -1
- data/lib/sequel/dataset/actions.rb +1 -1
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/query.rb +53 -28
- data/lib/sequel/dataset/sql.rb +6 -1
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/connection_validator.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +32 -9
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- data/lib/sequel/plugins/prepared_statements.rb +2 -1
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/version.rb +1 -1
- metadata +9 -3
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: ff5af191af88ca381359503cfd9601ec8f18a9d782b68a3de4a8c9dac080eddc
|
|
4
|
+
data.tar.gz: 786d20f7eac9325748c259130951643856e34051e64b02aa17493f6d0283eb52
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: db251488ea0739af5ecf6db332a1cff729cffbf31dfabfc7235f3c27f807366bc9feddf147797e4dd5980348fda6a640cc5ceef7e6255cddcd317acb7172be90
|
|
7
|
+
data.tar.gz: 45b0e24847ccb365f1d7d06d35c85f05718802375877ab3d0315deb952f72171e26b5676861c25cf1e840f831d216f99eaddcbb717fc752d75077f19ae13980c
|
data/CHANGELOG
CHANGED
|
@@ -1,3 +1,27 @@
|
|
|
1
|
+
=== 5.67.0 (2023-04-01)
|
|
2
|
+
|
|
3
|
+
* Fix dumping of string column sizes in the schema dumper on MSSQL (jeremyevans) (#2013)
|
|
4
|
+
|
|
5
|
+
* Improve dumping of tables in non-default schemas in the schema_dumper extension (jeremyevans) (#2006)
|
|
6
|
+
|
|
7
|
+
* Make Database#{tables,views} support :qualify option on Microsoft SQL Server (jeremyevans)
|
|
8
|
+
|
|
9
|
+
* Avoid use of singleton classes for datasets instances on Ruby 2.4+ (jeremyevans) (#2007)
|
|
10
|
+
|
|
11
|
+
* Deprecate registering datasets extensions using an object other than a module (jeremyevans)
|
|
12
|
+
|
|
13
|
+
* Add set_literalizer extension, for treating set usage in datasets similar to array usage (jeremyevans) (#1997)
|
|
14
|
+
|
|
15
|
+
=== 5.66.0 (2023-03-01)
|
|
16
|
+
|
|
17
|
+
* Recognize SQLite error related to strict tables as a constraint violation when using the amalgalite adapter (jeremyevans)
|
|
18
|
+
|
|
19
|
+
* Make Dataset#count work correctly for datasets using Dataset#values (jeremyevans) (#1992)
|
|
20
|
+
|
|
21
|
+
* Make Dataset#count with no argument/block handle dataset with custom SQL using ORDER BY on MSSQL (jeremyevans)
|
|
22
|
+
|
|
23
|
+
* Make Dataset#empty? correctly handle datasets with custom SQL or using Dataset#values where the first value is NULL (andy-k, jeremyevans) (#1990)
|
|
24
|
+
|
|
1
25
|
=== 5.65.0 (2023-02-01)
|
|
2
26
|
|
|
3
27
|
* Allow pg_auto_parameterize extension to use placeholder loaders (jeremyevans)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
= Improvements
|
|
2
|
+
|
|
3
|
+
* Dataset#empty? now correctly handles datasets using custom SQL or
|
|
4
|
+
Dataset#values where the first value in the first row is NULL.
|
|
5
|
+
|
|
6
|
+
* Dataset#count without an argument or block now works correctly on
|
|
7
|
+
Microsoft SQL Server when using custom SQL that uses ORDER BY.
|
|
8
|
+
|
|
9
|
+
* Dataset#count now works correctly for datasets using Dataset#values.
|
|
10
|
+
|
|
11
|
+
* Sequel now recognizes an additional SQLite constraint violation
|
|
12
|
+
error that occurs with recent versions of amalgalite.
|
|
13
|
+
|
|
14
|
+
* Dataset#values will now raise an exception when called with an empty
|
|
15
|
+
array. Previously, an exception would not be raised until the query
|
|
16
|
+
was sent to the database.
|
|
17
|
+
|
|
18
|
+
= Backwards Compatibility
|
|
19
|
+
|
|
20
|
+
* The changes to make Dataset#empty? and #count work with custom SQL
|
|
21
|
+
on Microsoft SQL Server now result in running the custom SQL, which
|
|
22
|
+
could result in worse performance than in previous versions. You can
|
|
23
|
+
wrap such datasets with Dataset#from_self manually to restore the
|
|
24
|
+
previous behavior.
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
= New Features
|
|
2
|
+
|
|
3
|
+
* A set_literalizer extension has been added, for treating Set
|
|
4
|
+
instances in datasets similar to Array instances:
|
|
5
|
+
|
|
6
|
+
DB.extension :set_literalizer
|
|
7
|
+
DB[:table].where(column: Set.new([1, 2, 3]))
|
|
8
|
+
# SELECT FROM table WHERE (column IN (1, 2, 3))
|
|
9
|
+
|
|
10
|
+
= Improvements
|
|
11
|
+
|
|
12
|
+
* Sequel now avoids the use of singleton classes for datasets on Ruby
|
|
13
|
+
2.4+, instead creating a regular subclass whenever a dataset would
|
|
14
|
+
be extended via #extension or #with_extend. This significantly
|
|
15
|
+
improves performance, up to 20-40% for common dataset usage,
|
|
16
|
+
because it avoids creating new singleton classes for every dataset
|
|
17
|
+
clone, and it allows for cached method lookup.
|
|
18
|
+
|
|
19
|
+
* Database#tables and #views now support a :qualify option on Microsoft
|
|
20
|
+
SQL Server to returned qualified identifiers.
|
|
21
|
+
|
|
22
|
+
* The schema_dumper extension can now dump tables in non-default schemas
|
|
23
|
+
when using Microsoft SQL Server.
|
|
24
|
+
|
|
25
|
+
* The schema_dumper extension now correctly dumps string column sizes
|
|
26
|
+
when using Microsoft SQL Server.
|
|
27
|
+
|
|
28
|
+
= Backwards Compatibility
|
|
29
|
+
|
|
30
|
+
* Calling Sequel::Dataset.register_extension where the second argument
|
|
31
|
+
is not a module now issues a deprecation warning. Support for this
|
|
32
|
+
will be removed in Sequel 6.
|
|
@@ -404,10 +404,15 @@ module Sequel
|
|
|
404
404
|
# Backbone of the tables and views support.
|
|
405
405
|
def information_schema_tables(type, opts)
|
|
406
406
|
m = output_identifier_meth
|
|
407
|
-
|
|
407
|
+
schema = opts[:schema]||'dbo'
|
|
408
|
+
tables = metadata_dataset.from(Sequel[:information_schema][:tables].as(:t)).
|
|
408
409
|
select(:table_name).
|
|
409
|
-
where(:table_type=>type, :table_schema=>
|
|
410
|
+
where(:table_type=>type, :table_schema=>schema.to_s).
|
|
410
411
|
map{|x| m.call(x[:table_name])}
|
|
412
|
+
|
|
413
|
+
tables.map!{|t| Sequel.qualify(m.call(schema).to_s, m.call(t).to_s)} if opts[:qualify]
|
|
414
|
+
|
|
415
|
+
tables
|
|
411
416
|
end
|
|
412
417
|
|
|
413
418
|
# Always quote identifiers in the metadata_dataset, so schema parsing works.
|
|
@@ -591,6 +596,18 @@ module Sequel
|
|
|
591
596
|
end
|
|
592
597
|
end
|
|
593
598
|
|
|
599
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
|
600
|
+
# cannot wrap it in a subquery. Load entire query in this case to get
|
|
601
|
+
# the number of rows. In general, you should avoid calling this method
|
|
602
|
+
# on datasets with custom SQL.
|
|
603
|
+
def count(*a, &block)
|
|
604
|
+
if (@opts[:sql] && a.empty? && !block)
|
|
605
|
+
naked.to_a.length
|
|
606
|
+
else
|
|
607
|
+
super
|
|
608
|
+
end
|
|
609
|
+
end
|
|
610
|
+
|
|
594
611
|
# Uses CROSS APPLY to join the given table into the current dataset.
|
|
595
612
|
def cross_apply(table)
|
|
596
613
|
join_table(:cross_apply, table)
|
|
@@ -601,6 +618,19 @@ module Sequel
|
|
|
601
618
|
clone(:disable_insert_output=>true)
|
|
602
619
|
end
|
|
603
620
|
|
|
621
|
+
# For a dataset with custom SQL, since it may include ORDER BY, you
|
|
622
|
+
# cannot wrap it in a subquery. Run query, and if it returns any
|
|
623
|
+
# records, return true. In general, you should avoid calling this method
|
|
624
|
+
# on datasets with custom SQL.
|
|
625
|
+
def empty?
|
|
626
|
+
if @opts[:sql]
|
|
627
|
+
naked.each{return false}
|
|
628
|
+
true
|
|
629
|
+
else
|
|
630
|
+
super
|
|
631
|
+
end
|
|
632
|
+
end
|
|
633
|
+
|
|
604
634
|
# MSSQL treats [] as a metacharacter in LIKE expresions.
|
|
605
635
|
def escape_like(string)
|
|
606
636
|
string.gsub(/[\\%_\[\]]/){|m| "\\#{m}"}
|
|
@@ -385,7 +385,12 @@ module Sequel
|
|
|
385
385
|
# Use a custom expression with EXISTS to determine whether a dataset
|
|
386
386
|
# is empty.
|
|
387
387
|
def empty?
|
|
388
|
-
|
|
388
|
+
if @opts[:sql]
|
|
389
|
+
naked.each{return false}
|
|
390
|
+
true
|
|
391
|
+
else
|
|
392
|
+
db[:dual].where(@opts[:offset] ? exists : unordered.exists).get(1) == nil
|
|
393
|
+
end
|
|
389
394
|
end
|
|
390
395
|
|
|
391
396
|
# Oracle requires SQL standard datetimes
|
|
@@ -805,6 +805,7 @@ module Sequel
|
|
|
805
805
|
# DB.values([[1, 2], [3, 4]]).order(:column2).limit(1, 1)
|
|
806
806
|
# # VALUES ((1, 2), (3, 4)) ORDER BY column2 LIMIT 1 OFFSET 1
|
|
807
807
|
def values(v)
|
|
808
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
|
808
809
|
@default_dataset.clone(:values=>v)
|
|
809
810
|
end
|
|
810
811
|
|
|
@@ -1706,6 +1707,12 @@ module Sequel
|
|
|
1706
1707
|
clone(:disable_insert_returning=>true)
|
|
1707
1708
|
end
|
|
1708
1709
|
|
|
1710
|
+
# Always return false when using VALUES
|
|
1711
|
+
def empty?
|
|
1712
|
+
return false if @opts[:values]
|
|
1713
|
+
super
|
|
1714
|
+
end
|
|
1715
|
+
|
|
1709
1716
|
# Return the results of an EXPLAIN query as a string
|
|
1710
1717
|
def explain(opts=OPTS)
|
|
1711
1718
|
with_sql((opts[:analyze] ? 'EXPLAIN ANALYZE ' : 'EXPLAIN ') + select_sql).map(:'QUERY PLAN').join("\r\n")
|
|
@@ -2125,6 +2132,11 @@ module Sequel
|
|
|
2125
2132
|
"TRUNCATE TABLE#{' ONLY' if to[:only]} #{table}#{' RESTART IDENTITY' if to[:restart]}#{' CASCADE' if to[:cascade]}"
|
|
2126
2133
|
end
|
|
2127
2134
|
|
|
2135
|
+
# Use from_self for aggregate dataset using VALUES.
|
|
2136
|
+
def aggreate_dataset_use_from_self?
|
|
2137
|
+
super || @opts[:values]
|
|
2138
|
+
end
|
|
2139
|
+
|
|
2128
2140
|
# Allow truncation of multiple source tables.
|
|
2129
2141
|
def check_truncation_allowed!
|
|
2130
2142
|
raise(InvalidOperation, "Grouped datasets cannot be truncated") if opts[:group]
|
|
@@ -169,6 +169,7 @@ module Sequel
|
|
|
169
169
|
# DB.values([[1, 2], [3, 4]])
|
|
170
170
|
# # VALUES ((1, 2), (3, 4))
|
|
171
171
|
def values(v)
|
|
172
|
+
raise Error, "Cannot provide an empty array for values" if v.empty?
|
|
172
173
|
@default_dataset.clone(:values=>v)
|
|
173
174
|
end
|
|
174
175
|
|
|
@@ -356,6 +357,7 @@ module Sequel
|
|
|
356
357
|
DATABASE_ERROR_REGEXPS = {
|
|
357
358
|
/(is|are) not unique\z|PRIMARY KEY must be unique\z|UNIQUE constraint failed: .+\z/ => UniqueConstraintViolation,
|
|
358
359
|
/foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
|
|
360
|
+
/\ASQLITE ERROR 3091/ => CheckConstraintViolation,
|
|
359
361
|
/\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
|
|
360
362
|
/\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
|
|
361
363
|
/\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
|
|
@@ -655,6 +657,12 @@ module Sequel
|
|
|
655
657
|
@opts[:where] ? super : where(1=>1).delete(&block)
|
|
656
658
|
end
|
|
657
659
|
|
|
660
|
+
# Always return false when using VALUES
|
|
661
|
+
def empty?
|
|
662
|
+
return false if @opts[:values]
|
|
663
|
+
super
|
|
664
|
+
end
|
|
665
|
+
|
|
658
666
|
# Return an array of strings specifying a query explanation for a SELECT of the
|
|
659
667
|
# current dataset. Currently, the options are ignored, but it accepts options
|
|
660
668
|
# to be compatible with other adapters.
|
|
@@ -868,6 +876,11 @@ module Sequel
|
|
|
868
876
|
end
|
|
869
877
|
end
|
|
870
878
|
|
|
879
|
+
# Use from_self for aggregate dataset using VALUES.
|
|
880
|
+
def aggreate_dataset_use_from_self?
|
|
881
|
+
super || @opts[:values]
|
|
882
|
+
end
|
|
883
|
+
|
|
871
884
|
# SQLite uses string literals instead of identifiers in AS clauses.
|
|
872
885
|
def as_sql_append(sql, aliaz, column_aliases=nil)
|
|
873
886
|
raise Error, "sqlite does not support derived column lists" if column_aliases
|
|
@@ -30,19 +30,29 @@ module Sequel
|
|
|
30
30
|
@dataset_class.new(self)
|
|
31
31
|
end
|
|
32
32
|
|
|
33
|
-
#
|
|
34
|
-
# it is used to iterate over the records:
|
|
33
|
+
# Returns a dataset instance for the given SQL string:
|
|
35
34
|
#
|
|
36
|
-
# DB.fetch('SELECT * FROM items')
|
|
35
|
+
# ds = DB.fetch('SELECT * FROM items')
|
|
36
|
+
#
|
|
37
|
+
# You can then call methods on the dataset to retrieve results:
|
|
37
38
|
#
|
|
38
|
-
#
|
|
39
|
+
# ds.all
|
|
40
|
+
# # SELECT * FROM items
|
|
41
|
+
# # => [{:column=>value, ...}, ...]
|
|
39
42
|
#
|
|
40
|
-
#
|
|
43
|
+
# If a block is given, it is passed to #each on the resulting dataset to
|
|
44
|
+
# iterate over the records returned by the query:
|
|
45
|
+
#
|
|
46
|
+
# DB.fetch('SELECT * FROM items'){|r| p r}
|
|
47
|
+
# # {:column=>value, ...}
|
|
48
|
+
# # ...
|
|
41
49
|
#
|
|
42
50
|
# +fetch+ can also perform parameterized queries for protection against SQL
|
|
43
51
|
# injection:
|
|
44
52
|
#
|
|
45
|
-
# DB.fetch('SELECT * FROM items WHERE name = ?',
|
|
53
|
+
# ds = DB.fetch('SELECT * FROM items WHERE name = ?', "my name")
|
|
54
|
+
# ds.all
|
|
55
|
+
# # SELECT * FROM items WHERE name = 'my name'
|
|
46
56
|
#
|
|
47
57
|
# See caveats listed in Dataset#with_sql regarding datasets using custom
|
|
48
58
|
# SQL and the methods that can be called on them.
|
|
@@ -307,7 +307,7 @@ module Sequel
|
|
|
307
307
|
# Examples:
|
|
308
308
|
# primary_key(:id)
|
|
309
309
|
# primary_key(:id, type: :Bignum, keep_order: true)
|
|
310
|
-
# primary_key([:street_number, :house_number], name: :
|
|
310
|
+
# primary_key([:street_number, :house_number], name: :some_constraint_name)
|
|
311
311
|
def primary_key(name, *args)
|
|
312
312
|
return composite_primary_key(name, *args) if name.is_a?(Array)
|
|
313
313
|
column = @db.serial_primary_key_options.merge({:name => name})
|
|
@@ -174,7 +174,7 @@ module Sequel
|
|
|
174
174
|
# # => false
|
|
175
175
|
def empty?
|
|
176
176
|
cached_dataset(:_empty_ds) do
|
|
177
|
-
single_value_ds.unordered.select(EMPTY_SELECT)
|
|
177
|
+
(@opts[:sql] ? from_self : self).single_value_ds.unordered.select(EMPTY_SELECT)
|
|
178
178
|
end.single_value!.nil?
|
|
179
179
|
end
|
|
180
180
|
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
class Dataset
|
|
5
|
+
# This module implements methods to support deprecated use of extensions registered
|
|
6
|
+
# not using a module. In such cases, for backwards compatibility, Sequel has to use
|
|
7
|
+
# a singleton class for the dataset.
|
|
8
|
+
module DeprecatedSingletonClassMethods
|
|
9
|
+
# Load the extension into a clone of the receiver.
|
|
10
|
+
def extension(*a)
|
|
11
|
+
c = _clone(:freeze=>false)
|
|
12
|
+
c.send(:_extension!, a)
|
|
13
|
+
c.freeze
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Extend the cloned of the receiver with the given modules, instead of the default
|
|
17
|
+
# approach of creating a subclass of the receiver's class and including the modules
|
|
18
|
+
# into that.
|
|
19
|
+
def with_extend(*mods, &block)
|
|
20
|
+
c = _clone(:freeze=>false)
|
|
21
|
+
c.extend(*mods) unless mods.empty?
|
|
22
|
+
c.extend(DatasetModule.new(&block)) if block
|
|
23
|
+
c.freeze
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
private
|
|
27
|
+
|
|
28
|
+
# Load the extensions into the receiver.
|
|
29
|
+
def _extension!(exts)
|
|
30
|
+
Sequel.extension(*exts)
|
|
31
|
+
exts.each do |ext|
|
|
32
|
+
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
|
33
|
+
pr.call(self)
|
|
34
|
+
else
|
|
35
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
self
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
data/lib/sequel/dataset/query.rb
CHANGED
|
@@ -12,6 +12,10 @@ module Sequel
|
|
|
12
12
|
# in the extension).
|
|
13
13
|
EXTENSIONS = {}
|
|
14
14
|
|
|
15
|
+
# Hash of extension name symbols to modules to load to implement the extension.
|
|
16
|
+
EXTENSION_MODULES = {}
|
|
17
|
+
private_constant :EXTENSION_MODULES
|
|
18
|
+
|
|
15
19
|
EMPTY_ARRAY = [].freeze
|
|
16
20
|
|
|
17
21
|
# The dataset options that require the removal of cached columns if changed.
|
|
@@ -45,12 +49,8 @@ module Sequel
|
|
|
45
49
|
METHS
|
|
46
50
|
|
|
47
51
|
# Register an extension callback for Dataset objects. ext should be the
|
|
48
|
-
# extension name symbol, and mod should
|
|
49
|
-
# dataset
|
|
50
|
-
# object. If mod is not provided, a block can be provided and is treated
|
|
51
|
-
# as the mod object.
|
|
52
|
-
#
|
|
53
|
-
# If mod is a module, this also registers a Database extension that will
|
|
52
|
+
# extension name symbol, and mod should be a Module that will be
|
|
53
|
+
# included in the dataset's class. This also registers a Database extension that will
|
|
54
54
|
# extend all of the database's datasets.
|
|
55
55
|
def self.register_extension(ext, mod=nil, &block)
|
|
56
56
|
if mod
|
|
@@ -58,10 +58,16 @@ module Sequel
|
|
|
58
58
|
if mod.is_a?(Module)
|
|
59
59
|
block = proc{|ds| ds.extend(mod)}
|
|
60
60
|
Sequel::Database.register_extension(ext){|db| db.extend_datasets(mod)}
|
|
61
|
+
Sequel.synchronize{EXTENSION_MODULES[ext] = mod}
|
|
61
62
|
else
|
|
62
63
|
block = mod
|
|
63
64
|
end
|
|
64
65
|
end
|
|
66
|
+
|
|
67
|
+
unless mod.is_a?(Module)
|
|
68
|
+
Sequel::Deprecation.deprecate("Providing a block or non-module to Sequel::Dataset.register_extension is deprecated and support for it will be removed in Sequel 6.")
|
|
69
|
+
end
|
|
70
|
+
|
|
65
71
|
Sequel.synchronize{EXTENSIONS[ext] = block}
|
|
66
72
|
end
|
|
67
73
|
|
|
@@ -195,11 +201,15 @@ module Sequel
|
|
|
195
201
|
if TRUE_FREEZE
|
|
196
202
|
# Return a clone of the dataset loaded with the given dataset extensions.
|
|
197
203
|
# If no related extension file exists or the extension does not have
|
|
198
|
-
# specific support for Dataset objects, an
|
|
199
|
-
def extension(*
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
204
|
+
# specific support for Dataset objects, an error will be raised.
|
|
205
|
+
def extension(*exts)
|
|
206
|
+
Sequel.extension(*exts)
|
|
207
|
+
mods = exts.map{|ext| Sequel.synchronize{EXTENSION_MODULES[ext]}}
|
|
208
|
+
if mods.all?
|
|
209
|
+
with_extend(*mods)
|
|
210
|
+
else
|
|
211
|
+
with_extend(DeprecatedSingletonClassMethods).extension(*exts)
|
|
212
|
+
end
|
|
203
213
|
end
|
|
204
214
|
else
|
|
205
215
|
# :nocov:
|
|
@@ -1199,16 +1209,27 @@ module Sequel
|
|
|
1199
1209
|
end
|
|
1200
1210
|
|
|
1201
1211
|
if TRUE_FREEZE
|
|
1202
|
-
#
|
|
1212
|
+
# Create a subclass of the receiver's class, and include the given modules
|
|
1213
|
+
# into it. If a block is provided, a DatasetModule is created using the block and
|
|
1214
|
+
# is included into the subclass. Create an instance of the subclass using the
|
|
1215
|
+
# same db and opts, so that the returned dataset operates similarly to a clone
|
|
1216
|
+
# extended with the given modules. This approach is used to avoid singleton
|
|
1217
|
+
# classes, which significantly improves performance.
|
|
1218
|
+
#
|
|
1203
1219
|
# Note that like Object#extend, when multiple modules are provided
|
|
1204
|
-
# as arguments the
|
|
1205
|
-
# order. If a block is provided, a DatasetModule is created using the block and
|
|
1206
|
-
# the clone is extended with that module after any modules given as arguments.
|
|
1220
|
+
# as arguments the subclass includes the modules in reverse order.
|
|
1207
1221
|
def with_extend(*mods, &block)
|
|
1208
|
-
c =
|
|
1209
|
-
c.
|
|
1210
|
-
c.
|
|
1211
|
-
c.freeze
|
|
1222
|
+
c = Class.new(self.class)
|
|
1223
|
+
c.include(*mods) unless mods.empty?
|
|
1224
|
+
c.include(DatasetModule.new(&block)) if block
|
|
1225
|
+
o = c.freeze.allocate
|
|
1226
|
+
o.instance_variable_set(:@db, @db)
|
|
1227
|
+
o.instance_variable_set(:@opts, @opts)
|
|
1228
|
+
o.instance_variable_set(:@cache, {})
|
|
1229
|
+
if cols = cache_get(:_columns)
|
|
1230
|
+
o.send(:columns=, cols)
|
|
1231
|
+
end
|
|
1232
|
+
o.freeze
|
|
1212
1233
|
end
|
|
1213
1234
|
else
|
|
1214
1235
|
# :nocov:
|
|
@@ -1315,18 +1336,22 @@ module Sequel
|
|
|
1315
1336
|
|
|
1316
1337
|
private
|
|
1317
1338
|
|
|
1318
|
-
#
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
exts
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1339
|
+
# :nocov:
|
|
1340
|
+
unless TRUE_FREEZE
|
|
1341
|
+
# Load the extensions into the receiver, without checking if the receiver is frozen.
|
|
1342
|
+
def _extension!(exts)
|
|
1343
|
+
Sequel.extension(*exts)
|
|
1344
|
+
exts.each do |ext|
|
|
1345
|
+
if pr = Sequel.synchronize{EXTENSIONS[ext]}
|
|
1346
|
+
pr.call(self)
|
|
1347
|
+
else
|
|
1348
|
+
raise(Error, "Extension #{ext} does not have specific support handling individual datasets (try: Sequel.extension #{ext.inspect})")
|
|
1349
|
+
end
|
|
1326
1350
|
end
|
|
1351
|
+
self
|
|
1327
1352
|
end
|
|
1328
|
-
self
|
|
1329
1353
|
end
|
|
1354
|
+
# :nocov:
|
|
1330
1355
|
|
|
1331
1356
|
# If invert is true, invert the condition.
|
|
1332
1357
|
def _invert_filter(cond, invert)
|
data/lib/sequel/dataset/sql.rb
CHANGED
|
@@ -977,7 +977,12 @@ module Sequel
|
|
|
977
977
|
# order if not. Also removes the row_proc, which isn't needed
|
|
978
978
|
# for aggregate calculations.
|
|
979
979
|
def aggregate_dataset
|
|
980
|
-
(
|
|
980
|
+
(aggreate_dataset_use_from_self? ? from_self : unordered).naked
|
|
981
|
+
end
|
|
982
|
+
|
|
983
|
+
# Whether to use from_self for an aggregate dataset.
|
|
984
|
+
def aggreate_dataset_use_from_self?
|
|
985
|
+
options_overlap(COUNT_FROM_SELF_OPTS)
|
|
981
986
|
end
|
|
982
987
|
|
|
983
988
|
# Append aliasing expression to SQL string.
|
data/lib/sequel/dataset.rb
CHANGED
|
@@ -53,4 +53,8 @@ module Sequel
|
|
|
53
53
|
require_relative "dataset/sql"
|
|
54
54
|
require_relative "dataset/placeholder_literalizer"
|
|
55
55
|
require_relative "dataset/dataset_module"
|
|
56
|
+
|
|
57
|
+
# :nocov:
|
|
58
|
+
require_relative "dataset/deprecated_singleton_class_methods" if Dataset::TRUE_FREEZE
|
|
59
|
+
# :nocov:
|
|
56
60
|
end
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
# connections on every checkout without setting up coarse
|
|
29
29
|
# connection checkouts will hurt performance, in some cases
|
|
30
30
|
# significantly. Note that setting up coarse connection
|
|
31
|
-
# checkouts reduces the concurrency level
|
|
31
|
+
# checkouts reduces the concurrency level achievable. For
|
|
32
32
|
# example, in a web application, using Database#synchronize
|
|
33
33
|
# in a rack middleware will limit the number of concurrent
|
|
34
34
|
# web requests to the number to connections in the database
|
|
@@ -88,11 +88,11 @@ module Sequel
|
|
|
88
88
|
# Note that the migration this produces does not have a down
|
|
89
89
|
# block, so you cannot reverse it.
|
|
90
90
|
def dump_foreign_key_migration(options=OPTS)
|
|
91
|
-
ts =
|
|
91
|
+
ts = _dump_tables(options)
|
|
92
92
|
<<END_MIG
|
|
93
93
|
Sequel.migration do
|
|
94
94
|
change do
|
|
95
|
-
#{ts.
|
|
95
|
+
#{ts.map{|t| dump_table_foreign_keys(t)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
|
|
96
96
|
end
|
|
97
97
|
end
|
|
98
98
|
END_MIG
|
|
@@ -106,11 +106,11 @@ END_MIG
|
|
|
106
106
|
# set to :namespace, prepend the table name to the index name if the
|
|
107
107
|
# database does not use a global index namespace.
|
|
108
108
|
def dump_indexes_migration(options=OPTS)
|
|
109
|
-
ts =
|
|
109
|
+
ts = _dump_tables(options)
|
|
110
110
|
<<END_MIG
|
|
111
111
|
Sequel.migration do
|
|
112
112
|
change do
|
|
113
|
-
#{ts.
|
|
113
|
+
#{ts.map{|t| dump_table_indexes(t, :add_index, options)}.reject{|x| x == ''}.join("\n\n").gsub(/^/, ' ')}
|
|
114
114
|
end
|
|
115
115
|
end
|
|
116
116
|
END_MIG
|
|
@@ -138,7 +138,7 @@ END_MIG
|
|
|
138
138
|
options[:foreign_keys] = false
|
|
139
139
|
end
|
|
140
140
|
|
|
141
|
-
ts = sort_dumped_tables(
|
|
141
|
+
ts = sort_dumped_tables(_dump_tables(options), options)
|
|
142
142
|
skipped_fks = if sfk = options[:skipped_foreign_keys]
|
|
143
143
|
# Handle skipped foreign keys by adding them at the end via
|
|
144
144
|
# alter_table/add_foreign_key. Note that skipped foreign keys
|
|
@@ -166,6 +166,21 @@ END_MIG
|
|
|
166
166
|
|
|
167
167
|
private
|
|
168
168
|
|
|
169
|
+
# Handle schema option to dump tables in a different schema. Such
|
|
170
|
+
# tables must be schema qualified for this to work correctly.
|
|
171
|
+
def _dump_tables(opts)
|
|
172
|
+
if opts[:schema]
|
|
173
|
+
_literal_table_sort(tables(opts.merge(:qualify=>true)))
|
|
174
|
+
else
|
|
175
|
+
tables(opts).sort
|
|
176
|
+
end
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
# Sort the given table by the literalized value.
|
|
180
|
+
def _literal_table_sort(tables)
|
|
181
|
+
tables.sort_by{|s| literal(s)}
|
|
182
|
+
end
|
|
183
|
+
|
|
169
184
|
# If a database default exists and can't be converted, and we are dumping with :same_db,
|
|
170
185
|
# return a string with the inspect method modified a literal string is created if the code is evaled.
|
|
171
186
|
def column_schema_to_ruby_default_fallback(default, options)
|
|
@@ -204,12 +219,20 @@ END_MIG
|
|
|
204
219
|
if database_type == :mysql && h[:type] =~ /\Atimestamp/
|
|
205
220
|
h[:null] = true
|
|
206
221
|
end
|
|
222
|
+
if database_type == :mssql && schema[:max_length]
|
|
223
|
+
h[:size] = schema[:max_length]
|
|
224
|
+
end
|
|
207
225
|
h
|
|
208
226
|
else
|
|
209
227
|
column_schema_to_ruby_type(schema)
|
|
210
228
|
end
|
|
211
229
|
type = col_opts.delete(:type)
|
|
212
|
-
col_opts.
|
|
230
|
+
if col_opts.key?(:size) && col_opts[:size].nil?
|
|
231
|
+
col_opts.delete(:size)
|
|
232
|
+
if max_length = schema[:max_length]
|
|
233
|
+
col_opts[:size] = max_length
|
|
234
|
+
end
|
|
235
|
+
end
|
|
213
236
|
if schema[:generated]
|
|
214
237
|
if options[:same_db] && database_type == :postgres
|
|
215
238
|
col_opts[:generated_always_as] = column_schema_to_ruby_default_fallback(schema[:default], options)
|
|
@@ -352,7 +375,7 @@ END_MIG
|
|
|
352
375
|
options[:skipped_foreign_keys] = skipped_foreign_keys
|
|
353
376
|
tables
|
|
354
377
|
else
|
|
355
|
-
tables
|
|
378
|
+
tables
|
|
356
379
|
end
|
|
357
380
|
end
|
|
358
381
|
|
|
@@ -377,14 +400,14 @@ END_MIG
|
|
|
377
400
|
# outstanding foreign keys and skipping those foreign keys.
|
|
378
401
|
# The skipped foreign keys will be added at the end of the
|
|
379
402
|
# migration.
|
|
380
|
-
skip_table, skip_fks = table_fks.sort_by{|table, fks| [fks.length, table]}.first
|
|
403
|
+
skip_table, skip_fks = table_fks.sort_by{|table, fks| [fks.length, literal(table)]}.first
|
|
381
404
|
skip_fks_hash = skipped_foreign_keys[skip_table] = {}
|
|
382
405
|
skip_fks.each{|fk| skip_fks_hash[fk[:columns]] = fk}
|
|
383
406
|
this_loop << skip_table
|
|
384
407
|
end
|
|
385
408
|
|
|
386
409
|
# Add sorted tables from this loop to the final list
|
|
387
|
-
sorted_tables.concat(this_loop
|
|
410
|
+
sorted_tables.concat(_literal_table_sort(this_loop))
|
|
388
411
|
|
|
389
412
|
# Remove tables that were handled this loop
|
|
390
413
|
this_loop.each{|t| table_fks.delete(t)}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# frozen-string-literal: true
|
|
2
|
+
#
|
|
3
|
+
# The set_literalizer extension allows for using Set instances in many of the
|
|
4
|
+
# same places that you would use Array instances:
|
|
5
|
+
#
|
|
6
|
+
# DB[:table].where(column: Set.new([1, 2, 3]))
|
|
7
|
+
# # SELECT FROM table WHERE (column IN (1, 2, 3))
|
|
8
|
+
#
|
|
9
|
+
# To load the extension into all datasets created from a given Database:
|
|
10
|
+
#
|
|
11
|
+
# DB.extension :set_literalizer
|
|
12
|
+
#
|
|
13
|
+
# Related module: Sequel::Dataset::SetLiteralizer
|
|
14
|
+
|
|
15
|
+
require 'set'
|
|
16
|
+
|
|
17
|
+
module Sequel
|
|
18
|
+
class Dataset
|
|
19
|
+
module SetLiteralizer
|
|
20
|
+
# Try to generate the same SQL for Set instances used in datasets
|
|
21
|
+
# that would be used for equivalent Array instances.
|
|
22
|
+
def complex_expression_sql_append(sql, op, args)
|
|
23
|
+
# Array instances are treated specially by
|
|
24
|
+
# Sequel::SQL::BooleanExpression.from_value_pairs. That cannot
|
|
25
|
+
# be modified by a dataset extension, so this tries to convert
|
|
26
|
+
# the complex expression values generated by default to what would
|
|
27
|
+
# be the complex expression values used for the equivalent array.
|
|
28
|
+
case op
|
|
29
|
+
when :'=', :'!='
|
|
30
|
+
if (set = args[1]).is_a?(Set)
|
|
31
|
+
op = op == :'=' ? :IN : :'NOT IN'
|
|
32
|
+
col = args[0]
|
|
33
|
+
array = set.to_a
|
|
34
|
+
if Sequel.condition_specifier?(array) && col.is_a?(Array)
|
|
35
|
+
array = Sequel.value_list(array)
|
|
36
|
+
end
|
|
37
|
+
args = [col, array]
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
super
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
private
|
|
45
|
+
|
|
46
|
+
# Literalize Set instances by converting the set to array.
|
|
47
|
+
def literal_other_append(sql, v)
|
|
48
|
+
if Set === v
|
|
49
|
+
literal_append(sql, v.to_a)
|
|
50
|
+
else
|
|
51
|
+
super
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
register_extension(:set_literalizer, SetLiteralizer)
|
|
57
|
+
end
|
|
58
|
+
end
|
|
@@ -3,7 +3,8 @@
|
|
|
3
3
|
module Sequel
|
|
4
4
|
module Plugins
|
|
5
5
|
# The prepared_statements plugin modifies the model to use prepared statements for
|
|
6
|
-
# instance level inserts and updates.
|
|
6
|
+
# instance level inserts and updates. This plugin exists for backwards compatibility
|
|
7
|
+
# and is not recommended for general use.
|
|
7
8
|
#
|
|
8
9
|
# Note that this plugin is unsafe in some circumstances, as it can allow up to
|
|
9
10
|
# 2^N prepared statements to be created for each type of insert and update query, where
|
|
@@ -5,7 +5,8 @@ module Sequel
|
|
|
5
5
|
# The prepared_statements_safe plugin modifies the model to reduce the number of
|
|
6
6
|
# prepared statements that can be created, by setting as many columns as possible
|
|
7
7
|
# before creating, and by changing +save_changes+ to save all columns instead of
|
|
8
|
-
# just the changed ones.
|
|
8
|
+
# just the changed ones. This plugin exists for backwards compatibility
|
|
9
|
+
# and is not recommended for general use.
|
|
9
10
|
#
|
|
10
11
|
# This plugin depends on the +prepared_statements+ plugin.
|
|
11
12
|
#
|
data/lib/sequel/version.rb
CHANGED
|
@@ -6,7 +6,7 @@ module Sequel
|
|
|
6
6
|
|
|
7
7
|
# The minor version of Sequel. Bumped for every non-patch level
|
|
8
8
|
# release, generally around once a month.
|
|
9
|
-
MINOR =
|
|
9
|
+
MINOR = 67
|
|
10
10
|
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
|
12
12
|
# releases that fix regressions from previous versions.
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: sequel
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 5.
|
|
4
|
+
version: 5.67.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Jeremy Evans
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2023-
|
|
11
|
+
date: 2023-04-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: minitest
|
|
@@ -197,6 +197,8 @@ extra_rdoc_files:
|
|
|
197
197
|
- doc/release_notes/5.63.0.txt
|
|
198
198
|
- doc/release_notes/5.64.0.txt
|
|
199
199
|
- doc/release_notes/5.65.0.txt
|
|
200
|
+
- doc/release_notes/5.66.0.txt
|
|
201
|
+
- doc/release_notes/5.67.0.txt
|
|
200
202
|
- doc/release_notes/5.7.0.txt
|
|
201
203
|
- doc/release_notes/5.8.0.txt
|
|
202
204
|
- doc/release_notes/5.9.0.txt
|
|
@@ -290,6 +292,8 @@ files:
|
|
|
290
292
|
- doc/release_notes/5.63.0.txt
|
|
291
293
|
- doc/release_notes/5.64.0.txt
|
|
292
294
|
- doc/release_notes/5.65.0.txt
|
|
295
|
+
- doc/release_notes/5.66.0.txt
|
|
296
|
+
- doc/release_notes/5.67.0.txt
|
|
293
297
|
- doc/release_notes/5.7.0.txt
|
|
294
298
|
- doc/release_notes/5.8.0.txt
|
|
295
299
|
- doc/release_notes/5.9.0.txt
|
|
@@ -374,6 +378,7 @@ files:
|
|
|
374
378
|
- lib/sequel/dataset.rb
|
|
375
379
|
- lib/sequel/dataset/actions.rb
|
|
376
380
|
- lib/sequel/dataset/dataset_module.rb
|
|
381
|
+
- lib/sequel/dataset/deprecated_singleton_class_methods.rb
|
|
377
382
|
- lib/sequel/dataset/features.rb
|
|
378
383
|
- lib/sequel/dataset/graph.rb
|
|
379
384
|
- lib/sequel/dataset/misc.rb
|
|
@@ -459,6 +464,7 @@ files:
|
|
|
459
464
|
- lib/sequel/extensions/sequel_4_dataset_methods.rb
|
|
460
465
|
- lib/sequel/extensions/server_block.rb
|
|
461
466
|
- lib/sequel/extensions/server_logging.rb
|
|
467
|
+
- lib/sequel/extensions/set_literalizer.rb
|
|
462
468
|
- lib/sequel/extensions/split_array_nil.rb
|
|
463
469
|
- lib/sequel/extensions/sql_comments.rb
|
|
464
470
|
- lib/sequel/extensions/sql_expr.rb
|
|
@@ -613,7 +619,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
|
613
619
|
- !ruby/object:Gem::Version
|
|
614
620
|
version: '0'
|
|
615
621
|
requirements: []
|
|
616
|
-
rubygems_version: 3.4.
|
|
622
|
+
rubygems_version: 3.4.10
|
|
617
623
|
signing_key:
|
|
618
624
|
specification_version: 4
|
|
619
625
|
summary: The Database Toolkit for Ruby
|