sequel 5.45.0 → 5.77.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 +434 -0
- data/MIT-LICENSE +1 -1
- data/README.rdoc +59 -27
- data/bin/sequel +11 -3
- data/doc/advanced_associations.rdoc +16 -14
- data/doc/association_basics.rdoc +119 -24
- data/doc/cheat_sheet.rdoc +11 -3
- data/doc/mass_assignment.rdoc +1 -1
- data/doc/migration.rdoc +27 -6
- data/doc/model_hooks.rdoc +1 -1
- data/doc/object_model.rdoc +8 -8
- data/doc/opening_databases.rdoc +28 -12
- data/doc/postgresql.rdoc +16 -8
- data/doc/querying.rdoc +5 -3
- 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/release_notes/5.59.0.txt +73 -0
- data/doc/release_notes/5.60.0.txt +22 -0
- data/doc/release_notes/5.61.0.txt +43 -0
- data/doc/release_notes/5.62.0.txt +132 -0
- data/doc/release_notes/5.63.0.txt +33 -0
- data/doc/release_notes/5.64.0.txt +50 -0
- data/doc/release_notes/5.65.0.txt +21 -0
- data/doc/release_notes/5.66.0.txt +24 -0
- data/doc/release_notes/5.67.0.txt +32 -0
- data/doc/release_notes/5.68.0.txt +61 -0
- data/doc/release_notes/5.69.0.txt +26 -0
- data/doc/release_notes/5.70.0.txt +35 -0
- data/doc/release_notes/5.71.0.txt +21 -0
- data/doc/release_notes/5.72.0.txt +33 -0
- data/doc/release_notes/5.73.0.txt +66 -0
- data/doc/release_notes/5.74.0.txt +45 -0
- data/doc/release_notes/5.75.0.txt +35 -0
- data/doc/release_notes/5.76.0.txt +86 -0
- data/doc/release_notes/5.77.0.txt +63 -0
- data/doc/schema_modification.rdoc +1 -1
- data/doc/security.rdoc +9 -9
- data/doc/sharding.rdoc +3 -1
- data/doc/sql.rdoc +27 -15
- data/doc/testing.rdoc +23 -13
- data/doc/transactions.rdoc +6 -6
- data/doc/virtual_rows.rdoc +1 -1
- data/lib/sequel/adapters/ado/access.rb +1 -1
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/amalgalite.rb +3 -5
- data/lib/sequel/adapters/ibmdb.rb +3 -3
- data/lib/sequel/adapters/jdbc/derby.rb +8 -0
- data/lib/sequel/adapters/jdbc/h2.rb +63 -10
- data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
- data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
- data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
- data/lib/sequel/adapters/jdbc.rb +24 -22
- data/lib/sequel/adapters/mysql.rb +92 -67
- data/lib/sequel/adapters/mysql2.rb +56 -51
- data/lib/sequel/adapters/odbc/mssql.rb +1 -1
- data/lib/sequel/adapters/odbc.rb +1 -1
- data/lib/sequel/adapters/oracle.rb +4 -3
- data/lib/sequel/adapters/postgres.rb +89 -45
- data/lib/sequel/adapters/shared/access.rb +11 -1
- data/lib/sequel/adapters/shared/db2.rb +42 -0
- data/lib/sequel/adapters/shared/mssql.rb +91 -10
- data/lib/sequel/adapters/shared/mysql.rb +78 -3
- data/lib/sequel/adapters/shared/oracle.rb +86 -7
- data/lib/sequel/adapters/shared/postgres.rb +576 -171
- data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
- data/lib/sequel/adapters/shared/sqlite.rb +92 -8
- data/lib/sequel/adapters/sqlanywhere.rb +1 -1
- data/lib/sequel/adapters/sqlite.rb +99 -18
- data/lib/sequel/adapters/tinytds.rb +1 -1
- data/lib/sequel/adapters/trilogy.rb +117 -0
- data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
- data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
- data/lib/sequel/ast_transformer.rb +6 -0
- data/lib/sequel/connection_pool/sharded_single.rb +5 -7
- data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
- data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
- data/lib/sequel/connection_pool/single.rb +6 -8
- data/lib/sequel/connection_pool/threaded.rb +14 -8
- data/lib/sequel/connection_pool/timed_queue.rb +270 -0
- data/lib/sequel/connection_pool.rb +57 -31
- data/lib/sequel/core.rb +17 -18
- data/lib/sequel/database/connecting.rb +27 -3
- data/lib/sequel/database/dataset.rb +16 -6
- data/lib/sequel/database/misc.rb +70 -14
- data/lib/sequel/database/query.rb +73 -2
- data/lib/sequel/database/schema_generator.rb +11 -6
- data/lib/sequel/database/schema_methods.rb +23 -4
- data/lib/sequel/database/transactions.rb +6 -0
- data/lib/sequel/dataset/actions.rb +111 -15
- data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
- data/lib/sequel/dataset/features.rb +20 -1
- data/lib/sequel/dataset/misc.rb +12 -2
- data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
- data/lib/sequel/dataset/query.rb +170 -41
- data/lib/sequel/dataset/sql.rb +190 -71
- data/lib/sequel/dataset.rb +4 -0
- data/lib/sequel/extensions/_model_pg_row.rb +0 -12
- data/lib/sequel/extensions/_pretty_table.rb +1 -1
- data/lib/sequel/extensions/any_not_empty.rb +2 -2
- data/lib/sequel/extensions/async_thread_pool.rb +14 -13
- data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
- data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
- data/lib/sequel/extensions/connection_expiration.rb +15 -9
- data/lib/sequel/extensions/connection_validator.rb +16 -11
- data/lib/sequel/extensions/constraint_validations.rb +1 -1
- data/lib/sequel/extensions/core_refinements.rb +36 -11
- data/lib/sequel/extensions/date_arithmetic.rb +36 -8
- 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 +11 -10
- data/lib/sequel/extensions/index_caching.rb +5 -1
- data/lib/sequel/extensions/inflector.rb +1 -1
- data/lib/sequel/extensions/is_distinct_from.rb +141 -0
- data/lib/sequel/extensions/looser_typecasting.rb +3 -0
- data/lib/sequel/extensions/migration.rb +57 -15
- data/lib/sequel/extensions/named_timezones.rb +22 -6
- data/lib/sequel/extensions/pagination.rb +1 -1
- data/lib/sequel/extensions/pg_array.rb +33 -4
- data/lib/sequel/extensions/pg_array_ops.rb +2 -2
- data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
- data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
- data/lib/sequel/extensions/pg_enum.rb +1 -2
- data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
- data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
- data/lib/sequel/extensions/pg_hstore.rb +6 -1
- data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
- data/lib/sequel/extensions/pg_inet.rb +10 -11
- data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
- data/lib/sequel/extensions/pg_interval.rb +11 -11
- data/lib/sequel/extensions/pg_json.rb +13 -15
- data/lib/sequel/extensions/pg_json_ops.rb +125 -2
- data/lib/sequel/extensions/pg_multirange.rb +367 -0
- data/lib/sequel/extensions/pg_range.rb +13 -26
- data/lib/sequel/extensions/pg_range_ops.rb +37 -9
- data/lib/sequel/extensions/pg_row.rb +20 -19
- data/lib/sequel/extensions/pg_row_ops.rb +1 -1
- data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
- data/lib/sequel/extensions/round_timestamps.rb +1 -1
- data/lib/sequel/extensions/s.rb +2 -1
- data/lib/sequel/extensions/schema_caching.rb +1 -1
- data/lib/sequel/extensions/schema_dumper.rb +45 -11
- data/lib/sequel/extensions/server_block.rb +10 -13
- data/lib/sequel/extensions/set_literalizer.rb +58 -0
- 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.rb +2 -0
- data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
- data/lib/sequel/model/associations.rb +286 -92
- data/lib/sequel/model/base.rb +53 -33
- data/lib/sequel/model/dataset_module.rb +3 -0
- data/lib/sequel/model/errors.rb +10 -1
- data/lib/sequel/model/exceptions.rb +15 -3
- data/lib/sequel/model/inflections.rb +1 -1
- data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
- data/lib/sequel/plugins/auto_validations.rb +74 -16
- data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
- data/lib/sequel/plugins/column_encryption.rb +29 -8
- data/lib/sequel/plugins/composition.rb +3 -2
- data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
- data/lib/sequel/plugins/constraint_validations.rb +8 -5
- data/lib/sequel/plugins/defaults_setter.rb +16 -0
- data/lib/sequel/plugins/dirty.rb +1 -1
- data/lib/sequel/plugins/enum.rb +124 -0
- data/lib/sequel/plugins/finder.rb +4 -2
- data/lib/sequel/plugins/insert_conflict.rb +4 -0
- data/lib/sequel/plugins/instance_specific_default.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +2 -2
- data/lib/sequel/plugins/lazy_attributes.rb +3 -0
- data/lib/sequel/plugins/list.rb +8 -3
- data/lib/sequel/plugins/many_through_many.rb +109 -10
- data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
- data/lib/sequel/plugins/nested_attributes.rb +4 -4
- data/lib/sequel/plugins/optimistic_locking.rb +9 -42
- data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
- data/lib/sequel/plugins/paged_operations.rb +181 -0
- data/lib/sequel/plugins/pg_array_associations.rb +46 -34
- data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
- data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
- data/lib/sequel/plugins/prepared_statements.rb +12 -2
- data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
- data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
- data/lib/sequel/plugins/rcte_tree.rb +7 -4
- data/lib/sequel/plugins/require_valid_schema.rb +67 -0
- data/lib/sequel/plugins/serialization.rb +1 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
- data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
- data/lib/sequel/plugins/sql_comments.rb +189 -0
- data/lib/sequel/plugins/static_cache.rb +39 -1
- data/lib/sequel/plugins/static_cache_cache.rb +5 -1
- data/lib/sequel/plugins/subclasses.rb +28 -11
- data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
- data/lib/sequel/plugins/timestamps.rb +1 -1
- data/lib/sequel/plugins/unused_associations.rb +521 -0
- data/lib/sequel/plugins/update_or_create.rb +1 -1
- data/lib/sequel/plugins/validate_associated.rb +22 -12
- data/lib/sequel/plugins/validation_helpers.rb +41 -11
- data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
- data/lib/sequel/plugins/xml_serializer.rb +1 -1
- data/lib/sequel/sql.rb +1 -1
- data/lib/sequel/timezones.rb +12 -14
- data/lib/sequel/version.rb +1 -1
- metadata +109 -19
data/lib/sequel/dataset/sql.rb
CHANGED
@@ -24,29 +24,7 @@ module Sequel
|
|
24
24
|
|
25
25
|
check_insert_allowed!
|
26
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
|
49
|
-
|
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)
|
@@ -104,7 +82,7 @@ module Sequel
|
|
104
82
|
when DateTime
|
105
83
|
literal_datetime_append(sql, v)
|
106
84
|
when Date
|
107
|
-
sql
|
85
|
+
literal_date_append(sql, v)
|
108
86
|
when Dataset
|
109
87
|
literal_dataset_append(sql, v)
|
110
88
|
else
|
@@ -112,6 +90,58 @@ 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
|
+
|
118
|
+
# Literalize a date or time value, as a SQL string value with no
|
119
|
+
# typecasting. If +raw+ is true, remove the surrounding single
|
120
|
+
# quotes. This is designed for usage by bound argument code that
|
121
|
+
# can work even if the auto_cast_date_and_time extension is
|
122
|
+
# used (either manually or implicitly in the related adapter).
|
123
|
+
def literal_date_or_time(dt, raw=false)
|
124
|
+
value = case dt
|
125
|
+
when SQLTime
|
126
|
+
literal_sqltime(dt)
|
127
|
+
when Time
|
128
|
+
literal_time(dt)
|
129
|
+
when DateTime
|
130
|
+
literal_datetime(dt)
|
131
|
+
when Date
|
132
|
+
literal_date(dt)
|
133
|
+
else
|
134
|
+
raise TypeError, "unsupported type: #{dt.inspect}"
|
135
|
+
end
|
136
|
+
|
137
|
+
if raw
|
138
|
+
value.sub!(/\A'/, '')
|
139
|
+
value.sub!(/'\z/, '')
|
140
|
+
end
|
141
|
+
|
142
|
+
value
|
143
|
+
end
|
144
|
+
|
115
145
|
# Returns an array of insert statements for inserting multiple records.
|
116
146
|
# This method is used by +multi_insert+ to format insert statements and
|
117
147
|
# expects a keys array and and an array of value arrays.
|
@@ -559,11 +589,9 @@ module Sequel
|
|
559
589
|
# Append literalization of JOIN USING clause to SQL string.
|
560
590
|
def join_using_clause_sql_append(sql, jc)
|
561
591
|
join_clause_sql_append(sql, jc)
|
562
|
-
sql
|
563
|
-
column_list_append(sql, jc.using)
|
564
|
-
sql << ')'
|
592
|
+
join_using_clause_using_sql_append(sql, jc.using)
|
565
593
|
end
|
566
|
-
|
594
|
+
|
567
595
|
# Append literalization of negative boolean constant to SQL string.
|
568
596
|
def negative_boolean_constant_sql_append(sql, constant)
|
569
597
|
sql << 'NOT '
|
@@ -852,6 +880,83 @@ module Sequel
|
|
852
880
|
|
853
881
|
private
|
854
882
|
|
883
|
+
# Append the INSERT sql used in a MERGE
|
884
|
+
def _merge_insert_sql(sql, data)
|
885
|
+
sql << " THEN INSERT"
|
886
|
+
columns, values = _parse_insert_sql_args(data[:values])
|
887
|
+
_insert_columns_sql(sql, columns)
|
888
|
+
_insert_values_sql(sql, values)
|
889
|
+
end
|
890
|
+
|
891
|
+
def _merge_update_sql(sql, data)
|
892
|
+
sql << " THEN UPDATE SET "
|
893
|
+
update_sql_values_hash(sql, data[:values])
|
894
|
+
end
|
895
|
+
|
896
|
+
def _merge_delete_sql(sql, data)
|
897
|
+
sql << " THEN DELETE"
|
898
|
+
end
|
899
|
+
|
900
|
+
# Mapping of merge types to related SQL
|
901
|
+
MERGE_TYPE_SQL = {
|
902
|
+
:insert => ' WHEN NOT MATCHED',
|
903
|
+
:delete => ' WHEN MATCHED',
|
904
|
+
:update => ' WHEN MATCHED',
|
905
|
+
:matched => ' WHEN MATCHED',
|
906
|
+
:not_matched => ' WHEN NOT MATCHED',
|
907
|
+
}.freeze
|
908
|
+
private_constant :MERGE_TYPE_SQL
|
909
|
+
|
910
|
+
# Add the WHEN clauses to the MERGE SQL
|
911
|
+
def _merge_when_sql(sql)
|
912
|
+
raise Error, "no WHEN [NOT] MATCHED clauses provided for MERGE" unless merge_when = @opts[:merge_when]
|
913
|
+
merge_when.each do |data|
|
914
|
+
type = data[:type]
|
915
|
+
sql << MERGE_TYPE_SQL[type]
|
916
|
+
_merge_when_conditions_sql(sql, data)
|
917
|
+
send(:"_merge_#{type}_sql", sql, data)
|
918
|
+
end
|
919
|
+
end
|
920
|
+
|
921
|
+
# Append MERGE WHEN conditions, if there are conditions provided.
|
922
|
+
def _merge_when_conditions_sql(sql, data)
|
923
|
+
if data.has_key?(:conditions)
|
924
|
+
sql << " AND "
|
925
|
+
literal_append(sql, data[:conditions])
|
926
|
+
end
|
927
|
+
end
|
928
|
+
|
929
|
+
# Parse the values passed to insert_sql, returning columns and values
|
930
|
+
# to use for the INSERT. Returned columns is always an array, but can be empty
|
931
|
+
# for an INSERT without explicit column references. Returned values can be an
|
932
|
+
# array, dataset, or literal string.
|
933
|
+
def _parse_insert_sql_args(values)
|
934
|
+
columns = []
|
935
|
+
|
936
|
+
case values.size
|
937
|
+
when 0
|
938
|
+
values = []
|
939
|
+
when 1
|
940
|
+
case vals = values[0]
|
941
|
+
when Hash
|
942
|
+
values = []
|
943
|
+
vals.each do |k,v|
|
944
|
+
columns << k
|
945
|
+
values << v
|
946
|
+
end
|
947
|
+
when Dataset, Array, LiteralString
|
948
|
+
values = vals
|
949
|
+
end
|
950
|
+
when 2
|
951
|
+
if (v0 = values[0]).is_a?(Array) && ((v1 = values[1]).is_a?(Array) || v1.is_a?(Dataset) || v1.is_a?(LiteralString))
|
952
|
+
columns, values = v0, v1
|
953
|
+
raise(Error, "Different number of values and columns given to insert_sql") if values.is_a?(Array) and columns.length != values.length
|
954
|
+
end
|
955
|
+
end
|
956
|
+
|
957
|
+
[columns, values]
|
958
|
+
end
|
959
|
+
|
855
960
|
# Formats the truncate statement. Assumes the table given has already been
|
856
961
|
# literalized.
|
857
962
|
def _truncate_sql(table)
|
@@ -896,9 +1001,15 @@ module Sequel
|
|
896
1001
|
# Clone of this dataset usable in aggregate operations. Does
|
897
1002
|
# a from_self if dataset contains any parameters that would
|
898
1003
|
# affect normal aggregation, or just removes an existing
|
899
|
-
# order if not.
|
1004
|
+
# order if not. Also removes the row_proc, which isn't needed
|
1005
|
+
# for aggregate calculations.
|
900
1006
|
def aggregate_dataset
|
901
|
-
|
1007
|
+
(aggreate_dataset_use_from_self? ? from_self : unordered).naked
|
1008
|
+
end
|
1009
|
+
|
1010
|
+
# Whether to use from_self for an aggregate dataset.
|
1011
|
+
def aggreate_dataset_use_from_self?
|
1012
|
+
options_overlap(COUNT_FROM_SELF_OPTS)
|
902
1013
|
end
|
903
1014
|
|
904
1015
|
# Append aliasing expression to SQL string.
|
@@ -1020,9 +1131,14 @@ module Sequel
|
|
1020
1131
|
:"t#{number}"
|
1021
1132
|
end
|
1022
1133
|
|
1023
|
-
# The strftime format to use when literalizing
|
1134
|
+
# The strftime format to use when literalizing time (Sequel::SQLTime) values.
|
1135
|
+
def default_time_format
|
1136
|
+
"'%H:%M:%S.%6N'"
|
1137
|
+
end
|
1138
|
+
|
1139
|
+
# The strftime format to use when literalizing timestamp (Time/DateTime) values.
|
1024
1140
|
def default_timestamp_format
|
1025
|
-
|
1141
|
+
"'%Y-%m-%d %H:%M:%S.%6N'"
|
1026
1142
|
end
|
1027
1143
|
|
1028
1144
|
def delete_delete_sql(sql)
|
@@ -1085,43 +1201,23 @@ module Sequel
|
|
1085
1201
|
{1 => ((op == :IN) ? 0 : 1)}
|
1086
1202
|
end
|
1087
1203
|
|
1088
|
-
# Format the timestamp based on the default_timestamp_format
|
1089
|
-
# of modifiers. First, allow %N to be used for fractions seconds (if the
|
1090
|
-
# database supports them), and override %z to always use a numeric offset
|
1091
|
-
# of hours and minutes.
|
1204
|
+
# Format the timestamp based on the default_timestamp_format.
|
1092
1205
|
def format_timestamp(v)
|
1093
|
-
|
1094
|
-
fmt = default_timestamp_format.gsub(/%[Nz]/) do |m|
|
1095
|
-
if m == '%N'
|
1096
|
-
# Ruby 1.9 supports %N in timestamp formats, but Sequel has supported %N
|
1097
|
-
# for longer in a different way, where the . is already appended and only 6
|
1098
|
-
# decimal places are used by default.
|
1099
|
-
format_timestamp_usec(v.is_a?(DateTime) ? v.sec_fraction*(1000000) : v.usec) if supports_timestamp_usecs?
|
1100
|
-
else
|
1101
|
-
if supports_timestamp_timezones?
|
1102
|
-
# Would like to just use %z format, but it doesn't appear to work on Windows
|
1103
|
-
# Instead, the offset fragment is constructed manually
|
1104
|
-
minutes = (v2.is_a?(DateTime) ? v2.offset * 1440 : v2.utc_offset/60).to_i
|
1105
|
-
format_timestamp_offset(*minutes.divmod(60))
|
1106
|
-
end
|
1107
|
-
end
|
1108
|
-
end
|
1109
|
-
v2.strftime(fmt)
|
1206
|
+
db.from_application_timestamp(v).strftime(default_timestamp_format)
|
1110
1207
|
end
|
1111
1208
|
|
1112
|
-
#
|
1113
|
-
def format_timestamp_offset(hour, minute)
|
1114
|
-
sprintf("%+03i%02i", hour, minute)
|
1115
|
-
end
|
1209
|
+
# :nocov:
|
1116
1210
|
|
1117
1211
|
# Return the SQL timestamp fragment to use for the fractional time part.
|
1118
1212
|
# Should start with the decimal point. Uses 6 decimal places by default.
|
1119
1213
|
def format_timestamp_usec(usec, ts=timestamp_precision)
|
1214
|
+
# SEQUEL6: Remove
|
1120
1215
|
unless ts == 6
|
1121
1216
|
usec = usec/(10 ** (6 - ts))
|
1122
1217
|
end
|
1123
1218
|
sprintf(".%0#{ts}d", usec)
|
1124
1219
|
end
|
1220
|
+
# :nocov:
|
1125
1221
|
|
1126
1222
|
# Append literalization of identifier to SQL string, considering regular strings
|
1127
1223
|
# as SQL identifiers instead of SQL strings.
|
@@ -1166,7 +1262,10 @@ module Sequel
|
|
1166
1262
|
end
|
1167
1263
|
|
1168
1264
|
def insert_columns_sql(sql)
|
1169
|
-
|
1265
|
+
_insert_columns_sql(sql, opts[:columns])
|
1266
|
+
end
|
1267
|
+
|
1268
|
+
def _insert_columns_sql(sql, columns)
|
1170
1269
|
if columns && !columns.empty?
|
1171
1270
|
sql << ' ('
|
1172
1271
|
identifier_list_append(sql, columns)
|
@@ -1185,7 +1284,11 @@ module Sequel
|
|
1185
1284
|
end
|
1186
1285
|
|
1187
1286
|
def insert_values_sql(sql)
|
1188
|
-
|
1287
|
+
_insert_values_sql(sql, opts[:values])
|
1288
|
+
end
|
1289
|
+
|
1290
|
+
def _insert_values_sql(sql, values)
|
1291
|
+
case values
|
1189
1292
|
when Array
|
1190
1293
|
if values.empty?
|
1191
1294
|
sql << " DEFAULT VALUES"
|
@@ -1218,6 +1321,13 @@ module Sequel
|
|
1218
1321
|
"#{join_type.to_s.gsub('_', ' ').upcase} JOIN"
|
1219
1322
|
end
|
1220
1323
|
|
1324
|
+
# Append USING clause for JOIN USING
|
1325
|
+
def join_using_clause_using_sql_append(sql, using_columns)
|
1326
|
+
sql << ' USING ('
|
1327
|
+
column_list_append(sql, using_columns)
|
1328
|
+
sql << ')'
|
1329
|
+
end
|
1330
|
+
|
1221
1331
|
# Append a literalization of the array to SQL string.
|
1222
1332
|
# Treats as an expression if an array of all two pairs, or as a SQL array otherwise.
|
1223
1333
|
def literal_array_append(sql, v)
|
@@ -1249,11 +1359,12 @@ module Sequel
|
|
1249
1359
|
|
1250
1360
|
# SQL fragment for Date, using the ISO8601 format.
|
1251
1361
|
def literal_date(v)
|
1252
|
-
|
1253
|
-
|
1254
|
-
|
1255
|
-
|
1256
|
-
|
1362
|
+
v.strftime("'%Y-%m-%d'")
|
1363
|
+
end
|
1364
|
+
|
1365
|
+
# Append literalization of date to SQL string.
|
1366
|
+
def literal_date_append(sql, v)
|
1367
|
+
sql << literal_date(v)
|
1257
1368
|
end
|
1258
1369
|
|
1259
1370
|
# SQL fragment for DateTime
|
@@ -1316,7 +1427,7 @@ module Sequel
|
|
1316
1427
|
|
1317
1428
|
# SQL fragment for Sequel::SQLTime, containing just the time part
|
1318
1429
|
def literal_sqltime(v)
|
1319
|
-
v.strftime(
|
1430
|
+
v.strftime(default_time_format)
|
1320
1431
|
end
|
1321
1432
|
|
1322
1433
|
# Append literalization of Sequel::SQLTime to SQL string.
|
@@ -1538,15 +1649,14 @@ module Sequel
|
|
1538
1649
|
|
1539
1650
|
def select_with_sql(sql)
|
1540
1651
|
return unless supports_cte?
|
1541
|
-
|
1542
|
-
return if !
|
1652
|
+
ctes = opts[:with]
|
1653
|
+
return if !ctes || ctes.empty?
|
1543
1654
|
sql << select_with_sql_base
|
1544
1655
|
c = false
|
1545
1656
|
comma = ', '
|
1546
|
-
|
1657
|
+
ctes.each do |cte|
|
1547
1658
|
sql << comma if c
|
1548
|
-
|
1549
|
-
literal_dataset_append(sql, w[:dataset])
|
1659
|
+
select_with_sql_cte(sql, cte)
|
1550
1660
|
c ||= true
|
1551
1661
|
end
|
1552
1662
|
sql << ' '
|
@@ -1559,6 +1669,11 @@ module Sequel
|
|
1559
1669
|
"WITH "
|
1560
1670
|
end
|
1561
1671
|
|
1672
|
+
def select_with_sql_cte(sql, cte)
|
1673
|
+
select_with_sql_prefix(sql, cte)
|
1674
|
+
literal_dataset_append(sql, cte[:dataset])
|
1675
|
+
end
|
1676
|
+
|
1562
1677
|
def select_with_sql_prefix(sql, w)
|
1563
1678
|
quote_identifier_append(sql, w[:name])
|
1564
1679
|
if args = w[:args]
|
@@ -1628,7 +1743,7 @@ module Sequel
|
|
1628
1743
|
# Append literalization of the subselect to SQL string.
|
1629
1744
|
def subselect_sql_append(sql, ds)
|
1630
1745
|
sds = subselect_sql_dataset(sql, ds)
|
1631
|
-
sds
|
1746
|
+
subselect_sql_append_sql(sql, sds)
|
1632
1747
|
unless sds.send(:cache_sql?)
|
1633
1748
|
# If subquery dataset does not allow caching SQL,
|
1634
1749
|
# then this dataset should not allow caching SQL.
|
@@ -1640,6 +1755,10 @@ module Sequel
|
|
1640
1755
|
ds.clone(:append_sql=>sql)
|
1641
1756
|
end
|
1642
1757
|
|
1758
|
+
def subselect_sql_append_sql(sql, ds)
|
1759
|
+
ds.sql
|
1760
|
+
end
|
1761
|
+
|
1643
1762
|
# The number of decimal digits of precision to use in timestamps.
|
1644
1763
|
def timestamp_precision
|
1645
1764
|
supports_timestamp_usecs? ? 6 : 0
|
data/lib/sequel/dataset.rb
CHANGED
@@ -53,4 +53,8 @@ module Sequel
|
|
53
53
|
require_relative "dataset/sql"
|
54
54
|
require_relative "dataset/placeholder_literalizer"
|
55
55
|
require_relative "dataset/dataset_module"
|
56
|
+
|
57
|
+
# :nocov:
|
58
|
+
require_relative "dataset/deprecated_singleton_class_methods" if Dataset::TRUE_FREEZE
|
59
|
+
# :nocov:
|
56
60
|
end
|
@@ -23,18 +23,6 @@ module Sequel
|
|
23
23
|
super
|
24
24
|
end
|
25
25
|
end
|
26
|
-
|
27
|
-
private
|
28
|
-
|
29
|
-
# Handle Sequel::Model instances in bound variable arrays.
|
30
|
-
def bound_variable_array(arg)
|
31
|
-
case arg
|
32
|
-
when Sequel::Model
|
33
|
-
"\"(#{arg.values.values_at(*arg.columns).map{|v| bound_variable_array(v)}.join(',').gsub(/("|\\)/, '\\\\\1')})\""
|
34
|
-
else
|
35
|
-
super
|
36
|
-
end
|
37
|
-
end
|
38
26
|
end
|
39
27
|
end
|
40
28
|
end
|
@@ -5,9 +5,9 @@
|
|
5
5
|
# code
|
6
6
|
#
|
7
7
|
# DB.extension :async_thread_pool
|
8
|
-
# foos = DB[:foos].async.where
|
8
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
9
9
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
10
|
-
# baz_1 = DB[:bazes].async.first(:
|
10
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
11
11
|
#
|
12
12
|
# All 3 queries will be run in separate threads. +foos+, +bar_names+
|
13
13
|
# and +baz_1+ will be proxy objects. Calling a method on the proxy
|
@@ -15,9 +15,9 @@
|
|
15
15
|
# of calling that method on the result of the query method. For example,
|
16
16
|
# if you run:
|
17
17
|
#
|
18
|
-
# foos = DB[:foos].async.where
|
18
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
19
19
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
20
|
-
# baz_1 = DB[:bazes].async.first(:
|
20
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
21
21
|
# sleep(1)
|
22
22
|
# foos.size
|
23
23
|
# bar_names.first
|
@@ -26,9 +26,9 @@
|
|
26
26
|
# These three queries will generally be run concurrently in separate
|
27
27
|
# threads. If you instead run:
|
28
28
|
#
|
29
|
-
# DB[:foos].async.where
|
29
|
+
# DB[:foos].async.where(name: 'A'..'M').all.size
|
30
30
|
# DB[:bars].async.select_order_map(:name).first
|
31
|
-
# DB[:bazes].async.first(:
|
31
|
+
# DB[:bazes].async.first(id: 1).name
|
32
32
|
#
|
33
33
|
# Then will run each query sequentially, since you need the result of
|
34
34
|
# one query before running the next query. The queries will still be
|
@@ -37,11 +37,11 @@
|
|
37
37
|
# What is run in the separate thread is the entire method call that
|
38
38
|
# returns results. So with the original example:
|
39
39
|
#
|
40
|
-
# foos = DB[:foos].async.where
|
40
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
41
41
|
# bar_names = DB[:bars].async.select_order_map(:name)
|
42
|
-
# baz_1 = DB[:bazes].async.first(:
|
42
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
43
43
|
#
|
44
|
-
# The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(:
|
44
|
+
# The +all+, <tt>select_order_map(:name)</tt>, and <tt>first(id: 1)</tt>
|
45
45
|
# calls are run in separate threads. If a block is passed to a method
|
46
46
|
# such as +all+ or +each+, the block is also run in that thread. If you
|
47
47
|
# have code such as:
|
@@ -156,10 +156,10 @@
|
|
156
156
|
# so that the query will run in the current thread instead of waiting
|
157
157
|
# for an async thread to become available. With the following code:
|
158
158
|
#
|
159
|
-
# foos = DB[:foos].async.where
|
159
|
+
# foos = DB[:foos].async.where(name: 'A'..'M').all
|
160
160
|
# bar_names = DB[:bar].async.select_order_map(:name)
|
161
161
|
# if foos.length > 4
|
162
|
-
# baz_1 = DB[:bazes].async.first(:
|
162
|
+
# baz_1 = DB[:bazes].async.first(id: 1)
|
163
163
|
# end
|
164
164
|
#
|
165
165
|
# Whether you need the +baz_1+ variable depends on the value of foos.
|
@@ -338,8 +338,9 @@ module Sequel
|
|
338
338
|
module DatabaseMethods
|
339
339
|
def self.extended(db)
|
340
340
|
db.instance_exec do
|
341
|
-
|
342
|
-
|
341
|
+
case pool.pool_type
|
342
|
+
when :single, :sharded_single
|
343
|
+
raise Error, "cannot load async_thread_pool extension if using single or sharded_single connection pool"
|
343
344
|
end
|
344
345
|
|
345
346
|
num_async_threads = opts[:num_async_threads] ? typecast_value_integer(opts[:num_async_threads]) : (Integer(opts[:max_connections] || 4))
|
@@ -0,0 +1,94 @@
|
|
1
|
+
# frozen-string-literal: true
|
2
|
+
#
|
3
|
+
# The auto_cast_date_and_time extension uses SQL standard type casting
|
4
|
+
# when literalizing date, time, and timestamp values:
|
5
|
+
#
|
6
|
+
# DB.literal(Time.now)
|
7
|
+
# # => "TIMESTAMP '...'"
|
8
|
+
#
|
9
|
+
# DB.literal(Date.today)
|
10
|
+
# # => "DATE '...'"
|
11
|
+
#
|
12
|
+
# DB.literal(Sequel::SQLTime.create(10, 20, 30))
|
13
|
+
# # => "TIME '10:20:30.000000'"
|
14
|
+
#
|
15
|
+
# The default behavior of Sequel on adapters that do not require the
|
16
|
+
# SQL standard behavior is to format the date or time value without:
|
17
|
+
# casting
|
18
|
+
#
|
19
|
+
# DB.literal(Sequel::SQLTime.create(10, 20, 30))
|
20
|
+
# # => "'10:20:30.000000'"
|
21
|
+
#
|
22
|
+
# However, then the database cannot determine the type of the string,
|
23
|
+
# and must perform some implicit casting. If implicit casting cannot
|
24
|
+
# be used, it will probably treat the value as a string:
|
25
|
+
#
|
26
|
+
# DB.get(Time.now).class
|
27
|
+
# # Without auto_cast_date_and_time: String
|
28
|
+
# # With auto_cast_date_and_time: Time
|
29
|
+
#
|
30
|
+
# Note that not all databases support this extension. PostgreSQL and
|
31
|
+
# MySQL support it, but SQLite and Microsoft SQL Server do not.
|
32
|
+
#
|
33
|
+
# You can load this extension into specific datasets:
|
34
|
+
#
|
35
|
+
# ds = DB[:table]
|
36
|
+
# ds = ds.extension(:auto_cast_date_and_time)
|
37
|
+
#
|
38
|
+
# Or you can load it into all of a database's datasets, which
|
39
|
+
# is probably the desired behavior if you are using this extension:
|
40
|
+
#
|
41
|
+
# DB.extension(:auto_cast_date_and_time)
|
42
|
+
#
|
43
|
+
# Related module: Sequel::AutoCastDateAndTime
|
44
|
+
|
45
|
+
#
|
46
|
+
module Sequel
|
47
|
+
module AutoCastDateAndTime
|
48
|
+
# :nocov:
|
49
|
+
|
50
|
+
# Mark the datasets as requiring sql standard date times. This is only needed
|
51
|
+
# for backwards compatibility.
|
52
|
+
def requires_sql_standard_datetimes?
|
53
|
+
# SEQUEL6: Remove
|
54
|
+
true
|
55
|
+
end
|
56
|
+
# :nocov:
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
# Explicitly cast SQLTime objects to TIME.
|
61
|
+
def literal_sqltime_append(sql, v)
|
62
|
+
sql << "TIME "
|
63
|
+
super
|
64
|
+
end
|
65
|
+
|
66
|
+
# Explicitly cast Time objects to TIMESTAMP.
|
67
|
+
def literal_time_append(sql, v)
|
68
|
+
sql << literal_datetime_timestamp_cast
|
69
|
+
super
|
70
|
+
end
|
71
|
+
|
72
|
+
# Explicitly cast DateTime objects to TIMESTAMP.
|
73
|
+
def literal_datetime_append(sql, v)
|
74
|
+
sql << literal_datetime_timestamp_cast
|
75
|
+
super
|
76
|
+
end
|
77
|
+
|
78
|
+
# Explicitly cast Date objects to DATE.
|
79
|
+
def literal_date_append(sql, v)
|
80
|
+
sql << "DATE "
|
81
|
+
super
|
82
|
+
end
|
83
|
+
|
84
|
+
# The default cast string to use for Time/DateTime objects.
|
85
|
+
# Respects existing method if already defined.
|
86
|
+
def literal_datetime_timestamp_cast
|
87
|
+
return super if defined?(super)
|
88
|
+
'TIMESTAMP '
|
89
|
+
end
|
90
|
+
end
|
91
|
+
|
92
|
+
Dataset.register_extension(:auto_cast_date_and_time, AutoCastDateAndTime)
|
93
|
+
end
|
94
|
+
|
@@ -22,7 +22,7 @@
|
|
22
22
|
#
|
23
23
|
# Named placeholders can also be used with a hash:
|
24
24
|
#
|
25
|
-
# ds.where("name > :a", :
|
25
|
+
# ds.where("name > :a", a: "A")
|
26
26
|
# # SELECT * FROM table WHERE (name > 'A')
|
27
27
|
#
|
28
28
|
# This extension also allows the use of a plain string passed to Dataset#update:
|
@@ -15,16 +15,16 @@
|
|
15
15
|
#
|
16
16
|
# DB.pool.connection_expiration_timeout = 3600 # 1 hour
|
17
17
|
#
|
18
|
-
# Note that this extension
|
19
|
-
# and
|
20
|
-
#
|
21
|
-
# not affected. As the only reason to use the single threaded
|
18
|
+
# Note that this extension does not work with the single
|
19
|
+
# threaded and sharded single threaded connection pools.
|
20
|
+
# As the only reason to use the single threaded
|
22
21
|
# pools is for speed, and this extension makes the connection
|
23
22
|
# pool slower, there's not much point in modifying this
|
24
23
|
# extension to work with the single threaded pools. The
|
25
|
-
# threaded pools work fine even in single threaded
|
26
|
-
# you are currently using a single threaded pool
|
27
|
-
# use this extension, switch to using
|
24
|
+
# non-single threaded pools work fine even in single threaded
|
25
|
+
# code, so if you are currently using a single threaded pool
|
26
|
+
# and want to use this extension, switch to using another
|
27
|
+
# pool.
|
28
28
|
#
|
29
29
|
# Related module: Sequel::ConnectionExpiration
|
30
30
|
|
@@ -45,6 +45,11 @@ module Sequel
|
|
45
45
|
|
46
46
|
# Initialize the data structures used by this extension.
|
47
47
|
def self.extended(pool)
|
48
|
+
case pool.pool_type
|
49
|
+
when :single, :sharded_single
|
50
|
+
raise Error, "cannot load connection_expiration extension if using single or sharded_single connection pool"
|
51
|
+
end
|
52
|
+
|
48
53
|
pool.instance_exec do
|
49
54
|
sync do
|
50
55
|
@connection_expiration_timestamps ||= {}
|
@@ -79,8 +84,9 @@ module Sequel
|
|
79
84
|
(cet = sync{@connection_expiration_timestamps[conn]}) &&
|
80
85
|
Sequel.elapsed_seconds_since(cet[0]) > cet[1]
|
81
86
|
|
82
|
-
|
83
|
-
|
87
|
+
case pool_type
|
88
|
+
when :sharded_threaded, :sharded_timed_queue
|
89
|
+
sync{@allocated[a.last].delete(Sequel.current)}
|
84
90
|
else
|
85
91
|
sync{@allocated.delete(Sequel.current)}
|
86
92
|
end
|