sequel 5.45.0 → 5.77.0

Sign up to get free protection for your applications and to get access to all the features.
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
@@ -91,11 +91,23 @@ module Sequel
91
91
  # The specific default size of string columns for this Sequel::Database, usually 255 by default.
92
92
  attr_accessor :default_string_column_size
93
93
 
94
+ # Whether to check the bytesize of strings before typecasting (to avoid typecasting strings that
95
+ # would be too long for the given type), true by default. Strings that are too long will raise
96
+ # a typecasting error.
97
+ attr_accessor :check_string_typecast_bytesize
98
+
94
99
  # Constructs a new instance of a database connection with the specified
95
100
  # options hash.
96
101
  #
97
102
  # Accepts the following options:
103
+ # :after_connect :: A callable object called after each new connection is made, with the
104
+ # connection object (and server argument if the callable accepts 2 arguments),
105
+ # useful for customizations that you want to apply to all connections.
106
+ # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
107
+ # but before any connections are created.
98
108
  # :cache_schema :: Whether schema should be cached for this Database instance
109
+ # :check_string_typecast_bytesize :: Whether to check the bytesize of strings before typecasting.
110
+ # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
99
111
  # :default_string_column_size :: The default size of string columns, 255 by default.
100
112
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
101
113
  # or string with extensions separated by columns. These extensions are loaded after
@@ -105,7 +117,7 @@ module Sequel
105
117
  # :loggers :: An array of loggers to use.
106
118
  # :log_connection_info :: Whether connection information should be logged when logging queries.
107
119
  # :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
108
- # :name :: A name to use for the Database object, displayed in PoolTimeout .
120
+ # :name :: A name to use for the Database object, displayed in PoolTimeout.
109
121
  # :preconnect :: Automatically create the maximum number of connections, so that they don't
110
122
  # need to be created as needed. This is useful when connecting takes a long time
111
123
  # and you want to avoid possible latency during runtime.
@@ -114,13 +126,15 @@ module Sequel
114
126
  # :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
115
127
  # connections are made by the :preconnect option.
116
128
  # :quote_identifiers :: Whether to quote identifiers.
117
- # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol .
129
+ # :servers :: A hash specifying a server/shard specific options, keyed by shard symbol.
118
130
  # :single_threaded :: Whether to use a single-threaded connection pool.
119
131
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
120
132
  #
133
+ # For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
134
+ #
121
135
  # All options given are also passed to the connection pool. Additional options respected by
122
- # the connection pool are :after_connect, :connect_sqls, :max_connections, :pool_timeout,
123
- # :servers, and :servers_hash. See the connection pool documentation for details.
136
+ # the connection pool are :max_connections, :pool_timeout, :servers, and :servers_hash. See the
137
+ # connection pool documentation for details.
124
138
  def initialize(opts = OPTS)
125
139
  @opts ||= opts
126
140
  @opts = connection_pool_default_options.merge(@opts)
@@ -130,6 +144,7 @@ module Sequel
130
144
  @opts[:adapter_class] = self.class
131
145
  @opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Sequel.single_threaded))
132
146
  @default_string_column_size = @opts[:default_string_column_size] || DEFAULT_STRING_COLUMN_SIZE
147
+ @check_string_typecast_bytesize = typecast_value_boolean(@opts.fetch(:check_string_typecast_bytesize, true))
133
148
 
134
149
  @schemas = {}
135
150
  @prepared_statements = {}
@@ -160,6 +175,10 @@ module Sequel
160
175
 
161
176
  initialize_load_extensions(:preconnect_extensions)
162
177
 
178
+ if before_preconnect = @opts[:before_preconnect]
179
+ before_preconnect.call(self)
180
+ end
181
+
163
182
  if typecast_value_boolean(@opts[:preconnect]) && @pool.respond_to?(:preconnect, true)
164
183
  concurrent = typecast_value_string(@opts[:preconnect]) == "concurrently"
165
184
  @pool.send(:preconnect, concurrent)
@@ -244,8 +263,8 @@ module Sequel
244
263
  # Proxy the literal call to the dataset.
245
264
  #
246
265
  # DB.literal(1) # 1
247
- # DB.literal(:a) # a
248
- # DB.literal('a') # 'a'
266
+ # DB.literal(:a) # "a" # or `a`, [a], or a, depending on identifier quoting
267
+ # DB.literal("a") # 'a'
249
268
  def literal(v)
250
269
  schema_utility_dataset.literal(v)
251
270
  end
@@ -459,6 +478,21 @@ module Sequel
459
478
  # Don't rescue other exceptions, they will be raised normally.
460
479
  end
461
480
 
481
+ # Check the bytesize of a string before conversion. There is no point
482
+ # trying to typecast strings that would be way too long.
483
+ def typecast_check_string_length(string, max_size)
484
+ if @check_string_typecast_bytesize && string.bytesize > max_size
485
+ raise InvalidValue, "string too long to typecast (bytesize: #{string.bytesize}, max: #{max_size})"
486
+ end
487
+ string
488
+ end
489
+
490
+ # Check the bytesize of the string value, if value is a string.
491
+ def typecast_check_length(value, max_size)
492
+ typecast_check_string_length(value, max_size) if String === value
493
+ value
494
+ end
495
+
462
496
  # Typecast the value to an SQL::Blob
463
497
  def typecast_value_blob(value)
464
498
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -482,9 +516,9 @@ module Sequel
482
516
  when Date
483
517
  value
484
518
  when String
485
- Sequel.string_to_date(value)
519
+ Sequel.string_to_date(typecast_check_string_length(value, 100))
486
520
  when Hash
487
- Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
521
+ Date.new(*[:year, :month, :day].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
488
522
  else
489
523
  raise InvalidValue, "invalid value for Date: #{value.inspect}"
490
524
  end
@@ -492,7 +526,17 @@ module Sequel
492
526
 
493
527
  # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
494
528
  def typecast_value_datetime(value)
495
- Sequel.typecast_to_application_timestamp(value)
529
+ case value
530
+ when String
531
+ Sequel.typecast_to_application_timestamp(typecast_check_string_length(value, 100))
532
+ when Hash
533
+ [:year, :month, :day, :hour, :minute, :second, :nanos, :offset].each do |x|
534
+ typecast_check_length(value[x] || value[x.to_s], 100)
535
+ end
536
+ Sequel.typecast_to_application_timestamp(value)
537
+ else
538
+ Sequel.typecast_to_application_timestamp(value)
539
+ end
496
540
  end
497
541
 
498
542
  if RUBY_VERSION >= '2.4'
@@ -525,18 +569,30 @@ module Sequel
525
569
  when Numeric
526
570
  BigDecimal(value.to_s)
527
571
  when String
528
- _typecast_value_string_to_decimal(value)
572
+ _typecast_value_string_to_decimal(typecast_check_string_length(value, 1000))
529
573
  else
530
574
  raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
531
575
  end
532
576
  end
533
577
 
534
578
  # Typecast the value to a Float
535
- alias typecast_value_float Float
579
+ def typecast_value_float(value)
580
+ Float(typecast_check_length(value, 1000))
581
+ end
536
582
 
537
583
  # Typecast the value to an Integer
538
584
  def typecast_value_integer(value)
539
- (value.is_a?(String) && value =~ /\A0+(\d)/) ? Integer(value, 10) : Integer(value)
585
+ case value
586
+ when String
587
+ typecast_check_string_length(value, 100)
588
+ if value =~ /\A-?0+(\d)/
589
+ Integer(value, 10)
590
+ else
591
+ Integer(value)
592
+ end
593
+ else
594
+ Integer(value)
595
+ end
540
596
  end
541
597
 
542
598
  # Typecast the value to a String
@@ -559,9 +615,9 @@ module Sequel
559
615
  SQLTime.create(value.hour, value.min, value.sec, value.nsec/1000.0)
560
616
  end
561
617
  when String
562
- Sequel.string_to_time(value)
618
+ Sequel.string_to_time(typecast_check_string_length(value, 100))
563
619
  when Hash
564
- SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
620
+ SQLTime.create(*[:hour, :minute, :second].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
565
621
  else
566
622
  raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
567
623
  end
@@ -175,6 +175,15 @@ module Sequel
175
175
  if !c[:max_length] && c[:type] == :string && (max_length = column_schema_max_length(c[:db_type]))
176
176
  c[:max_length] = max_length
177
177
  end
178
+ if !c[:max_value] && !c[:min_value]
179
+ min_max = case c[:type]
180
+ when :integer
181
+ column_schema_integer_min_max_values(c)
182
+ when :decimal
183
+ column_schema_decimal_min_max_values(c)
184
+ end
185
+ c[:min_value], c[:max_value] = min_max if min_max
186
+ end
178
187
  end
179
188
  schema_post_process(cols)
180
189
 
@@ -236,7 +245,7 @@ module Sequel
236
245
  when :date
237
246
  Sequel.string_to_date(default)
238
247
  when :datetime
239
- DateTime.parse(default)
248
+ Sequel.string_to_datetime(default)
240
249
  when :time
241
250
  Sequel.string_to_time(default)
242
251
  when :decimal
@@ -272,6 +281,68 @@ module Sequel
272
281
  column_schema_default_to_ruby_value(default, type) rescue nil
273
282
  end
274
283
 
284
+ INTEGER1_MIN_MAX = [-128, 127].freeze
285
+ INTEGER2_MIN_MAX = [-32768, 32767].freeze
286
+ INTEGER3_MIN_MAX = [-8388608, 8388607].freeze
287
+ INTEGER4_MIN_MAX = [-2147483648, 2147483647].freeze
288
+ INTEGER8_MIN_MAX = [-9223372036854775808, 9223372036854775807].freeze
289
+ UNSIGNED_INTEGER1_MIN_MAX = [0, 255].freeze
290
+ UNSIGNED_INTEGER2_MIN_MAX = [0, 65535].freeze
291
+ UNSIGNED_INTEGER3_MIN_MAX = [0, 16777215].freeze
292
+ UNSIGNED_INTEGER4_MIN_MAX = [0, 4294967295].freeze
293
+ UNSIGNED_INTEGER8_MIN_MAX = [0, 18446744073709551615].freeze
294
+
295
+ # Look at the db_type and guess the minimum and maximum integer values for
296
+ # the column.
297
+ def column_schema_integer_min_max_values(column)
298
+ db_type = column[:db_type]
299
+ if /decimal|numeric|number/i =~ db_type
300
+ if min_max = column_schema_decimal_min_max_values(column)
301
+ min_max.map!(&:to_i)
302
+ end
303
+ return min_max
304
+ end
305
+
306
+ unsigned = /unsigned/i =~ db_type
307
+ case db_type
308
+ when /big|int8/i
309
+ unsigned ? UNSIGNED_INTEGER8_MIN_MAX : INTEGER8_MIN_MAX
310
+ when /medium/i
311
+ unsigned ? UNSIGNED_INTEGER3_MIN_MAX : INTEGER3_MIN_MAX
312
+ when /small|int2/i
313
+ unsigned ? UNSIGNED_INTEGER2_MIN_MAX : INTEGER2_MIN_MAX
314
+ when /tiny/i
315
+ (unsigned || column_schema_tinyint_type_is_unsigned?) ? UNSIGNED_INTEGER1_MIN_MAX : INTEGER1_MIN_MAX
316
+ else
317
+ unsigned ? UNSIGNED_INTEGER4_MIN_MAX : INTEGER4_MIN_MAX
318
+ end
319
+ end
320
+
321
+ # Look at the db_type and guess the minimum and maximum decimal values for
322
+ # the column.
323
+ def column_schema_decimal_min_max_values(column)
324
+ if column[:column_size] && column[:scale]
325
+ precision = column[:column_size]
326
+ scale = column[:scale]
327
+ elsif /\((\d+)(?:,\s*(-?\d+))?\)/ =~ column[:db_type]
328
+ precision = $1.to_i
329
+ scale = $2.to_i if $2
330
+ end
331
+
332
+ if precision
333
+ limit = BigDecimal("9" * precision)
334
+ if scale
335
+ limit /= 10**(scale)
336
+ end
337
+ [-limit, limit]
338
+ end
339
+ end
340
+
341
+ # Whether the tinyint type (if supported by the database) is unsigned by default.
342
+ def column_schema_tinyint_type_is_unsigned?
343
+ false
344
+ end
345
+
275
346
  # Look at the db_type and guess the maximum length of the column.
276
347
  # This assumes types such as varchar(255).
277
348
  def column_schema_max_length(db_type)
@@ -333,7 +404,7 @@ module Sequel
333
404
  :boolean
334
405
  when /\A(real|float( unsigned)?|double( precision)?|double\(\d+,\d+\)( unsigned)?)\z/io
335
406
  :float
336
- when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(\d+|false|true)\))?))\z/io
407
+ when /\A(?:(?:(?:num(?:ber|eric)?|decimal)(?:\(\d+,\s*(-?\d+|false|true)\))?))\z/io
337
408
  $1 && ['0', 'false'].include?($1) ? :integer : :decimal
338
409
  when /bytea|blob|image|(var)?binary/io
339
410
  :blob
@@ -146,6 +146,9 @@ module Sequel
146
146
  #
147
147
  # :generated_type :: Set the type of column when using :generated_always_as,
148
148
  # should be :virtual or :stored to force a type.
149
+ # :on_update_current_timestamp :: Use ON UPDATE CURRENT TIMESTAMP when defining the column,
150
+ # which will update the column value to CURRENT_TIMESTAMP
151
+ # on every UPDATE.
149
152
  #
150
153
  # Microsoft SQL Server specific options:
151
154
  #
@@ -259,6 +262,7 @@ module Sequel
259
262
  # operations on the table while the index is being
260
263
  # built.
261
264
  # :if_not_exists :: Only create the index if an index of the same name doesn't already exist.
265
+ # :nulls_distinct :: Set whether separate NULLs should be considered distinct values in unique indexes.
262
266
  # :opclass :: Set an opclass to use for all columns (per-column opclasses require
263
267
  # custom SQL).
264
268
  # :tablespace :: Specify tablespace for index.
@@ -303,7 +307,7 @@ module Sequel
303
307
  # Examples:
304
308
  # primary_key(:id)
305
309
  # primary_key(:id, type: :Bignum, keep_order: true)
306
- # primary_key([:street_number, :house_number], name: :some constraint_name)
310
+ # primary_key([:street_number, :house_number], name: :some_constraint_name)
307
311
  def primary_key(name, *args)
308
312
  return composite_primary_key(name, *args) if name.is_a?(Array)
309
313
  column = @db.serial_primary_key_options.merge({:name => name})
@@ -387,8 +391,7 @@ module Sequel
387
391
  end
388
392
 
389
393
  # Add a column with the given name, type, and opts.
390
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
391
- # separate +add_index+ call to add an index for the column).
394
+ # See CreateTableGenerator#column for the available options.
392
395
  #
393
396
  # add_column(:name, String) # ADD COLUMN name varchar(255)
394
397
  #
@@ -401,7 +404,10 @@ module Sequel
401
404
  # :after :: The name of an existing column that the new column should be positioned after
402
405
  # :first :: Create this new column before all other existing columns
403
406
  def add_column(name, type, opts = OPTS)
404
- @operations << {:op => :add_column, :name => name, :type => type}.merge!(opts)
407
+ op = {:op => :add_column, :name => name, :type => type}.merge!(opts)
408
+ index_opts = op.delete(:index)
409
+ @operations << op
410
+ add_index(name, index_opts.is_a?(Hash) ? index_opts : OPTS) if index_opts
405
411
  nil
406
412
  end
407
413
 
@@ -430,8 +436,7 @@ module Sequel
430
436
  end
431
437
 
432
438
  # Add a foreign key with the given name and referencing the given table.
433
- # See CreateTableGenerator#column for the available options (except for +:index+, use a
434
- # separate +add_index+ call to add an index for the column).
439
+ # See CreateTableGenerator#column for the available options.
435
440
  #
436
441
  # You can also pass an array of column names for creating composite foreign
437
442
  # keys. In this case, it will assume the columns exist and will only add
@@ -63,7 +63,7 @@ module Sequel
63
63
  # definitions using <tt>create_table</tt>, and +add_index+ accepts all the options
64
64
  # available for index definition.
65
65
  #
66
- # See <tt>Schema::AlterTableGenerator</tt> and the {"Migrations and Schema Modification" guide}[rdoc-ref:doc/migration.rdoc].
66
+ # See <tt>Schema::AlterTableGenerator</tt> and the {Migrations guide}[rdoc-ref:doc/migration.rdoc].
67
67
  def alter_table(name, &block)
68
68
  generator = alter_table_generator(&block)
69
69
  remove_cached_schema(name)
@@ -183,6 +183,21 @@ module Sequel
183
183
  # keys.
184
184
  # :tablespace :: The tablespace to use for the table.
185
185
  #
186
+ # SQLite specific options:
187
+ # :strict :: Create a STRICT table, which checks that the values for the columns
188
+ # are the correct type (similar to all other SQL databases). Note that
189
+ # when using this option, all column types used should be one of the
190
+ # following: +int+, +integer+, +real+, +text+, +blob+, and +any+.
191
+ # The +any+ type is treated like a SQLite column in a non-strict table,
192
+ # allowing any type of data to be stored. This option is supported on
193
+ # SQLite 3.37.0+.
194
+ # :without_rowid :: Create a WITHOUT ROWID table. Every row in SQLite has a special
195
+ # 'rowid' column, that uniquely identifies that row within the table.
196
+ # If this option is used, the 'rowid' column is omitted, which can
197
+ # sometimes provide some space and speed advantages. Note that you
198
+ # must then provide an explicit primary key when you create the table.
199
+ # This option is supported on SQLite 3.8.2+.
200
+ #
186
201
  # See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
187
202
  def create_table(name, options=OPTS, &block)
188
203
  remove_cached_schema(name)
@@ -286,6 +301,9 @@ module Sequel
286
301
  # in a subquery, if you are providing a Dataset as the source
287
302
  # argument, if should probably call the union method with the
288
303
  # all: true and from_self: false options.
304
+ # :security_invoker :: Set the security_invoker property on the view, making
305
+ # the access to the view use the current user's permissions,
306
+ # instead of the view owner's permissions.
289
307
  # :tablespace :: The tablespace to use for materialized views.
290
308
  def create_view(name, source, options = OPTS)
291
309
  execute_ddl(create_view_sql(name, source, options))
@@ -700,8 +718,9 @@ module Sequel
700
718
  e = options[:ignore_index_errors] || options[:if_not_exists]
701
719
  generator.indexes.each do |index|
702
720
  begin
703
- pr = proc{index_sql_list(name, [index]).each{|sql| execute_ddl(sql)}}
704
- supports_transactional_ddl? ? transaction(:savepoint=>:only, &pr) : pr.call
721
+ transaction(:savepoint=>:only, :skip_transaction=>supports_transactional_ddl? == false) do
722
+ index_sql_list(name, [index]).each{|sql| execute_ddl(sql)}
723
+ end
705
724
  rescue Error
706
725
  raise unless e
707
726
  end
@@ -888,7 +907,7 @@ module Sequel
888
907
  #
889
908
  # Any other object given is just converted to a string, with "_" converted to " " and upcased.
890
909
  def on_delete_clause(action)
891
- action.to_s.gsub("_", " ").upcase
910
+ action.to_s.tr("_", " ").upcase
892
911
  end
893
912
 
894
913
  # Alias of #on_delete_clause, since the two usually behave the same.
@@ -166,6 +166,8 @@ module Sequel
166
166
  # uses :auto_savepoint, you can set this to false to not use a savepoint.
167
167
  # If the value given for this option is :only, it will only create a
168
168
  # savepoint if it is inside a transaction.
169
+ # :skip_transaction :: If set, do not actually open a transaction or savepoint,
170
+ # just checkout a connection and yield it.
169
171
  #
170
172
  # PostgreSQL specific options:
171
173
  #
@@ -193,6 +195,10 @@ module Sequel
193
195
  end
194
196
  else
195
197
  synchronize(opts[:server]) do |conn|
198
+ if opts[:skip_transaction]
199
+ return yield(conn)
200
+ end
201
+
196
202
  if opts[:savepoint] == :only
197
203
  if supports_savepoints?
198
204
  if _trans(conn)
@@ -19,7 +19,7 @@ module Sequel
19
19
  METHS
20
20
 
21
21
  # The clone options to use when retrieving columns for a dataset.
22
- COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 1, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
22
+ COLUMNS_CLONE_OPTIONS = {:distinct => nil, :limit => 0, :offset=>nil, :where=>nil, :having=>nil, :order=>nil, :row_proc=>nil, :graph=>nil, :eager_graph=>nil}.freeze
23
23
 
24
24
  # Inserts the given argument into the database. Returns self so it
25
25
  # can be used safely when chaining:
@@ -127,6 +127,18 @@ module Sequel
127
127
  #
128
128
  # DB[:table].delete # DELETE * FROM table
129
129
  # # => 3
130
+ #
131
+ # Some databases support using multiple tables in a DELETE query. This requires
132
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
133
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
134
+ # appropriate filters for the FROM tables:
135
+ #
136
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>c[:h]}}.
137
+ # delete
138
+ # # DELETE FROM a
139
+ # # USING b
140
+ # # INNER JOIN c ON (c.d = b.e)
141
+ # # WHERE ((a.f = b.g) AND (a.id = c.h))
130
142
  def delete(&block)
131
143
  sql = delete_sql
132
144
  if uses_returning?(:delete)
@@ -162,7 +174,7 @@ module Sequel
162
174
  # # => false
163
175
  def empty?
164
176
  cached_dataset(:_empty_ds) do
165
- single_value_ds.unordered.select(EMPTY_SELECT)
177
+ (@opts[:sql] ? from_self : self).single_value_ds.unordered.select(EMPTY_SELECT)
166
178
  end.single_value!.nil?
167
179
  end
168
180
 
@@ -313,14 +325,18 @@ module Sequel
313
325
 
314
326
  # Inserts multiple records into the associated table. This method can be
315
327
  # used to efficiently insert a large number of records into a table in a
316
- # single query if the database supports it. Inserts
317
- # are automatically wrapped in a transaction.
328
+ # single query if the database supports it. Inserts are automatically
329
+ # wrapped in a transaction if necessary.
318
330
  #
319
331
  # This method is called with a columns array and an array of value arrays:
320
332
  #
321
333
  # DB[:table].import([:x, :y], [[1, 2], [3, 4]])
322
334
  # # INSERT INTO table (x, y) VALUES (1, 2)
323
- # # INSERT INTO table (x, y) VALUES (3, 4)
335
+ # # INSERT INTO table (x, y) VALUES (3, 4)
336
+ #
337
+ # or, if the database supports it:
338
+ #
339
+ # # INSERT INTO table (x, y) VALUES (1, 2), (3, 4)
324
340
  #
325
341
  # This method also accepts a dataset instead of an array of value arrays:
326
342
  #
@@ -328,17 +344,23 @@ module Sequel
328
344
  # # INSERT INTO table (x, y) SELECT a, b FROM table2
329
345
  #
330
346
  # Options:
331
- # :commit_every :: Open a new transaction for every given number of records.
332
- # For example, if you provide a value of 50, will commit
333
- # after every 50 records.
347
+ # :commit_every :: Open a new transaction for every given number of
348
+ # records. For example, if you provide a value of 50,
349
+ # will commit after every 50 records. When a
350
+ # transaction is not required, this option controls
351
+ # the maximum number of values to insert with a single
352
+ # statement; it does not force the use of a
353
+ # transaction.
334
354
  # :return :: When this is set to :primary_key, returns an array of
335
355
  # autoincremented primary key values for the rows inserted.
336
356
  # This does not have an effect if +values+ is a Dataset.
337
357
  # :server :: Set the server/shard to use for the transaction and insert
338
358
  # queries.
359
+ # :skip_transaction :: Do not use a transaction even when using multiple
360
+ # INSERT queries.
339
361
  # :slice :: Same as :commit_every, :commit_every takes precedence.
340
362
  def import(columns, values, opts=OPTS)
341
- return @db.transaction{insert(columns, values)} if values.is_a?(Dataset)
363
+ return insert(columns, values) if values.is_a?(Dataset)
342
364
 
343
365
  return if values.empty?
344
366
  raise(Error, 'Using Sequel::Dataset#import with an empty column array is not allowed') if columns.empty?
@@ -457,6 +479,55 @@ module Sequel
457
479
  _aggregate(:max, arg)
458
480
  end
459
481
 
482
+ # Execute a MERGE statement, which allows for INSERT, UPDATE, and DELETE
483
+ # behavior in a single query, based on whether rows from a source table
484
+ # match rows in the current table, based on the join conditions.
485
+ #
486
+ # Unless the dataset uses static SQL, to use #merge, you must first have
487
+ # called #merge_using to specify the merge source and join conditions.
488
+ # You will then likely to call one or more of the following methods
489
+ # to specify MERGE behavior by adding WHEN [NOT] MATCHED clauses:
490
+ #
491
+ # * #merge_insert
492
+ # * #merge_update
493
+ # * #merge_delete
494
+ #
495
+ # The WHEN [NOT] MATCHED clauses are added to the SQL in the order these
496
+ # methods were called on the dataset. If none of these methods are
497
+ # called, an error is raised.
498
+ #
499
+ # Example:
500
+ #
501
+ # DB[:m1]
502
+ # merge_using(:m2, i1: :i2).
503
+ # merge_insert(i1: :i2, a: Sequel[:b]+11).
504
+ # merge_delete{a > 30}.
505
+ # merge_update(i1: Sequel[:i1]+:i2+10, a: Sequel[:a]+:b+20).
506
+ # merge
507
+ #
508
+ # SQL:
509
+ #
510
+ # MERGE INTO m1 USING m2 ON (i1 = i2)
511
+ # WHEN NOT MATCHED THEN INSERT (i1, a) VALUES (i2, (b + 11))
512
+ # WHEN MATCHED AND (a > 30) THEN DELETE
513
+ # WHEN MATCHED THEN UPDATE SET i1 = (i1 + i2 + 10), a = (a + b + 20)
514
+ #
515
+ # On PostgreSQL, two additional merge methods are supported, for the
516
+ # PostgreSQL-specific DO NOTHING syntax.
517
+ #
518
+ # * #merge_do_nothing_when_matched
519
+ # * #merge_do_nothing_when_not_matched
520
+ #
521
+ # This method is supported on Oracle, but Oracle's MERGE support is
522
+ # non-standard, and has the following issues:
523
+ #
524
+ # * DELETE clause requires UPDATE clause
525
+ # * DELETE clause requires a condition
526
+ # * DELETE clause only affects rows updated by UPDATE clause
527
+ def merge
528
+ execute_ddl(merge_sql)
529
+ end
530
+
460
531
  # Returns the minimum value for the given column/expression.
461
532
  # Uses a virtual row block if no argument is given.
462
533
  #
@@ -519,6 +590,8 @@ module Sequel
519
590
  # if your ORDER BY expressions are not simple columns, if they contain
520
591
  # qualified identifiers that would be ambiguous unqualified, if they contain
521
592
  # any identifiers that are aliased in SELECT, and potentially other cases.
593
+ # :skip_transaction :: Do not use a transaction. This can be useful if you want to prevent
594
+ # a lock on the database table, at the expense of consistency.
522
595
  #
523
596
  # Examples:
524
597
  #
@@ -527,7 +600,7 @@ module Sequel
527
600
  # # SELECT * FROM table ORDER BY id LIMIT 1000 OFFSET 1000
528
601
  # # ...
529
602
  #
530
- # DB[:table].order(:id).paged_each(:rows_per_fetch=>100){|row| }
603
+ # DB[:table].order(:id).paged_each(rows_per_fetch: 100){|row| }
531
604
  # # SELECT * FROM table ORDER BY id LIMIT 100
532
605
  # # SELECT * FROM table ORDER BY id LIMIT 100 OFFSET 100
533
606
  # # ...
@@ -546,7 +619,7 @@ module Sequel
546
619
  unless @opts[:order]
547
620
  raise Sequel::Error, "Dataset#paged_each requires the dataset be ordered"
548
621
  end
549
- unless block_given?
622
+ unless defined?(yield)
550
623
  return enum_for(:paged_each, opts)
551
624
  end
552
625
 
@@ -869,6 +942,19 @@ module Sequel
869
942
  #
870
943
  # DB[:table].update(x: Sequel[:x]+1, y: 0) # UPDATE table SET x = (x + 1), y = 0
871
944
  # # => 10
945
+ #
946
+ # Some databases support using multiple tables in an UPDATE query. This requires
947
+ # multiple FROM tables (JOINs can also be used). As multiple FROM tables use
948
+ # an implicit CROSS JOIN, you should make sure your WHERE condition uses the
949
+ # appropriate filters for the FROM tables:
950
+ #
951
+ # DB.from(:a, :b).join(:c, :d=>Sequel[:b][:e]).where{{a[:f]=>b[:g], a[:id]=>10}}.
952
+ # update(:f=>Sequel[:c][:h])
953
+ # # UPDATE a
954
+ # # SET f = c.h
955
+ # # FROM b
956
+ # # INNER JOIN c ON (c.d = b.e)
957
+ # # WHERE ((a.f = b.g) AND (a.id = 10))
872
958
  def update(values=OPTS, &block)
873
959
  sql = update_sql(values)
874
960
  if uses_returning?(:update)
@@ -974,18 +1060,19 @@ module Sequel
974
1060
 
975
1061
  # Internals of #import. If primary key values are requested, use
976
1062
  # separate insert commands for each row. Otherwise, call #multi_insert_sql
977
- # and execute each statement it gives separately.
1063
+ # and execute each statement it gives separately. A transaction is only used
1064
+ # if there are multiple statements to execute.
978
1065
  def _import(columns, values, opts)
979
1066
  trans_opts = Hash[opts]
980
1067
  trans_opts[:server] = @opts[:server]
981
1068
  if opts[:return] == :primary_key
982
- @db.transaction(trans_opts){values.map{|v| insert(columns, v)}}
1069
+ _import_transaction(values, trans_opts){values.map{|v| insert(columns, v)}}
983
1070
  else
984
1071
  stmts = multi_insert_sql(columns, values)
985
- @db.transaction(trans_opts){stmts.each{|st| execute_dui(st)}}
1072
+ _import_transaction(stmts, trans_opts){stmts.each{|st| execute_dui(st)}}
986
1073
  end
987
1074
  end
988
-
1075
+
989
1076
  # Return an array of arrays of values given by the symbols in ret_cols.
990
1077
  def _select_map_multiple(ret_cols)
991
1078
  map{|r| r.values_at(*ret_cols)}
@@ -1024,6 +1111,15 @@ module Sequel
1024
1111
  end
1025
1112
  end
1026
1113
 
1114
+ # Use a transaction when yielding to the block if multiple values/statements
1115
+ # are provided. When only a single value or statement is provided, then yield
1116
+ # without using a transaction.
1117
+ def _import_transaction(values, trans_opts, &block)
1118
+ # OK to mutate trans_opts as it is generated by _import
1119
+ trans_opts[:skip_transaction] = true if values.length <= 1
1120
+ @db.transaction(trans_opts, &block)
1121
+ end
1122
+
1027
1123
  # Internals of +select_hash+ and +select_hash_groups+
1028
1124
  def _select_hash(meth, key_column, value_column, opts=OPTS)
1029
1125
  select(*(key_column.is_a?(Array) ? key_column : [key_column]) + (value_column.is_a?(Array) ? value_column : [value_column])).