sequel 5.58.0 → 5.78.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 (161) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +288 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +24 -23
  5. data/bin/sequel +11 -3
  6. data/doc/advanced_associations.rdoc +16 -14
  7. data/doc/association_basics.rdoc +53 -17
  8. data/doc/cheat_sheet.rdoc +3 -3
  9. data/doc/mass_assignment.rdoc +1 -1
  10. data/doc/migration.rdoc +15 -0
  11. data/doc/model_hooks.rdoc +1 -1
  12. data/doc/object_model.rdoc +8 -8
  13. data/doc/opening_databases.rdoc +20 -12
  14. data/doc/postgresql.rdoc +8 -8
  15. data/doc/querying.rdoc +1 -1
  16. data/doc/release_notes/5.59.0.txt +73 -0
  17. data/doc/release_notes/5.60.0.txt +22 -0
  18. data/doc/release_notes/5.61.0.txt +43 -0
  19. data/doc/release_notes/5.62.0.txt +132 -0
  20. data/doc/release_notes/5.63.0.txt +33 -0
  21. data/doc/release_notes/5.64.0.txt +50 -0
  22. data/doc/release_notes/5.65.0.txt +21 -0
  23. data/doc/release_notes/5.66.0.txt +24 -0
  24. data/doc/release_notes/5.67.0.txt +32 -0
  25. data/doc/release_notes/5.68.0.txt +61 -0
  26. data/doc/release_notes/5.69.0.txt +26 -0
  27. data/doc/release_notes/5.70.0.txt +35 -0
  28. data/doc/release_notes/5.71.0.txt +21 -0
  29. data/doc/release_notes/5.72.0.txt +33 -0
  30. data/doc/release_notes/5.73.0.txt +66 -0
  31. data/doc/release_notes/5.74.0.txt +45 -0
  32. data/doc/release_notes/5.75.0.txt +35 -0
  33. data/doc/release_notes/5.76.0.txt +86 -0
  34. data/doc/release_notes/5.77.0.txt +63 -0
  35. data/doc/release_notes/5.78.0.txt +67 -0
  36. data/doc/schema_modification.rdoc +3 -3
  37. data/doc/security.rdoc +9 -9
  38. data/doc/sharding.rdoc +3 -1
  39. data/doc/sql.rdoc +14 -14
  40. data/doc/testing.rdoc +16 -12
  41. data/doc/transactions.rdoc +6 -6
  42. data/doc/virtual_rows.rdoc +1 -1
  43. data/lib/sequel/adapters/ibmdb.rb +1 -1
  44. data/lib/sequel/adapters/jdbc/h2.rb +3 -0
  45. data/lib/sequel/adapters/jdbc/hsqldb.rb +2 -0
  46. data/lib/sequel/adapters/jdbc/postgresql.rb +3 -0
  47. data/lib/sequel/adapters/jdbc/sqlanywhere.rb +15 -0
  48. data/lib/sequel/adapters/jdbc/sqlserver.rb +4 -0
  49. data/lib/sequel/adapters/jdbc.rb +10 -6
  50. data/lib/sequel/adapters/mysql.rb +19 -7
  51. data/lib/sequel/adapters/mysql2.rb +2 -2
  52. data/lib/sequel/adapters/odbc/mssql.rb +1 -1
  53. data/lib/sequel/adapters/oracle.rb +1 -0
  54. data/lib/sequel/adapters/postgres.rb +62 -16
  55. data/lib/sequel/adapters/shared/access.rb +9 -1
  56. data/lib/sequel/adapters/shared/db2.rb +12 -0
  57. data/lib/sequel/adapters/shared/mssql.rb +71 -9
  58. data/lib/sequel/adapters/shared/mysql.rb +80 -1
  59. data/lib/sequel/adapters/shared/oracle.rb +17 -7
  60. data/lib/sequel/adapters/shared/postgres.rb +494 -164
  61. data/lib/sequel/adapters/shared/sqlanywhere.rb +18 -5
  62. data/lib/sequel/adapters/shared/sqlite.rb +40 -4
  63. data/lib/sequel/adapters/sqlite.rb +42 -3
  64. data/lib/sequel/adapters/trilogy.rb +117 -0
  65. data/lib/sequel/connection_pool/sharded_threaded.rb +16 -11
  66. data/lib/sequel/connection_pool/sharded_timed_queue.rb +374 -0
  67. data/lib/sequel/connection_pool/threaded.rb +14 -8
  68. data/lib/sequel/connection_pool/timed_queue.rb +270 -0
  69. data/lib/sequel/connection_pool.rb +57 -31
  70. data/lib/sequel/database/connecting.rb +25 -1
  71. data/lib/sequel/database/dataset.rb +16 -6
  72. data/lib/sequel/database/misc.rb +65 -14
  73. data/lib/sequel/database/query.rb +72 -1
  74. data/lib/sequel/database/schema_generator.rb +2 -1
  75. data/lib/sequel/database/schema_methods.rb +13 -3
  76. data/lib/sequel/database/transactions.rb +6 -0
  77. data/lib/sequel/dataset/actions.rb +60 -13
  78. data/lib/sequel/dataset/deprecated_singleton_class_methods.rb +42 -0
  79. data/lib/sequel/dataset/features.rb +15 -1
  80. data/lib/sequel/dataset/misc.rb +12 -2
  81. data/lib/sequel/dataset/placeholder_literalizer.rb +20 -9
  82. data/lib/sequel/dataset/query.rb +62 -37
  83. data/lib/sequel/dataset/sql.rb +58 -36
  84. data/lib/sequel/dataset.rb +4 -0
  85. data/lib/sequel/exceptions.rb +5 -0
  86. data/lib/sequel/extensions/_model_pg_row.rb +0 -12
  87. data/lib/sequel/extensions/_pretty_table.rb +1 -1
  88. data/lib/sequel/extensions/any_not_empty.rb +2 -2
  89. data/lib/sequel/extensions/async_thread_pool.rb +21 -13
  90. data/lib/sequel/extensions/auto_cast_date_and_time.rb +94 -0
  91. data/lib/sequel/extensions/auto_literal_strings.rb +1 -1
  92. data/lib/sequel/extensions/connection_expiration.rb +15 -9
  93. data/lib/sequel/extensions/connection_validator.rb +16 -11
  94. data/lib/sequel/extensions/constraint_validations.rb +1 -1
  95. data/lib/sequel/extensions/date_arithmetic.rb +36 -8
  96. data/lib/sequel/extensions/duplicate_columns_handler.rb +10 -9
  97. data/lib/sequel/extensions/index_caching.rb +5 -1
  98. data/lib/sequel/extensions/is_distinct_from.rb +3 -1
  99. data/lib/sequel/extensions/looser_typecasting.rb +3 -0
  100. data/lib/sequel/extensions/migration.rb +65 -15
  101. data/lib/sequel/extensions/named_timezones.rb +22 -6
  102. data/lib/sequel/extensions/pg_array.rb +33 -4
  103. data/lib/sequel/extensions/pg_auto_parameterize.rb +509 -0
  104. data/lib/sequel/extensions/pg_auto_parameterize_in_array.rb +110 -0
  105. data/lib/sequel/extensions/pg_enum.rb +1 -2
  106. data/lib/sequel/extensions/pg_extended_date_support.rb +38 -27
  107. data/lib/sequel/extensions/pg_extended_integer_support.rb +116 -0
  108. data/lib/sequel/extensions/pg_hstore.rb +5 -0
  109. data/lib/sequel/extensions/pg_inet.rb +10 -11
  110. data/lib/sequel/extensions/pg_interval.rb +10 -11
  111. data/lib/sequel/extensions/pg_json.rb +10 -10
  112. data/lib/sequel/extensions/pg_json_ops.rb +52 -0
  113. data/lib/sequel/extensions/pg_multirange.rb +6 -11
  114. data/lib/sequel/extensions/pg_range.rb +9 -14
  115. data/lib/sequel/extensions/pg_row.rb +20 -19
  116. data/lib/sequel/extensions/pg_timestamptz.rb +27 -3
  117. data/lib/sequel/extensions/round_timestamps.rb +1 -1
  118. data/lib/sequel/extensions/schema_caching.rb +1 -1
  119. data/lib/sequel/extensions/schema_dumper.rb +32 -9
  120. data/lib/sequel/extensions/server_block.rb +2 -1
  121. data/lib/sequel/extensions/set_literalizer.rb +58 -0
  122. data/lib/sequel/extensions/sqlite_json_ops.rb +76 -18
  123. data/lib/sequel/extensions/symbol_aref.rb +2 -0
  124. data/lib/sequel/extensions/transaction_connection_validator.rb +78 -0
  125. data/lib/sequel/model/associations.rb +50 -11
  126. data/lib/sequel/model/base.rb +45 -21
  127. data/lib/sequel/model/dataset_module.rb +3 -0
  128. data/lib/sequel/model/exceptions.rb +15 -3
  129. data/lib/sequel/plugins/auto_validations.rb +53 -15
  130. data/lib/sequel/plugins/class_table_inheritance.rb +2 -2
  131. data/lib/sequel/plugins/column_encryption.rb +27 -6
  132. data/lib/sequel/plugins/composition.rb +2 -2
  133. data/lib/sequel/plugins/concurrent_eager_loading.rb +4 -4
  134. data/lib/sequel/plugins/constraint_validations.rb +8 -5
  135. data/lib/sequel/plugins/defaults_setter.rb +16 -0
  136. data/lib/sequel/plugins/dirty.rb +1 -1
  137. data/lib/sequel/plugins/finder.rb +4 -2
  138. data/lib/sequel/plugins/list.rb +8 -3
  139. data/lib/sequel/plugins/many_through_many.rb +1 -1
  140. data/lib/sequel/plugins/mssql_optimistic_locking.rb +8 -38
  141. data/lib/sequel/plugins/nested_attributes.rb +4 -4
  142. data/lib/sequel/plugins/optimistic_locking.rb +9 -42
  143. data/lib/sequel/plugins/optimistic_locking_base.rb +55 -0
  144. data/lib/sequel/plugins/paged_operations.rb +181 -0
  145. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +9 -3
  146. data/lib/sequel/plugins/pg_xmin_optimistic_locking.rb +109 -0
  147. data/lib/sequel/plugins/prepared_statements.rb +2 -1
  148. data/lib/sequel/plugins/prepared_statements_safe.rb +2 -1
  149. data/lib/sequel/plugins/primary_key_lookup_check_values.rb +154 -0
  150. data/lib/sequel/plugins/rcte_tree.rb +7 -4
  151. data/lib/sequel/plugins/require_valid_schema.rb +67 -0
  152. data/lib/sequel/plugins/single_table_inheritance.rb +8 -0
  153. data/lib/sequel/plugins/sql_comments.rb +5 -5
  154. data/lib/sequel/plugins/static_cache.rb +38 -0
  155. data/lib/sequel/plugins/static_cache_cache.rb +5 -1
  156. data/lib/sequel/plugins/tactical_eager_loading.rb +21 -14
  157. data/lib/sequel/plugins/validate_associated.rb +22 -12
  158. data/lib/sequel/plugins/validation_helpers.rb +29 -2
  159. data/lib/sequel/plugins/validation_helpers_generic_type_messages.rb +73 -0
  160. data/lib/sequel/version.rb +1 -1
  161. metadata +76 -6
@@ -30,8 +30,12 @@ class Sequel::ConnectionPool
30
30
  :threaded => :ThreadedConnectionPool,
31
31
  :single => :SingleConnectionPool,
32
32
  :sharded_threaded => :ShardedThreadedConnectionPool,
33
- :sharded_single => :ShardedSingleConnectionPool
34
- }.freeze
33
+ :sharded_single => :ShardedSingleConnectionPool,
34
+ :timed_queue => :TimedQueueConnectionPool,
35
+ :sharded_timed_queue => :ShardedTimedQueueConnectionPool,
36
+ }
37
+ POOL_CLASS_MAP.to_a.each{|k, v| POOL_CLASS_MAP[k.to_s] = v}
38
+ POOL_CLASS_MAP.freeze
35
39
 
36
40
  # Class methods used to return an appropriate pool subclass, separated
37
41
  # into a module for easier overridding by extensions.
@@ -39,7 +43,8 @@ class Sequel::ConnectionPool
39
43
  # Return a pool subclass instance based on the given options. If a <tt>:pool_class</tt>
40
44
  # option is provided is provided, use that pool class, otherwise
41
45
  # use a new instance of an appropriate pool subclass based on the
42
- # <tt>:single_threaded</tt> and <tt>:servers</tt> options.
46
+ # +SEQUEL_DEFAULT_CONNECTION_POOL+ environment variable if set, or
47
+ # the <tt>:single_threaded</tt> and <tt>:servers</tt> options, otherwise.
43
48
  def get_pool(db, opts = OPTS)
44
49
  connection_pool_class(opts).new(db, opts)
45
50
  end
@@ -59,9 +64,16 @@ class Sequel::ConnectionPool
59
64
  end
60
65
 
61
66
  pc
67
+ elsif pc = ENV['SEQUEL_DEFAULT_CONNECTION_POOL']
68
+ pc = "sharded_#{pc}" if opts[:servers] && !pc.start_with?('sharded_')
69
+ connection_pool_class(:pool_class=>pc)
62
70
  else
63
71
  pc = if opts[:single_threaded]
64
72
  opts[:servers] ? :sharded_single : :single
73
+ # :nocov:
74
+ elsif RUBY_VERSION >= '3.4' # SEQUEL6 or maybe earlier switch to 3.2
75
+ opts[:servers] ? :sharded_timed_queue : :timed_queue
76
+ # :nocov:
65
77
  else
66
78
  opts[:servers] ? :sharded_threaded : :threaded
67
79
  end
@@ -74,26 +86,35 @@ class Sequel::ConnectionPool
74
86
 
75
87
  # The after_connect proc used for this pool. This is called with each new
76
88
  # connection made, and is usually used to set custom per-connection settings.
77
- attr_accessor :after_connect
89
+ # Deprecated.
90
+ attr_reader :after_connect # SEQUEL6: Remove
91
+
92
+ # Override the after_connect proc for the connection pool. Deprecated.
93
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
94
+ def after_connect=(v) # SEQUEL6: Remove
95
+ @use_old_connect_api = true
96
+ @after_connect = v
97
+ end
78
98
 
79
- # An array of sql strings to execute on each new connection.
80
- attr_accessor :connect_sqls
99
+ # An array of sql strings to execute on each new connection. Deprecated.
100
+ attr_reader :connect_sqls # SEQUEL6: Remove
101
+
102
+ # Override the connect_sqls for the connection pool. Deprecated.
103
+ # Disables support for shard-specific :after_connect and :connect_sqls if used.
104
+ def connect_sqls=(v) # SEQUEL6: Remove
105
+ @use_old_connect_api = true
106
+ @connect_sqls = v
107
+ end
81
108
 
82
109
  # The Sequel::Database object tied to this connection pool.
83
110
  attr_accessor :db
84
111
 
85
- # Instantiates a connection pool with the given options. The block is called
86
- # with a single symbol (specifying the server/shard to use) every time a new
87
- # connection is needed. The following options are respected for all connection
88
- # pools:
89
- # :after_connect :: A callable object called after each new connection is made, with the
90
- # connection object (and server argument if the callable accepts 2 arguments),
91
- # useful for customizations that you want to apply to all connections.
92
- # :connect_sqls :: An array of sql strings to execute on each new connection, after :after_connect runs.
93
- def initialize(db, opts=OPTS)
112
+ # Instantiates a connection pool with the given Database and options.
113
+ def initialize(db, opts=OPTS) # SEQUEL6: Remove second argument, always use db.opts
94
114
  @db = db
95
- @after_connect = opts[:after_connect]
96
- @connect_sqls = opts[:connect_sqls]
115
+ @use_old_connect_api = false # SEQUEL6: Remove
116
+ @after_connect = opts[:after_connect] # SEQUEL6: Remove
117
+ @connect_sqls = opts[:connect_sqls] # SEQUEL6: Remove
97
118
  @error_classes = db.send(:database_error_classes).dup.freeze
98
119
  end
99
120
 
@@ -119,25 +140,30 @@ class Sequel::ConnectionPool
119
140
  # and checking for connection errors.
120
141
  def make_new(server)
121
142
  begin
122
- conn = @db.connect(server)
143
+ if @use_old_connect_api
144
+ # SEQUEL6: Remove block
145
+ conn = @db.connect(server)
123
146
 
124
- if ac = @after_connect
125
- if ac.arity == 2
126
- ac.call(conn, server)
127
- else
128
- ac.call(conn)
147
+ if ac = @after_connect
148
+ if ac.arity == 2
149
+ ac.call(conn, server)
150
+ else
151
+ ac.call(conn)
152
+ end
129
153
  end
130
- end
131
-
132
- if cs = @connect_sqls
133
- cs.each do |sql|
134
- db.send(:log_connection_execute, conn, sql)
154
+
155
+ if cs = @connect_sqls
156
+ cs.each do |sql|
157
+ db.send(:log_connection_execute, conn, sql)
158
+ end
135
159
  end
160
+
161
+ conn
162
+ else
163
+ @db.new_connection(server)
136
164
  end
137
165
  rescue Exception=>exception
138
166
  raise Sequel.convert_exception_class(exception, Sequel::DatabaseConnectionError)
139
- end
140
- raise(Sequel::DatabaseConnectionError, "Connection parameters not valid") unless conn
141
- conn
167
+ end || raise(Sequel::DatabaseConnectionError, "Connection parameters not valid")
142
168
  end
143
169
  end
@@ -8,7 +8,7 @@ module Sequel
8
8
  # ---------------------
9
9
 
10
10
  # Array of supported database adapters
11
- ADAPTERS = %w'ado amalgalite ibmdb jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite tinytds'.map(&:to_sym)
11
+ ADAPTERS = %w'ado amalgalite ibmdb jdbc mock mysql mysql2 odbc oracle postgres sqlanywhere sqlite tinytds trilogy'.map(&:to_sym)
12
12
 
13
13
  # The Database subclass for the given adapter scheme.
14
14
  # Raises Sequel::AdapterNotFound if the adapter
@@ -241,6 +241,30 @@ module Sequel
241
241
  pool.servers
242
242
  end
243
243
 
244
+ # Connect to the given server/shard. Handles database-generic post-connection
245
+ # setup not handled by #connect, using the :after_connect and :connect_sqls
246
+ # options.
247
+ def new_connection(server)
248
+ conn = connect(server)
249
+ opts = server_opts(server)
250
+
251
+ if ac = opts[:after_connect]
252
+ if ac.arity == 2
253
+ ac.call(conn, server)
254
+ else
255
+ ac.call(conn)
256
+ end
257
+ end
258
+
259
+ if cs = opts[:connect_sqls]
260
+ cs.each do |sql|
261
+ log_connection_execute(conn, sql)
262
+ end
263
+ end
264
+
265
+ conn
266
+ end
267
+
244
268
  # Returns true if the database is using a single-threaded connection pool.
245
269
  def single_threaded?
246
270
  @single_threaded
@@ -30,19 +30,29 @@ module Sequel
30
30
  @dataset_class.new(self)
31
31
  end
32
32
 
33
- # Fetches records for an arbitrary SQL statement. If a block is given,
34
- # it is used to iterate over the records:
33
+ # Returns a dataset instance for the given SQL string:
35
34
  #
36
- # DB.fetch('SELECT * FROM items'){|r| p r}
35
+ # ds = DB.fetch('SELECT * FROM items')
36
+ #
37
+ # You can then call methods on the dataset to retrieve results:
37
38
  #
38
- # The +fetch+ method returns a dataset instance:
39
+ # ds.all
40
+ # # SELECT * FROM items
41
+ # # => [{:column=>value, ...}, ...]
39
42
  #
40
- # DB.fetch('SELECT * FROM items').all
43
+ # If a block is given, it is passed to #each on the resulting dataset to
44
+ # iterate over the records returned by the query:
45
+ #
46
+ # DB.fetch('SELECT * FROM items'){|r| p r}
47
+ # # {:column=>value, ...}
48
+ # # ...
41
49
  #
42
50
  # +fetch+ can also perform parameterized queries for protection against SQL
43
51
  # injection:
44
52
  #
45
- # DB.fetch('SELECT * FROM items WHERE name = ?', my_name).all
53
+ # ds = DB.fetch('SELECT * FROM items WHERE name = ?', "my name")
54
+ # ds.all
55
+ # # SELECT * FROM items WHERE name = 'my name'
46
56
  #
47
57
  # See caveats listed in Dataset#with_sql regarding datasets using custom
48
58
  # SQL and the methods that can be called on them.
@@ -91,13 +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.
98
106
  # :before_preconnect :: Callable that runs after extensions from :preconnect_extensions are loaded,
99
107
  # but before any connections are created.
100
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.
101
111
  # :default_string_column_size :: The default size of string columns, 255 by default.
102
112
  # :extensions :: Extensions to load into this Database instance. Can be a symbol, array of symbols,
103
113
  # or string with extensions separated by columns. These extensions are loaded after
@@ -107,7 +117,7 @@ module Sequel
107
117
  # :loggers :: An array of loggers to use.
108
118
  # :log_connection_info :: Whether connection information should be logged when logging queries.
109
119
  # :log_warn_duration :: The number of elapsed seconds after which queries should be logged at warn level.
110
- # :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.
111
121
  # :preconnect :: Automatically create the maximum number of connections, so that they don't
112
122
  # need to be created as needed. This is useful when connecting takes a long time
113
123
  # and you want to avoid possible latency during runtime.
@@ -116,13 +126,15 @@ module Sequel
116
126
  # :preconnect_extensions :: Similar to the :extensions option, but loads the extensions before the
117
127
  # connections are made by the :preconnect option.
118
128
  # :quote_identifiers :: Whether to quote identifiers.
119
- # :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.
120
130
  # :single_threaded :: Whether to use a single-threaded connection pool.
121
131
  # :sql_log_level :: Method to use to log SQL to a logger, :info by default.
122
132
  #
133
+ # For sharded connection pools, :after_connect and :connect_sqls can be specified per-shard.
134
+ #
123
135
  # All options given are also passed to the connection pool. Additional options respected by
124
- # the connection pool are :after_connect, :connect_sqls, :max_connections, :pool_timeout,
125
- # :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.
126
138
  def initialize(opts = OPTS)
127
139
  @opts ||= opts
128
140
  @opts = connection_pool_default_options.merge(@opts)
@@ -132,10 +144,12 @@ module Sequel
132
144
  @opts[:adapter_class] = self.class
133
145
  @opts[:single_threaded] = @single_threaded = typecast_value_boolean(@opts.fetch(:single_threaded, Sequel.single_threaded))
134
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))
135
148
 
136
149
  @schemas = {}
137
150
  @prepared_statements = {}
138
151
  @transactions = {}
152
+ @transactions.compare_by_identity
139
153
  @symbol_literal_cache = {}
140
154
 
141
155
  @timezone = nil
@@ -250,8 +264,8 @@ module Sequel
250
264
  # Proxy the literal call to the dataset.
251
265
  #
252
266
  # DB.literal(1) # 1
253
- # DB.literal(:a) # a
254
- # DB.literal('a') # 'a'
267
+ # DB.literal(:a) # "a" # or `a`, [a], or a, depending on identifier quoting
268
+ # DB.literal("a") # 'a'
255
269
  def literal(v)
256
270
  schema_utility_dataset.literal(v)
257
271
  end
@@ -465,6 +479,21 @@ module Sequel
465
479
  # Don't rescue other exceptions, they will be raised normally.
466
480
  end
467
481
 
482
+ # Check the bytesize of a string before conversion. There is no point
483
+ # trying to typecast strings that would be way too long.
484
+ def typecast_check_string_length(string, max_size)
485
+ if @check_string_typecast_bytesize && string.bytesize > max_size
486
+ raise InvalidValue, "string too long to typecast (bytesize: #{string.bytesize}, max: #{max_size})"
487
+ end
488
+ string
489
+ end
490
+
491
+ # Check the bytesize of the string value, if value is a string.
492
+ def typecast_check_length(value, max_size)
493
+ typecast_check_string_length(value, max_size) if String === value
494
+ value
495
+ end
496
+
468
497
  # Typecast the value to an SQL::Blob
469
498
  def typecast_value_blob(value)
470
499
  value.is_a?(Sequel::SQL::Blob) ? value : Sequel::SQL::Blob.new(value)
@@ -488,9 +517,9 @@ module Sequel
488
517
  when Date
489
518
  value
490
519
  when String
491
- Sequel.string_to_date(value)
520
+ Sequel.string_to_date(typecast_check_string_length(value, 100))
492
521
  when Hash
493
- Date.new(*[:year, :month, :day].map{|x| (value[x] || value[x.to_s]).to_i})
522
+ Date.new(*[:year, :month, :day].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
494
523
  else
495
524
  raise InvalidValue, "invalid value for Date: #{value.inspect}"
496
525
  end
@@ -498,7 +527,17 @@ module Sequel
498
527
 
499
528
  # Typecast the value to a DateTime or Time depending on Sequel.datetime_class
500
529
  def typecast_value_datetime(value)
501
- Sequel.typecast_to_application_timestamp(value)
530
+ case value
531
+ when String
532
+ Sequel.typecast_to_application_timestamp(typecast_check_string_length(value, 100))
533
+ when Hash
534
+ [:year, :month, :day, :hour, :minute, :second, :nanos, :offset].each do |x|
535
+ typecast_check_length(value[x] || value[x.to_s], 100)
536
+ end
537
+ Sequel.typecast_to_application_timestamp(value)
538
+ else
539
+ Sequel.typecast_to_application_timestamp(value)
540
+ end
502
541
  end
503
542
 
504
543
  if RUBY_VERSION >= '2.4'
@@ -531,18 +570,30 @@ module Sequel
531
570
  when Numeric
532
571
  BigDecimal(value.to_s)
533
572
  when String
534
- _typecast_value_string_to_decimal(value)
573
+ _typecast_value_string_to_decimal(typecast_check_string_length(value, 1000))
535
574
  else
536
575
  raise InvalidValue, "invalid value for BigDecimal: #{value.inspect}"
537
576
  end
538
577
  end
539
578
 
540
579
  # Typecast the value to a Float
541
- alias typecast_value_float Float
580
+ def typecast_value_float(value)
581
+ Float(typecast_check_length(value, 1000))
582
+ end
542
583
 
543
584
  # Typecast the value to an Integer
544
585
  def typecast_value_integer(value)
545
- (value.is_a?(String) && value =~ /\A0+(\d)/) ? Integer(value, 10) : Integer(value)
586
+ case value
587
+ when String
588
+ typecast_check_string_length(value, 100)
589
+ if value =~ /\A-?0+(\d)/
590
+ Integer(value, 10)
591
+ else
592
+ Integer(value)
593
+ end
594
+ else
595
+ Integer(value)
596
+ end
546
597
  end
547
598
 
548
599
  # Typecast the value to a String
@@ -565,9 +616,9 @@ module Sequel
565
616
  SQLTime.create(value.hour, value.min, value.sec, value.nsec/1000.0)
566
617
  end
567
618
  when String
568
- Sequel.string_to_time(value)
619
+ Sequel.string_to_time(typecast_check_string_length(value, 100))
569
620
  when Hash
570
- SQLTime.create(*[:hour, :minute, :second].map{|x| (value[x] || value[x.to_s]).to_i})
621
+ SQLTime.create(*[:hour, :minute, :second].map{|x| typecast_check_length(value[x] || value[x.to_s], 100).to_i})
571
622
  else
572
623
  raise Sequel::InvalidValue, "invalid value for Time: #{value.inspect}"
573
624
  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
 
@@ -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
@@ -262,6 +262,7 @@ module Sequel
262
262
  # operations on the table while the index is being
263
263
  # built.
264
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.
265
266
  # :opclass :: Set an opclass to use for all columns (per-column opclasses require
266
267
  # custom SQL).
267
268
  # :tablespace :: Specify tablespace for index.
@@ -306,7 +307,7 @@ module Sequel
306
307
  # Examples:
307
308
  # primary_key(:id)
308
309
  # primary_key(:id, type: :Bignum, keep_order: true)
309
- # primary_key([:street_number, :house_number], name: :some constraint_name)
310
+ # primary_key([:street_number, :house_number], name: :some_constraint_name)
310
311
  def primary_key(name, *args)
311
312
  return composite_primary_key(name, *args) if name.is_a?(Array)
312
313
  column = @db.serial_primary_key_options.merge({:name => name})
@@ -191,6 +191,12 @@ module Sequel
191
191
  # The +any+ type is treated like a SQLite column in a non-strict table,
192
192
  # allowing any type of data to be stored. This option is supported on
193
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+.
194
200
  #
195
201
  # See <tt>Schema::CreateTableGenerator</tt> and the {"Schema Modification" guide}[rdoc-ref:doc/schema_modification.rdoc].
196
202
  def create_table(name, options=OPTS, &block)
@@ -295,6 +301,9 @@ module Sequel
295
301
  # in a subquery, if you are providing a Dataset as the source
296
302
  # argument, if should probably call the union method with the
297
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.
298
307
  # :tablespace :: The tablespace to use for materialized views.
299
308
  def create_view(name, source, options = OPTS)
300
309
  execute_ddl(create_view_sql(name, source, options))
@@ -709,8 +718,9 @@ module Sequel
709
718
  e = options[:ignore_index_errors] || options[:if_not_exists]
710
719
  generator.indexes.each do |index|
711
720
  begin
712
- pr = proc{index_sql_list(name, [index]).each{|sql| execute_ddl(sql)}}
713
- 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
714
724
  rescue Error
715
725
  raise unless e
716
726
  end
@@ -897,7 +907,7 @@ module Sequel
897
907
  #
898
908
  # Any other object given is just converted to a string, with "_" converted to " " and upcased.
899
909
  def on_delete_clause(action)
900
- action.to_s.gsub("_", " ").upcase
910
+ action.to_s.tr("_", " ").upcase
901
911
  end
902
912
 
903
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)