sequel 5.39.0 → 5.63.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +308 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +57 -25
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +13 -13
- data/doc/association_basics.rdoc +89 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/migration.rdoc +12 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +18 -11
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- data/doc/release_notes/5.40.0.txt +40 -0
- data/doc/release_notes/5.41.0.txt +25 -0
- data/doc/release_notes/5.42.0.txt +136 -0
- data/doc/release_notes/5.43.0.txt +98 -0
- data/doc/release_notes/5.44.0.txt +32 -0
- data/doc/release_notes/5.45.0.txt +34 -0
- data/doc/release_notes/5.46.0.txt +87 -0
- data/doc/release_notes/5.47.0.txt +59 -0
- data/doc/release_notes/5.48.0.txt +14 -0
- data/doc/release_notes/5.49.0.txt +59 -0
- data/doc/release_notes/5.50.0.txt +78 -0
- data/doc/release_notes/5.51.0.txt +47 -0
- data/doc/release_notes/5.52.0.txt +87 -0
- data/doc/release_notes/5.53.0.txt +23 -0
- data/doc/release_notes/5.54.0.txt +27 -0
- data/doc/release_notes/5.55.0.txt +21 -0
- data/doc/release_notes/5.56.0.txt +51 -0
- data/doc/release_notes/5.57.0.txt +23 -0
- data/doc/release_notes/5.58.0.txt +31 -0
- data/doc/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +22 -11
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +17 -17
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +2 -2
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +60 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +6 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +16 -18
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +6 -2
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/postgres.rb +83 -40
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +58 -7
- data/lib/sequel/adapters/shared/mysql.rb +40 -2
- data/lib/sequel/adapters/shared/oracle.rb +76 -0
- data/lib/sequel/adapters/shared/postgres.rb +418 -174
- data/lib/sequel/adapters/shared/sqlanywhere.rb +10 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -11
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +5 -1
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +8 -8
- data/lib/sequel/connection_pool/timed_queue.rb +257 -0
- data/lib/sequel/connection_pool.rb +47 -30
- data/lib/sequel/core.rb +28 -18
- data/lib/sequel/database/connecting.rb +26 -2
- data/lib/sequel/database/misc.rb +69 -14
- data/lib/sequel/database/query.rb +38 -1
- data/lib/sequel/database/schema_generator.rb +45 -52
- data/lib/sequel/database/schema_methods.rb +17 -1
- data/lib/sequel/dataset/actions.rb +107 -13
- data/lib/sequel/dataset/features.rb +20 -0
- data/lib/sequel/dataset/misc.rb +1 -1
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +118 -16
- data/lib/sequel/dataset/sql.rb +177 -47
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +71 -31
- data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
- data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
- data/lib/sequel/extensions/duplicate_columns_handler.rb +1 -1
- data/lib/sequel/extensions/eval_inspect.rb +2 -0
- data/lib/sequel/extensions/inflector.rb +9 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +7 -2
- data/lib/sequel/extensions/named_timezones.rb +26 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +23 -3
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +478 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +28 -25
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +45 -19
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +73 -2
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +10 -23
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +19 -13
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/query.rb +2 -0
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_dumper.rb +13 -2
- data/lib/sequel/extensions/server_block.rb +8 -12
- data/lib/sequel/extensions/sql_comments.rb +110 -3
- data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
- data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
- data/lib/sequel/extensions/string_agg.rb +1 -1
- data/lib/sequel/extensions/string_date_time.rb +19 -23
- data/lib/sequel/extensions/symbol_aref.rb +2 -0
- data/lib/sequel/model/associations.rb +325 -96
- data/lib/sequel/model/base.rb +51 -27
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +5 -0
- data/lib/sequel/plugins/association_proxies.rb +2 -0
- data/lib/sequel/plugins/async_thread_pool.rb +39 -0
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +87 -15
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +10 -4
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +3 -1
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +3 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +12 -7
- data/lib/sequel/plugins/pg_array_associations.rb +56 -38
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +3 -1
- data/lib/sequel/plugins/prepared_statements.rb +10 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +27 -19
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +9 -3
- data/lib/sequel/plugins/serialization_modification_detection.rb +2 -1
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +38 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +97 -43
@@ -0,0 +1,136 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An async_thread_pool Database extension has been added, which
|
4
|
+
executes queries and processes results using a separate thread
|
5
|
+
pool. This allows you do do things like:
|
6
|
+
|
7
|
+
foos = DB[:foos].async.all
|
8
|
+
bars = DB[:bars].async.select_map(:name)
|
9
|
+
foo_bars = DB[:foo_bars].async.each{|x| p x}
|
10
|
+
|
11
|
+
and have the three method calls (all, select_map, and each)
|
12
|
+
execute concurrently. On Ruby implementations without a global
|
13
|
+
VM lock, such as JRuby, it will allow for parallel execution of
|
14
|
+
the method calls. On CRuby, the main benefit will be for cases
|
15
|
+
where query execution takes a long time or there is significant
|
16
|
+
latency between the application and the database.
|
17
|
+
|
18
|
+
When you call a method on foos, bars, or foo_bars, if the thread
|
19
|
+
pool hasn't finished processing the method, the calling code will
|
20
|
+
block until the method call has finished.
|
21
|
+
|
22
|
+
By default, for consistency, calling code will not preempt the
|
23
|
+
async thread pool. For example, if you do:
|
24
|
+
|
25
|
+
DB[:foos].async.all.size
|
26
|
+
|
27
|
+
The calling code will always wait for the async thread pool to
|
28
|
+
run the all method, and then the calling code will call size on
|
29
|
+
the result. This ensures that async queries will not use the
|
30
|
+
same connection as the the calling thread, even if calling thread
|
31
|
+
has a connection checked out.
|
32
|
+
|
33
|
+
In some cases, such as when the async thread pool is very busy,
|
34
|
+
preemption is desired for performance reasons. If you set the
|
35
|
+
:preempt_async_thread Database option before loading the
|
36
|
+
async_thread_pool extension, preemption will be allowed. With
|
37
|
+
preemption allowed, if the async thread pool has not started the
|
38
|
+
processing of the method at the time the calling code needs the
|
39
|
+
results of the method, the calling code will preempt the async
|
40
|
+
thread pool, and run the method on the current thread.
|
41
|
+
|
42
|
+
By default, the async thread pool uses the same number of threads as
|
43
|
+
the Database objects :max_connections attribute (the default for
|
44
|
+
that is 4). You can modify the number of async threads by setting
|
45
|
+
the :num_async_threads Database option before loading the Database
|
46
|
+
async_thread_pool extension.
|
47
|
+
|
48
|
+
Most Dataset methods that execute queries on the database and return
|
49
|
+
results will operate asynchronously if the the dataset is set to be
|
50
|
+
asynchronous via the Dataset#async method. This includes most
|
51
|
+
methods available due to the inclusion in Enumerable, even if not
|
52
|
+
defined by Dataset itself.
|
53
|
+
|
54
|
+
There are multiple caveats when using the async_thread_pool
|
55
|
+
extension:
|
56
|
+
|
57
|
+
* Asynchronous behavior is harder to understand and harder to
|
58
|
+
debug. It would be wise to only use this support in cases where
|
59
|
+
it provides is significant performance benefit.
|
60
|
+
|
61
|
+
* Dataset methods executed asynchronously will use a separate
|
62
|
+
database connection than the calling thread, so they will not
|
63
|
+
respect transactions in the calling thread, or other cases where
|
64
|
+
the calling thread checks out a connection directly using
|
65
|
+
Database#synchronize. They will also not respect the use of
|
66
|
+
Database#with_server (from the server_block extension) in the
|
67
|
+
calling thread.
|
68
|
+
|
69
|
+
* Dataset methods executed asynchronously should never ignore their
|
70
|
+
return value. Code such as:
|
71
|
+
|
72
|
+
DB[:table].async.insert(1)
|
73
|
+
|
74
|
+
is probablematic because without storing the return value, you
|
75
|
+
have no way to block until the insert has been completed.
|
76
|
+
|
77
|
+
* The returned object for Dataset methods executed asynchronously is
|
78
|
+
a proxy object (promise). So you should never do:
|
79
|
+
|
80
|
+
row = DB[:table].async.first
|
81
|
+
# ...
|
82
|
+
if row
|
83
|
+
end
|
84
|
+
|
85
|
+
# or:
|
86
|
+
|
87
|
+
bool = DB[:table].async.get(:boolean_column)
|
88
|
+
# ...
|
89
|
+
if bool
|
90
|
+
end
|
91
|
+
|
92
|
+
because the if branches will always be taken as row and bool will
|
93
|
+
never be nil or false. If you want to get the underlying value,
|
94
|
+
call itself on the proxy object (or __value if using Ruby <2.2).
|
95
|
+
|
96
|
+
For the same reason, you should not use the proxy objects directly
|
97
|
+
in case expressions or as arguments to Class#===. Use itself or
|
98
|
+
__value in those cases.
|
99
|
+
|
100
|
+
* Dataset methods executed asynchronously that include blocks have the
|
101
|
+
block executed asynchronously as well, assuming that the method
|
102
|
+
calls the block. Because these blocks are executed in a separate
|
103
|
+
thread, you cannot use control flow modifiers such as break or
|
104
|
+
return in them.
|
105
|
+
|
106
|
+
* An async_thread_pool model plugin has been added. This requires the
|
107
|
+
async_thread_pool extension has been loaded into the model's Database
|
108
|
+
object, and allows you to call Model.async instead of
|
109
|
+
Model.dataset.async. It also adds async support to the destroy,
|
110
|
+
with_pk, and with_pk! model dataset methods.
|
111
|
+
|
112
|
+
* Model#to_json_data has been added to the json_serializer plugin, for
|
113
|
+
returning a hash of data that can be converted to JSON, instead of
|
114
|
+
a JSON string.
|
115
|
+
|
116
|
+
* A :reject_nil option has been added to the nested_attributes method
|
117
|
+
in the nested_attributes plugin. This will ignore calls to the
|
118
|
+
nested attributes setter method where nil is passed as the setter
|
119
|
+
method argument.
|
120
|
+
|
121
|
+
= Other Improvements
|
122
|
+
|
123
|
+
* Model#freeze now works in case where model validation modifies the
|
124
|
+
object beyond adding errors.
|
125
|
+
|
126
|
+
* Model#freeze in the composition, serialization, and
|
127
|
+
serialization_modification_detection plugins now works in cases
|
128
|
+
where validation would end up loading the composed or
|
129
|
+
serialized values.
|
130
|
+
|
131
|
+
* Database#extension now avoids a possible thread safety issue that
|
132
|
+
could result in the extension being loaded into the Database twice.
|
133
|
+
|
134
|
+
* The ado adapter now supports overriding the timestamp conversion
|
135
|
+
proc. Previously, unlike other conversion procs, the timestamp
|
136
|
+
conversion proc was hard coded and could not be overridden.
|
@@ -0,0 +1,98 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A column_encryption plugin has been added to support encrypting the
|
4
|
+
content of individual columns in a table.
|
5
|
+
|
6
|
+
Column values are encrypted with AES-256-GCM using a per-value
|
7
|
+
cipher key derived from a key provided in the configuration using
|
8
|
+
HMAC-SHA256.
|
9
|
+
|
10
|
+
If you would like to support encryption of columns in more than one
|
11
|
+
model, you should probably load the plugin into the parent class of
|
12
|
+
your models and specify the keys:
|
13
|
+
|
14
|
+
Sequel::Model.plugin :column_encryption do |enc|
|
15
|
+
enc.key 0, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
|
16
|
+
end
|
17
|
+
|
18
|
+
This specifies a single master encryption key. Unless you are
|
19
|
+
actively rotating keys, it is best to use a single master key.
|
20
|
+
|
21
|
+
In the above call, 0 is the id of the key, and
|
22
|
+
ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"] is the content of the key, which
|
23
|
+
must be a string with exactly 32 bytes. As indicated, this key
|
24
|
+
should not be hardcoded or otherwise committed to the source control
|
25
|
+
repository.
|
26
|
+
|
27
|
+
For models that need encrypted columns, you load the plugin again,
|
28
|
+
but specify the columns to encrypt:
|
29
|
+
|
30
|
+
ConfidentialModel.plugin :column_encryption do |enc|
|
31
|
+
enc.column :encrypted_column_name
|
32
|
+
enc.column :searchable_column_name, searchable: true
|
33
|
+
enc.column :ci_searchable_column_name, searchable: :case_insensitive
|
34
|
+
end
|
35
|
+
|
36
|
+
With this, all three specified columns (encrypted_column_name,
|
37
|
+
searchable_column_name, and ci_searchable_column_name) will be
|
38
|
+
marked as encrypted columns. When you run the following code:
|
39
|
+
|
40
|
+
ConfidentialModel.create(
|
41
|
+
encrypted_column_name: 'These',
|
42
|
+
searchable_column_name: 'will be',
|
43
|
+
ci_searchable_column_name: 'Encrypted'
|
44
|
+
)
|
45
|
+
|
46
|
+
It will save encrypted versions to the database.
|
47
|
+
encrypted_column_name will not be searchable, searchable_column_name
|
48
|
+
will be searchable with an exact match, and
|
49
|
+
ci_searchable_column_name will be searchable with a case insensitive
|
50
|
+
match.
|
51
|
+
|
52
|
+
To search searchable encrypted columns, use with_encrypted_value.
|
53
|
+
This example code will return the model instance created in the code
|
54
|
+
example in the previous section:
|
55
|
+
|
56
|
+
ConfidentialModel.
|
57
|
+
with_encrypted_value(:searchable_column_name, "will be")
|
58
|
+
with_encrypted_value(:ci_searchable_column_name, "encrypted").
|
59
|
+
first
|
60
|
+
|
61
|
+
To rotate encryption keys, add a new key above the existing key,
|
62
|
+
with a new key ID:
|
63
|
+
|
64
|
+
Sequel::Model.plugin :column_encryption do |enc|
|
65
|
+
enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
|
66
|
+
enc.key 0, ENV["SEQUEL_OLD_COLUMN_ENCRYPTION_KEY"]
|
67
|
+
end
|
68
|
+
|
69
|
+
Newly encrypted data will then use the new key. Records encrypted
|
70
|
+
with the older key will still be decrypted correctly.
|
71
|
+
|
72
|
+
To force reencryption for existing records that are using the older
|
73
|
+
key, you can use the needing_reencryption dataset method and the
|
74
|
+
reencrypt instance method. For a small number of records, you can
|
75
|
+
probably do:
|
76
|
+
|
77
|
+
ConfidentialModel.needing_reencryption.all(&:reencrypt)
|
78
|
+
|
79
|
+
With more than a small number of records, you'll want to do this in
|
80
|
+
batches. It's possible you could use an approach such as:
|
81
|
+
|
82
|
+
ds = ConfidentialModel.needing_reencryption.limit(100)
|
83
|
+
true until ds.all(&:reencrypt).empty?
|
84
|
+
|
85
|
+
After all values have been reencrypted for all models, and no models
|
86
|
+
use the older encryption key, you can remove it from the
|
87
|
+
configuration:
|
88
|
+
|
89
|
+
Sequel::Model.plugin :column_encryption do |enc|
|
90
|
+
enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
|
91
|
+
end
|
92
|
+
|
93
|
+
The column_encryption plugin supports encrypting serialized data,
|
94
|
+
as well as enforcing uniquenss of searchable encrypted columns
|
95
|
+
(in the absence of key rotation). By design, it does not support
|
96
|
+
compression, mixing encrypted and unencrypted data in the same
|
97
|
+
column, or support arbitrary encryption ciphers. See the plugin
|
98
|
+
documentation for more details.
|
@@ -0,0 +1,32 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A concurrent_eager_loading plugin has been added. This plugin
|
4
|
+
builds on top of the async_thread_pool Database extension and
|
5
|
+
allows eager loading multiple associations concurrently in
|
6
|
+
separate threads. With this plugin, you can mark datasets for
|
7
|
+
concurrent eager loading using eager_load_concurrently:
|
8
|
+
|
9
|
+
Album.eager_load_concurrently.eager(:artist, :genre, :tracks).all
|
10
|
+
|
11
|
+
Datasets that are marked for concurrent eager loading will use
|
12
|
+
concurrent eager loading if they are eager loading more than one
|
13
|
+
association. If you would like to make concurrent eager loading
|
14
|
+
the default, you can load the plugin with the :always option.
|
15
|
+
|
16
|
+
All of the association types that ship with Sequel now support
|
17
|
+
concurrent eager loading when using this plugin. For custom eager
|
18
|
+
loaders using the :eager_loader association option, please see the
|
19
|
+
documentation for the plugin for how to enable custom eager loading
|
20
|
+
for them.
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* The date_arithmetic extension now handles ActiveSupport::Duration
|
25
|
+
values with weeks, as well as :weeks as a key in a hash value. Weeks
|
26
|
+
are converted into 7 days internally.
|
27
|
+
|
28
|
+
* The shared SQLite adapter now emulates the dropping of non-composite
|
29
|
+
unique constraints. Non-composite unique constraints are now
|
30
|
+
treated similarly to composite unique constraints, in that dropping
|
31
|
+
any unique constraints on a table will drop all unique constraints
|
32
|
+
on that table.
|
@@ -0,0 +1,34 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A auto_validations_constraint_validations_presence_message plugin
|
4
|
+
has been added that provides integration for the auto_validations
|
5
|
+
and constraint_validations plugin in the following conditions:
|
6
|
+
|
7
|
+
* The column has a NOT NULL constraint
|
8
|
+
* The column has a presence constraint validation with both
|
9
|
+
the :message and :allow_nil options used.
|
10
|
+
|
11
|
+
In this case, when saving a nil value in the column, the plugin
|
12
|
+
will make it so the more specific message from the presence
|
13
|
+
constraint validation is used, instead of the generic message
|
14
|
+
from auto_validations.
|
15
|
+
|
16
|
+
= Other Improvements
|
17
|
+
|
18
|
+
* On SQLite 3.35.0+, Sequel now uses ALTER TABLE DROP COLUMN for
|
19
|
+
dropping columns, instead of emulating the dropped column by
|
20
|
+
recreating the table.
|
21
|
+
|
22
|
+
* The Dataset#with :materialized option is now supported on SQLite
|
23
|
+
3.35.0+ for specifying whether common table expressions should be
|
24
|
+
materialized.
|
25
|
+
|
26
|
+
* The odbc adapter now correct handles boolean columns with NULL
|
27
|
+
values. Previously, such values were returned as false instead
|
28
|
+
of nil.
|
29
|
+
|
30
|
+
= Backwards Compatibility
|
31
|
+
|
32
|
+
* The change to use ALTER TABLE DROP COLUMN on SQLite 3.35.0+ can
|
33
|
+
cause backwards compatibility issues if SQLite 3.35.0+ does
|
34
|
+
not allow dropping the column.
|
@@ -0,0 +1,87 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* An unused_associations plugin has been added, which allows you to
|
4
|
+
determine which associations and association methods are not used.
|
5
|
+
You can use this to avoid defining the unused associations and
|
6
|
+
association methods, which can save memory.
|
7
|
+
|
8
|
+
This plugin is supported on Ruby 2.5+, and uses method coverage to
|
9
|
+
determine if the plugin's methods are called. Because Sequel::Model
|
10
|
+
adds association methods to an anonymous module included in the
|
11
|
+
class, directly using the method coverage data to determine which
|
12
|
+
associations are used is challenging.
|
13
|
+
|
14
|
+
This plugin is mostly designed for reporting. You can have a
|
15
|
+
test suite that runs with method coverage enabled, and use the
|
16
|
+
coverage information to get data on unused associations:
|
17
|
+
|
18
|
+
# Calls Coverage.result
|
19
|
+
cov_data = Sequel::Model.update_associations_coverage
|
20
|
+
unused_associations_data = Sequel::Model.update_unused_associations_data(coverage_data: cov_data)
|
21
|
+
Sequel::Model.unused_associations(unused_associations_data: unused_associations_data)
|
22
|
+
# => [["Class1", "assoc1"], ...]
|
23
|
+
|
24
|
+
unused_associations returns an array of two element arrays, where
|
25
|
+
the first element is the class name and the second element is the
|
26
|
+
association name. The returned values will be associations where
|
27
|
+
all of the association methods are not used.
|
28
|
+
|
29
|
+
In addition to determining which associations are not used, you can
|
30
|
+
also use this to determine if you are defining association methods
|
31
|
+
that are not used:
|
32
|
+
|
33
|
+
Sequel::Model.unused_association_options(unused_associations_data: unused_associations_data)
|
34
|
+
# => [["Class2", "assoc2", {:read_only=>true}], ...]
|
35
|
+
|
36
|
+
unused_association_options is similar to unused_associations, but
|
37
|
+
returns an array of three element arrays, where the third element
|
38
|
+
is a hash of association options that should be used to avoid
|
39
|
+
defining the unused association methods. It's common in Sequel to
|
40
|
+
define associations and only use them for reading data and not for
|
41
|
+
modifications, and you can use this to easily see which associations
|
42
|
+
are only used for reading data.
|
43
|
+
|
44
|
+
As the determination of whether associations are used is based on
|
45
|
+
method coverage, this will report as unused any associations that are
|
46
|
+
used but where the association methods are not called. These cases
|
47
|
+
are rare, but can happen if you have libraries that use the
|
48
|
+
association reflection metadata without calling the association
|
49
|
+
methods, or use the association only in combination with another
|
50
|
+
plugin such as dataset_associations. You can set the :is_used
|
51
|
+
association option to explicitly mark an association as used, and
|
52
|
+
have this plugin avoid reporting it as unused.
|
53
|
+
|
54
|
+
In addition to just reporting on unused associations, you can also
|
55
|
+
directly use the unused associations metadata to automatically avoid
|
56
|
+
defining unused associations or unused associations methods. You
|
57
|
+
can set a :file option when loading the plugin:
|
58
|
+
|
59
|
+
Sequel::Model.plugin :unused_associations, file: 'unused_associations.json'
|
60
|
+
|
61
|
+
Then run the method coverage testing. This will save the unused
|
62
|
+
associations metadata to the file. Then you can use this metadata
|
63
|
+
automatically by also setting the :modify_associations option:
|
64
|
+
|
65
|
+
Sequel::Model.plugin :unused_associations, file: 'unused_associations.json',
|
66
|
+
modify_associations: true
|
67
|
+
|
68
|
+
With the :modify_associations option, unused associations are
|
69
|
+
skipped instead of being defined, and the options returned by
|
70
|
+
unused_association_options are automatically used. Note that using
|
71
|
+
the :modify_associations option is risky unless you have complete
|
72
|
+
coverage and do not have cases where the associations are used
|
73
|
+
without calling methods.
|
74
|
+
|
75
|
+
It is common to have multiple test suites where you need to combine
|
76
|
+
coverage. The plugin supports this by using a :coverage_file option:
|
77
|
+
|
78
|
+
Sequel::Model.plugin :unused_associations, coverage_file: 'unused_associations_coverage.json'
|
79
|
+
|
80
|
+
In this case, you would run update_associations_coverage after each
|
81
|
+
test suite, and update_unused_associations_data only after all test
|
82
|
+
suites have been run.
|
83
|
+
|
84
|
+
* Passing nil as the value of the :setter, :adder, :remover, or
|
85
|
+
:clearer association options will cause the related method to not be
|
86
|
+
defined, instead of using the default value. This allows you to
|
87
|
+
only define the methods you will actually be using.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Sequel now supports using separate queries for each table for both
|
4
|
+
lazy and eager loading of the following associations:
|
5
|
+
|
6
|
+
* many_to_many
|
7
|
+
* one_through_one
|
8
|
+
* many_through_many # many_through_many plugin
|
9
|
+
* one_through_many # many_through_many plugin
|
10
|
+
|
11
|
+
For many_to_many/one_through_one, you specify the :join_table_db
|
12
|
+
association option, which should be a Sequel::Database instance
|
13
|
+
containing the join table. It is possible for the current table,
|
14
|
+
join table, and associated table all to be in separate databases:
|
15
|
+
|
16
|
+
JOIN_TABLE_DB = Sequel.connect('...')
|
17
|
+
Album.many_to_many :artists, join_table_db: JOIN_TABLE_DB
|
18
|
+
|
19
|
+
For many_through_many/one_through_many, you can use the :db option
|
20
|
+
in each join table specification. All join tables can be in
|
21
|
+
separate databases:
|
22
|
+
|
23
|
+
JTDB1 = Sequel.connect('...')
|
24
|
+
JTDB2 = Sequel.connect('...')
|
25
|
+
# Tracks on all albums this artist appears on
|
26
|
+
Artist.many_through_many :album_tracks, [
|
27
|
+
{table: :albums_artists, left: :artist_id, right: :album_id, db: JTDB1},
|
28
|
+
{table: :artists, left: :id, right: :id, db: JTDB2}
|
29
|
+
],
|
30
|
+
class: :Track, right_primary_key: :album_id
|
31
|
+
|
32
|
+
* The :allow_eager_graph association option has been added. Setting
|
33
|
+
this option to false will disallow eager loading via #eager_graph.
|
34
|
+
This is useful if you can eager load the association via #eager,
|
35
|
+
but not with #eager_graph.
|
36
|
+
|
37
|
+
* The :allow_filtering_by association option has been added. Setting
|
38
|
+
this option to false will disallow the use of filtering by
|
39
|
+
associations for the association.
|
40
|
+
|
41
|
+
* Dataset#returning is now supported on SQLite 3.35.0+. To work around
|
42
|
+
bugs in the SQLite implementation, identifiers used in the RETURNING
|
43
|
+
clause are automatically aliased. Additionally, prepared statements
|
44
|
+
that use the RETURNING clause on SQLite seem to have issues, so the
|
45
|
+
prepared_statements plugin does not automatically use prepared
|
46
|
+
statements on SQLite for queries that use the RETURNING clause.
|
47
|
+
|
48
|
+
* Database#rename_tables has been added on MySQL to support renaming
|
49
|
+
multiple tables in the same query.
|
50
|
+
|
51
|
+
= Other Improvements
|
52
|
+
|
53
|
+
* The unused_associations plugin now tracks access to the association
|
54
|
+
reflection for associations, so it will no longer show an
|
55
|
+
association as completely unused if something is accessing the
|
56
|
+
association reflection for it. This eliminates most of the false
|
57
|
+
positives, where the plugin would show an association as unused
|
58
|
+
even though something was using it without calling the association
|
59
|
+
methods.
|
@@ -0,0 +1,14 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A Sequel::Database#like_without_collate accessor has been added on
|
4
|
+
Microsoft SQL Server, which avoids using the COLLATE clause for
|
5
|
+
LIKE expressions. This can speed up query performance significantly.
|
6
|
+
|
7
|
+
* A private Sequel::Model::Errors#full_message method has been added
|
8
|
+
to make it easier to support internationalization for Sequel::Model
|
9
|
+
error messages.
|
10
|
+
|
11
|
+
= Other Improvements
|
12
|
+
|
13
|
+
* The association reflection tracking in the unused_associations
|
14
|
+
plugin now works correctly when combining coverage runs.
|
@@ -0,0 +1,59 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* Model#validates_no_null_byte has been added to the
|
4
|
+
validation_helpers. It checks that the value being validated does
|
5
|
+
not contain an ASCII NUL ('\0') byte. Some databases will return an
|
6
|
+
error if a string contains a NUL byte.
|
7
|
+
|
8
|
+
The auto_validations plugin will now automatically add no_null_byte
|
9
|
+
validations for all string columns in the model's table. This will
|
10
|
+
change exceptions raised by NUL bytes from database errors to
|
11
|
+
validation failures.
|
12
|
+
|
13
|
+
If you are using auto_validations and would like to have a table
|
14
|
+
accept NUL bytes in string columns, use the following code inside
|
15
|
+
the model:
|
16
|
+
|
17
|
+
skip_auto_validations(:no_null_byte)
|
18
|
+
|
19
|
+
* JSONB subscripts are now supported on PostgreSQL 14+ when using the
|
20
|
+
pg_json_ops extension. You can use JSONB subscripts to more easily
|
21
|
+
update part of a JSONB column:
|
22
|
+
|
23
|
+
DB[:table].update(Sequel.pg_jsonb_op(:column)['key'] => 'value')
|
24
|
+
UPDATE "table" SET "column"['key'] = 'value'
|
25
|
+
|
26
|
+
* hstore subscripts are now supported on PostgreSQL 14+ when using the
|
27
|
+
pg_hstore_ops extension. You can use hstore subscripts to more
|
28
|
+
easily update part of an hstore column:
|
29
|
+
|
30
|
+
DB[:table].update(Sequel.hstore_op(:column)['key'] => 'value')
|
31
|
+
UPDATE "table" SET "column"['key'] = 'value'
|
32
|
+
|
33
|
+
* Sequel now supports table aliases for JOIN USING columns on
|
34
|
+
PostgreSQL 14+. These allow you to reference the USING columns in
|
35
|
+
the query using a qualified identifier. To use this support, pass an
|
36
|
+
SQL::AliasedExpression as the expression to join on:
|
37
|
+
|
38
|
+
DB[:t1].join(:t2, Sequel.as([:c1, :c2], :alias))
|
39
|
+
# SELECT * FROM "t1" INNER JOIN "t2" USING ("c1", "c2") AS "alias"
|
40
|
+
|
41
|
+
* Database#create_trigger on PostgreSQL now supports a :replace option
|
42
|
+
for CREATE OR REPLACE TRIGGER (supported in PostgreSQL 14+).
|
43
|
+
|
44
|
+
* SQL::Expression#sequel_ast_transform has been added to support
|
45
|
+
AST transforms of custom expression classes.
|
46
|
+
|
47
|
+
= Other Improvements
|
48
|
+
|
49
|
+
* Sequel now supports calling PostgreSQL procedures without arguments
|
50
|
+
when using Database#call_procedure. Previously, attempts to call
|
51
|
+
procuredures without arguments would call the procedure with a
|
52
|
+
single NULL argument.
|
53
|
+
|
54
|
+
* Sequel now uses defined?(yield) instead of block_given? internally
|
55
|
+
for better performance on CRuby. defined?(yield) is faster as it is
|
56
|
+
built into the VM, while block_given? is a regular method and has
|
57
|
+
the overhead of calling a regular method. Note that defined?(yield)
|
58
|
+
is not implemented correctly on JRuby before 9.0.0.0, so this
|
59
|
+
release of Sequel drops support for JRuby versions before 9.0.0.0.
|
@@ -0,0 +1,78 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* A pg_multirange extension has been added with support for PostgreSQL
|
4
|
+
14+ multirange types. Multirange types are similar to an array of
|
5
|
+
ranges, where a value is in the multirange if it is in any of the
|
6
|
+
ranges contained in the multirange. Multiranges are useful when you
|
7
|
+
need to check against multiple ranges that do not overlap.
|
8
|
+
|
9
|
+
You can create multiranges using Sequel.pg_multirange, passing
|
10
|
+
an array of ranges and a multirange type:
|
11
|
+
|
12
|
+
DB.extension :pg_multirange
|
13
|
+
multirange = Sequel.pg_multirange(array_of_date_ranges, :datemultirange)
|
14
|
+
|
15
|
+
Sequel.pg_multirange returns a PGMultiRange, which operates as a
|
16
|
+
delegate to an array of PGRange objects. Behavior of the object
|
17
|
+
is similar to an array, except that cover? is supported, which will
|
18
|
+
test if any of the included ranges covers the argument:
|
19
|
+
|
20
|
+
multirange.cover?(Date.today)
|
21
|
+
|
22
|
+
Like the pg_range extension, this also supports registering custom
|
23
|
+
multirange types, and using multirange types as bound variables.
|
24
|
+
|
25
|
+
The pg_range_ops extension now supports both ranges and multiranges,
|
26
|
+
with a few new methods added to Postgres::RangeOp for converting
|
27
|
+
between them:
|
28
|
+
|
29
|
+
* range_merge
|
30
|
+
* multirange
|
31
|
+
* and unnest
|
32
|
+
|
33
|
+
* An sql_log_normalizer extension has been added for normalizing
|
34
|
+
logged SQL, replacing numbers and strings inside the SQL string
|
35
|
+
with question marks. This is useful for analytics and sensitive
|
36
|
+
data.
|
37
|
+
|
38
|
+
DB[:table].first(a: 1, b: 'something')
|
39
|
+
# Without sql_log_normalizer extension, logged SQL is:
|
40
|
+
# SELECT * FROM "table" WHERE (("a" = 1) AND ("b" = 'something')) LIMIT 1
|
41
|
+
|
42
|
+
DB.extension :sql_log_normalizer
|
43
|
+
DB[:table].first(a: 1, b: 'something')
|
44
|
+
# With sql_log_normalizer_extension, logged SQL is:
|
45
|
+
# SELECT * FROM "table" WHERE (("a" = ?) AND ("b" = ?)) LIMIT ?
|
46
|
+
|
47
|
+
This extension scans the logged SQL for numbers and strings,
|
48
|
+
attempting to support the database's rules for string quoting. This
|
49
|
+
means it should work with SQL that Sequel didn't itself create.
|
50
|
+
However, there are corner cases that the extension doesn't handle,
|
51
|
+
such as the use of apostrophes inside quoted identifiers, and
|
52
|
+
potentially other cases of database specific SQL where the normal
|
53
|
+
string quoting rules are changed, such as the use of escape strings
|
54
|
+
on PostgreSQL (E'escape string').
|
55
|
+
|
56
|
+
* A :before_preconnect Database option has been added. This is useful
|
57
|
+
for configuring extensions added via :preconnect_extensions before
|
58
|
+
the connection takes place.
|
59
|
+
|
60
|
+
= Other Improvements
|
61
|
+
|
62
|
+
* Dataset#columns! now uses a LIMIT 0 query instead of a LIMIT 1 query
|
63
|
+
by default. This can improve performance in cases where the row
|
64
|
+
returned would be large. Some databases do not support a LIMIT 0
|
65
|
+
query, and some adapters that ship with Sequel have been updated to
|
66
|
+
continue using LIMIT 1. Custom adapters should be updated to use
|
67
|
+
LIMIT 1 if the database does not support LIMIT 0.
|
68
|
+
|
69
|
+
* The lazy_attributes plugin no longer modifies the database schema.
|
70
|
+
Previously, it could modify the database schema indirectly,
|
71
|
+
resulting in the loss of typecasting for models that were not
|
72
|
+
based on a single table or view, such as usage with the
|
73
|
+
class_table_inheritance plugin.
|
74
|
+
|
75
|
+
* Model#freeze in the composition, serialization, and
|
76
|
+
serialization_modification_detection plugins now returns self. In
|
77
|
+
addition to being more correct, this fixes usage of these plugins
|
78
|
+
with the static_cache plugin.
|
@@ -0,0 +1,47 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* On PostgreSQL 14+, Dataset#with_recursive now supports :search and
|
4
|
+
:cycle options for result ordering and cycle detection. These use
|
5
|
+
the SEARCH and CYCLE clauses added in PostgreSQL 14:
|
6
|
+
|
7
|
+
DB[:t].with_recursive(:t,
|
8
|
+
DB[:i1].where(parent_id: nil),
|
9
|
+
DB[:i1].join(:t, id: :parent_id).select_all(:i1),
|
10
|
+
search: {by: :id, type: :breadth},
|
11
|
+
cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
|
12
|
+
|
13
|
+
# WITH RECURSIVE t AS (
|
14
|
+
# SELECT * FROM i1 WHERE (parent_id IS NULL)
|
15
|
+
# UNION ALL
|
16
|
+
# (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
|
17
|
+
# )
|
18
|
+
# SEARCH BREADTH FIRST BY id SET ordercol
|
19
|
+
# CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
|
20
|
+
|
21
|
+
* On MySQL, column schema hashes now contain an :extra entry, which
|
22
|
+
contains the Extra string returned in MySQL's DESCRIBE results
|
23
|
+
for the column.
|
24
|
+
|
25
|
+
= Other Improvements
|
26
|
+
|
27
|
+
* When eager loading via the tactical_eager_loading plugin, objects
|
28
|
+
that already have an association loaded will not have it reloaded
|
29
|
+
unless the :eager_reload option is given.
|
30
|
+
|
31
|
+
* When cloning an association and using a different :class option
|
32
|
+
than the cloned association, the :class option given when cloning
|
33
|
+
will now take precedence over the :class option for the cloned
|
34
|
+
association.
|
35
|
+
|
36
|
+
* When using the mock postgres adapter, the adapter defaults to
|
37
|
+
supporting PostgreSQL 14 (previously, it defaulted to supporting
|
38
|
+
PostgreSQL 9.5).
|
39
|
+
|
40
|
+
* Sequel now avoids a method redefined warning in the lazy attributes
|
41
|
+
plugin in verbose warnings mode.
|
42
|
+
|
43
|
+
= Other
|
44
|
+
|
45
|
+
* Sequel's primary discussion forum is now GitHub Discussions. The
|
46
|
+
sequel-talk Google Group is still available for users who would
|
47
|
+
prefer to use that instead.
|