sequel 5.33.0 → 5.58.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG +318 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +40 -9
- data/doc/association_basics.rdoc +77 -13
- data/doc/cheat_sheet.rdoc +13 -5
- data/doc/code_order.rdoc +0 -12
- data/doc/dataset_filtering.rdoc +2 -2
- data/doc/fork_safety.rdoc +84 -0
- data/doc/migration.rdoc +12 -6
- data/doc/model_plugins.rdoc +1 -1
- data/doc/opening_databases.rdoc +15 -3
- data/doc/postgresql.rdoc +9 -1
- data/doc/querying.rdoc +7 -5
- data/doc/release_notes/5.34.0.txt +40 -0
- data/doc/release_notes/5.35.0.txt +56 -0
- data/doc/release_notes/5.36.0.txt +60 -0
- data/doc/release_notes/5.37.0.txt +30 -0
- data/doc/release_notes/5.38.0.txt +28 -0
- data/doc/release_notes/5.39.0.txt +19 -0
- 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/sql.rdoc +14 -2
- data/doc/testing.rdoc +10 -1
- data/doc/transactions.rdoc +0 -8
- data/doc/validations.rdoc +1 -1
- data/doc/virtual_rows.rdoc +1 -1
- 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/mysql.rb +4 -4
- data/lib/sequel/adapters/jdbc/postgresql.rb +4 -4
- data/lib/sequel/adapters/jdbc.rb +29 -19
- data/lib/sequel/adapters/mysql.rb +80 -67
- data/lib/sequel/adapters/mysql2.rb +54 -49
- data/lib/sequel/adapters/odbc.rb +8 -6
- data/lib/sequel/adapters/oracle.rb +5 -4
- data/lib/sequel/adapters/postgres.rb +27 -29
- data/lib/sequel/adapters/shared/access.rb +2 -0
- data/lib/sequel/adapters/shared/db2.rb +30 -0
- data/lib/sequel/adapters/shared/mssql.rb +84 -7
- data/lib/sequel/adapters/shared/mysql.rb +33 -2
- data/lib/sequel/adapters/shared/oracle.rb +82 -7
- data/lib/sequel/adapters/shared/postgres.rb +158 -20
- data/lib/sequel/adapters/shared/sqlanywhere.rb +3 -0
- data/lib/sequel/adapters/shared/sqlite.rb +102 -10
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +60 -18
- data/lib/sequel/adapters/tinytds.rb +2 -1
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +2 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +9 -8
- data/lib/sequel/connection_pool/sharded_threaded.rb +10 -10
- data/lib/sequel/connection_pool/single.rb +7 -9
- data/lib/sequel/connection_pool/threaded.rb +1 -1
- data/lib/sequel/core.rb +33 -24
- data/lib/sequel/database/connecting.rb +3 -4
- data/lib/sequel/database/misc.rb +37 -12
- data/lib/sequel/database/query.rb +3 -1
- data/lib/sequel/database/schema_generator.rb +50 -53
- data/lib/sequel/database/schema_methods.rb +45 -23
- data/lib/sequel/database/transactions.rb +9 -6
- data/lib/sequel/dataset/actions.rb +61 -8
- data/lib/sequel/dataset/features.rb +15 -0
- data/lib/sequel/dataset/placeholder_literalizer.rb +3 -7
- data/lib/sequel/dataset/prepared_statements.rb +2 -0
- data/lib/sequel/dataset/query.rb +114 -11
- data/lib/sequel/dataset/sql.rb +172 -46
- data/lib/sequel/deprecated.rb +3 -1
- data/lib/sequel/exceptions.rb +2 -0
- data/lib/sequel/extensions/_pretty_table.rb +1 -2
- data/lib/sequel/extensions/any_not_empty.rb +1 -1
- data/lib/sequel/extensions/async_thread_pool.rb +438 -0
- data/lib/sequel/extensions/blank.rb +8 -0
- data/lib/sequel/extensions/columns_introspection.rb +1 -2
- data/lib/sequel/extensions/core_refinements.rb +38 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -24
- 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 +3 -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 +139 -0
- data/lib/sequel/extensions/migration.rb +13 -2
- data/lib/sequel/extensions/named_timezones.rb +5 -1
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +1 -0
- data/lib/sequel/extensions/pg_array_ops.rb +6 -2
- data/lib/sequel/extensions/pg_enum.rb +3 -1
- data/lib/sequel/extensions/pg_extended_date_support.rb +2 -2
- data/lib/sequel/extensions/pg_hstore.rb +1 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +55 -3
- data/lib/sequel/extensions/pg_inet.rb +2 -0
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +35 -8
- data/lib/sequel/extensions/pg_json.rb +3 -5
- data/lib/sequel/extensions/pg_json_ops.rb +119 -4
- data/lib/sequel/extensions/pg_loose_count.rb +3 -1
- data/lib/sequel/extensions/pg_multirange.rb +372 -0
- data/lib/sequel/extensions/pg_range.rb +7 -19
- data/lib/sequel/extensions/pg_range_ops.rb +39 -9
- data/lib/sequel/extensions/pg_row.rb +1 -1
- data/lib/sequel/extensions/pg_row_ops.rb +25 -1
- data/lib/sequel/extensions/query.rb +3 -0
- data/lib/sequel/extensions/run_transaction_hooks.rb +1 -1
- data/lib/sequel/extensions/s.rb +4 -1
- data/lib/sequel/extensions/schema_dumper.rb +16 -5
- 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_refinement.rb +2 -0
- data/lib/sequel/extensions/symbol_as_refinement.rb +2 -0
- data/lib/sequel/extensions/to_dot.rb +9 -3
- data/lib/sequel/model/associations.rb +342 -114
- data/lib/sequel/model/base.rb +45 -24
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/model/plugins.rb +8 -3
- data/lib/sequel/model.rb +3 -1
- data/lib/sequel/plugins/association_pks.rb +60 -18
- data/lib/sequel/plugins/association_proxies.rb +3 -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 +39 -5
- data/lib/sequel/plugins/auto_validations_constraint_validations_presence_message.rb +68 -0
- data/lib/sequel/plugins/blacklist_security.rb +1 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +3 -8
- data/lib/sequel/plugins/column_encryption.rb +728 -0
- data/lib/sequel/plugins/composition.rb +8 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
- data/lib/sequel/plugins/constraint_validations.rb +2 -1
- data/lib/sequel/plugins/csv_serializer.rb +2 -0
- data/lib/sequel/plugins/dataset_associations.rb +4 -1
- data/lib/sequel/plugins/dirty.rb +44 -0
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/forbid_lazy_load.rb +2 -0
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +113 -0
- data/lib/sequel/plugins/json_serializer.rb +39 -24
- data/lib/sequel/plugins/lazy_attributes.rb +4 -1
- data/lib/sequel/plugins/many_through_many.rb +108 -9
- data/lib/sequel/plugins/nested_attributes.rb +8 -3
- data/lib/sequel/plugins/pg_array_associations.rb +58 -41
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
- data/lib/sequel/plugins/prepared_statements.rb +15 -12
- data/lib/sequel/plugins/prepared_statements_safe.rb +1 -3
- data/lib/sequel/plugins/rcte_tree.rb +37 -35
- 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 +7 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +1 -1
- data/lib/sequel/plugins/string_stripper.rb +1 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +8 -2
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/tree.rb +9 -4
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validation_class_methods.rb +5 -1
- data/lib/sequel/plugins/validation_helpers.rb +18 -11
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +20 -17
- data/lib/sequel/version.rb +1 -1
- metadata +93 -39
data/lib/sequel/dataset/query.rb
CHANGED
@@ -81,7 +81,7 @@ module Sequel
|
|
81
81
|
# If the options changed include options in COLUMN_CHANGE_OPTS, the cached
|
82
82
|
# columns are deleted. This method should generally not be called
|
83
83
|
# directly by user code.
|
84
|
-
def clone(opts = (return self
|
84
|
+
def clone(opts = nil || (return self))
|
85
85
|
# return self used above because clone is called by almost all
|
86
86
|
# other query methods, and it is the fastest approach
|
87
87
|
c = super(:freeze=>false)
|
@@ -330,16 +330,17 @@ module Sequel
|
|
330
330
|
# # SELECT * FROM a WHERE ((a LIKE '%foo%' ESCAPE '\') AND (b LIKE '%foo%' ESCAPE '\')
|
331
331
|
# # AND (a LIKE '%bar%' ESCAPE '\') AND (b LIKE '%bar%' ESCAPE '\'))
|
332
332
|
def grep(columns, patterns, opts=OPTS)
|
333
|
+
column_op = opts[:all_columns] ? :AND : :OR
|
333
334
|
if opts[:all_patterns]
|
334
335
|
conds = Array(patterns).map do |pat|
|
335
|
-
SQL::BooleanExpression.new(
|
336
|
+
SQL::BooleanExpression.new(column_op, *Array(columns).map{|c| SQL::StringExpression.like(c, pat, opts)})
|
336
337
|
end
|
337
|
-
where(SQL::BooleanExpression.new(
|
338
|
+
where(SQL::BooleanExpression.new(:AND, *conds))
|
338
339
|
else
|
339
340
|
conds = Array(columns).map do |c|
|
340
341
|
SQL::BooleanExpression.new(:OR, *Array(patterns).map{|pat| SQL::StringExpression.like(c, pat, opts)})
|
341
342
|
end
|
342
|
-
where(SQL::BooleanExpression.new(
|
343
|
+
where(SQL::BooleanExpression.new(column_op, *conds))
|
343
344
|
end
|
344
345
|
end
|
345
346
|
|
@@ -507,6 +508,7 @@ module Sequel
|
|
507
508
|
# argument.
|
508
509
|
# :implicit_qualifier :: The name to use for qualifying implicit conditions. By default,
|
509
510
|
# the last joined or primary table is used.
|
511
|
+
# :join_using :: Force the using of JOIN USING, even if +expr+ is not an array of symbols.
|
510
512
|
# :reset_implicit_qualifier :: Can set to false to ignore this join when future joins determine qualifier
|
511
513
|
# for implicit conditions.
|
512
514
|
# :qualify :: Can be set to false to not do any implicit qualification. Can be set
|
@@ -540,7 +542,7 @@ module Sequel
|
|
540
542
|
return s.join_table(type, ds, expr, options, &block)
|
541
543
|
end
|
542
544
|
|
543
|
-
using_join = expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)}
|
545
|
+
using_join = options[:join_using] || (expr.is_a?(Array) && !expr.empty? && expr.all?{|x| x.is_a?(Symbol)})
|
544
546
|
if using_join && !supports_join_using?
|
545
547
|
h = {}
|
546
548
|
expr.each{|e| h[e] = e}
|
@@ -615,7 +617,7 @@ module Sequel
|
|
615
617
|
UNCONDITIONED_JOIN_TYPES.each do |jtype|
|
616
618
|
class_eval(<<-END, __FILE__, __LINE__+1)
|
617
619
|
def #{jtype}_join(table, opts=Sequel::OPTS)
|
618
|
-
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if
|
620
|
+
raise(Sequel::Error, '#{jtype}_join does not accept join table blocks') if defined?(yield)
|
619
621
|
raise(Sequel::Error, '#{jtype}_join 2nd argument should be an options hash, not conditions') unless opts.is_a?(Hash)
|
620
622
|
join_table(:#{jtype}, table, nil, opts)
|
621
623
|
end
|
@@ -676,6 +678,56 @@ module Sequel
|
|
676
678
|
clone(:lock => style)
|
677
679
|
end
|
678
680
|
|
681
|
+
# Return a dataset with a WHEN MATCHED THEN DELETE clause added to the
|
682
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
683
|
+
# use it as additional conditions for the match.
|
684
|
+
#
|
685
|
+
# merge_delete
|
686
|
+
# # WHEN MATCHED THEN DELETE
|
687
|
+
#
|
688
|
+
# merge_delete{a > 30}
|
689
|
+
# # WHEN MATCHED AND (a > 30) THEN DELETE
|
690
|
+
def merge_delete(&block)
|
691
|
+
_merge_when(:type=>:delete, &block)
|
692
|
+
end
|
693
|
+
|
694
|
+
# Return a dataset with a WHEN NOT MATCHED THEN INSERT clause added to the
|
695
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
696
|
+
# use it as additional conditions for the match.
|
697
|
+
#
|
698
|
+
# The arguments provided can be any arguments that would be accepted by
|
699
|
+
# #insert.
|
700
|
+
#
|
701
|
+
# merge_insert(i1: :i2, a: Sequel[:b]+11)
|
702
|
+
# # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
|
703
|
+
#
|
704
|
+
# merge_insert(:i2, Sequel[:b]+11){a > 30}
|
705
|
+
# # WHEN NOT MATCHED AND (a > 30) THEN INSERT VALUES (i2, (b + 11))
|
706
|
+
def merge_insert(*values, &block)
|
707
|
+
_merge_when(:type=>:insert, :values=>values, &block)
|
708
|
+
end
|
709
|
+
|
710
|
+
# Return a dataset with a WHEN MATCHED THEN UPDATE clause added to the
|
711
|
+
# MERGE statement. If a block is passed, treat it as a virtual row and
|
712
|
+
# use it as additional conditions for the match.
|
713
|
+
#
|
714
|
+
# merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20)
|
715
|
+
# # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
|
716
|
+
#
|
717
|
+
# merge_update(i1: :i2){a > 30}
|
718
|
+
# # WHEN MATCHED AND (a > 30) THEN UPDATE SET i1 = i2
|
719
|
+
def merge_update(values, &block)
|
720
|
+
_merge_when(:type=>:update, :values=>values, &block)
|
721
|
+
end
|
722
|
+
|
723
|
+
# Return a dataset with the source and join condition to use for the MERGE statement.
|
724
|
+
#
|
725
|
+
# merge_using(:m2, i1: :i2)
|
726
|
+
# # USING m2 ON (i1 = i2)
|
727
|
+
def merge_using(source, join_condition)
|
728
|
+
clone(:merge_using => [source, join_condition].freeze)
|
729
|
+
end
|
730
|
+
|
679
731
|
# Returns a cloned dataset without a row_proc.
|
680
732
|
#
|
681
733
|
# ds = DB[:items].with_row_proc(:invert.to_proc)
|
@@ -698,7 +750,7 @@ module Sequel
|
|
698
750
|
end
|
699
751
|
|
700
752
|
# Returns a copy of the dataset with a specified order. Can be safely combined with limit.
|
701
|
-
# If you call limit with an offset, it will override
|
753
|
+
# If you call limit with an offset, it will override the offset if you've called
|
702
754
|
# offset first.
|
703
755
|
#
|
704
756
|
# DB[:items].offset(10) # SELECT * FROM items OFFSET 10
|
@@ -1058,13 +1110,12 @@ module Sequel
|
|
1058
1110
|
|
1059
1111
|
# Add a common table expression (CTE) with the given name and a dataset that defines the CTE.
|
1060
1112
|
# A common table expression acts as an inline view for the query.
|
1113
|
+
#
|
1061
1114
|
# Options:
|
1062
1115
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
1063
1116
|
# :recursive :: Specify that this is a recursive CTE
|
1064
|
-
#
|
1065
|
-
# PostgreSQL Specific Options:
|
1066
1117
|
# :materialized :: Set to false to force inlining of the CTE, or true to force not inlining
|
1067
|
-
# the CTE (PostgreSQL 12+).
|
1118
|
+
# the CTE (PostgreSQL 12+/SQLite 3.35+).
|
1068
1119
|
#
|
1069
1120
|
# DB[:items].with(:items, DB[:syx].where(Sequel[:name].like('A%')))
|
1070
1121
|
# # WITH items AS (SELECT * FROM syx WHERE (name LIKE 'A%' ESCAPE '\')) SELECT * FROM items
|
@@ -1080,10 +1131,35 @@ module Sequel
|
|
1080
1131
|
|
1081
1132
|
# Add a recursive common table expression (CTE) with the given name, a dataset that
|
1082
1133
|
# defines the nonrecursive part of the CTE, and a dataset that defines the recursive part
|
1083
|
-
# of the CTE.
|
1134
|
+
# of the CTE.
|
1135
|
+
#
|
1136
|
+
# Options:
|
1084
1137
|
# :args :: Specify the arguments/columns for the CTE, should be an array of symbols.
|
1085
1138
|
# :union_all :: Set to false to use UNION instead of UNION ALL combining the nonrecursive and recursive parts.
|
1086
1139
|
#
|
1140
|
+
# PostgreSQL 14+ Options:
|
1141
|
+
# :cycle :: Stop recursive searching when a cycle is detected. Includes two columns in the
|
1142
|
+
# result of the CTE, a cycle column indicating whether a cycle was detected for
|
1143
|
+
# the current row, and a path column for the path traversed to get to the current
|
1144
|
+
# row. If given, must be a hash with the following keys:
|
1145
|
+
# :columns :: (required) The column or array of columns to use to detect a cycle.
|
1146
|
+
# If the value of these columns match columns already traversed, then
|
1147
|
+
# a cycle is detected, and recursive searching will not traverse beyond
|
1148
|
+
# the cycle (the CTE will include the row where the cycle was detected).
|
1149
|
+
# :cycle_column :: The name of the cycle column in the output, defaults to :is_cycle.
|
1150
|
+
# :cycle_value :: The value of the cycle column in the output if the current row was
|
1151
|
+
# detected as a cycle, defaults to true.
|
1152
|
+
# :noncycle_value :: The value of the cycle column in the output if the current row
|
1153
|
+
# was not detected as a cycle, defaults to false. Only respected
|
1154
|
+
# if :cycle_value is given.
|
1155
|
+
# :path_column :: The name of the path column in the output, defaults to :path.
|
1156
|
+
# :search :: Include an order column in the result of the CTE that allows for breadth or
|
1157
|
+
# depth first searching. If given, must be a hash with the following keys:
|
1158
|
+
# :by :: (required) The column or array of columns to search by.
|
1159
|
+
# :order_column :: The name of the order column in the output, defaults to :ordercol.
|
1160
|
+
# :type :: Set to :breadth to use breadth-first searching (depth-first searching
|
1161
|
+
# is the default).
|
1162
|
+
#
|
1087
1163
|
# DB[:t].with_recursive(:t,
|
1088
1164
|
# DB[:i1].select(:id, :parent_id).where(parent_id: nil),
|
1089
1165
|
# DB[:i1].join(:t, id: :parent_id).select(Sequel[:i1][:id], Sequel[:i1][:parent_id]),
|
@@ -1094,6 +1170,21 @@ module Sequel
|
|
1094
1170
|
# # UNION ALL
|
1095
1171
|
# # SELECT i1.id, i1.parent_id FROM i1 INNER JOIN t ON (t.id = i1.parent_id)
|
1096
1172
|
# # ) SELECT * FROM t
|
1173
|
+
#
|
1174
|
+
# DB[:t].with_recursive(:t,
|
1175
|
+
# DB[:i1].where(parent_id: nil),
|
1176
|
+
# DB[:i1].join(:t, id: :parent_id).select_all(:i1),
|
1177
|
+
# search: {by: :id, type: :breadth},
|
1178
|
+
# cycle: {columns: :id, cycle_value: 1, noncycle_value: 2})
|
1179
|
+
#
|
1180
|
+
# # WITH RECURSIVE t AS (
|
1181
|
+
# # SELECT * FROM i1 WHERE (parent_id IS NULL)
|
1182
|
+
# # UNION ALL
|
1183
|
+
# # (SELECT i1.* FROM i1 INNER JOIN t ON (t.id = i1.parent_id))
|
1184
|
+
# # )
|
1185
|
+
# # SEARCH BREADTH FIRST BY id SET ordercol
|
1186
|
+
# # CYCLE id SET is_cycle TO 1 DEFAULT 2 USING path
|
1187
|
+
# # SELECT * FROM t
|
1097
1188
|
def with_recursive(name, nonrecursive, recursive, opts=OPTS)
|
1098
1189
|
raise(Error, 'This dataset does not support common table expressions') unless supports_cte?
|
1099
1190
|
if hoist_cte?(nonrecursive)
|
@@ -1246,6 +1337,18 @@ module Sequel
|
|
1246
1337
|
end
|
1247
1338
|
end
|
1248
1339
|
|
1340
|
+
# Append to the current MERGE WHEN clauses.
|
1341
|
+
# Mutates the hash to add the conditions, if a virtual row block is passed.
|
1342
|
+
def _merge_when(hash, &block)
|
1343
|
+
hash[:conditions] = Sequel.virtual_row(&block) if block
|
1344
|
+
|
1345
|
+
if merge_when = @opts[:merge_when]
|
1346
|
+
clone(:merge_when => (merge_when.dup << hash.freeze).freeze)
|
1347
|
+
else
|
1348
|
+
clone(:merge_when => [hash.freeze].freeze)
|
1349
|
+
end
|
1350
|
+
end
|
1351
|
+
|
1249
1352
|
# Add the given filter condition. Arguments:
|
1250
1353
|
# clause :: Symbol or which SQL clause to effect, should be :where or :having
|
1251
1354
|
# cond :: The filter condition to add
|
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -22,31 +22,9 @@ module Sequel
|
|
22
22
|
def insert_sql(*values)
|
23
23
|
return static_sql(@opts[:sql]) if @opts[:sql]
|
24
24
|
|
25
|
-
|
26
|
-
|
27
|
-
columns = []
|
28
|
-
|
29
|
-
case values.size
|
30
|
-
when 0
|
31
|
-
return insert_sql(OPTS)
|
32
|
-
when 1
|
33
|
-
case vals = values[0]
|
34
|
-
when Hash
|
35
|
-
values = []
|
36
|
-
vals.each do |k,v|
|
37
|
-
columns << k
|
38
|
-
values << v
|
39
|
-
end
|
40
|
-
when Dataset, Array, LiteralString
|
41
|
-
values = vals
|
42
|
-
end
|
43
|
-
when 2
|
44
|
-
if (v0 = values[0]).is_a?(Array) && ((v1 = values[1]).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
|
45
|
-
columns, values = v0, v1
|
46
|
-
raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
|
47
|
-
end
|
48
|
-
end
|
25
|
+
check_insert_allowed!
|
49
26
|
|
27
|
+
columns, values = _parse_insert_sql_args(values)
|
50
28
|
if values.is_a?(Array) && values.empty? && !insert_supports_empty_values?
|
51
29
|
columns, values = insert_empty_columns_values
|
52
30
|
elsif values.is_a?(Dataset) && hoist_cte?(values) && supports_cte?(:insert)
|
@@ -112,6 +90,31 @@ module Sequel
|
|
112
90
|
end
|
113
91
|
end
|
114
92
|
|
93
|
+
# The SQL to use for the MERGE statement.
|
94
|
+
def merge_sql
|
95
|
+
raise Error, "This database doesn't support MERGE" unless supports_merge?
|
96
|
+
if sql = opts[:sql]
|
97
|
+
return static_sql(sql)
|
98
|
+
end
|
99
|
+
if sql = cache_get(:_merge_sql)
|
100
|
+
return sql
|
101
|
+
end
|
102
|
+
source, join_condition = @opts[:merge_using]
|
103
|
+
raise Error, "No USING clause for MERGE" unless source
|
104
|
+
sql = @opts[:append_sql] || sql_string_origin
|
105
|
+
|
106
|
+
select_with_sql(sql)
|
107
|
+
sql << "MERGE INTO "
|
108
|
+
source_list_append(sql, @opts[:from])
|
109
|
+
sql << " USING "
|
110
|
+
identifier_append(sql, source)
|
111
|
+
sql << " ON "
|
112
|
+
literal_append(sql, join_condition)
|
113
|
+
_merge_when_sql(sql)
|
114
|
+
cache_set(:_merge_sql, sql) if cache_sql?
|
115
|
+
sql
|
116
|
+
end
|
117
|
+
|
115
118
|
# Returns an array of insert statements for inserting multiple records.
|
116
119
|
# This method is used by +multi_insert+ to format insert statements and
|
117
120
|
# expects a keys array and and an array of value arrays.
|
@@ -172,7 +175,7 @@ module Sequel
|
|
172
175
|
# than one table.
|
173
176
|
def update_sql(values = OPTS)
|
174
177
|
return static_sql(opts[:sql]) if opts[:sql]
|
175
|
-
|
178
|
+
check_update_allowed!
|
176
179
|
check_not_limited!(:update)
|
177
180
|
|
178
181
|
case values
|
@@ -215,7 +218,7 @@ module Sequel
|
|
215
218
|
lines << "def #{'_' if priv}#{type}_sql"
|
216
219
|
lines << 'if sql = opts[:sql]; return static_sql(sql) end' unless priv
|
217
220
|
lines << "if sql = cache_get(:_#{type}_sql); return sql end" if cacheable
|
218
|
-
lines << '
|
221
|
+
lines << 'check_delete_allowed!' << 'check_not_limited!(:delete)' if type == :delete
|
219
222
|
lines << 'sql = @opts[:append_sql] || sql_string_origin'
|
220
223
|
|
221
224
|
if clauses.all?{|c| c.is_a?(Array)}
|
@@ -559,11 +562,9 @@ module Sequel
|
|
559
562
|
# Append literalization of JOIN USING clause to SQL string.
|
560
563
|
def join_using_clause_sql_append(sql, jc)
|
561
564
|
join_clause_sql_append(sql, jc)
|
562
|
-
sql
|
563
|
-
column_list_append(sql, jc.using)
|
564
|
-
sql << ')'
|
565
|
+
join_using_clause_using_sql_append(sql, jc.using)
|
565
566
|
end
|
566
|
-
|
567
|
+
|
567
568
|
# Append literalization of negative boolean constant to SQL string.
|
568
569
|
def negative_boolean_constant_sql_append(sql, constant)
|
569
570
|
sql << 'NOT '
|
@@ -852,6 +853,83 @@ module Sequel
|
|
852
853
|
|
853
854
|
private
|
854
855
|
|
856
|
+
# Append the INSERT sql used in a MERGE
|
857
|
+
def _merge_insert_sql(sql, data)
|
858
|
+
sql << " THEN INSERT"
|
859
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
860
|
+
_insert_columns_sql(sql, columns)
|
861
|
+
_insert_values_sql(sql, values)
|
862
|
+
end
|
863
|
+
|
864
|
+
def _merge_update_sql(sql, data)
|
865
|
+
sql << " THEN UPDATE SET "
|
866
|
+
update_sql_values_hash(sql, data[:values])
|
867
|
+
end
|
868
|
+
|
869
|
+
def _merge_delete_sql(sql, data)
|
870
|
+
sql << " THEN DELETE"
|
871
|
+
end
|
872
|
+
|
873
|
+
# Mapping of merge types to related SQL
|
874
|
+
MERGE_TYPE_SQL = {
|
875
|
+
:insert => ' WHEN NOT MATCHED',
|
876
|
+
:delete => ' WHEN MATCHED',
|
877
|
+
:update => ' WHEN MATCHED',
|
878
|
+
:matched => ' WHEN MATCHED',
|
879
|
+
:not_matched => ' WHEN NOT MATCHED',
|
880
|
+
}.freeze
|
881
|
+
private_constant :MERGE_TYPE_SQL
|
882
|
+
|
883
|
+
# Add the WHEN clauses to the MERGE SQL
|
884
|
+
def _merge_when_sql(sql)
|
885
|
+
raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
|
886
|
+
merge_when.each do |data|
|
887
|
+
type = data[:type]
|
888
|
+
sql << MERGE_TYPE_SQL[type]
|
889
|
+
_merge_when_conditions_sql(sql, data)
|
890
|
+
send(:"_merge_#{type}_sql", sql, data)
|
891
|
+
end
|
892
|
+
end
|
893
|
+
|
894
|
+
# Append MERGE WHEN conditions, if there are conditions provided.
|
895
|
+
def _merge_when_conditions_sql(sql, data)
|
896
|
+
if data.has_key?(:conditions)
|
897
|
+
sql << " AND "
|
898
|
+
literal_append(sql, data[:conditions])
|
899
|
+
end
|
900
|
+
end
|
901
|
+
|
902
|
+
# Parse the values passed to insert_sql, returning columns and values
|
903
|
+
# to use for the INSERT. Returned columns is always an array, but can be empty
|
904
|
+
# for an INSERT without explicit column references. Returned values can be an
|
905
|
+
# array, dataset, or literal string.
|
906
|
+
def _parse_insert_sql_args(values)
|
907
|
+
columns = []
|
908
|
+
|
909
|
+
case values.size
|
910
|
+
when 0
|
911
|
+
values = []
|
912
|
+
when 1
|
913
|
+
case vals = values[0]
|
914
|
+
when Hash
|
915
|
+
values = []
|
916
|
+
vals.each do |k,v|
|
917
|
+
columns << k
|
918
|
+
values << v
|
919
|
+
end
|
920
|
+
when Dataset, Array, LiteralString
|
921
|
+
values = vals
|
922
|
+
end
|
923
|
+
when 2
|
924
|
+
if (v0 = values[0]).is_a?(Array) && ((v1 = values[1]).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
|
925
|
+
columns, values = v0, v1
|
926
|
+
raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
|
927
|
+
end
|
928
|
+
end
|
929
|
+
|
930
|
+
[columns, values]
|
931
|
+
end
|
932
|
+
|
855
933
|
# Formats the truncate statement. Assumes the table given has already been
|
856
934
|
# literalized.
|
857
935
|
def _truncate_sql(table)
|
@@ -896,9 +974,10 @@ module Sequel
|
|
896
974
|
# Clone of this dataset usable in aggregate operations. Does
|
897
975
|
# a from_self if dataset contains any parameters that would
|
898
976
|
# affect normal aggregation, or just removes an existing
|
899
|
-
# order if not.
|
977
|
+
# order if not. Also removes the row_proc, which isn't needed
|
978
|
+
# for aggregate calculations.
|
900
979
|
def aggregate_dataset
|
901
|
-
options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered
|
980
|
+
(options_overlap(COUNT_FROM_SELF_OPTS) ? from_self : unordered).naked
|
902
981
|
end
|
903
982
|
|
904
983
|
# Append aliasing expression to SQL string.
|
@@ -918,10 +997,35 @@ module Sequel
|
|
918
997
|
!@opts[:no_cache_sql] && !cache_get(:_no_cache_sql)
|
919
998
|
end
|
920
999
|
|
921
|
-
# Raise an InvalidOperation exception if
|
1000
|
+
# Raise an InvalidOperation exception if modification is not allowed for this dataset.
|
1001
|
+
# Check whether it is allowed to insert into this dataset.
|
1002
|
+
# Only for backwards compatibility with older external adapters.
|
922
1003
|
def check_modification_allowed!
|
1004
|
+
# SEQUEL6: Remove
|
1005
|
+
Sequel::Deprecation.deprecate("Dataset#check_modification_allowed!", "Use check_{insert,delete,update,truncation}_allowed! instead")
|
1006
|
+
_check_modification_allowed!(supports_modifying_joins?)
|
1007
|
+
end
|
1008
|
+
|
1009
|
+
# Check whether it is allowed to insert into this dataset.
|
1010
|
+
def check_insert_allowed!
|
1011
|
+
_check_modification_allowed!(false)
|
1012
|
+
end
|
1013
|
+
alias check_truncation_allowed! check_insert_allowed!
|
1014
|
+
|
1015
|
+
# Check whether it is allowed to delete from this dataset.
|
1016
|
+
def check_delete_allowed!
|
1017
|
+
_check_modification_allowed!(supports_deleting_joins?)
|
1018
|
+
end
|
1019
|
+
|
1020
|
+
# Check whether it is allowed to update this dataset.
|
1021
|
+
def check_update_allowed!
|
1022
|
+
_check_modification_allowed!(supports_updating_joins?)
|
1023
|
+
end
|
1024
|
+
|
1025
|
+
# Internals of the check_*_allowed! methods
|
1026
|
+
def _check_modification_allowed!(modifying_joins_supported)
|
923
1027
|
raise(InvalidOperation, "Grouped datasets cannot be modified") if opts[:group]
|
924
|
-
raise(InvalidOperation, "Joined datasets cannot be modified") if !
|
1028
|
+
raise(InvalidOperation, "Joined datasets cannot be modified") if !modifying_joins_supported && joined_dataset?
|
925
1029
|
end
|
926
1030
|
|
927
1031
|
# Raise error if the dataset uses limits or offsets.
|
@@ -930,11 +1034,6 @@ module Sequel
|
|
930
1034
|
raise InvalidOperation, "Dataset##{type} not supported on datasets with limits or offsets" if opts[:limit] || opts[:offset]
|
931
1035
|
end
|
932
1036
|
|
933
|
-
# Alias of check_modification_allowed!
|
934
|
-
def check_truncation_allowed!
|
935
|
-
check_modification_allowed!
|
936
|
-
end
|
937
|
-
|
938
1037
|
# Append column list to SQL string.
|
939
1038
|
# If the column list is empty, a wildcard (*) is appended.
|
940
1039
|
def column_list_append(sql, columns)
|
@@ -971,7 +1070,9 @@ module Sequel
|
|
971
1070
|
# operators unsupported by some databases. Used by adapters for databases
|
972
1071
|
# that don't support the operators natively.
|
973
1072
|
def complex_expression_emulate_append(sql, op, args)
|
1073
|
+
# :nocov:
|
974
1074
|
case op
|
1075
|
+
# :nocov:
|
975
1076
|
when :%
|
976
1077
|
complex_expression_arg_pairs_append(sql, args){|a, b| Sequel.function(:MOD, a, b)}
|
977
1078
|
when :>>
|
@@ -1144,7 +1245,10 @@ module Sequel
|
|
1144
1245
|
end
|
1145
1246
|
|
1146
1247
|
def insert_columns_sql(sql)
|
1147
|
-
|
1248
|
+
_insert_columns_sql(sql, opts[:columns])
|
1249
|
+
end
|
1250
|
+
|
1251
|
+
def _insert_columns_sql(sql, columns)
|
1148
1252
|
if columns && !columns.empty?
|
1149
1253
|
sql << ' ('
|
1150
1254
|
identifier_list_append(sql, columns)
|
@@ -1163,7 +1267,11 @@ module Sequel
|
|
1163
1267
|
end
|
1164
1268
|
|
1165
1269
|
def insert_values_sql(sql)
|
1166
|
-
|
1270
|
+
_insert_values_sql(sql, opts[:values])
|
1271
|
+
end
|
1272
|
+
|
1273
|
+
def _insert_values_sql(sql, values)
|
1274
|
+
case values
|
1167
1275
|
when Array
|
1168
1276
|
if values.empty?
|
1169
1277
|
sql << " DEFAULT VALUES"
|
@@ -1196,6 +1304,13 @@ module Sequel
|
|
1196
1304
|
"#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
|
1197
1305
|
end
|
1198
1306
|
|
1307
|
+
# Append USING clause for JOIN USING
|
1308
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
1309
|
+
sql << ' USING ('
|
1310
|
+
column_list_append(sql, using_columns)
|
1311
|
+
sql << ')'
|
1312
|
+
end
|
1313
|
+
|
1199
1314
|
# Append a literalization of the array to SQL string.
|
1200
1315
|
# Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
1201
1316
|
def literal_array_append(sql, v)
|
@@ -1516,15 +1631,14 @@ module Sequel
|
|
1516
1631
|
|
1517
1632
|
def select_with_sql(sql)
|
1518
1633
|
return unless supports_cte?
|
1519
|
-
|
1520
|
-
return if !
|
1634
|
+
ctes = opts[:with]
|
1635
|
+
return if !ctes || ctes.empty?
|
1521
1636
|
sql << select_with_sql_base
|
1522
1637
|
c = false
|
1523
1638
|
comma = ', '
|
1524
|
-
|
1639
|
+
ctes.each do |cte|
|
1525
1640
|
sql << comma if c
|
1526
|
-
|
1527
|
-
literal_dataset_append(sql, w[:dataset])
|
1641
|
+
select_with_sql_cte(sql, cte)
|
1528
1642
|
c ||= true
|
1529
1643
|
end
|
1530
1644
|
sql << ' '
|
@@ -1537,6 +1651,11 @@ module Sequel
|
|
1537
1651
|
"WITH "
|
1538
1652
|
end
|
1539
1653
|
|
1654
|
+
def select_with_sql_cte(sql, cte)
|
1655
|
+
select_with_sql_prefix(sql, cte)
|
1656
|
+
literal_dataset_append(sql, cte[:dataset])
|
1657
|
+
end
|
1658
|
+
|
1540
1659
|
def select_with_sql_prefix(sql, w)
|
1541
1660
|
quote_identifier_append(sql, w[:name])
|
1542
1661
|
if args = w[:args]
|
@@ -1545,6 +1664,13 @@ module Sequel
|
|
1545
1664
|
sql << ')'
|
1546
1665
|
end
|
1547
1666
|
sql << ' AS '
|
1667
|
+
|
1668
|
+
case w[:materialized]
|
1669
|
+
when true
|
1670
|
+
sql << "MATERIALIZED "
|
1671
|
+
when false
|
1672
|
+
sql << "NOT MATERIALIZED "
|
1673
|
+
end
|
1548
1674
|
end
|
1549
1675
|
|
1550
1676
|
# Whether the symbol cache should be skipped when literalizing the dataset
|
data/lib/sequel/deprecated.rb
CHANGED
@@ -39,7 +39,7 @@ module Sequel
|
|
39
39
|
# Print the message and possibly backtrace to the output.
|
40
40
|
def self.deprecate(method, instead=nil)
|
41
41
|
return unless output
|
42
|
-
message = instead ? "#{method} is deprecated and will be removed in Sequel
|
42
|
+
message = instead ? "#{method} is deprecated and will be removed in Sequel 6. #{instead}." : method
|
43
43
|
message = "#{prefix}#{message}" if prefix
|
44
44
|
output.puts(message)
|
45
45
|
case b = backtrace_filter
|
@@ -60,7 +60,9 @@ module Sequel
|
|
60
60
|
# If using ruby 2.3+, use Module#deprecate_constant to deprecate the constant,
|
61
61
|
# otherwise do nothing as the ruby implementation does not support constant deprecation.
|
62
62
|
def self.deprecate_constant(mod, constant)
|
63
|
+
# :nocov:
|
63
64
|
if RUBY_VERSION > '2.3'
|
65
|
+
# :nocov:
|
64
66
|
mod.deprecate_constant(constant)
|
65
67
|
end
|
66
68
|
end
|
data/lib/sequel/exceptions.rb
CHANGED