sequel 5.82.0 → 5.83.1
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 +20 -0
- data/bin/sequel +9 -17
- data/doc/release_notes/5.83.0.txt +56 -0
- data/lib/sequel/adapters/jdbc/derby.rb +1 -1
- data/lib/sequel/adapters/shared/db2.rb +1 -1
- data/lib/sequel/adapters/shared/mssql.rb +14 -2
- data/lib/sequel/adapters/shared/postgres.rb +42 -4
- data/lib/sequel/database/connecting.rb +1 -4
- data/lib/sequel/database/misc.rb +27 -7
- data/lib/sequel/database/schema_methods.rb +15 -1
- data/lib/sequel/dataset/sql.rb +13 -0
- data/lib/sequel/extensions/stdio_logger.rb +48 -0
- data/lib/sequel/extensions/string_agg.rb +15 -2
- data/lib/sequel/plugins/defaults_setter.rb +16 -4
- data/lib/sequel/plugins/optimistic_locking.rb +2 -0
- data/lib/sequel/version.rb +2 -2
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 892c809f3a378e80b21c29cc1c7f95f4a9fd0b47621a10d314b6177641f1ed4b
|
4
|
+
data.tar.gz: be68a7d44ecd5af4e7d7d67484ea6fc200e663b8649c250fcfc4be11de9f4c3d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: ac626e338969ddf7e99b554070b7383e3ce2e712ca2033b1c65dc2181c36636740a4e1858fa6cdb6bede1493a93aeac8299bbc26acfb2ed515a654a2957de7e4
|
7
|
+
data.tar.gz: 660dc4b4bded4eb08e357d92f093be43ac8613e62f1a8a693d1c6d81b64ddec3081147303ac10cd9153f8d62d2ce3459d8fd871412156fd5342c325ca654371c
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,23 @@
|
|
1
|
+
=== 5.83.1 (2024-08-08)
|
2
|
+
|
3
|
+
* Restore unescaping of database file paths in the sqlite and amalgalite adapters (jeremyevans) (#2201)
|
4
|
+
|
5
|
+
=== 5.83.0 (2024-08-01)
|
6
|
+
|
7
|
+
* Make optimistic_locking plugin not keep lock column in changed_columns after updating instance (jeremyevans) (#2196)
|
8
|
+
|
9
|
+
* Have defaults_setter plugin pass model instance to default_values callable if it has arity 1 (pedrocarmona) (#2195)
|
10
|
+
|
11
|
+
* Support string_agg extension on SQLite 3.44+ (segiddins) (#2191)
|
12
|
+
|
13
|
+
* Support schema-qualified table names when using create_table :temp with a Sequel::SQL::QualifiedIdentifier (jeremyevans) (#2185)
|
14
|
+
|
15
|
+
* Support MERGE WHEN NOT MATCHED BY SOURCE on PostgreSQL 17+ (jeremyevans)
|
16
|
+
|
17
|
+
* Add stdio_logger extension for a minimal logger that Sequel::Database can use (jeremyevans)
|
18
|
+
|
19
|
+
* Simplify Database#inspect output to database_type, host, database, and user (jeremyevans)
|
20
|
+
|
1
21
|
=== 5.82.0 (2024-07-01)
|
2
22
|
|
3
23
|
* Limit tactically eager loading to objects that have the same association reflection (jeremyevans) (#2181)
|
data/bin/sequel
CHANGED
@@ -18,21 +18,6 @@ load_dirs = []
|
|
18
18
|
exclusive_options = []
|
19
19
|
loggers = []
|
20
20
|
|
21
|
-
logger_class = Class.new do
|
22
|
-
def initialize(device)
|
23
|
-
@device = device
|
24
|
-
end
|
25
|
-
|
26
|
-
def debug(msg)
|
27
|
-
end
|
28
|
-
|
29
|
-
[:info, :warn, :error].each do |meth|
|
30
|
-
define_method(meth) do |msg|
|
31
|
-
@device.puts("#{Time.now.strftime('%Y-%m-%d %T')} #{meth.to_s.upcase}: #{msg}")
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
21
|
options = OptionParser.new do |opts|
|
37
22
|
opts.banner = "Sequel: The Database Toolkit for Ruby"
|
38
23
|
opts.define_head "Usage: sequel [options] <uri|path> [file]"
|
@@ -76,7 +61,7 @@ options = OptionParser.new do |opts|
|
|
76
61
|
end
|
77
62
|
|
78
63
|
opts.on("-E", "--echo", "echo SQL statements") do
|
79
|
-
loggers <<
|
64
|
+
loggers << $stdout
|
80
65
|
end
|
81
66
|
|
82
67
|
opts.on("-I", "--include dir", "specify $LOAD_PATH directory") do |v|
|
@@ -84,7 +69,9 @@ options = OptionParser.new do |opts|
|
|
84
69
|
end
|
85
70
|
|
86
71
|
opts.on("-l", "--log logfile", "log SQL statements to log file") do |v|
|
87
|
-
|
72
|
+
file = File.open(v, 'a')
|
73
|
+
file.sync = true
|
74
|
+
loggers << file
|
88
75
|
end
|
89
76
|
|
90
77
|
opts.on("-L", "--load-dir DIR", "loads all *.rb under specifed directory") do |v|
|
@@ -168,6 +155,11 @@ begin
|
|
168
155
|
end
|
169
156
|
end
|
170
157
|
|
158
|
+
unless loggers.empty?
|
159
|
+
Sequel.extension :stdio_logger
|
160
|
+
loggers.map!{|io| Sequel::StdioLogger.new(io)}
|
161
|
+
end
|
162
|
+
|
171
163
|
DB = connect_proc[db]
|
172
164
|
load_dirs.each{|d| d.is_a?(Array) ? require(d.first) : Dir["#{d}/**/*.rb"].each{|f| load(f)}}
|
173
165
|
if migrate_dir
|
@@ -0,0 +1,56 @@
|
|
1
|
+
= New Features
|
2
|
+
|
3
|
+
* MERGE WHEN NOT MATCHED BY SOURCE is now supported when using
|
4
|
+
PostgreSQL 17+. You can use this SQL syntax via the following
|
5
|
+
Dataset methods:
|
6
|
+
|
7
|
+
* merge_delete_when_not_matched_by_source
|
8
|
+
* merge_update_when_not_matched_by_source
|
9
|
+
* merge_do_nothing_when_not_matched_by_source
|
10
|
+
|
11
|
+
These are similar to the existing merge_delete, merge_update,
|
12
|
+
and merge_do_nothing_when_matched, except they use
|
13
|
+
WHEN NOT MATCHED BY SOURCE instead of WHEN MATCHED.
|
14
|
+
|
15
|
+
* An stdio_logger extension has been added. This adds the
|
16
|
+
Sequel::StdioLogger class, which is a minimal logger implementation
|
17
|
+
that is compatible for usage with Sequel::Database. Example:
|
18
|
+
|
19
|
+
Sequel.extension :stdio_logger
|
20
|
+
DB.loggers << Sequel::StdioLogger.new($stdout)
|
21
|
+
|
22
|
+
= Other Improvements
|
23
|
+
|
24
|
+
* Database#inspect now only displays the database type, host, database
|
25
|
+
name, and user. In addition to being easier to read, this also
|
26
|
+
prevents displaying the password, enhancing security.
|
27
|
+
|
28
|
+
* The string_agg extension now supports SQLite 3.44+.
|
29
|
+
|
30
|
+
* The defaults_setter plugin now passes the model instance to a
|
31
|
+
default_values proc if the proc has arity 1. This allows default
|
32
|
+
values to depend on model instance state.
|
33
|
+
|
34
|
+
* The optimistic_locking plugin no longer adds the lock column to
|
35
|
+
changed_columns after updating the model instance.
|
36
|
+
|
37
|
+
* Database#create_temp with :temp option and an
|
38
|
+
SQL::QualifiedIdentifier table name will now attempt to create a
|
39
|
+
schema qualified table. Note that schema qualified temporary
|
40
|
+
tables are not supported by many (any?) databases, but this
|
41
|
+
change prevents the CREATE TABLE statement from succeeding with
|
42
|
+
an unexpected table name.
|
43
|
+
|
44
|
+
= Backwards Compatibility
|
45
|
+
|
46
|
+
* The Database.uri_to_options private class method now handles
|
47
|
+
conversion of URI parameters to options. Previously, this was
|
48
|
+
handled by callers of this method (change reverted in 5.83.1).
|
49
|
+
|
50
|
+
* The _merge_matched_sql and _merge_not_matched_sql private Dataset
|
51
|
+
methods in PostgreSQL have been replaced with
|
52
|
+
_merge_do_nothing_sql.
|
53
|
+
|
54
|
+
* An unnecessary space in submitted SQL has been removed when using
|
55
|
+
MERGE INSERT on PostgreSQL. This should only affect your code if
|
56
|
+
you are explicitly checking the produced SQL.
|
@@ -115,7 +115,7 @@ module Sequel
|
|
115
115
|
# Temporary table creation on Derby uses DECLARE instead of CREATE.
|
116
116
|
def create_table_prefix_sql(name, options)
|
117
117
|
if options[:temp]
|
118
|
-
"DECLARE GLOBAL TEMPORARY TABLE #{
|
118
|
+
"DECLARE GLOBAL TEMPORARY TABLE #{create_table_table_name_sql(name, options)}"
|
119
119
|
else
|
120
120
|
super
|
121
121
|
end
|
@@ -198,7 +198,7 @@ module Sequel
|
|
198
198
|
# http://www.ibm.com/developerworks/data/library/techarticle/dm-0912globaltemptable/
|
199
199
|
def create_table_prefix_sql(name, options)
|
200
200
|
if options[:temp]
|
201
|
-
"DECLARE GLOBAL TEMPORARY TABLE #{
|
201
|
+
"DECLARE GLOBAL TEMPORARY TABLE #{create_table_table_name_sql(name, options)}"
|
202
202
|
else
|
203
203
|
super
|
204
204
|
end
|
@@ -379,9 +379,21 @@ module Sequel
|
|
379
379
|
# a regular and temporary table, with temporary table names starting with
|
380
380
|
# a #.
|
381
381
|
def create_table_prefix_sql(name, options)
|
382
|
-
"CREATE TABLE #{
|
382
|
+
"CREATE TABLE #{create_table_table_name_sql(name, options)}"
|
383
383
|
end
|
384
|
-
|
384
|
+
|
385
|
+
# The SQL to use for the table name for a temporary table.
|
386
|
+
def create_table_temp_table_name_sql(name, _options)
|
387
|
+
case name
|
388
|
+
when String, Symbol
|
389
|
+
"##{name}"
|
390
|
+
when SQL::Identifier
|
391
|
+
"##{name.value}"
|
392
|
+
else
|
393
|
+
raise Error, "temporary table names must be strings, symbols, or Sequel::SQL::Identifier instances on Microsoft SQL Server"
|
394
|
+
end
|
395
|
+
end
|
396
|
+
|
385
397
|
# MSSQL doesn't support CREATE TABLE AS, it only supports SELECT INTO.
|
386
398
|
# Emulating CREATE TABLE AS using SELECT INTO is only possible if a dataset
|
387
399
|
# is given as the argument, it can't work with a string, so raise an
|
@@ -1429,7 +1429,7 @@ module Sequel
|
|
1429
1429
|
'UNLOGGED '
|
1430
1430
|
end
|
1431
1431
|
|
1432
|
-
"CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{
|
1432
|
+
"CREATE #{prefix_sql}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}"
|
1433
1433
|
end
|
1434
1434
|
|
1435
1435
|
# SQL for creating a table with PostgreSQL specific options
|
@@ -2068,6 +2068,19 @@ module Sequel
|
|
2068
2068
|
end
|
2069
2069
|
end
|
2070
2070
|
|
2071
|
+
# Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN DELETE clause added to the
|
2072
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2073
|
+
# use it as additional conditions for the match.
|
2074
|
+
#
|
2075
|
+
# merge_delete_not_matched_by_source
|
2076
|
+
# # WHEN NOT MATCHED BY SOURCE THEN DELETE
|
2077
|
+
#
|
2078
|
+
# merge_delete_not_matched_by_source{a > 30}
|
2079
|
+
# # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN DELETE
|
2080
|
+
def merge_delete_when_not_matched_by_source(&block)
|
2081
|
+
_merge_when(:type=>:delete_not_matched_by_source, &block)
|
2082
|
+
end
|
2083
|
+
|
2071
2084
|
# Return a dataset with a WHEN MATCHED THEN DO NOTHING clause added to the
|
2072
2085
|
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2073
2086
|
# use it as additional conditions for the match.
|
@@ -2094,6 +2107,19 @@ module Sequel
|
|
2094
2107
|
_merge_when(:type=>:not_matched, &block)
|
2095
2108
|
end
|
2096
2109
|
|
2110
|
+
# Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN DO NOTHING clause added to the
|
2111
|
+
# MERGE BY SOURCE statement. If a block is passed, treat it as a virtual row and
|
2112
|
+
# use it as additional conditions for the match.
|
2113
|
+
#
|
2114
|
+
# merge_do_nothing_when_not_matched_by_source
|
2115
|
+
# # WHEN NOT MATCHED BY SOURCE THEN DO NOTHING
|
2116
|
+
#
|
2117
|
+
# merge_do_nothing_when_not_matched_by_source{a > 30}
|
2118
|
+
# # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN DO NOTHING
|
2119
|
+
def merge_do_nothing_when_not_matched_by_source(&block)
|
2120
|
+
_merge_when(:type=>:not_matched_by_source, &block)
|
2121
|
+
end
|
2122
|
+
|
2097
2123
|
# Support OVERRIDING USER|SYSTEM VALUE for MERGE INSERT.
|
2098
2124
|
def merge_insert(*values, &block)
|
2099
2125
|
h = {:type=>:insert, :values=>values}
|
@@ -2103,6 +2129,19 @@ module Sequel
|
|
2103
2129
|
_merge_when(h, &block)
|
2104
2130
|
end
|
2105
2131
|
|
2132
|
+
# Return a dataset with a WHEN NOT MATCHED BY SOURCE THEN UPDATE clause added to the
|
2133
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
2134
|
+
# use it as additional conditions for the match.
|
2135
|
+
#
|
2136
|
+
# merge_update_not_matched_by_source(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
|
2137
|
+
# # WHEN NOT MATCHED BY SOURCE THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
|
2138
|
+
#
|
2139
|
+
# merge_update_not_matched_by_source(i1: :i2){a > 30}
|
2140
|
+
# # WHEN NOT MATCHED BY SOURCE AND (a > 30) THEN UPDATE SET i1 = i2
|
2141
|
+
def merge_update_when_not_matched_by_source(values, &block)
|
2142
|
+
_merge_when(:type=>:update_not_matched_by_source, :values=>values, &block)
|
2143
|
+
end
|
2144
|
+
|
2106
2145
|
# Use OVERRIDING USER VALUE for INSERT statements, so that identity columns
|
2107
2146
|
# always use the user supplied value, and an error is not raised for identity
|
2108
2147
|
# columns that are GENERATED ALWAYS.
|
@@ -2296,7 +2335,7 @@ module Sequel
|
|
2296
2335
|
|
2297
2336
|
# Append the INSERT sql used in a MERGE
|
2298
2337
|
def _merge_insert_sql(sql, data)
|
2299
|
-
sql << " THEN INSERT
|
2338
|
+
sql << " THEN INSERT"
|
2300
2339
|
columns, values = _parse_insert_sql_args(data[:values])
|
2301
2340
|
_insert_columns_sql(sql, columns)
|
2302
2341
|
if override = data[:override]
|
@@ -2305,10 +2344,9 @@ module Sequel
|
|
2305
2344
|
_insert_values_sql(sql, values)
|
2306
2345
|
end
|
2307
2346
|
|
2308
|
-
def
|
2347
|
+
def _merge_do_nothing_sql(sql, data)
|
2309
2348
|
sql << " THEN DO NOTHING"
|
2310
2349
|
end
|
2311
|
-
alias _merge_not_matched_sql _merge_matched_sql
|
2312
2350
|
|
2313
2351
|
# Support MERGE RETURNING on PostgreSQL 17+.
|
2314
2352
|
def _merge_when_sql(sql)
|
@@ -34,10 +34,7 @@ module Sequel
|
|
34
34
|
uri = URI.parse(conn_string)
|
35
35
|
scheme = uri.scheme
|
36
36
|
c = adapter_class(scheme)
|
37
|
-
|
38
|
-
uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
|
39
|
-
uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
|
40
|
-
opts = uri_options.merge(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
|
37
|
+
opts = c.send(:options_from_uri, uri).merge!(opts).merge!(:orig_opts=>opts.dup, :uri=>conn_string, :adapter=>scheme)
|
41
38
|
end
|
42
39
|
when Hash
|
43
40
|
opts = conn_string.merge(opts)
|
data/lib/sequel/database/misc.rb
CHANGED
@@ -82,6 +82,14 @@ module Sequel
|
|
82
82
|
end
|
83
83
|
private_class_method :uri_to_options
|
84
84
|
|
85
|
+
def self.options_from_uri(uri)
|
86
|
+
uri_options = uri_to_options(uri)
|
87
|
+
uri.query.split('&').map{|s| s.split('=')}.each{|k,v| uri_options[k.to_sym] = v if k && !k.empty?} unless uri.query.to_s.strip.empty?
|
88
|
+
uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
|
89
|
+
uri_options
|
90
|
+
end
|
91
|
+
private_class_method :options_from_uri
|
92
|
+
|
85
93
|
# The options hash for this database
|
86
94
|
attr_reader :opts
|
87
95
|
|
@@ -253,15 +261,27 @@ module Sequel
|
|
253
261
|
Sequel.convert_output_timestamp(v, timezone)
|
254
262
|
end
|
255
263
|
|
256
|
-
# Returns a string representation of the
|
257
|
-
#
|
264
|
+
# Returns a string representation of the Database object, including
|
265
|
+
# the database type, host, database, and user, if present.
|
258
266
|
def inspect
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
267
|
+
s = String.new
|
268
|
+
s << "#<#{self.class}"
|
269
|
+
s << " database_type=#{database_type}" if database_type && database_type != adapter_scheme
|
270
|
+
|
271
|
+
keys = [:host, :database, :user]
|
272
|
+
opts = self.opts
|
273
|
+
if !keys.any?{|k| opts[k]} && opts[:uri]
|
274
|
+
opts = self.class.send(:options_from_uri, URI.parse(opts[:uri]))
|
275
|
+
end
|
276
|
+
|
277
|
+
keys.each do |key|
|
278
|
+
val = opts[key]
|
279
|
+
if val && val != ''
|
280
|
+
s << " #{key}=#{val}"
|
281
|
+
end
|
263
282
|
end
|
264
|
-
|
283
|
+
|
284
|
+
s << ">"
|
265
285
|
end
|
266
286
|
|
267
287
|
# Proxy the literal call to the dataset.
|
@@ -775,7 +775,21 @@ module Sequel
|
|
775
775
|
|
776
776
|
# SQL fragment for initial part of CREATE TABLE statement
|
777
777
|
def create_table_prefix_sql(name, options)
|
778
|
-
"CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{
|
778
|
+
"CREATE #{temporary_table_sql if options[:temp]}TABLE#{' IF NOT EXISTS' if options[:if_not_exists]} #{create_table_table_name_sql(name, options)}"
|
779
|
+
end
|
780
|
+
|
781
|
+
# The SQL to use for a table name when creating a table.
|
782
|
+
# Use of the :temp option can result in different SQL,
|
783
|
+
# because the rules for temp table naming can differ
|
784
|
+
# between databases, and temp tables should not use the
|
785
|
+
# default_schema.
|
786
|
+
def create_table_table_name_sql(name, options)
|
787
|
+
options[:temp] ? create_table_temp_table_name_sql(name, options) : quote_schema_table(name)
|
788
|
+
end
|
789
|
+
|
790
|
+
# The SQL to use for the table name for a temporary table.
|
791
|
+
def create_table_temp_table_name_sql(name, _options)
|
792
|
+
name.is_a?(String) ? quote_identifier(name) : literal(name)
|
779
793
|
end
|
780
794
|
|
781
795
|
# SQL fragment for initial part of CREATE VIEW statement
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -901,18 +901,31 @@ module Sequel
|
|
901
901
|
MERGE_TYPE_SQL = {
|
902
902
|
:insert => ' WHEN NOT MATCHED',
|
903
903
|
:delete => ' WHEN MATCHED',
|
904
|
+
:delete_not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
|
904
905
|
:update => ' WHEN MATCHED',
|
906
|
+
:update_not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
|
905
907
|
:matched => ' WHEN MATCHED',
|
906
908
|
:not_matched => ' WHEN NOT MATCHED',
|
909
|
+
:not_matched_by_source => ' WHEN NOT MATCHED BY SOURCE',
|
907
910
|
}.freeze
|
908
911
|
private_constant :MERGE_TYPE_SQL
|
909
912
|
|
913
|
+
MERGE_NORMALIZE_TYPE_MAP = {
|
914
|
+
:delete_not_matched_by_source => :delete,
|
915
|
+
:update_not_matched_by_source => :update,
|
916
|
+
:matched => :do_nothing,
|
917
|
+
:not_matched => :do_nothing,
|
918
|
+
:not_matched_by_source => :do_nothing,
|
919
|
+
}.freeze
|
920
|
+
private_constant :MERGE_NORMALIZE_TYPE_MAP
|
921
|
+
|
910
922
|
# Add the WHEN clauses to the MERGE SQL
|
911
923
|
def _merge_when_sql(sql)
|
912
924
|
raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
|
913
925
|
merge_when.each do |data|
|
914
926
|
type = data[:type]
|
915
927
|
sql << MERGE_TYPE_SQL[type]
|
928
|
+
type = MERGE_NORMALIZE_TYPE_MAP[type] || type
|
916
929
|
_merge_when_conditions_sql(sql, data)
|
917
930
|
send(:"_merge_#{type}_sql", sql, data)
|
918
931
|
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The stdio_logger extension exposes a Sequel::StdioLogger class that
|
4
|
+
# can be used for logging with Sequel, as a minimal alternative to
|
5
|
+
# the logger library. It exposes debug/info/warn/error methods for the
|
6
|
+
# different warning levels. The debug method is a no-op, so that setting
|
7
|
+
# the Database sql_log_level to debug will result in no output for normal
|
8
|
+
# queries. The info/warn/error methods log the current time, log level,
|
9
|
+
# and the given message.
|
10
|
+
#
|
11
|
+
# To use this extension:
|
12
|
+
#
|
13
|
+
# Sequel.extension :stdio_logger
|
14
|
+
#
|
15
|
+
# Then you you can use Sequel::StdioLogger to wrap IO objects that you
|
16
|
+
# would like Sequel to log to:
|
17
|
+
#
|
18
|
+
# DB.loggers << Sequel::StdioLogger.new($stdout)
|
19
|
+
#
|
20
|
+
# log_file = File.open("db_queries.log", 'a')
|
21
|
+
# log_file.sync = true
|
22
|
+
# DB.loggers << Sequel::StdioLogger.new(log_file)
|
23
|
+
#
|
24
|
+
# This is implemented as a global extension instead of a Database extension
|
25
|
+
# because Database loggers must be set before Database extensions are loaded.
|
26
|
+
#
|
27
|
+
# Related module: Sequel::StdioLogger
|
28
|
+
|
29
|
+
#
|
30
|
+
module Sequel
|
31
|
+
class StdioLogger
|
32
|
+
def initialize(device)
|
33
|
+
@device = device
|
34
|
+
end
|
35
|
+
|
36
|
+
# Do not log debug messages. This is so setting the Database
|
37
|
+
# sql_log_level to debug will result in no output.
|
38
|
+
def debug(msg)
|
39
|
+
end
|
40
|
+
|
41
|
+
[:info, :warn, :error].each do |meth|
|
42
|
+
define_method(meth) do |msg|
|
43
|
+
@device.write("#{Time.now.strftime('%F %T')} #{meth.to_s.upcase}: #{msg}\n")
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
@@ -54,6 +54,7 @@
|
|
54
54
|
# * MySQL
|
55
55
|
# * HSQLDB
|
56
56
|
# * H2
|
57
|
+
# * SQLite 3.44+ (distinct only works when separator is ',')
|
57
58
|
#
|
58
59
|
# Related module: Sequel::SQL::StringAgg
|
59
60
|
|
@@ -94,6 +95,19 @@ module Sequel
|
|
94
95
|
distinct = sa.is_distinct?
|
95
96
|
|
96
97
|
case db_type = db.database_type
|
98
|
+
when :sqlite
|
99
|
+
raise Error, "string_agg(DISTINCT) is not supported with a non-comma separator on #{db.database_type}" if distinct && separator != ","
|
100
|
+
|
101
|
+
args = [expr]
|
102
|
+
args << separator unless distinct
|
103
|
+
f = Function.new(:group_concat, *args)
|
104
|
+
if order
|
105
|
+
f = f.order(*order)
|
106
|
+
end
|
107
|
+
if distinct
|
108
|
+
f = f.distinct
|
109
|
+
end
|
110
|
+
literal_append(sql, f)
|
97
111
|
when :postgres, :sqlanywhere
|
98
112
|
f = Function.new(db_type == :postgres ? :string_agg : :list, expr, separator)
|
99
113
|
if order
|
@@ -151,7 +165,7 @@ module Sequel
|
|
151
165
|
freeze
|
152
166
|
end
|
153
167
|
|
154
|
-
# Whether the current expression uses distinct expressions
|
168
|
+
# Whether the current expression uses distinct expressions
|
155
169
|
def is_distinct?
|
156
170
|
@distinct == true
|
157
171
|
end
|
@@ -178,4 +192,3 @@ module Sequel
|
|
178
192
|
|
179
193
|
Dataset.register_extension(:string_agg, SQL::StringAgg::DatasetMethods)
|
180
194
|
end
|
181
|
-
|
@@ -26,6 +26,12 @@ module Sequel
|
|
26
26
|
# Album.default_values[:a] = lambda{Date.today}
|
27
27
|
# Album.new.a # => Date.today
|
28
28
|
#
|
29
|
+
# If the proc accepts a single argument, it is passed the instance, allowing
|
30
|
+
# default values to depend on instance-specific state:
|
31
|
+
#
|
32
|
+
# Album.default_values[:a] = lambda{|album| album.b + 1}
|
33
|
+
# Album.new(b: 10).a # => 11
|
34
|
+
#
|
29
35
|
# By default, default values returned are not cached:
|
30
36
|
#
|
31
37
|
# Album.new.a.equal?(Album.new.a) # => false
|
@@ -43,13 +49,13 @@ module Sequel
|
|
43
49
|
# album.values # => {}
|
44
50
|
# album.a
|
45
51
|
# album.values # => {:a => Date.today}
|
46
|
-
#
|
52
|
+
#
|
47
53
|
# Usage:
|
48
54
|
#
|
49
55
|
# # Make all model subclass instances set defaults (called before loading subclasses)
|
50
56
|
# Sequel::Model.plugin :defaults_setter
|
51
57
|
#
|
52
|
-
# # Make the Album class set defaults
|
58
|
+
# # Make the Album class set defaults
|
53
59
|
# Album.plugin :defaults_setter
|
54
60
|
module DefaultsSetter
|
55
61
|
# Set the default values based on the model schema. Options:
|
@@ -76,7 +82,7 @@ module Sequel
|
|
76
82
|
def cache_default_values?
|
77
83
|
@cache_default_values
|
78
84
|
end
|
79
|
-
|
85
|
+
|
80
86
|
# Freeze default values when freezing model class
|
81
87
|
def freeze
|
82
88
|
@default_values.freeze
|
@@ -133,7 +139,13 @@ module Sequel
|
|
133
139
|
def [](k)
|
134
140
|
if new? && !values.has_key?(k)
|
135
141
|
v = model.default_values.fetch(k){return}
|
136
|
-
|
142
|
+
if v.respond_to?(:call)
|
143
|
+
v = if v.respond_to?(:arity) && v.arity == 1
|
144
|
+
v.call(self)
|
145
|
+
else
|
146
|
+
v.call
|
147
|
+
end
|
148
|
+
end
|
137
149
|
values[k] = v if model.cache_default_values?
|
138
150
|
v
|
139
151
|
else
|
data/lib/sequel/version.rb
CHANGED
@@ -6,11 +6,11 @@ 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 = 83
|
10
10
|
|
11
11
|
# The tiny version of Sequel. Usually 0, only bumped for bugfix
|
12
12
|
# releases that fix regressions from previous versions.
|
13
|
-
TINY =
|
13
|
+
TINY = 1
|
14
14
|
|
15
15
|
# The version of Sequel you are using, as a string (e.g. "2.11.0")
|
16
16
|
VERSION = [MAJOR, MINOR, TINY].join('.').freeze
|
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.83.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Jeremy Evans
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2024-
|
11
|
+
date: 2024-08-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bigdecimal
|
@@ -230,6 +230,7 @@ extra_rdoc_files:
|
|
230
230
|
- doc/release_notes/5.80.0.txt
|
231
231
|
- doc/release_notes/5.81.0.txt
|
232
232
|
- doc/release_notes/5.82.0.txt
|
233
|
+
- doc/release_notes/5.83.0.txt
|
233
234
|
- doc/release_notes/5.9.0.txt
|
234
235
|
files:
|
235
236
|
- CHANGELOG
|
@@ -340,6 +341,7 @@ files:
|
|
340
341
|
- doc/release_notes/5.80.0.txt
|
341
342
|
- doc/release_notes/5.81.0.txt
|
342
343
|
- doc/release_notes/5.82.0.txt
|
344
|
+
- doc/release_notes/5.83.0.txt
|
343
345
|
- doc/release_notes/5.9.0.txt
|
344
346
|
- doc/schema_modification.rdoc
|
345
347
|
- doc/security.rdoc
|
@@ -519,6 +521,7 @@ files:
|
|
519
521
|
- lib/sequel/extensions/sql_expr.rb
|
520
522
|
- lib/sequel/extensions/sql_log_normalizer.rb
|
521
523
|
- lib/sequel/extensions/sqlite_json_ops.rb
|
524
|
+
- lib/sequel/extensions/stdio_logger.rb
|
522
525
|
- lib/sequel/extensions/string_agg.rb
|
523
526
|
- lib/sequel/extensions/string_date_time.rb
|
524
527
|
- lib/sequel/extensions/symbol_aref.rb
|
@@ -674,7 +677,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
674
677
|
- !ruby/object:Gem::Version
|
675
678
|
version: '0'
|
676
679
|
requirements: []
|
677
|
-
rubygems_version: 3.5.
|
680
|
+
rubygems_version: 3.5.11
|
678
681
|
signing_key:
|
679
682
|
specification_version: 4
|
680
683
|
summary: The Database Toolkit for Ruby
|