sequel 5.58.0 → 5.78.0

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