sequel 5.33.0 → 5.58.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 +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