sequel 5.51.0 → 5.56.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 (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)