sequel 5.51.0 → 5.56.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (59) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +62 -0
  3. data/MIT-LICENSE +1 -1
  4. data/README.rdoc +5 -0
  5. data/doc/opening_databases.rdoc +4 -1
  6. data/doc/querying.rdoc +3 -1
  7. data/doc/release_notes/5.52.0.txt +87 -0
  8. data/doc/release_notes/5.53.0.txt +23 -0
  9. data/doc/release_notes/5.54.0.txt +27 -0
  10. data/doc/release_notes/5.55.0.txt +21 -0
  11. data/doc/release_notes/5.56.0.txt +51 -0
  12. data/doc/sql.rdoc +1 -1
  13. data/doc/testing.rdoc +3 -1
  14. data/lib/sequel/adapters/amalgalite.rb +3 -5
  15. data/lib/sequel/adapters/jdbc/h2.rb +55 -10
  16. data/lib/sequel/adapters/jdbc.rb +12 -14
  17. data/lib/sequel/adapters/mysql.rb +80 -67
  18. data/lib/sequel/adapters/mysql2.rb +53 -48
  19. data/lib/sequel/adapters/postgres.rb +17 -21
  20. data/lib/sequel/adapters/shared/mysql.rb +3 -2
  21. data/lib/sequel/adapters/shared/postgres.rb +2 -2
  22. data/lib/sequel/adapters/shared/sqlite.rb +6 -0
  23. data/lib/sequel/adapters/sqlite.rb +60 -18
  24. data/lib/sequel/adapters/utils/mysql_mysql2.rb +1 -1
  25. data/lib/sequel/connection_pool/sharded_single.rb +5 -7
  26. data/lib/sequel/connection_pool/single.rb +6 -8
  27. data/lib/sequel/core.rb +17 -18
  28. data/lib/sequel/database/query.rb +1 -1
  29. data/lib/sequel/database/schema_generator.rb +6 -5
  30. data/lib/sequel/database/schema_methods.rb +9 -0
  31. data/lib/sequel/dataset/sql.rb +3 -2
  32. data/lib/sequel/extensions/core_refinements.rb +36 -11
  33. data/lib/sequel/extensions/date_parse_input_handler.rb +67 -0
  34. data/lib/sequel/extensions/datetime_parse_to_time.rb +5 -1
  35. data/lib/sequel/extensions/pg_array_ops.rb +2 -2
  36. data/lib/sequel/extensions/pg_hstore_ops.rb +1 -1
  37. data/lib/sequel/extensions/pg_inet_ops.rb +1 -1
  38. data/lib/sequel/extensions/pg_interval.rb +1 -0
  39. data/lib/sequel/extensions/pg_json.rb +3 -5
  40. data/lib/sequel/extensions/pg_json_ops.rb +3 -2
  41. data/lib/sequel/extensions/pg_range_ops.rb +1 -1
  42. data/lib/sequel/extensions/pg_row_ops.rb +1 -1
  43. data/lib/sequel/extensions/s.rb +2 -1
  44. data/lib/sequel/extensions/schema_dumper.rb +2 -2
  45. data/lib/sequel/extensions/server_block.rb +8 -12
  46. data/lib/sequel/extensions/sql_comments.rb +110 -3
  47. data/lib/sequel/extensions/sqlite_json_ops.rb +255 -0
  48. data/lib/sequel/extensions/string_date_time.rb +19 -23
  49. data/lib/sequel/model/base.rb +8 -12
  50. data/lib/sequel/plugins/auto_restrict_eager_graph.rb +62 -0
  51. data/lib/sequel/plugins/column_encryption.rb +1 -1
  52. data/lib/sequel/plugins/enum.rb +124 -0
  53. data/lib/sequel/plugins/instance_specific_default.rb +1 -1
  54. data/lib/sequel/plugins/sql_comments.rb +189 -0
  55. data/lib/sequel/plugins/subclasses.rb +28 -11
  56. data/lib/sequel/plugins/unused_associations.rb +2 -2
  57. data/lib/sequel/timezones.rb +12 -14
  58. data/lib/sequel/version.rb +1 -1
  59. metadata +21 -6
@@ -1,7 +1,7 @@
1
1
  # frozen-string-literal: true
2
2
 
3
3
  require 'mysql'
4
- raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
4
+ raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!, so it not supported. Please install the mysql or ruby-mysql gem.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
5
5
 
6
6
  require_relative 'utils/mysql_mysql2'
7
7
  require_relative 'utils/mysql_prepared_statements'
@@ -71,21 +71,43 @@ module Sequel
71
71
  # disconnect this connection (a.k.a @@wait_timeout).
72
72
  def connect(server)
73
73
  opts = server_opts(server)
74
- conn = Mysql.init
75
- conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
76
- conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
77
- conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
78
- if encoding = opts[:encoding] || opts[:charset]
79
- # Set encoding before connecting so that the mysql driver knows what
80
- # encoding we want to use, but this can be overridden by READ_DEFAULT_GROUP.
81
- conn.options(Mysql::SET_CHARSET_NAME, encoding)
82
- end
83
- if read_timeout = opts[:read_timeout] and defined? Mysql::OPT_READ_TIMEOUT
84
- conn.options(Mysql::OPT_READ_TIMEOUT, read_timeout)
85
- end
86
- if connect_timeout = opts[:connect_timeout] and defined? Mysql::OPT_CONNECT_TIMEOUT
87
- conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
74
+
75
+ if Mysql.respond_to?(:init)
76
+ conn = Mysql.init
77
+ conn.options(Mysql::READ_DEFAULT_GROUP, opts[:config_default_group] || "client")
78
+ conn.options(Mysql::OPT_LOCAL_INFILE, opts[:config_local_infile]) if opts.has_key?(:config_local_infile)
79
+ if encoding = opts[:encoding] || opts[:charset]
80
+ # Set encoding before connecting so that the mysql driver knows what
81
+ # encoding we want to use, but this can be overridden by READ_DEFAULT_GROUP.
82
+ conn.options(Mysql::SET_CHARSET_NAME, encoding)
83
+ end
84
+ if read_timeout = opts[:read_timeout] and defined? Mysql::OPT_READ_TIMEOUT
85
+ conn.options(Mysql::OPT_READ_TIMEOUT, read_timeout)
86
+ end
87
+ if connect_timeout = opts[:connect_timeout] and defined? Mysql::OPT_CONNECT_TIMEOUT
88
+ conn.options(Mysql::OPT_CONNECT_TIMEOUT, connect_timeout)
89
+ end
90
+ else
91
+ # ruby-mysql 3 API
92
+ conn = Mysql.new
93
+ # no support for default group
94
+ conn.local_infile = opts[:config_local_infile] if opts.has_key?(:config_local_infile)
95
+ if encoding = opts[:encoding] || opts[:charset]
96
+ conn.charset = encoding
97
+ end
98
+ if read_timeout = opts[:read_timeout]
99
+ conn.read_timeout = read_timeout
100
+ end
101
+ if connect_timeout = opts[:connect_timeout]
102
+ conn.connect_timeout = connect_timeout
103
+ end
104
+ conn.singleton_class.class_eval do
105
+ alias real_connect connect
106
+ alias use_result store_result
107
+ end
88
108
  end
109
+
110
+ conn.ssl_set(opts[:sslkey], opts[:sslcert], opts[:sslca], opts[:sslcapath], opts[:sslcipher]) if opts[:sslca] || opts[:sslkey]
89
111
  conn.real_connect(
90
112
  opts[:host] || 'localhost',
91
113
  opts[:user],
@@ -152,56 +174,49 @@ module Sequel
152
174
  super
153
175
  end
154
176
 
155
- # Return the version of the MySQL server to which we are connecting.
156
- def server_version(server=nil)
157
- @server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
158
- end
159
-
160
177
  private
161
178
 
162
179
  # Execute the given SQL on the given connection. If the :type
163
180
  # option is :select, yield the result of the query, otherwise
164
181
  # yield the connection if a block is given.
165
182
  def _execute(conn, sql, opts)
166
- begin
167
- r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn){conn.query(sql)}
168
- if opts[:type] == :select
169
- yield r if r
170
- elsif defined?(yield)
171
- yield conn
172
- end
173
- if conn.respond_to?(:more_results?)
174
- while conn.more_results? do
175
- if r
176
- r.free
177
- r = nil
178
- end
179
- begin
180
- conn.next_result
181
- r = conn.use_result
182
- rescue Mysql::Error => e
183
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
184
- break
185
- end
186
- yield r if opts[:type] == :select
183
+ r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn){conn.query(sql)}
184
+ if opts[:type] == :select
185
+ yield r if r
186
+ elsif defined?(yield)
187
+ yield conn
188
+ end
189
+ if conn.respond_to?(:more_results?)
190
+ while conn.more_results? do
191
+ if r
192
+ r.free
193
+ r = nil
194
+ end
195
+ begin
196
+ conn.next_result
197
+ r = conn.use_result
198
+ rescue Mysql::Error => e
199
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
200
+ break
187
201
  end
202
+ yield r if opts[:type] == :select
188
203
  end
189
- rescue Mysql::Error => e
190
- raise_error(e)
191
- ensure
192
- r.free if r
193
- # Use up all results to avoid a commands out of sync message.
194
- if conn.respond_to?(:more_results?)
195
- while conn.more_results? do
196
- begin
197
- conn.next_result
198
- r = conn.use_result
199
- rescue Mysql::Error => e
200
- raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
201
- break
202
- end
203
- r.free if r
204
+ end
205
+ rescue Mysql::Error => e
206
+ raise_error(e)
207
+ ensure
208
+ r.free if r
209
+ # Use up all results to avoid a commands out of sync message.
210
+ if conn.respond_to?(:more_results?)
211
+ while conn.more_results? do
212
+ begin
213
+ conn.next_result
214
+ r = conn.use_result
215
+ rescue Mysql::Error => e
216
+ raise_error(e, :disconnect=>true) if MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message)
217
+ break
204
218
  end
219
+ r.free if r
205
220
  end
206
221
  end
207
222
  end
@@ -233,17 +248,15 @@ module Sequel
233
248
  # the conversion raises an InvalidValue exception, return v
234
249
  # if :string and nil otherwise.
235
250
  def convert_date_time(v)
236
- begin
237
- yield v
238
- rescue InvalidValue
239
- case @convert_invalid_date_time
240
- when nil, :nil
241
- nil
242
- when :string
243
- v
244
- else
245
- raise
246
- end
251
+ yield v
252
+ rescue InvalidValue
253
+ case @convert_invalid_date_time
254
+ when nil, :nil
255
+ nil
256
+ when :string
257
+ v
258
+ else
259
+ raise
247
260
  end
248
261
  end
249
262
 
@@ -86,20 +86,26 @@ module Sequel
86
86
  if NativePreparedStatements
87
87
  # Use a native mysql2 prepared statement to implement prepared statements.
88
88
  def execute_prepared_statement(ps_name, opts, &block)
89
- ps = prepared_statement(ps_name)
89
+ if ps_name.is_a?(Sequel::Dataset::ArgumentMapper)
90
+ ps = ps_name
91
+ ps_name = ps.prepared_statement_name
92
+ else
93
+ ps = prepared_statement(ps_name)
94
+ end
90
95
  sql = ps.prepared_sql
91
96
 
92
97
  synchronize(opts[:server]) do |conn|
93
98
  stmt, ps_sql = conn.prepared_statements[ps_name]
94
99
  unless ps_sql == sql
95
100
  stmt.close if stmt
96
- stmt = log_connection_yield(conn, "Preparing #{ps_name}: #{sql}"){conn.prepare(sql)}
101
+ stmt = log_connection_yield("Preparing #{ps_name}: #{sql}", conn){conn.prepare(sql)}
97
102
  conn.prepared_statements[ps_name] = [stmt, sql]
98
103
  end
99
104
 
100
- if ps.log_sql
101
- opts = Hash[opts]
102
- opts = opts[:log_sql] = " (#{sql})"
105
+ opts = Hash[opts]
106
+ opts[:sql] = "Executing #{ps_name || sql}"
107
+ if ps_name && ps.log_sql
108
+ opts[:log_sql] = " (#{sql})"
103
109
  end
104
110
 
105
111
  _execute(conn, stmt, opts, &block)
@@ -111,56 +117,55 @@ module Sequel
111
117
  # option is :select, yield the result of the query, otherwise
112
118
  # yield the connection if a block is given.
113
119
  def _execute(conn, sql, opts)
114
- begin
115
- stream = opts[:stream]
116
- if NativePreparedStatements
117
- if args = opts[:arguments]
118
- args = args.map{|arg| bound_variable_value(arg)}
119
- end
120
+ stream = opts[:stream]
121
+ if NativePreparedStatements
122
+ if args = opts[:arguments]
123
+ args = args.map{|arg| bound_variable_value(arg)}
124
+ end
120
125
 
121
- case sql
122
- when ::Mysql2::Statement
123
- stmt = sql
124
- when Dataset
125
- sql = sql.sql
126
- close_stmt = true
127
- stmt = conn.prepare(sql)
128
- end
126
+ case sql
127
+ when ::Mysql2::Statement
128
+ stmt = sql
129
+ sql = opts[:sql] || ''
130
+ when Dataset
131
+ sql = sql.sql
132
+ close_stmt = true
133
+ stmt = conn.prepare(sql)
129
134
  end
135
+ end
130
136
 
131
- r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
132
- if stmt
133
- conn.query_options.merge!(:cache_rows=>true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream, :cast_booleans=>convert_tinyint_to_bool)
134
- stmt.execute(*args)
135
- else
136
- conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream)
137
- end
137
+ r = log_connection_yield((log_sql = opts[:log_sql]) ? sql + log_sql : sql, conn, args) do
138
+ if stmt
139
+ conn.query_options.merge!(:cache_rows=>true, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream, :cast_booleans=>convert_tinyint_to_bool)
140
+ stmt.execute(*args)
141
+ else
142
+ conn.query(sql, :database_timezone => timezone, :application_timezone => Sequel.application_timezone, :stream=>stream)
138
143
  end
139
- if opts[:type] == :select
140
- if r
141
- if stream
142
- begin
143
- r2 = yield r
144
- ensure
145
- # If r2 is nil, it means the block did not exit normally,
146
- # so the rest of the results must be drained to prevent
147
- # "commands out of sync" errors.
148
- r.each{} unless r2
149
- end
150
- else
151
- yield r
144
+ end
145
+ if opts[:type] == :select
146
+ if r
147
+ if stream
148
+ begin
149
+ r2 = yield r
150
+ ensure
151
+ # If r2 is nil, it means the block did not exit normally,
152
+ # so the rest of the results must be drained to prevent
153
+ # "commands out of sync" errors.
154
+ r.each{} unless r2
152
155
  end
156
+ else
157
+ yield r
153
158
  end
154
- elsif defined?(yield)
155
- yield conn
156
- end
157
- rescue ::Mysql2::Error => e
158
- raise_error(e)
159
- ensure
160
- if stmt
161
- conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
162
- stmt.close if close_stmt
163
159
  end
160
+ elsif defined?(yield)
161
+ yield conn
162
+ end
163
+ rescue ::Mysql2::Error => e
164
+ raise_error(e)
165
+ ensure
166
+ if stmt
167
+ conn.query_options.replace(conn.instance_variable_get(:@sequel_default_query_options))
168
+ stmt.close if close_stmt
164
169
  end
165
170
  end
166
171
 
@@ -116,25 +116,23 @@ module Sequel
116
116
  # error classes is raised, or a PGError is raised and the connection
117
117
  # status cannot be determined or it is not OK.
118
118
  def check_disconnect_errors
119
+ yield
120
+ rescue *DISCONNECT_ERROR_CLASSES => e
121
+ disconnect = true
122
+ raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
123
+ rescue PGError => e
124
+ disconnect = false
119
125
  begin
120
- yield
121
- rescue *DISCONNECT_ERROR_CLASSES => e
126
+ s = status
127
+ rescue PGError
122
128
  disconnect = true
123
- raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError))
124
- rescue PGError => e
125
- disconnect = false
126
- begin
127
- s = status
128
- rescue PGError
129
- disconnect = true
130
- end
131
- status_ok = (s == Adapter::CONNECTION_OK)
132
- disconnect ||= !status_ok
133
- disconnect ||= e.message =~ DISCONNECT_ERROR_RE
134
- disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
135
- ensure
136
- block if status_ok && !disconnect
137
129
  end
130
+ status_ok = (s == Adapter::CONNECTION_OK)
131
+ disconnect ||= !status_ok
132
+ disconnect ||= e.message =~ DISCONNECT_ERROR_RE
133
+ disconnect ? raise(Sequel.convert_exception_class(e, Sequel::DatabaseDisconnectError)) : raise
134
+ ensure
135
+ block if status_ok && !disconnect
138
136
  end
139
137
 
140
138
  # Execute the given SQL with this connection. If a block is given,
@@ -518,11 +516,9 @@ module Sequel
518
516
 
519
517
  # Convert exceptions raised from the block into DatabaseErrors.
520
518
  def check_database_errors
521
- begin
522
- yield
523
- rescue => e
524
- raise_error(e, :classes=>database_error_classes)
525
- end
519
+ yield
520
+ rescue => e
521
+ raise_error(e, :classes=>database_error_classes)
526
522
  end
527
523
 
528
524
  # Set the DateStyle to ISO if configured, for faster date parsing.
@@ -544,9 +544,10 @@ module Sequel
544
544
  server_version >= 50600 && (op[:op] == :drop_index || (op[:op] == :drop_constraint && op[:type] == :unique))
545
545
  end
546
546
 
547
- # Whether the database supports CHECK constraints
547
+ # CHECK constraints only supported on MariaDB 10.2+ and MySQL 8.0.19+
548
+ # (at least MySQL documents DROP CONSTRAINT was supported in 8.0.19+).
548
549
  def supports_check_constraints?
549
- mariadb? && server_version >= 100200
550
+ server_version >= (mariadb? ? 100200 : 80019)
550
551
  end
551
552
 
552
553
  # MySQL can combine multiple alter table ops into a single query.
@@ -1504,9 +1504,9 @@ module Sequel
1504
1504
  if column[:text]
1505
1505
  :text
1506
1506
  elsif column[:fixed]
1507
- "char(#{column[:size]||255})"
1507
+ "char(#{column[:size]||default_string_column_size})"
1508
1508
  elsif column[:text] == false || column[:size]
1509
- "varchar(#{column[:size]||255})"
1509
+ "varchar(#{column[:size]||default_string_column_size})"
1510
1510
  else
1511
1511
  :text
1512
1512
  end
@@ -337,6 +337,11 @@ module Sequel
337
337
  ps
338
338
  end
339
339
 
340
+ # Support creating STRICT tables via :strict option
341
+ def create_table_sql(name, generator, options)
342
+ "#{super}#{' STRICT' if options[:strict]}"
343
+ end
344
+
340
345
  # SQLite support creating temporary views.
341
346
  def create_view_prefix_sql(name, options)
342
347
  create_view_sql_append_columns("CREATE #{'TEMPORARY 'if options[:temp]}VIEW #{quote_schema_table(name)}", options[:columns])
@@ -347,6 +352,7 @@ module Sequel
347
352
  /foreign key constraint failed\z/i => ForeignKeyConstraintViolation,
348
353
  /\A(SQLITE ERROR 275 \(CONSTRAINT_CHECK\) : )?CHECK constraint failed/ => CheckConstraintViolation,
349
354
  /\A(SQLITE ERROR 19 \(CONSTRAINT\) : )?constraint failed\z/ => ConstraintViolation,
355
+ /\Acannot store [A-Z]+ value in [A-Z]+ column / => ConstraintViolation,
350
356
  /may not be NULL\z|NOT NULL constraint failed: .+\z/ => NotNullConstraintViolation,
351
357
  /\ASQLITE ERROR \d+ \(\) : CHECK constraint failed: / => CheckConstraintViolation
352
358
  }.freeze
@@ -98,6 +98,11 @@ module Sequel
98
98
  # The conversion procs to use for this database
99
99
  attr_reader :conversion_procs
100
100
 
101
+ def initialize(opts = OPTS)
102
+ super
103
+ @allow_regexp = typecast_value_boolean(opts[:setup_regexp_function])
104
+ end
105
+
101
106
  # Connect to the database. Since SQLite is a file based database,
102
107
  # available options are limited:
103
108
  #
@@ -119,6 +124,12 @@ module Sequel
119
124
  end
120
125
 
121
126
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
127
+
128
+ if typecast_value_boolean(opts[:setup_regexp_function])
129
+ db.create_function("regexp", 2) do |func, regexp_str, string|
130
+ func.result = Regexp.new(regexp_str).match(string) ? 1 : 0
131
+ end
132
+ end
122
133
 
123
134
  class << db
124
135
  attr_reader :prepared_statements
@@ -128,6 +139,12 @@ module Sequel
128
139
  db
129
140
  end
130
141
 
142
+ # Whether this Database instance is setup to allow regexp matching.
143
+ # True if the :setup_regexp_function option was passed when creating the Database.
144
+ def allow_regexp?
145
+ @allow_regexp
146
+ end
147
+
131
148
  # Disconnect given connections from the database.
132
149
  def disconnect_connection(c)
133
150
  c.prepared_statements.each_value{|v| v.first.close}
@@ -189,26 +206,24 @@ module Sequel
189
206
  # Yield an available connection. Rescue
190
207
  # any SQLite3::Exceptions and turn them into DatabaseErrors.
191
208
  def _execute(type, sql, opts, &block)
192
- begin
193
- synchronize(opts[:server]) do |conn|
194
- return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
195
- log_args = opts[:arguments]
196
- args = {}
197
- opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
198
- case type
199
- when :select
200
- log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
201
- when :insert
202
- log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
203
- conn.last_insert_row_id
204
- when :update
205
- log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
206
- conn.changes
207
- end
209
+ synchronize(opts[:server]) do |conn|
210
+ return execute_prepared_statement(conn, type, sql, opts, &block) if sql.is_a?(Symbol)
211
+ log_args = opts[:arguments]
212
+ args = {}
213
+ opts.fetch(:arguments, OPTS).each{|k, v| args[k] = prepared_statement_argument(v)}
214
+ case type
215
+ when :select
216
+ log_connection_yield(sql, conn, log_args){conn.query(sql, args, &block)}
217
+ when :insert
218
+ log_connection_yield(sql, conn, log_args){conn.execute(sql, args)}
219
+ conn.last_insert_row_id
220
+ when :update
221
+ log_connection_yield(sql, conn, log_args){conn.execute_batch(sql, args)}
222
+ conn.changes
208
223
  end
209
- rescue SQLite3::Exception => e
210
- raise_error(e)
211
224
  end
225
+ rescue SQLite3::Exception => e
226
+ raise_error(e)
212
227
  end
213
228
 
214
229
  # The SQLite adapter does not need the pool to convert exceptions.
@@ -323,6 +338,28 @@ module Sequel
323
338
  BindArgumentMethods = prepared_statements_module(:bind, ArgumentMapper)
324
339
  PreparedStatementMethods = prepared_statements_module(:prepare, BindArgumentMethods)
325
340
 
341
+ # Support regexp functions if using :setup_regexp_function Database option.
342
+ def complex_expression_sql_append(sql, op, args)
343
+ case op
344
+ when :~, :'!~', :'~*', :'!~*'
345
+ return super unless supports_regexp?
346
+
347
+ case_insensitive = [:'~*', :'!~*'].include?(op)
348
+ sql << 'NOT ' if [:'!~', :'!~*'].include?(op)
349
+ sql << '('
350
+ sql << 'LOWER(' if case_insensitive
351
+ literal_append(sql, args[0])
352
+ sql << ')' if case_insensitive
353
+ sql << ' REGEXP '
354
+ sql << 'LOWER(' if case_insensitive
355
+ literal_append(sql, args[1])
356
+ sql << ')' if case_insensitive
357
+ sql << ')'
358
+ else
359
+ super
360
+ end
361
+ end
362
+
326
363
  def fetch_rows(sql)
327
364
  execute(sql) do |result|
328
365
  cps = db.conversion_procs
@@ -346,6 +383,11 @@ module Sequel
346
383
  end
347
384
  end
348
385
  end
386
+
387
+ # Support regexp if using :setup_regexp_function Database option.
388
+ def supports_regexp?
389
+ db.allow_regexp?
390
+ end
349
391
 
350
392
  private
351
393
 
@@ -34,7 +34,7 @@ module Sequel
34
34
  def execute(sql, opts=OPTS, &block)
35
35
  if opts[:sproc]
36
36
  call_sproc(sql, opts, &block)
37
- elsif sql.is_a?(Symbol)
37
+ elsif sql.is_a?(Symbol) || sql.is_a?(Sequel::Dataset::ArgumentMapper)
38
38
  execute_prepared_statement(sql, opts, &block)
39
39
  else
40
40
  synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
@@ -55,13 +55,11 @@ class Sequel::ShardedSingleConnectionPool < Sequel::ConnectionPool
55
55
  # Yields the connection to the supplied block for the given server.
56
56
  # This method simulates the ConnectionPool#hold API.
57
57
  def hold(server=:default)
58
- begin
59
- server = pick_server(server)
60
- yield(@conns[server] ||= make_new(server))
61
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
62
- disconnect_server(server) if disconnect_error?(e)
63
- raise
64
- end
58
+ server = pick_server(server)
59
+ yield(@conns[server] ||= make_new(server))
60
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
61
+ disconnect_server(server) if disconnect_error?(e)
62
+ raise
65
63
  end
66
64
 
67
65
  # The ShardedSingleConnectionPool always has a maximum size of 1.
@@ -24,15 +24,13 @@ class Sequel::SingleConnectionPool < Sequel::ConnectionPool
24
24
 
25
25
  # Yield the connection to the block.
26
26
  def hold(server=nil)
27
- begin
28
- unless c = @conn.first
29
- @conn.replace([c = make_new(:default)])
30
- end
31
- yield c
32
- rescue Sequel::DatabaseDisconnectError, *@error_classes => e
33
- disconnect if disconnect_error?(e)
34
- raise
27
+ unless c = @conn.first
28
+ @conn.replace([c = make_new(:default)])
35
29
  end
30
+ yield c
31
+ rescue Sequel::DatabaseDisconnectError, *@error_classes => e
32
+ disconnect if disconnect_error?(e)
33
+ raise
36
34
  end
37
35
 
38
36
  # The SingleConnectionPool always has a maximum size of 1.
data/lib/sequel/core.rb CHANGED
@@ -278,11 +278,9 @@ module Sequel
278
278
  #
279
279
  # Sequel.string_to_date('2010-09-10') # Date.civil(2010, 09, 10)
280
280
  def string_to_date(string)
281
- begin
282
- Date.parse(string, Sequel.convert_two_digit_years)
283
- rescue => e
284
- raise convert_exception_class(e, InvalidValue)
285
- end
281
+ Date.parse(string, Sequel.convert_two_digit_years)
282
+ rescue => e
283
+ raise convert_exception_class(e, InvalidValue)
286
284
  end
287
285
 
288
286
  # Converts the given +string+ into a +Time+ or +DateTime+ object, depending on the
@@ -290,15 +288,13 @@ module Sequel
290
288
  #
291
289
  # Sequel.string_to_datetime('2010-09-10 10:20:30') # Time.local(2010, 09, 10, 10, 20, 30)
292
290
  def string_to_datetime(string)
293
- begin
294
- if datetime_class == DateTime
295
- DateTime.parse(string, convert_two_digit_years)
296
- else
297
- datetime_class.parse(string)
298
- end
299
- rescue => e
300
- raise convert_exception_class(e, InvalidValue)
291
+ if datetime_class == DateTime
292
+ DateTime.parse(string, convert_two_digit_years)
293
+ else
294
+ datetime_class.parse(string)
301
295
  end
296
+ rescue => e
297
+ raise convert_exception_class(e, InvalidValue)
302
298
  end
303
299
 
304
300
  # Converts the given +string+ into a <tt>Sequel::SQLTime</tt> object.
@@ -306,11 +302,9 @@ module Sequel
306
302
  # v = Sequel.string_to_time('10:20:30') # Sequel::SQLTime.parse('10:20:30')
307
303
  # DB.literal(v) # => '10:20:30'
308
304
  def string_to_time(string)
309
- begin
310
- SQLTime.parse(string)
311
- rescue => e
312
- raise convert_exception_class(e, InvalidValue)
313
- end
305
+ SQLTime.parse(string)
306
+ rescue => e
307
+ raise convert_exception_class(e, InvalidValue)
314
308
  end
315
309
 
316
310
  # Unless in single threaded mode, protects access to any mutable
@@ -400,6 +394,11 @@ module Sequel
400
394
 
401
395
  private
402
396
 
397
+ # Return a hash of date information parsed from the given string.
398
+ def _date_parse(string)
399
+ Date._parse(string)
400
+ end
401
+
403
402
  # Helper method that the database adapter class methods that are added to Sequel via
404
403
  # metaprogramming use to parse arguments.
405
404
  def adapter_method(adapter, *args, &block)