sequel 5.45.0 → 5.77.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|