sequel 5.78.0 → 5.80.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 2d821d7a1270874ced864c3a4bc66b519b3b5e3c050d2792e8dfc907a677e36a
4
- data.tar.gz: 67390870d998d266a1a07d44e72dc85e357226e7f5dd215ca695ab9037840638
3
+ metadata.gz: ddf0e8e9009f39ca7f6dffca8aef10ef4fbdf22a8c5002def5ecea4042f38763
4
+ data.tar.gz: b9f3cb97273dd9885e8002a64e17bddd0a6db2f932d1c68ce97844e2ac843096
5
5
  SHA512:
6
- metadata.gz: 50b737d3a944ec7d1df62dce2df68b6558e912c0d333d0cdf1d32bccd18ecb5dad8439e73f8c5497b4e627ea8caee34f131dee5a57217a479acfab95287f306d
7
- data.tar.gz: 3b9631235606432b44cc57aa624c6509dd14833fdc0e5e900945232c3593904a249dbe3106aef1e4e863cf939517cb1abbd16ca179eec6e5b1de90b815de941f
6
+ metadata.gz: 7387d403f32cb492abe6fad35d6a87a4ac274fc3602aee0f8afba38d9feb30578ee774de2b168a88d86ab740a41fa2a62333f695c0d2fa507fde775943917daa
7
+ data.tar.gz: 11b1c1e9daf92153585f158358ee85078b83decfca8bcf4569fa90e59335dfd123a0d8fddf8f6e58e166801029de23937279dc3aa746d0d3878a34dac90ce636
data/CHANGELOG CHANGED
@@ -1,3 +1,19 @@
1
+ === 5.80.0 (2024-05-01)
2
+
3
+ * Support Dataset#skip_locked on MariaDB 10.6+ (simi) (#2150)
4
+
5
+ * Avoid allocating datasets in cases where the returned dataset would be the same as the receiver (jeremyevans)
6
+
7
+ * Add provenance dataset extension, which includes comments in queries showing how and where the dataset was built (jeremyevans)
8
+
9
+ === 5.79.0 (2024-04-01)
10
+
11
+ * Support create_or_replace_view with :materialized option on PostgreSQL (nashby) (#2144)
12
+
13
+ * Support :unlogged_tables_default Database option on Postgres for making created tables unlogged by default (jeremyevans) (#2134)
14
+
15
+ * Add Dataset#select_prepend for prepending to the current selected columns (jeremyevans) (#2139)
16
+
1
17
  === 5.78.0 (2024-03-01)
2
18
 
3
19
  * Support SQLite 3.45+ jsonb functions in the sqlite_json_ops extension (jeremyevans) (#2133)
data/README.rdoc CHANGED
@@ -22,10 +22,10 @@ RDoc Documentation :: https://sequel.jeremyevans.net/rdoc
22
22
  Source Code :: https://github.com/jeremyevans/sequel
23
23
  Bug tracking (GitHub Issues) :: https://github.com/jeremyevans/sequel/issues
24
24
  Discussion Forum (GitHub Discussions) :: https://github.com/jeremyevans/sequel/discussions
25
- Alternate Discussion Forum (sequel-talk Google Group) :: http://groups.google.com/group/sequel-talk
25
+ Archived Discussion Forum (sequel-talk Google Group) :: https://www.mail-archive.com/sequel-talk@googlegroups.com/
26
26
 
27
27
  If you have questions about how to use Sequel, please ask on
28
- GitHub Discussions or the sequel-talk Google Group.
28
+ GitHub Discussions.
29
29
  Only use the the bug tracker to report
30
30
  bugs in Sequel, not to ask for help on using Sequel.
31
31
 
@@ -368,10 +368,12 @@ Like +order+, +select+ overrides an existing selection:
368
368
  posts.select(:stamp).select(:name)
369
369
  # SELECT name FROM posts
370
370
 
371
- As you might expect, there is an +order_append+ equivalent for +select+ called +select_append+:
371
+ As you might expect, there are +order_append+ and +order_prepend+ equivalents for +select+ called +select_append+ and +select_prepend+:
372
372
 
373
373
  posts.select(:stamp).select_append(:name)
374
374
  # SELECT stamp, name FROM posts
375
+ posts.select(:stamp).select_prepend(:name)
376
+ # SELECT name, stamp FROM posts
375
377
 
376
378
  === Deleting Records
377
379
 
@@ -65,7 +65,7 @@ Most Dataset methods that users will use can be broken down into two types:
65
65
 
66
66
  Most dataset methods fall into this category, which can be further broken down by the clause they affect:
67
67
 
68
- SELECT:: select, select_all, select_append, select_group, select_more
68
+ SELECT:: select, select_all, select_append, select_group, select_more, select_prepend
69
69
  FROM:: from, from_self
70
70
  JOIN:: join, left_join, right_join, full_join, natural_join, natural_left_join, natural_right_join, natural_full_join, cross_join, inner_join, left_outer_join, right_outer_join, full_outer_join, join_table
71
71
  WHERE:: where, filter, exclude, or, grep, invert, unfiltered
@@ -346,6 +346,8 @@ The following additional options are supported:
346
346
  separated by commas (for use via a URL: <tt>postgres:///?search_path=schema1,schema2</tt>), or it
347
347
  can be an array of strings (for use via an option:
348
348
  <tt>Sequel.postgres(search_path: ['schema1', 'schema2'])</tt>).
349
+ :unlogged_tables_default :: Set to true to use UNLOGGED by default for created tables, for potentially better performance
350
+ when data integrity is not important.
349
351
  :use_iso_date_format :: This can be set to false to not force the ISO date format. Sequel forces
350
352
  it by default to allow for an optimization.
351
353
 
data/doc/querying.rdoc CHANGED
@@ -624,11 +624,16 @@ Like +order+, +select+ replaces the existing selected columns:
624
624
  Artist.select(:id).select(:name)
625
625
  # SELECT name FROM artists
626
626
 
627
- To add to the existing selected columns, use +select_append+:
627
+ To append to the existing selected columns, use +select_append+:
628
628
 
629
629
  Artist.select(:id).select_append(:name)
630
630
  # SELECT id, name FROM artists
631
631
 
632
+ To prepend to the existing selected columns, use +select_prepend+:
633
+
634
+ Artist.select(:id).select_prepend(:name)
635
+ # SELECT name, id FROM artists
636
+
632
637
  To remove specifically selected columns, and default back to all
633
638
  columns, use +select_all+:
634
639
 
@@ -0,0 +1,28 @@
1
+ = New Features
2
+
3
+ * Dataset#select_prepend has been added for prepending to the
4
+ currently selected columns:
5
+
6
+ DB[:table].select_prepend(:column)
7
+ # SELECT column, table.*
8
+
9
+ As not all databases support "SELECT column, *", select_prepend
10
+ qualifies wildcard selections to all tables referenced in the
11
+ query.
12
+
13
+ The only reason to use select_prepend is if you want the hashes
14
+ returned by Sequel to be in a specific order. Otherwise, it is
15
+ better to use select_append.
16
+
17
+ * On PostgreSQL, Sequel now supports an :unlogged_tables_default
18
+ Database option, which will default created tables to be UNLOGGED.
19
+ This can be useful to speedup testing in some cases, but it should
20
+ never be used in cases where data integrity is important.
21
+
22
+ = Other Improvements
23
+
24
+ * On PostgreSQL, Database#create_or_replace_view now supports the
25
+ :materialized option. This allows for dropping an existing
26
+ materialized view and creating a new one with the same name
27
+ (PostgreSQL does not have native support for replacing materialized
28
+ views).
@@ -0,0 +1,40 @@
1
+ = New Features
2
+
3
+ * A provenance dataset extension has been added. This extension makes
4
+ SQL queries include a comment describing how the dataset was built.
5
+ This can make debugging complex cases significantly easier. Here's
6
+ a simple example:
7
+
8
+ DB.extension :provenance
9
+
10
+ DB[:table].
11
+ select(:a).
12
+ where{b > 10}.
13
+ order(:c).
14
+ limit(10)
15
+ # SQL:
16
+ # SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
17
+ # -- Dataset Provenance
18
+ # -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
19
+ # -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
20
+ # -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
21
+ # -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
22
+ # -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
23
+
24
+ With the above example, it's obvious how the dataset is created, but
25
+ but in real applications, where datasets can be built from multiple
26
+ files, seeing where each dataset clone was made can be helpful.
27
+
28
+ The source listed will skip locations in the Ruby standard library
29
+ as well as Sequel itself. Other locations can be skipped by
30
+ providing a Database :provenance_caller_ignore Regexp option:
31
+
32
+ DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
33
+
34
+ = Other Improvements
35
+
36
+ * For dataset methods where Sequel can determine that the return
37
+ value would be equivalent to the receiver, Sequel now returns the
38
+ receiver. This reduces the number of dataset allocations.
39
+
40
+ * Sequel now supports Dataset#skip_locked on MariaDB 10.6+.
@@ -926,9 +926,9 @@ module Sequel
926
926
  (type == :insert && db.mariadb? && db.adapter_scheme != :jdbc) ? (db.server_version >= 100500) : false
927
927
  end
928
928
 
929
- # MySQL 8+ supports SKIP LOCKED.
929
+ # MySQL 8+ and MariaDB 10.6+ support SKIP LOCKED.
930
930
  def supports_skip_locked?
931
- !db.mariadb? && db.server_version >= 80000
931
+ db.server_version >= (db.mariadb? ? 100600 : 80000)
932
932
  end
933
933
 
934
934
  # Check the database setting for whether fractional timestamps
@@ -1425,7 +1425,7 @@ module Sequel
1425
1425
  elsif options[:foreign]
1426
1426
  raise(Error, "can't provide both :foreign and :unlogged to create_table") if options[:unlogged]
1427
1427
  'FOREIGN '
1428
- elsif options[:unlogged]
1428
+ elsif options.fetch(:unlogged){typecast_value_boolean(@opts[:unlogged_tables_default])}
1429
1429
  'UNLOGGED '
1430
1430
  end
1431
1431
 
@@ -252,10 +252,10 @@ module Sequel
252
252
  # For databases where replacing a view is not natively supported, support
253
253
  # is emulated by dropping a view with the same name before creating the view.
254
254
  def create_or_replace_view(name, source, options = OPTS)
255
- if supports_create_or_replace_view?
255
+ if supports_create_or_replace_view? && !options[:materialized]
256
256
  options = options.merge(:replace=>true)
257
257
  else
258
- swallow_database_error{drop_view(name)}
258
+ swallow_database_error{drop_view(name, options)}
259
259
  end
260
260
 
261
261
  create_view(name, source, options)
@@ -21,7 +21,7 @@ module Sequel
21
21
  where exclude exclude_having having
22
22
  distinct grep group group_and_count group_append
23
23
  limit offset order order_append order_prepend reverse
24
- select select_all select_append select_group server
24
+ select select_all select_append select_group select_prepend server
25
25
  METHS
26
26
 
27
27
  # Define a method in the module
@@ -253,6 +253,7 @@ module Sequel
253
253
  # Remove the splitting of results into subhashes, and all metadata
254
254
  # related to the current graph (if any).
255
255
  def ungraphed
256
+ return self unless opts[:graph]
256
257
  clone(:graph=>nil)
257
258
  end
258
259
 
@@ -43,7 +43,7 @@ module Sequel
43
43
  add_graph_aliases distinct except exclude exclude_having
44
44
  filter for_update from from_self graph grep group group_and_count group_append group_by having intersect invert
45
45
  limit lock_style naked offset or order order_append order_by order_more order_prepend qualify
46
- reverse reverse_order select select_all select_append select_group select_more server
46
+ reverse reverse_order select select_all select_append select_group select_more select_prepend server
47
47
  set_graph_aliases unfiltered ungraphed ungrouped union
48
48
  unlimited unordered where with with_recursive with_sql
49
49
  METHS
@@ -129,6 +129,7 @@ module Sequel
129
129
  def distinct(*args, &block)
130
130
  virtual_row_columns(args, block)
131
131
  if args.empty?
132
+ return self if opts[:distinct] == EMPTY_ARRAY
132
133
  cached_dataset(:_distinct_ds){clone(:distinct => EMPTY_ARRAY)}
133
134
  else
134
135
  raise(InvalidOperation, "DISTINCT ON not supported") unless supports_distinct_on?
@@ -230,6 +231,7 @@ module Sequel
230
231
  #
231
232
  # DB[:table].for_update # SELECT * FROM table FOR UPDATE
232
233
  def for_update
234
+ return self if opts[:lock] == :update
233
235
  cached_dataset(:_for_update_ds){lock_style(:update)}
234
236
  end
235
237
 
@@ -641,6 +643,7 @@ module Sequel
641
643
  # DB.from(:a, DB[:b].where(Sequel[:a][:c]=>Sequel[:b][:d]).lateral)
642
644
  # # SELECT * FROM a, LATERAL (SELECT * FROM b WHERE (a.c = b.d))
643
645
  def lateral
646
+ return self if opts[:lateral]
644
647
  cached_dataset(:_lateral_ds){clone(:lateral=>true)}
645
648
  end
646
649
 
@@ -744,6 +747,7 @@ module Sequel
744
747
  # ds.all # => [{2=>:id}]
745
748
  # ds.naked.all # => [{:id=>2}]
746
749
  def naked
750
+ return self unless opts[:row_proc]
747
751
  cached_dataset(:_naked_ds){with_row_proc(nil)}
748
752
  end
749
753
 
@@ -753,6 +757,7 @@ module Sequel
753
757
  # DB[:items].for_update.nowait
754
758
  # # SELECT * FROM items FOR UPDATE NOWAIT
755
759
  def nowait
760
+ return self if opts[:nowait]
756
761
  cached_dataset(:_nowait_ds) do
757
762
  raise(Error, 'This dataset does not support raises errors instead of waiting for locked rows') unless supports_nowait?
758
763
  clone(:nowait=>true)
@@ -878,6 +883,7 @@ module Sequel
878
883
  # end
879
884
  def returning(*values)
880
885
  if values.empty?
886
+ return self if opts[:returning] == EMPTY_ARRAY
881
887
  cached_dataset(:_returning_ds) do
882
888
  raise Error, "RETURNING is not supported on #{db.database_type}" unless supports_returning?(:insert)
883
889
  clone(:returning=>EMPTY_ARRAY)
@@ -930,6 +936,7 @@ module Sequel
930
936
  # DB[:items].select_all(:items, :foo) # SELECT items.*, foo.* FROM items
931
937
  def select_all(*tables)
932
938
  if tables.empty?
939
+ return self unless opts[:select]
933
940
  cached_dataset(:_select_all_ds){clone(:select => nil)}
934
941
  else
935
942
  select(*tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze)
@@ -944,14 +951,8 @@ module Sequel
944
951
  # DB[:items].select(:a).select_append(:b) # SELECT a, b FROM items
945
952
  # DB[:items].select_append(:b) # SELECT *, b FROM items
946
953
  def select_append(*columns, &block)
947
- cur_sel = @opts[:select]
948
- if !cur_sel || cur_sel.empty?
949
- unless supports_select_all_and_column?
950
- return select_all(*(Array(@opts[:from]) + Array(@opts[:join]))).select_append(*columns, &block)
951
- end
952
- cur_sel = [WILDCARD]
953
- end
954
- select(*(cur_sel + columns), &block)
954
+ virtual_row_columns(columns, block)
955
+ select(*(_current_select(true) + columns))
955
956
  end
956
957
 
957
958
  # Set both the select and group clauses with the given +columns+.
@@ -973,6 +974,18 @@ module Sequel
973
974
  select_append(*columns, &block)
974
975
  end
975
976
 
977
+ # Returns a copy of the dataset with the given columns added
978
+ # to the existing selected columns. If no columns are currently selected,
979
+ # it will select the columns given in addition to *.
980
+ #
981
+ # DB[:items].select(:a).select(:b) # SELECT b FROM items
982
+ # DB[:items].select(:a).select_prepend(:b) # SELECT b, a FROM items
983
+ # DB[:items].select_prepend(:b) # SELECT b, * FROM items
984
+ def select_prepend(*columns, &block)
985
+ virtual_row_columns(columns, block)
986
+ select(*(columns + _current_select(false)))
987
+ end
988
+
976
989
  # Set the server for this dataset to use. Used to pick a specific database
977
990
  # shard to run a query against, or to override the default (where SELECT uses
978
991
  # :read_only database and all other queries use the :default database). This
@@ -999,6 +1012,7 @@ module Sequel
999
1012
 
1000
1013
  # Specify that the check for limits/offsets when updating/deleting be skipped for the dataset.
1001
1014
  def skip_limit_check
1015
+ return self if opts[:skip_limit_check]
1002
1016
  cached_dataset(:_skip_limit_check_ds) do
1003
1017
  clone(:skip_limit_check=>true)
1004
1018
  end
@@ -1006,6 +1020,7 @@ module Sequel
1006
1020
 
1007
1021
  # Skip locked rows when returning results from this dataset.
1008
1022
  def skip_locked
1023
+ return self if opts[:skip_locked]
1009
1024
  cached_dataset(:_skip_locked_ds) do
1010
1025
  raise(Error, 'This dataset does not support skipping locked rows') unless supports_skip_locked?
1011
1026
  clone(:skip_locked=>true)
@@ -1017,6 +1032,7 @@ module Sequel
1017
1032
  # DB[:items].group(:a).having(a: 1).where(:b).unfiltered
1018
1033
  # # SELECT * FROM items GROUP BY a
1019
1034
  def unfiltered
1035
+ return self unless opts[:where] || opts[:having]
1020
1036
  cached_dataset(:_unfiltered_ds){clone(:where => nil, :having => nil)}
1021
1037
  end
1022
1038
 
@@ -1025,6 +1041,7 @@ module Sequel
1025
1041
  # DB[:items].group(:a).having(a: 1).where(:b).ungrouped
1026
1042
  # # SELECT * FROM items WHERE b
1027
1043
  def ungrouped
1044
+ return self unless opts[:group] || opts[:having]
1028
1045
  cached_dataset(:_ungrouped_ds){clone(:group => nil, :having => nil)}
1029
1046
  end
1030
1047
 
@@ -1052,6 +1069,7 @@ module Sequel
1052
1069
  #
1053
1070
  # DB[:items].limit(10, 20).unlimited # SELECT * FROM items
1054
1071
  def unlimited
1072
+ return self unless opts[:limit] || opts[:offset]
1055
1073
  cached_dataset(:_unlimited_ds){clone(:limit=>nil, :offset=>nil)}
1056
1074
  end
1057
1075
 
@@ -1059,6 +1077,7 @@ module Sequel
1059
1077
  #
1060
1078
  # DB[:items].order(:a).unordered # SELECT * FROM items
1061
1079
  def unordered
1080
+ return self unless opts[:order]
1062
1081
  cached_dataset(:_unordered_ds){clone(:order=>nil)}
1063
1082
  end
1064
1083
 
@@ -1353,6 +1372,36 @@ module Sequel
1353
1372
  end
1354
1373
  # :nocov:
1355
1374
 
1375
+ # A frozen array for the currently selected columns.
1376
+ def _current_select(allow_plain_wildcard)
1377
+ cur_sel = @opts[:select]
1378
+
1379
+ if !cur_sel || cur_sel.empty?
1380
+ cur_sel = if allow_plain_wildcard && supports_select_all_and_column?
1381
+ [WILDCARD].freeze
1382
+ else
1383
+ _current_select_column_all
1384
+ end
1385
+ elsif !allow_plain_wildcard && cur_sel.include?(WILDCARD)
1386
+ cur_sel = cur_sel.dup
1387
+ index = cur_sel.index(WILDCARD)
1388
+ cur_sel.delete(WILDCARD)
1389
+ _current_select_column_all.each_with_index do |ca, i|
1390
+ cur_sel.insert(index+i, ca)
1391
+ end
1392
+ cur_sel.freeze
1393
+ end
1394
+
1395
+ cur_sel
1396
+ end
1397
+
1398
+ # An array of SQL::ColumnAll objects for all FROM and JOIN tables. Used for select_append
1399
+ # and select_prepend.
1400
+ def _current_select_column_all
1401
+ tables = Array(@opts[:from]) + Array(@opts[:join])
1402
+ tables.map{|t| i, a = split_alias(t); a || i}.map!{|t| SQL::ColumnAll.new(t)}.freeze
1403
+ end
1404
+
1356
1405
  # If invert is true, invert the condition.
1357
1406
  def _invert_filter(cond, invert)
1358
1407
  if invert
@@ -36,6 +36,7 @@ require 'rbconfig'
36
36
  module Sequel
37
37
  module CallerLogging
38
38
  SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
39
+ RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
39
40
 
40
41
  # A regexp of caller lines to ignore, in addition to internal Sequel and Ruby code.
41
42
  attr_accessor :caller_logging_ignore
@@ -59,7 +60,7 @@ module Sequel
59
60
  ignore = caller_logging_ignore
60
61
  c = caller.find do |line|
61
62
  !(line.start_with?(SEQUEL_LIB_PATH) ||
62
- line.start_with?(RbConfig::CONFIG["rubylibdir"]) ||
63
+ line.start_with?(RUBY_STDLIB) ||
63
64
  (ignore && line =~ ignore))
64
65
  end
65
66
 
@@ -0,0 +1,108 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The provenance dataset extension tracks the locations of all
4
+ # dataset clones that resulted in the current dataset, and includes
5
+ # the information as a comment in the dataset's SQL. This makes it
6
+ # possible to see how a query was built, which can aid debugging.
7
+ # Example:
8
+ #
9
+ # DB[:table].
10
+ # select(:a).
11
+ # where{b > 10}.
12
+ # order(:c).
13
+ # limit(10)
14
+ # # SQL:
15
+ # # SELECT a FROM table WHERE (b > 10) ORDER BY c LIMIT 10 --
16
+ # # -- Dataset Provenance
17
+ # # -- Keys:[:from] Source:(eval at bin/sequel:257):2:in `<main>'
18
+ # # -- Keys:[:select] Source:(eval at bin/sequel:257):3:in `<main>'
19
+ # # -- Keys:[:where] Source:(eval at bin/sequel:257):4:in `<main>'
20
+ # # -- Keys:[:order] Source:(eval at bin/sequel:257):5:in `<main>'
21
+ # # -- Keys:[:limit] Source:(eval at bin/sequel:257):6:in `<main>'
22
+ #
23
+ # With the above example, the source is fairly obvious and not helpful,
24
+ # but in real applications, where datasets can be built from multiple
25
+ # files, seeing where each dataset clone was made can be helpful.
26
+ #
27
+ # The Source listed will skip locations in the Ruby standard library
28
+ # as well as Sequel itself. Other locations can be skipped by
29
+ # providing a Database :provenance_caller_ignore Regexp option:
30
+ #
31
+ # DB.opts[:provenance_caller_ignore] = /\/gems\/library_name-/
32
+ #
33
+ # Related module: Sequel::Dataset::Provenance
34
+
35
+ #
36
+ module Sequel
37
+ class Dataset
38
+ module Provenance
39
+ SEQUEL_LIB_PATH = (File.expand_path('../../..', __FILE__) + '/').freeze
40
+ RUBY_STDLIB = RbConfig::CONFIG["rubylibdir"]
41
+
42
+ if TRUE_FREEZE
43
+ # Include provenance information when cloning datasets.
44
+ def clone(opts = nil || (return self))
45
+ super(provenance_opts(opts))
46
+ end
47
+ else
48
+ # :nocov:
49
+ def clone(opts = OPTS) # :nodoc:
50
+ super(provenance_opts(opts))
51
+ end
52
+ # :nocov:
53
+ end
54
+
55
+ %w'select insert update delete'.each do |type|
56
+ # Include the provenance information as a comment when preparing dataset SQL
57
+ define_method(:"#{type}_sql") do |*a|
58
+ sql = super(*a)
59
+
60
+ if provenance = @opts[:provenance]
61
+ comment = provenance.map do |hash|
62
+ " -- Keys:#{hash[:keys].inspect} Source:#{hash[:source]}".to_s.gsub(/\s+/, ' ')
63
+ end
64
+ comment << ""
65
+ comment.unshift " -- Dataset Provenance"
66
+ comment.unshift " -- "
67
+ comment = comment.join("\n")
68
+
69
+ if sql.frozen?
70
+ sql += comment
71
+ sql.freeze
72
+ elsif @opts[:append_sql] || @opts[:placeholder_literalizer]
73
+ sql << comment
74
+ else
75
+ sql += comment
76
+ end
77
+ end
78
+
79
+ sql
80
+ end
81
+ end
82
+
83
+ private
84
+
85
+ # Return a copy of opts with provenance information added.
86
+ def provenance_opts(opts)
87
+ provenance = {source: provenance_source, keys: opts.keys.freeze}.freeze
88
+ opts = opts.dup
89
+ opts[:provenance] = ((@opts[:provenance] || EMPTY_ARRAY).dup << provenance).freeze
90
+ opts
91
+ end
92
+
93
+ # Return the caller line for the provenance change. This skips
94
+ # Sequel itself and the standard library. Additional locations
95
+ # can be skipped using the :provenance_caller_ignore Dataset option.
96
+ def provenance_source
97
+ ignore = db.opts[:provenance_caller_ignore]
98
+ caller.find do |line|
99
+ !(line.start_with?(SEQUEL_LIB_PATH) ||
100
+ line.start_with?(RUBY_STDLIB) ||
101
+ (ignore && line =~ ignore))
102
+ end
103
+ end
104
+ end
105
+
106
+ register_extension(:provenance, Provenance)
107
+ end
108
+ end
@@ -17,7 +17,7 @@ module Sequel
17
17
  # natural_join, natural_left_join, natural_right_join, offset, order, order_append, order_by,
18
18
  # order_more, order_prepend, paged_each, qualify, reverse, reverse_order, right_join,
19
19
  # right_outer_join, select, select_all, select_append, select_group, select_hash,
20
- # select_hash_groups, select_map, select_more, select_order_map, server,
20
+ # select_hash_groups, select_map, select_more, select_order_map, select_prepend, server,
21
21
  # single_record, single_record!, single_value, single_value!, sum, to_hash, to_hash_groups,
22
22
  # truncate, unfiltered, ungraphed, ungrouped, union, unlimited, unordered, where, where_all,
23
23
  # where_each, where_single_value, with, with_recursive, with_sql
@@ -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 = 78
9
+ MINOR = 80
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.78.0
4
+ version: 5.80.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-03-01 00:00:00.000000000 Z
11
+ date: 2024-05-01 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bigdecimal
@@ -225,7 +225,9 @@ extra_rdoc_files:
225
225
  - doc/release_notes/5.76.0.txt
226
226
  - doc/release_notes/5.77.0.txt
227
227
  - doc/release_notes/5.78.0.txt
228
+ - doc/release_notes/5.79.0.txt
228
229
  - doc/release_notes/5.8.0.txt
230
+ - doc/release_notes/5.80.0.txt
229
231
  - doc/release_notes/5.9.0.txt
230
232
  files:
231
233
  - CHANGELOG
@@ -331,7 +333,9 @@ files:
331
333
  - doc/release_notes/5.76.0.txt
332
334
  - doc/release_notes/5.77.0.txt
333
335
  - doc/release_notes/5.78.0.txt
336
+ - doc/release_notes/5.79.0.txt
334
337
  - doc/release_notes/5.8.0.txt
338
+ - doc/release_notes/5.80.0.txt
335
339
  - doc/release_notes/5.9.0.txt
336
340
  - doc/schema_modification.rdoc
337
341
  - doc/security.rdoc
@@ -494,6 +498,7 @@ files:
494
498
  - lib/sequel/extensions/pg_static_cache_updater.rb
495
499
  - lib/sequel/extensions/pg_timestamptz.rb
496
500
  - lib/sequel/extensions/pretty_table.rb
501
+ - lib/sequel/extensions/provenance.rb
497
502
  - lib/sequel/extensions/query.rb
498
503
  - lib/sequel/extensions/round_timestamps.rb
499
504
  - lib/sequel/extensions/run_transaction_hooks.rb
@@ -664,7 +669,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
664
669
  - !ruby/object:Gem::Version
665
670
  version: '0'
666
671
  requirements: []
667
- rubygems_version: 3.5.3
672
+ rubygems_version: 3.5.9
668
673
  signing_key:
669
674
  specification_version: 4
670
675
  summary: The Database Toolkit for Ruby