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.
Files changed (218) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +434 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +59 -27
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +119 -24
  8. data/doc/cheat_sheet.rdoc +11 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +27 -6
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +28 -12
  14. data/doc/postgresql.rdoc +16 -8
  15. data/doc/querying.rdoc +5 -3
  16. data/doc/release_notes/5.46.0.txt +87 -0
  17. data/doc/release_notes/5.47.0.txt +59 -0
  18. data/doc/release_notes/5.48.0.txt +14 -0
  19. data/doc/release_notes/5.49.0.txt +59 -0
  20. data/doc/release_notes/5.50.0.txt +78 -0
  21. data/doc/release_notes/5.51.0.txt +47 -0
  22. data/doc/release_notes/5.52.0.txt +87 -0
  23. data/doc/release_notes/5.53.0.txt +23 -0
  24. data/doc/release_notes/5.54.0.txt +27 -0
  25. data/doc/release_notes/5.55.0.txt +21 -0
  26. data/doc/release_notes/5.56.0.txt +51 -0
  27. data/doc/release_notes/5.57.0.txt +23 -0
  28. data/doc/release_notes/5.58.0.txt +31 -0
  29. data/doc/release_notes/5.59.0.txt +73 -0
  30. data/doc/release_notes/5.60.0.txt +22 -0
  31. data/doc/release_notes/5.61.0.txt +43 -0
  32. data/doc/release_notes/5.62.0.txt +132 -0
  33. data/doc/release_notes/5.63.0.txt +33 -0
  34. data/doc/release_notes/5.64.0.txt +50 -0
  35. data/doc/release_notes/5.65.0.txt +21 -0
  36. data/doc/release_notes/5.66.0.txt +24 -0
  37. data/doc/release_notes/5.67.0.txt +32 -0
  38. data/doc/release_notes/5.68.0.txt +61 -0
  39. data/doc/release_notes/5.69.0.txt +26 -0
  40. data/doc/release_notes/5.70.0.txt +35 -0
  41. data/doc/release_notes/5.71.0.txt +21 -0
  42. data/doc/release_notes/5.72.0.txt +33 -0
  43. data/doc/release_notes/5.73.0.txt +66 -0
  44. data/doc/release_notes/5.74.0.txt +45 -0
  45. data/doc/release_notes/5.75.0.txt +35 -0
  46. data/doc/release_notes/5.76.0.txt +86 -0
  47. data/doc/release_notes/5.77.0.txt +63 -0
  48. data/doc/schema_modification.rdoc +1 -1
  49. data/doc/security.rdoc +9 -9
  50. data/doc/sharding.rdoc +3 -1
  51. data/doc/sql.rdoc +27 -15
  52. data/doc/testing.rdoc +23 -13
  53. data/doc/transactions.rdoc +6 -6
  54. data/doc/virtual_rows.rdoc +1 -1
  55. data/lib/sequel/adapters/ado/access.rb +1 -1
  56. data/lib/sequel/adapters/ado.rb +1 -1
  57. data/lib/sequel/adapters/amalgalite.rb +3 -5
  58. data/lib/sequel/adapters/ibmdb.rb +3 -3
  59. data/lib/sequel/adapters/jdbc/derby.rb +8 -0
  60. data/lib/sequel/adapters/jdbc/h2.rb +63 -10
  61. data/lib/sequel/adapters/jdbc/hsqldb.rb +8 -0
  62. data/lib/sequel/adapters/jdbc/postgresql.rb +7 -4
  63. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  64. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  65. data/lib/sequel/adapters/jdbc.rb +24 -22
  66. data/lib/sequel/adapters/mysql.rb +92 -67
  67. data/lib/sequel/adapters/mysql2.rb +56 -51
  68. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  69. data/lib/sequel/adapters/odbc.rb +1 -1
  70. data/lib/sequel/adapters/oracle.rb +4 -3
  71. data/lib/sequel/adapters/postgres.rb +89 -45
  72. data/lib/sequel/adapters/shared/access.rb +11 -1
  73. data/lib/sequel/adapters/shared/db2.rb +42 -0
  74. data/lib/sequel/adapters/shared/mssql.rb +91 -10
  75. data/lib/sequel/adapters/shared/mysql.rb +78 -3
  76. data/lib/sequel/adapters/shared/oracle.rb +86 -7
  77. data/lib/sequel/adapters/shared/postgres.rb +576 -171
  78. data/lib/sequel/adapters/shared/sqlanywhere.rb +21 -5
  79. data/lib/sequel/adapters/shared/sqlite.rb +92 -8
  80. data/lib/sequel/adapters/sqlanywhere.rb +1 -1
  81. data/lib/sequel/adapters/sqlite.rb +99 -18
  82. data/lib/sequel/adapters/tinytds.rb +1 -1
  83. data/lib/sequel/adapters/trilogy.rb +117 -0
  84. data/lib/sequel/adapters/utils/columns_limit_1.rb +22 -0
  85. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  86. data/lib/sequel/ast_transformer.rb +6 -0
  87. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  88. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  89. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  90. data/lib/sequel/connection_pool/single.rb +6 -8
  91. data/lib/sequel/connection_pool/threaded.rb +14 -8
  92. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  93. data/lib/sequel/connection_pool.rb +57 -31
  94. data/lib/sequel/core.rb +17 -18
  95. data/lib/sequel/database/connecting.rb +27 -3
  96. data/lib/sequel/database/dataset.rb +16 -6
  97. data/lib/sequel/database/misc.rb +70 -14
  98. data/lib/sequel/database/query.rb +73 -2
  99. data/lib/sequel/database/schema_generator.rb +11 -6
  100. data/lib/sequel/database/schema_methods.rb +23 -4
  101. data/lib/sequel/database/transactions.rb +6 -0
  102. data/lib/sequel/dataset/actions.rb +111 -15
  103. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  104. data/lib/sequel/dataset/features.rb +20 -1
  105. data/lib/sequel/dataset/misc.rb +12 -2
  106. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  107. data/lib/sequel/dataset/query.rb +170 -41
  108. data/lib/sequel/dataset/sql.rb +190 -71
  109. data/lib/sequel/dataset.rb +4 -0
  110. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  111. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  112. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  113. data/lib/sequel/extensions/async_thread_pool.rb +14 -13
  114. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  115. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  116. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  117. data/lib/sequel/extensions/connection_validator.rb +16 -11
  118. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  119. data/lib/sequel/extensions/core_refinements.rb +36 -11
  120. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  121. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  122. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  123. data/lib/sequel/extensions/duplicate_columns_handler.rb +11 -10
  124. data/lib/sequel/extensions/index_caching.rb +5 -1
  125. data/lib/sequel/extensions/inflector.rb +1 -1
  126. data/lib/sequel/extensions/is_distinct_from.rb +141 -0
  127. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  128. data/lib/sequel/extensions/migration.rb +57 -15
  129. data/lib/sequel/extensions/named_timezones.rb +22 -6
  130. data/lib/sequel/extensions/pagination.rb +1 -1
  131. data/lib/sequel/extensions/pg_array.rb +33 -4
  132. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  133. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  134. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  135. data/lib/sequel/extensions/pg_enum.rb +1 -2
  136. data/lib/sequel/extensions/pg_extended_date_support.rb +39 -28
  137. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  138. data/lib/sequel/extensions/pg_hstore.rb +6 -1
  139. data/lib/sequel/extensions/pg_hstore_ops.rb +53 -3
  140. data/lib/sequel/extensions/pg_inet.rb +10 -11
  141. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  142. data/lib/sequel/extensions/pg_interval.rb +11 -11
  143. data/lib/sequel/extensions/pg_json.rb +13 -15
  144. data/lib/sequel/extensions/pg_json_ops.rb +125 -2
  145. data/lib/sequel/extensions/pg_multirange.rb +367 -0
  146. data/lib/sequel/extensions/pg_range.rb +13 -26
  147. data/lib/sequel/extensions/pg_range_ops.rb +37 -9
  148. data/lib/sequel/extensions/pg_row.rb +20 -19
  149. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  150. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  151. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  152. data/lib/sequel/extensions/s.rb +2 -1
  153. data/lib/sequel/extensions/schema_caching.rb +1 -1
  154. data/lib/sequel/extensions/schema_dumper.rb +45 -11
  155. data/lib/sequel/extensions/server_block.rb +10 -13
  156. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  157. data/lib/sequel/extensions/sql_comments.rb +110 -3
  158. data/lib/sequel/extensions/sql_log_normalizer.rb +108 -0
  159. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  160. data/lib/sequel/extensions/string_agg.rb +1 -1
  161. data/lib/sequel/extensions/string_date_time.rb +19 -23
  162. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  163. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  164. data/lib/sequel/model/associations.rb +286 -92
  165. data/lib/sequel/model/base.rb +53 -33
  166. data/lib/sequel/model/dataset_module.rb +3 -0
  167. data/lib/sequel/model/errors.rb +10 -1
  168. data/lib/sequel/model/exceptions.rb +15 -3
  169. data/lib/sequel/model/inflections.rb +1 -1
  170. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  171. data/lib/sequel/plugins/auto_validations.rb +74 -16
  172. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  173. data/lib/sequel/plugins/column_encryption.rb +29 -8
  174. data/lib/sequel/plugins/composition.rb +3 -2
  175. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  176. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  177. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  178. data/lib/sequel/plugins/dirty.rb +1 -1
  179. data/lib/sequel/plugins/enum.rb +124 -0
  180. data/lib/sequel/plugins/finder.rb +4 -2
  181. data/lib/sequel/plugins/insert_conflict.rb +4 -0
  182. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  183. data/lib/sequel/plugins/json_serializer.rb +2 -2
  184. data/lib/sequel/plugins/lazy_attributes.rb +3 -0
  185. data/lib/sequel/plugins/list.rb +8 -3
  186. data/lib/sequel/plugins/many_through_many.rb +109 -10
  187. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  188. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  189. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  190. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  191. data/lib/sequel/plugins/paged_operations.rb +181 -0
  192. data/lib/sequel/plugins/pg_array_associations.rb +46 -34
  193. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  194. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  195. data/lib/sequel/plugins/prepared_statements.rb +12 -2
  196. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  197. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  198. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  199. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  200. data/lib/sequel/plugins/serialization.rb +1 -0
  201. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -0
  202. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  203. data/lib/sequel/plugins/sql_comments.rb +189 -0
  204. data/lib/sequel/plugins/static_cache.rb +39 -1
  205. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  206. data/lib/sequel/plugins/subclasses.rb +28 -11
  207. data/lib/sequel/plugins/tactical_eager_loading.rb +23 -10
  208. data/lib/sequel/plugins/timestamps.rb +1 -1
  209. data/lib/sequel/plugins/unused_associations.rb +521 -0
  210. data/lib/sequel/plugins/update_or_create.rb +1 -1
  211. data/lib/sequel/plugins/validate_associated.rb +22 -12
  212. data/lib/sequel/plugins/validation_helpers.rb +41 -11
  213. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  214. data/lib/sequel/plugins/xml_serializer.rb +1 -1
  215. data/lib/sequel/sql.rb +1 -1
  216. data/lib/sequel/timezones.rb +12 -14
  217. data/lib/sequel/version.rb +1 -1
  218. metadata +109 -19
@@ -0,0 +1,110 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_auto_parameterize_in_array extension builds on the pg_auto_parameterize
4
+ # extension, adding support for handling additional types when converting from
5
+ # IN to = ANY and NOT IN to != ALL:
6
+ #
7
+ # DB[:table].where(column: [1.0, 2.0, ...])
8
+ # # Without extension: column IN ($1::numeric, $2:numeric, ...) # bound variables: 1.0, 2.0, ...
9
+ # # With extension: column = ANY($1::numeric[]) # bound variables: [1.0, 2.0, ...]
10
+ #
11
+ # This prevents the use of an unbounded number of bound variables based on the
12
+ # size of the array, as well as using different SQL for different array sizes.
13
+ #
14
+ # The following types are supported when doing the conversions, with the database
15
+ # type used:
16
+ #
17
+ # Float :: if any are infinite or NaN, double precision, otherwise numeric
18
+ # BigDecimal :: numeric
19
+ # Date :: date
20
+ # Time :: timestamp (or timestamptz if pg_timestamptz extension is used)
21
+ # DateTime :: timestamp (or timestamptz if pg_timestamptz extension is used)
22
+ # Sequel::SQLTime :: time
23
+ # Sequel::SQL::Blob :: bytea
24
+ #
25
+ # String values are also supported using the +text+ type, but only if the
26
+ # +:treat_string_list_as_text_array+ Database option is used. This is because
27
+ # treating strings as text can break programs, since the type for
28
+ # literal strings in PostgreSQL is +unknown+, not +text+.
29
+ #
30
+ # The conversion is only done for single dimensional arrays that have more
31
+ # than two elements, where all elements are of the same class (other than
32
+ # nil values).
33
+ #
34
+ # Related module: Sequel::Postgres::AutoParameterizeInArray
35
+
36
+ module Sequel
37
+ module Postgres
38
+ # Enable automatically parameterizing queries.
39
+ module AutoParameterizeInArray
40
+ # Transform column IN (...) expressions into column = ANY($)
41
+ # and column NOT IN (...) expressions into column != ALL($)
42
+ # using an array bound variable for the ANY/ALL argument,
43
+ # if all values inside the predicate are of the same type and
44
+ # the type is handled by the extension.
45
+ # This is the same optimization PostgreSQL performs internally,
46
+ # but this reduces the number of bound variables.
47
+ def complex_expression_sql_append(sql, op, args)
48
+ case op
49
+ when :IN, :"NOT IN"
50
+ l, r = args
51
+ if auto_param?(sql) && (type = _bound_variable_type_for_array(r))
52
+ if op == :IN
53
+ op = :"="
54
+ func = :ANY
55
+ else
56
+ op = :!=
57
+ func = :ALL
58
+ end
59
+ args = [l, Sequel.function(func, Sequel.pg_array(r, type))]
60
+ end
61
+ end
62
+
63
+ super
64
+ end
65
+
66
+ private
67
+
68
+ # The bound variable type string to use for the bound variable array.
69
+ # Returns nil if a bound variable should not be used for the array.
70
+ def _bound_variable_type_for_array(r)
71
+ return unless Array === r && r.size > 1
72
+ classes = r.map(&:class)
73
+ classes.uniq!
74
+ classes.delete(NilClass)
75
+ return unless classes.size == 1
76
+
77
+ klass = classes[0]
78
+ if klass == Integer
79
+ # This branch is not taken on Ruby <2.4, because of the Fixnum/Bignum split.
80
+ # However, that causes no problems as pg_auto_parameterize handles integer
81
+ # arrays natively (though the SQL used is different)
82
+ "int8"
83
+ elsif klass == String
84
+ "text" if db.typecast_value(:boolean, db.opts[:treat_string_list_as_text_array])
85
+ elsif klass == BigDecimal
86
+ "numeric"
87
+ elsif klass == Date
88
+ "date"
89
+ elsif klass == Time
90
+ @db.cast_type_literal(Time)
91
+ elsif klass == Float
92
+ # PostgreSQL treats literal floats as numeric, not double precision
93
+ # But older versions of PostgreSQL don't handle Infinity/NaN in numeric
94
+ r.all?{|v| v.nil? || v.finite?} ? "numeric" : "double precision"
95
+ elsif klass == Sequel::SQLTime
96
+ "time"
97
+ elsif klass == DateTime
98
+ @db.cast_type_literal(DateTime)
99
+ elsif klass == Sequel::SQL::Blob
100
+ "bytea"
101
+ end
102
+ end
103
+ end
104
+ end
105
+
106
+ Database.register_extension(:pg_auto_parameterize_in_array) do |db|
107
+ db.extension(:pg_array, :pg_auto_parameterize)
108
+ db.extend_datasets(Postgres::AutoParameterizeInArray)
109
+ end
110
+ end
@@ -166,8 +166,7 @@ module Sequel
166
166
  def schema_post_process(_)
167
167
  super.each do |_, s|
168
168
  oid = s[:oid]
169
- if values = Sequel.synchronize{@enum_labels[oid]}
170
- s[:type] = :enum
169
+ if s[:type] == :enum && (values = Sequel.synchronize{@enum_labels[oid]})
171
170
  s[:enum_values] = values
172
171
  end
173
172
  end
@@ -22,13 +22,14 @@
22
22
  module Sequel
23
23
  module Postgres
24
24
  module ExtendedDateSupport
25
- DATE_YEAR_1 = Date.new(1)
26
25
  DATETIME_YEAR_1 = DateTime.new(1)
27
26
  TIME_YEAR_1 = Time.at(-62135596800).utc
28
27
  INFINITE_TIMESTAMP_STRINGS = ['infinity'.freeze, '-infinity'.freeze].freeze
29
28
  INFINITE_DATETIME_VALUES = ([PLUS_INFINITY, MINUS_INFINITY] + INFINITE_TIMESTAMP_STRINGS).freeze
30
29
  PLUS_DATE_INFINITY = Date::Infinity.new
31
30
  MINUS_DATE_INFINITY = -PLUS_DATE_INFINITY
31
+ RATIONAL_60 = Rational(60)
32
+ TIME_CAN_PARSE_BC = RUBY_VERSION >= '2.5'
32
33
 
33
34
  # Add dataset methods and update the conversion proces for dates and timestamps.
34
35
  def self.extended(db)
@@ -36,6 +37,27 @@ module Sequel
36
37
  procs = db.conversion_procs
37
38
  procs[1082] = ::Sequel.method(:string_to_date)
38
39
  procs[1184] = procs[1114] = db.method(:to_application_timestamp)
40
+ if ocps = db.instance_variable_get(:@oid_convertor_map)
41
+ # Clear the oid convertor map entries for timestamps if they
42
+ # exist, so it will regenerate new ones that use this extension.
43
+ # This is only taken when using the jdbc adapter.
44
+ Sequel.synchronize do
45
+ ocps.delete(1184)
46
+ ocps.delete(1114)
47
+ end
48
+ end
49
+ end
50
+
51
+ # Handle BC dates and times in bound variables. This is necessary for Date values
52
+ # when using both the postgres and jdbc adapters, but also necessary for Time values
53
+ # on jdbc.
54
+ def bound_variable_arg(arg, conn)
55
+ case arg
56
+ when Time, Date
57
+ @default_dataset.literal_date_or_time(arg)
58
+ else
59
+ super
60
+ end
39
61
  end
40
62
 
41
63
  # Whether infinite timestamps/dates should be converted on retrieval. By default, no
@@ -83,30 +105,21 @@ module Sequel
83
105
  # If convert_infinite_timestamps is true and the value is infinite, return an appropriate
84
106
  # value based on the convert_infinite_timestamps setting.
85
107
  def to_application_timestamp(value)
86
- if value.is_a?(String) && (m = value.match(/((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/)) && (m[2] || m[3])
108
+ if value.is_a?(String) && (m = /((?:[-+]\d\d:\d\d)(:\d\d)?)?( BC)?\z/.match(value)) && (m[2] || m[3])
87
109
  if m[3]
88
110
  value = value.sub(' BC', '').sub(' ', ' BC ')
89
- conv = defined?(JRUBY_VERSION) && JRUBY_VERSION == '9.2.0.0'
90
111
  end
91
- if m[2] || conv
92
- dt = DateTime.parse(value)
93
- if conv
94
- # :nocov:
95
- if Sequel.datetime_class == DateTime
96
- dt >>= 12
97
- else
98
- dt >>= 24
99
- end
100
- # :nocov:
101
- end
102
- unless Sequel.datetime_class == DateTime
103
- dt = dt.to_time
104
- if conv && (timezone == nil || timezone == :local) && !m[1]
105
- # :nocov:
106
- dt = Sequel.send(:convert_input_timestamp, dt.strftime("%F %T.%6N"), :local)
107
- # :nocov:
108
- end
112
+ if m[2]
113
+ dt = if Sequel.datetime_class == DateTime
114
+ DateTime.parse(value)
115
+ elsif TIME_CAN_PARSE_BC
116
+ Time.parse(value)
117
+ # :nocov:
118
+ else
119
+ DateTime.parse(value).to_time
120
+ # :nocov:
109
121
  end
122
+
110
123
  Sequel.convert_output_timestamp(dt, Sequel.application_timezone)
111
124
  else
112
125
  super(value)
@@ -176,7 +189,7 @@ module Sequel
176
189
 
177
190
  # Handle BC Date objects.
178
191
  def literal_date(date)
179
- if date < DATE_YEAR_1
192
+ if date.year < 1
180
193
  date <<= ((date.year) * 24 - 12)
181
194
  date.strftime("'%Y-%m-%d BC'")
182
195
  else
@@ -190,7 +203,7 @@ module Sequel
190
203
  date <<= ((date.year) * 24 - 12)
191
204
  date = db.from_application_timestamp(date)
192
205
  minutes = (date.offset * 1440).to_i
193
- date.strftime("'%Y-%m-%d %H:%M:%S.%N#{format_timestamp_offset(*minutes.divmod(60))} BC'")
206
+ date.strftime("'%Y-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *minutes.divmod(60))} BC'")
194
207
  else
195
208
  super
196
209
  end
@@ -223,10 +236,7 @@ module Sequel
223
236
  # Work around JRuby bug #4822 in Time#to_datetime for times before date of calendar reform
224
237
  def literal_time(time)
225
238
  if time < TIME_YEAR_1
226
- dt = DateTime.parse(super)
227
- # Work around JRuby bug #5191
228
- dt >>= 12 if JRUBY_VERSION == '9.2.0.0'
229
- literal_datetime(dt)
239
+ literal_datetime(DateTime.parse(super))
230
240
  else
231
241
  super
232
242
  end
@@ -236,7 +246,8 @@ module Sequel
236
246
  # Handle BC Time objects.
237
247
  def literal_time(time)
238
248
  if time < TIME_YEAR_1
239
- literal_datetime(time.to_datetime)
249
+ time = db.from_application_timestamp(time)
250
+ time.strftime("'#{sprintf('%04i', time.year.abs+1)}-%m-%d %H:%M:%S.%6N#{sprintf("%+03i%02i", *(time.utc_offset/RATIONAL_60).divmod(60))} BC'")
240
251
  else
241
252
  super
242
253
  end
@@ -0,0 +1,116 @@
1
+ # frozen-string-literal: true
2
+ #
3
+ # The pg_extended_integer_support extension supports literalizing
4
+ # Ruby integers outside of PostgreSQL bigint range on PostgreSQL.
5
+ # Sequel by default will raise exceptions when
6
+ # literalizing such integers, as PostgreSQL would treat them
7
+ # as numeric type values instead of integer/bigint type values
8
+ # if unquoted, which can result in unexpected negative performance
9
+ # (e.g. forcing sequential scans when index scans would be used for
10
+ # an integer/bigint type).
11
+ #
12
+ # To load the extension into a Dataset (this returns a new Dataset):
13
+ #
14
+ # dataset = dataset.extension(:pg_extended_integer_support)
15
+ #
16
+ # To load the extension into a Database, so it affects all of the
17
+ # Database's datasets:
18
+ #
19
+ # DB.extension :pg_extended_integer_support
20
+ #
21
+ # By default, the extension will quote integers outside
22
+ # bigint range:
23
+ #
24
+ # DB.literal(2**63) # => "'9223372036854775808'"
25
+ #
26
+ # Quoting the value treats the type as unknown:
27
+ #
28
+ # DB.get{pg_typeof(2**63)} # => 'unknown'
29
+ #
30
+ # PostgreSQL will implicitly cast the unknown type to the appropriate
31
+ # database type, raising an error if it cannot be casted. Be aware this
32
+ # can result in the integer value being implicitly casted to text or
33
+ # any other PostgreSQL type:
34
+ #
35
+ # # Returns a string, not an integer:
36
+ # DB.get{2**63}
37
+ # # => "9223372036854775808"
38
+ #
39
+ # You can use the Dataset#integer_outside_bigint_range_strategy method
40
+ # with the value +:raw+ to change the strategy to not quote the variable:
41
+ #
42
+ # DB.dataset.
43
+ # integer_outside_bigint_range_strategy(:raw).
44
+ # literal(2**63)
45
+ # # => "9223372036854775808"
46
+ #
47
+ # Note that not quoting the value will result in PostgreSQL treating
48
+ # the type as numeric instead of integer:
49
+ #
50
+ # DB.dataset.
51
+ # integer_outside_bigint_range_strategy(:raw).
52
+ # get{pg_typeof(2**63)}
53
+ # # => "numeric"
54
+ #
55
+ # The +:raw+ behavior was Sequel's historical behavior, but unless
56
+ # you fully understand the reprecussions of PostgreSQL using a
57
+ # numeric type for integer values, you should not use it.
58
+ #
59
+ # To get the current default behavior of raising an exception for
60
+ # integers outside of PostgreSQL bigint range, you can use a strategy
61
+ # of +:raise+.
62
+ #
63
+ # To specify a default strategy for handling integers outside
64
+ # bigint range that applies to all of a Database's datasets, you can
65
+ # use the +:integer_outside_bigint_range_strategy+ Database option with
66
+ # a value of +:raise+ or +:raw+:
67
+ #
68
+ # DB.opts[:integer_outside_bigint_range_strategy] = :raw
69
+ #
70
+ # The Database option will be used as a fallback if you did not call
71
+ # the Dataset#integer_outside_bigint_range_strategy method to specify
72
+ # a strategy for the dataset.
73
+ #
74
+ # Related module: Sequel::Postgres::ExtendedIntegerSupport
75
+
76
+ #
77
+ module Sequel
78
+ module Postgres
79
+ module ExtendedIntegerSupport
80
+ # Set the strategy for handling integers outside PostgreSQL
81
+ # bigint range. Supported values:
82
+ #
83
+ # :quote :: Quote the integer value. PostgreSQL will treat
84
+ # the integer as a unknown type, implicitly casting
85
+ # to any other type as needed. This is the default
86
+ # value when using the pg_extended_integer_support
87
+ # extension.
88
+ # :raise :: Raise error when attempting to literalize the integer
89
+ # (the default behavior of Sequel on PostgreSQL when
90
+ # not using the pg_extended_integer_support extension).
91
+ # :raw :: Use raw integer value without quoting. PostgreSQL
92
+ # will treat the integer as a numeric. This was Sequel's
93
+ # historical behavior, but it is unlikely to be desired.
94
+ def integer_outside_bigint_range_strategy(strategy)
95
+ clone(:integer_outside_bigint_range_strategy=>strategy)
96
+ end
97
+
98
+ private
99
+
100
+ # Handle integers outside the bigint range by using
101
+ # the configured strategy.
102
+ def literal_integer_outside_bigint_range(v)
103
+ case @opts[:integer_outside_bigint_range_strategy] || @db.opts[:integer_outside_bigint_range_strategy]
104
+ when :raise
105
+ super
106
+ when :raw
107
+ v.to_s
108
+ else # when :quote
109
+ "'#{v}'"
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ Dataset.register_extension(:pg_extended_integer_support, Postgres::ExtendedIntegerSupport)
116
+ end
@@ -76,7 +76,7 @@
76
76
  #
77
77
  # This extension integrates with the pg_array extension. If you plan
78
78
  # to use arrays of hstore types, load the pg_array extension before the
79
- # pg_interval extension:
79
+ # pg_hstore extension:
80
80
  #
81
81
  # DB.extension :pg_array, :pg_hstore
82
82
  #
@@ -280,6 +280,11 @@ module Sequel
280
280
  str
281
281
  end
282
282
 
283
+ # Allow automatic parameterization.
284
+ def sequel_auto_param_type(ds)
285
+ "::hstore"
286
+ end
287
+
283
288
  private
284
289
 
285
290
  # Return a new hash based on the input hash with string
@@ -62,6 +62,19 @@
62
62
  # # Delete a key
63
63
  # DB[:tab].update(h: Sequel.hstore_op(:h).delete('k1'))
64
64
  #
65
+ # On PostgreSQL 14+, The hstore <tt>[]</tt> method will use subscripts instead of being
66
+ # the same as +get+, if the value being wrapped is an identifer:
67
+ #
68
+ # Sequel.hstore_op(:hstore_column)['a'] # hstore_column['a']
69
+ # Sequel.hstore_op(Sequel[:h][:s])['a'] # h.s['a']
70
+ #
71
+ # This support allows you to use hstore subscripts in UPDATE statements to update only
72
+ # part of a column:
73
+ #
74
+ # h = Sequel.hstore_op(:h)
75
+ # DB[:t].update(h['key1'] => 'val1', h['key2'] => 'val2')
76
+ # # UPDATE "t" SET "h"['key1'] = 'val1', "h"['key2'] = 'val2'
77
+ #
65
78
  # See the PostgreSQL hstore function and operator documentation for more
66
79
  # details on what these functions and operators do.
67
80
  #
@@ -114,10 +127,15 @@ module Sequel
114
127
  #
115
128
  # hstore_op['a'] # (hstore -> 'a')
116
129
  def [](key)
117
- v = Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)])
118
130
  if key.is_a?(Array) || (defined?(Sequel::Postgres::PGArray) && key.is_a?(Sequel::Postgres::PGArray)) || (defined?(Sequel::Postgres::ArrayOp) && key.is_a?(Sequel::Postgres::ArrayOp))
119
- wrap_output_array(v)
131
+ wrap_output_array(Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, wrap_input_array(key)]))
120
132
  else
133
+ v = case @value
134
+ when Symbol, SQL::Identifier, SQL::QualifiedIdentifier
135
+ HStoreSubscriptOp.new(self, key)
136
+ else
137
+ Sequel::SQL::PlaceholderLiteralString.new(LOOKUP, [value, key])
138
+ end
121
139
  Sequel::SQL::StringExpression.new(:NOOP, v)
122
140
  end
123
141
  end
@@ -304,6 +322,38 @@ module Sequel
304
322
  end
305
323
  end
306
324
 
325
+ # Represents hstore subscripts. This is abstracted because the
326
+ # subscript support depends on the database version.
327
+ class HStoreSubscriptOp < SQL::Expression
328
+ SUBSCRIPT = ["".freeze, "[".freeze, "]".freeze].freeze
329
+
330
+ # The expression being subscripted
331
+ attr_reader :expression
332
+
333
+ # The subscript to use
334
+ attr_reader :sub
335
+
336
+ # Set the expression and subscript to the given arguments
337
+ def initialize(expression, sub)
338
+ @expression = expression
339
+ @sub = sub
340
+ freeze
341
+ end
342
+
343
+ # Use subscripts instead of -> operator on PostgreSQL 14+
344
+ def to_s_append(ds, sql)
345
+ server_version = ds.db.server_version
346
+ frag = server_version && server_version >= 140000 ? SUBSCRIPT : HStoreOp::LOOKUP
347
+ ds.literal_append(sql, Sequel::SQL::PlaceholderLiteralString.new(frag, [@expression, @sub]))
348
+ end
349
+
350
+ # Support transforming of hstore subscripts
351
+ def sequel_ast_transform(transformer)
352
+ self.class.new(transformer.call(@expression), transformer.call(@sub))
353
+ end
354
+ end
355
+
356
+
307
357
  module HStoreOpMethods
308
358
  # Wrap the receiver in an HStoreOp so you can easily use the PostgreSQL
309
359
  # hstore functions and operators with it.
@@ -356,7 +406,7 @@ end
356
406
  if defined?(Sequel::CoreRefinements)
357
407
  module Sequel::CoreRefinements
358
408
  refine Symbol do
359
- include Sequel::Postgres::HStoreOpMethods
409
+ send INCLUDE_METH, Sequel::Postgres::HStoreOpMethods
360
410
  end
361
411
  end
362
412
  end
@@ -75,16 +75,6 @@ module Sequel
75
75
 
76
76
  private
77
77
 
78
- # Handle inet[]/cidr[] types in bound variables.
79
- def bound_variable_array(a)
80
- case a
81
- when IPAddr
82
- "\"#{a.to_s}/#{a.instance_variable_get(:@mask_addr).to_s(2).count('1')}\""
83
- else
84
- super
85
- end
86
- end
87
-
88
78
  # Make the column type detection recognize the inet and cidr types.
89
79
  def schema_column_type(db_type)
90
80
  case db_type
@@ -111,7 +101,7 @@ module Sequel
111
101
  when IPAddr
112
102
  value
113
103
  when String
114
- IPAddr.new(value)
104
+ IPAddr.new(typecast_check_string_length(value, 100))
115
105
  else
116
106
  raise Sequel::InvalidValue, "invalid value for inet/cidr: #{value.inspect}"
117
107
  end
@@ -121,6 +111,15 @@ module Sequel
121
111
  module InetDatasetMethods
122
112
  private
123
113
 
114
+ # Allow auto parameterization of IPAddr instances.
115
+ def auto_param_type_fallback(v)
116
+ if defined?(super) && (type = super)
117
+ type
118
+ elsif IPAddr === v
119
+ "::inet"
120
+ end
121
+ end
122
+
124
123
  # Convert IPAddr value to a string and append a literal version
125
124
  # of the string to the sql.
126
125
  def literal_other_append(sql, value)
@@ -197,7 +197,7 @@ end
197
197
  if defined?(Sequel::CoreRefinements)
198
198
  module Sequel::CoreRefinements
199
199
  refine Symbol do
200
- include Sequel::Postgres::InetOpMethods
200
+ send INCLUDE_METH, Sequel::Postgres::InetOpMethods
201
201
  end
202
202
  end
203
203
  end
@@ -32,6 +32,7 @@
32
32
  #
33
33
  # Related module: Sequel::Postgres::IntervalDatabaseMethods
34
34
 
35
+ require 'active_support'
35
36
  require 'active_support/duration'
36
37
 
37
38
  # :nocov:
@@ -162,16 +163,6 @@ module Sequel
162
163
 
163
164
  private
164
165
 
165
- # Handle arrays of interval types in bound variables.
166
- def bound_variable_array(a)
167
- case a
168
- when ActiveSupport::Duration
169
- "\"#{IntervalDatabaseMethods.literal_duration(a)}\""
170
- else
171
- super
172
- end
173
- end
174
-
175
166
  # Set the :ruby_default value if the default value is recognized as an interval.
176
167
  def schema_post_process(_)
177
168
  super.each do |a|
@@ -196,7 +187,7 @@ module Sequel
196
187
  when Numeric
197
188
  ActiveSupport::Duration.new(value, [[:seconds, value]])
198
189
  when String
199
- PARSER.call(value)
190
+ PARSER.call(typecast_check_string_length(value, 1000))
200
191
  else
201
192
  raise Sequel::InvalidValue, "invalid value for interval type: #{value.inspect}"
202
193
  end
@@ -206,6 +197,15 @@ module Sequel
206
197
  module IntervalDatasetMethods
207
198
  private
208
199
 
200
+ # Allow auto parameterization of ActiveSupport::Duration instances.
201
+ def auto_param_type_fallback(v)
202
+ if defined?(super) && (type = super)
203
+ type
204
+ elsif ActiveSupport::Duration === v
205
+ "::interval"
206
+ end
207
+ end
208
+
209
209
  # Handle literalization of ActiveSupport::Duration objects, treating them as
210
210
  # PostgreSQL intervals.
211
211
  def literal_other_append(sql, v)
@@ -142,6 +142,11 @@ module Sequel
142
142
  ds.literal_append(sql, Sequel.object_to_json(self))
143
143
  sql << '::json'
144
144
  end
145
+
146
+ # Allow automatic parameterization.
147
+ def sequel_auto_param_type(ds)
148
+ "::json"
149
+ end
145
150
  end
146
151
 
147
152
  jsonb_class = Class.new(base_class) do
@@ -151,6 +156,11 @@ module Sequel
151
156
  ds.literal_append(sql, Sequel.object_to_json(self))
152
157
  sql << '::jsonb'
153
158
  end
159
+
160
+ # Allow automatic parameterization.
161
+ def sequel_auto_param_type(ds)
162
+ "::jsonb"
163
+ end
154
164
  end
155
165
 
156
166
  const_set(:"JSON#{name}Base", base_class)
@@ -387,11 +397,9 @@ module Sequel
387
397
  # argument is true), or a String, Numeric, true, false, or nil
388
398
  # if the json library used supports that.
389
399
  def _parse_json(s)
390
- begin
391
- Sequel.parse_json(s)
392
- rescue Sequel.json_parser_error_class => e
393
- raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
394
- end
400
+ Sequel.parse_json(s)
401
+ rescue Sequel.json_parser_error_class => e
402
+ raise Sequel.convert_exception_class(e, Sequel::InvalidValue)
395
403
  end
396
404
 
397
405
  # Wrap the parsed JSON value in the appropriate JSON wrapper class.
@@ -426,16 +434,6 @@ module Sequel
426
434
  end
427
435
  end
428
436
 
429
- # Handle json[] and jsonb[] types in bound variables.
430
- def bound_variable_array(a)
431
- case a
432
- when JSONObject, JSONBObject
433
- "\"#{Sequel.object_to_json(a).gsub('"', '\\"')}\""
434
- else
435
- super
436
- end
437
- end
438
-
439
437
  # Make the column type detection recognize the json types.
440
438
  def schema_column_type(db_type)
441
439
  case db_type