sequel 5.82.0 → 5.83.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +16 -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 +23 -8
- 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 +1 -1
- 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: 66956ac28806c0389a6c809b0aa70d2746a7aff47fd5b039d238c08f621cdbff
|
4
|
+
data.tar.gz: d2b8d3f88b13432c60e6164fda3577725300e18a0dfa03682871405d074d7ba0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 55d45f7c3cb7154f275ddf5ea3649d78a38f147abdb49f1286358d8dbec9f607e3bf43c9ebe1dc31276d28820f455fc82dcce7b4c9808b325288380acf90edf4
|
7
|
+
data.tar.gz: b8301c7880dcb598ca027d225f5be791200ac1f13f4efe5351222d351b218019351a8efa1f485d499a0650cd4213ba0a148c3f10f0a67aa0b3c04f4ee611b074
|
data/CHANGELOG
CHANGED
@@ -1,3 +1,19 @@
|
|
1
|
+
=== 5.83.0 (2024-08-01)
|
2
|
+
|
3
|
+
* Make optimistic_locking plugin not keep lock column in changed_columns after updating instance (jeremyevans) (#2196)
|
4
|
+
|
5
|
+
* Have defaults_setter plugin pass model instance to default_values callable if it has arity 1 (pedrocarmona) (#2195)
|
6
|
+
|
7
|
+
* Support string_agg extension on SQLite 3.44+ (segiddins) (#2191)
|
8
|
+
|
9
|
+
* Support schema-qualified table names when using create_table :temp with a Sequel::SQL::QualifiedIdentifier (jeremyevans) (#2185)
|
10
|
+
|
11
|
+
* Support MERGE WHEN NOT MATCHED BY SOURCE on PostgreSQL 17+ (jeremyevans)
|
12
|
+
|
13
|
+
* Add stdio_logger extension for a minimal logger that Sequel::Database can use (jeremyevans)
|
14
|
+
|
15
|
+
* Simplify Database#inspect output to database_type, host, database, and user (jeremyevans)
|
16
|
+
|
1
17
|
=== 5.82.0 (2024-07-01)
|
2
18
|
|
3
19
|
* 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.
|
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(:uri_to_options, 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
@@ -72,13 +72,16 @@ module Sequel
|
|
72
72
|
# Converts a uri to an options hash. These options are then passed
|
73
73
|
# to a newly created database object.
|
74
74
|
def self.uri_to_options(uri)
|
75
|
-
{
|
75
|
+
uri_options = {
|
76
76
|
:user => uri.user,
|
77
77
|
:password => uri.password,
|
78
78
|
:port => uri.port,
|
79
79
|
:host => uri.hostname,
|
80
80
|
:database => (m = /\/(.*)/.match(uri.path)) && (m[1])
|
81
81
|
}
|
82
|
+
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?
|
83
|
+
uri_options.to_a.each{|k,v| uri_options[k] = URI::DEFAULT_PARSER.unescape(v) if v.is_a?(String)}
|
84
|
+
uri_options
|
82
85
|
end
|
83
86
|
private_class_method :uri_to_options
|
84
87
|
|
@@ -253,15 +256,27 @@ module Sequel
|
|
253
256
|
Sequel.convert_output_timestamp(v, timezone)
|
254
257
|
end
|
255
258
|
|
256
|
-
# Returns a string representation of the
|
257
|
-
#
|
259
|
+
# Returns a string representation of the Database object, including
|
260
|
+
# the database type, host, database, and user, if present.
|
258
261
|
def inspect
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
262
|
+
s = String.new
|
263
|
+
s << "#<#{self.class}"
|
264
|
+
s << " database_type=#{database_type}" if database_type && database_type != adapter_scheme
|
265
|
+
|
266
|
+
keys = [:host, :database, :user]
|
267
|
+
opts = self.opts
|
268
|
+
if !keys.any?{|k| opts[k]} && opts[:uri]
|
269
|
+
opts = self.class.send(:uri_to_options, URI.parse(opts[:uri]))
|
263
270
|
end
|
264
|
-
|
271
|
+
|
272
|
+
keys.each do |key|
|
273
|
+
val = opts[key]
|
274
|
+
if val && val != ''
|
275
|
+
s << " #{key}=#{val}"
|
276
|
+
end
|
277
|
+
end
|
278
|
+
|
279
|
+
s << ">"
|
265
280
|
end
|
266
281
|
|
267
282
|
# 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,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 = 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.
|
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.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: 2024-
|
11
|
+
date: 2024-08-01 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
|