sequel 5.18.0 → 5.20.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +40 -0
  3. data/doc/opening_databases.rdoc +5 -2
  4. data/doc/release_notes/5.19.0.txt +28 -0
  5. data/doc/release_notes/5.20.0.txt +89 -0
  6. data/doc/sharding.rdoc +12 -0
  7. data/doc/transactions.rdoc +38 -0
  8. data/lib/sequel/adapters/jdbc.rb +7 -2
  9. data/lib/sequel/adapters/mysql2.rb +2 -2
  10. data/lib/sequel/adapters/shared/postgres.rb +8 -8
  11. data/lib/sequel/adapters/shared/sqlite.rb +3 -1
  12. data/lib/sequel/adapters/sqlanywhere.rb +33 -17
  13. data/lib/sequel/adapters/sqlite.rb +20 -13
  14. data/lib/sequel/connection_pool.rb +0 -5
  15. data/lib/sequel/database/misc.rb +10 -9
  16. data/lib/sequel/database/query.rb +1 -1
  17. data/lib/sequel/database/schema_generator.rb +1 -1
  18. data/lib/sequel/database/transactions.rb +57 -5
  19. data/lib/sequel/dataset/actions.rb +6 -5
  20. data/lib/sequel/dataset/graph.rb +2 -2
  21. data/lib/sequel/dataset/placeholder_literalizer.rb +4 -1
  22. data/lib/sequel/dataset/prepared_statements.rb +1 -1
  23. data/lib/sequel/dataset/query.rb +1 -1
  24. data/lib/sequel/extensions/constraint_validations.rb +14 -0
  25. data/lib/sequel/extensions/pg_enum.rb +23 -15
  26. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  27. data/lib/sequel/model/associations.rb +38 -12
  28. data/lib/sequel/model/base.rb +1 -1
  29. data/lib/sequel/model/plugins.rb +104 -0
  30. data/lib/sequel/plugins/association_dependencies.rb +3 -3
  31. data/lib/sequel/plugins/association_pks.rb +14 -4
  32. data/lib/sequel/plugins/class_table_inheritance.rb +1 -0
  33. data/lib/sequel/plugins/composition.rb +13 -9
  34. data/lib/sequel/plugins/finder.rb +2 -2
  35. data/lib/sequel/plugins/hook_class_methods.rb +17 -5
  36. data/lib/sequel/plugins/inverted_subsets.rb +2 -2
  37. data/lib/sequel/plugins/json_serializer.rb +3 -3
  38. data/lib/sequel/plugins/nested_attributes.rb +1 -1
  39. data/lib/sequel/plugins/pg_array_associations.rb +8 -4
  40. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +61 -32
  41. data/lib/sequel/plugins/prepared_statements.rb +1 -1
  42. data/lib/sequel/plugins/prepared_statements_safe.rb +1 -1
  43. data/lib/sequel/plugins/subset_conditions.rb +2 -2
  44. data/lib/sequel/plugins/validation_class_methods.rb +5 -3
  45. data/lib/sequel/plugins/validation_helpers.rb +2 -2
  46. data/lib/sequel/sql.rb +1 -1
  47. data/lib/sequel/version.rb +1 -1
  48. data/spec/adapters/postgres_spec.rb +40 -0
  49. data/spec/core/database_spec.rb +73 -2
  50. data/spec/core/schema_spec.rb +7 -1
  51. data/spec/extensions/class_table_inheritance_spec.rb +30 -8
  52. data/spec/extensions/constraint_validations_spec.rb +20 -2
  53. data/spec/extensions/core_refinements_spec.rb +1 -1
  54. data/spec/extensions/hook_class_methods_spec.rb +22 -0
  55. data/spec/extensions/migration_spec.rb +13 -0
  56. data/spec/extensions/pg_auto_constraint_validations_spec.rb +8 -0
  57. data/spec/extensions/pg_enum_spec.rb +5 -0
  58. data/spec/extensions/s_spec.rb +1 -1
  59. data/spec/extensions/schema_dumper_spec.rb +4 -2
  60. data/spec/integration/plugin_test.rb +15 -0
  61. data/spec/integration/transaction_test.rb +50 -0
  62. data/spec/model/associations_spec.rb +84 -4
  63. data/spec/model/plugins_spec.rb +111 -0
  64. metadata +7 -3
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8f181e0ec05e457e72b2517202bbd159a6a066c9dd946201de44576b64bca302
4
- data.tar.gz: f554f6268798f72c667851c6d952f960564e70ebb1e2d3d1208db74bbcd98834
3
+ metadata.gz: e092b5439a78eec1bb50a0bbc8a62c1d84ee5b27b9c5f034c250b632d03e17af
4
+ data.tar.gz: 231708d52405407b11113c55d5fdcd95e7d6155b0a8283f736876569ced6e4b1
5
5
  SHA512:
6
- metadata.gz: 474c441a62bb901abaf12968ec62cdd239f9679ce4cb1743cd58e7ec6032cb1e26d128fd49bbe7c3edfce9a0fb63b33975aa6426ebb8ee79f1309a09967ffef5
7
- data.tar.gz: 8717400fcb03235ad51ab208d35e9accca05018c6531da025bbcefb8f4b557345e1afbbfd65862f2c213ca4b05826db72c5ecc6b8abdda5920af83dcd8fe35d9
6
+ metadata.gz: 4c2d7e19056e53f2d780f57f5da3c4675531f9678c6a54e922e0d59895bd2e485f12ee0b2644ed801c832a53653f75531d849cb7d60dcd9fb6a4edbfa8cb4c0b
7
+ data.tar.gz: 701b041185cc57c1966ea3b4be040e26a9e0aabfd6df45f0192f5b0b51133bfb94629f0634fa6edfc513a1c9193d455cb6222b388683d638e474b344e663ca50
data/CHANGELOG CHANGED
@@ -1,3 +1,43 @@
1
+ === 5.20.0 (2019-05-01)
2
+
3
+ * Fix reversing of alter_table add_foreign_key when :type option is used (jeremyevans) (#1615)
4
+
5
+ * Switch from using instance_exec to define_method for model associations and in some plugins (jeremyevans)
6
+
7
+ * Fix Database#server_version when using mysql2 adapter with mysql driver on MariaDB 10+ database (v-kolesnikov) (#1614)
8
+
9
+ * Make one_to_one setter method handle models that use joined datasets (jeremyevans) (#1612)
10
+
11
+ * Make auto_validations plugin work with the class_table_inheritance plugin (jeremyevans) (#1611)
12
+
13
+ * Avoid use of instance_exec for PlaceholderLiteralString#with_dataset (jeremyevans)
14
+
15
+ * Recognize float unsigned database types as float (keeguon, jeremyevans) (#1609)
16
+
17
+ * Support :savepoint options to Database#{after_commit,after_rollback} for making the hooks handle savepoints (jeremyevans)
18
+
19
+ * Avoid use of instance_exec in association_dependencies plugin (jeremyevans)
20
+
21
+ * Add pg_auto_constraint_validation_override to the pg_auto_constraint_validations plugin, for customizing columns and error message per constraint (jeremyevans)
22
+
23
+ * Make Database#check_constraints on PostgreSQL also include constraints where the related columns are not known (jeremyevans)
24
+
25
+ === 5.19.0 (2019-04-02)
26
+
27
+ * Use more optimized approach to merging hashes in ruby 2.5+ (jeremyevans)
28
+
29
+ * Use SQLite extended result codes when using ruby-sqlite3 1.4.0+ (jeremyevans)
30
+
31
+ * Recognize additional SQLite extended result codes in the shared sqlite adapter (jeremyevans)
32
+
33
+ * Add Database#rename_enum_value to the pg_enum extension (AlexWayfer) (#1603)
34
+
35
+ * Make Database#drop_table delete constraint validations metadata for that table if using the constraint_validations extension (jeremyevans)
36
+
37
+ * Speed up row fetching in the sqlite adapter (jeremyevans)
38
+
39
+ * Speed up row fetching and type conversion in the sqlanywhere adapter (jeremyevans)
40
+
1
41
  === 5.18.0 (2019-03-01)
2
42
 
3
43
  * Use singleton .call methods on plain objects instead of procs/methods for faster type conversion (jeremyevans)
@@ -187,9 +187,8 @@ For Derby, H2, HSQLDB, JTDS, MySQL, Postgres, SQLite3
187
187
  the adapters can use the `jdbc-*` gem, for the others you need to have the `.jar` in your CLASSPATH
188
188
  or load the Java class manually before calling Sequel.connect.
189
189
 
190
-
191
190
  Note that when using a JDBC adapter, the best way to use Sequel
192
- is via Sequel.connect, NOT Sequel.jdbc. Use the JDBC connection
191
+ is via Sequel.connect using a connection string, NOT Sequel.jdbc. Use the JDBC connection
193
192
  string when connecting, which will be in a different format than
194
193
  the native connection string. The connection string should start
195
194
  with 'jdbc:'. For PostgreSQL, use 'jdbc:postgresql:', and for
@@ -201,6 +200,10 @@ Sequel does no preprocessing of JDBC connection strings, it passes them directly
201
200
  So if you have problems getting a connection string to work, look up the
202
201
  documentation for the JDBC driver.
203
202
 
203
+ The jdbc adapter does not handle common options such as +:host+,
204
+ +:user+, and +:port+. If you must use a hash of options when connecting,
205
+ provide the full JDBC connection string as the :uri option.
206
+
204
207
  Example connection strings:
205
208
 
206
209
  jdbc:sqlite::memory:
@@ -0,0 +1,28 @@
1
+ = New Features
2
+
3
+ * A Database#rename_enum_value method has been added to the pg_enum
4
+ extension. It is supported on PostgreSQL 10+:
5
+
6
+ DB.rename_enum_value(:enum_type, 'old_name', 'new_name')
7
+
8
+ = Other Improvements
9
+
10
+ * The performance of row fetching and type conversion in the
11
+ sqlanywhere adapter has been improved.
12
+
13
+ * The performance of row fetching in the sqlite adapter has been
14
+ improved.
15
+
16
+ * Calling Database#drop_table now drops any constraint validations
17
+ metadata for the table if using the constraint_validations
18
+ extension. However, modifying the table using Database#alter_table
19
+ does not affect the constraint validations metadata.
20
+
21
+ * The sqlite adapter when used with ruby-sqlite3 1.4.0+ now uses
22
+ SQLite extended result codes for a more accurate determination of
23
+ specific database errors types.
24
+
25
+ * Performance for typecasting to decimal and floats has been improved
26
+ slightly.
27
+
28
+ * Performance when merging hashes has been improved slightly.
@@ -0,0 +1,89 @@
1
+ = New Features
2
+
3
+ * Database#after_commit and #after_rollback transaction hook methods
4
+ now support a :savepoint option. Using the :savepoint option makes
5
+ the hooks savepoint-aware, so after_commit will only be called if
6
+ all enclosing savepoints and the transaction are committed, and
7
+ after_rollback will be called when any of the enclosing savepoints
8
+ are rolled back (which may be before transaction commit/rollback).
9
+ Examples:
10
+
11
+ x = nil
12
+ DB.transaction do # BEGIN
13
+ DB.transaction(savepoint: true) do # SAVEPOINT
14
+ DB.after_commit(savepoint: true){x = 1}
15
+ DB.after_rollback(savepoint: true){x = 2}
16
+ x # nil
17
+ end # RELEASE SAVEPOINT
18
+ x # nil
19
+ end # COMMIT
20
+ x # 1
21
+
22
+ x = nil
23
+ DB.transaction do # BEGIN
24
+ DB.transaction(savepoint: true) do # SAVEPOINT
25
+ DB.after_commit(savepoint: true){x = 1}
26
+ DB.after_rollback(savepoint: true){x = 2}
27
+ x # nil
28
+ raise Sequel::Rollback
29
+ end # ROLLBACK TO SAVEPOINT
30
+ x # 2
31
+ end # COMMIT
32
+ x # 2
33
+
34
+ x = nil
35
+ DB.transaction do # BEGIN
36
+ DB.transaction(savepoint: true) do # SAVEPOINT
37
+ DB.after_commit(savepoint: true){x = 1}
38
+ DB.after_rollback(savepoint: true){x = 2}
39
+ end # RELEASE SAVEPOINT
40
+ x # nil
41
+ raise Sequel::Rollback
42
+ end
43
+ x # 2
44
+
45
+ * The pg_auto_constraint_validations plugin now supports a
46
+ pg_auto_constraint_validation_override method for overriding
47
+ the columns and message for a specific constraint. This is
48
+ useful if the database cannot determine the columns (due
49
+ to the constraint containing a database function call), or
50
+ if you would like to customize the message per constraint.
51
+
52
+ = Other Improvements
53
+
54
+ * The one_to_one association setter now works with models that use
55
+ joined datasets, such as child models when using the
56
+ class_table_inheritance plugin.
57
+
58
+ * Database#check_constraints on PostgreSQL now also includes CHECK
59
+ constraints where the related columns are not known. The :columns
60
+ entry in the hash will be an empty array in such cases. The
61
+ exclusion of such constraints in previous versions was not
62
+ intentional, and the documentation implied that all CHECK
63
+ constraints were returned.
64
+
65
+ * Many cases where instance_exec was previously used on model
66
+ instances have been changed so that instance methods are defined
67
+ and called instead. This avoids the creation of singleton classes
68
+ for model instances, and can significantly improve performance in
69
+ some cases. This affects all associations as well as the
70
+ following plugins:
71
+
72
+ * composition
73
+ * hook_class_methods
74
+ * validation_class_methods
75
+
76
+ Other cases where instance_exec is now avoided and a different
77
+ approach is used:
78
+
79
+ * association_dependencies plugin
80
+ * PlaceholderLiteralString#with_dataset
81
+
82
+ * The auto_validations plugin now works with child models when using
83
+ the class_table_inheritance plugin.
84
+
85
+ * Database#server_version now works correctly in the mysql2 adapter
86
+ when using the MySQL driver with MariaDB 10+.
87
+
88
+ * The float unsigned type is now recognized and supported in the
89
+ schema parser and schema_dumper extension.
@@ -268,3 +268,15 @@ the Database, just remove that option. If you are setting:
268
268
  Sequel.single_threaded = true
269
269
 
270
270
  just remove or comment out that code.
271
+
272
+ == JDBC
273
+
274
+ If you are using the jdbc adapter, note that it does not handle separate
275
+ options such as +:host+, +:user+, and +:port+. If you would like to use
276
+ the +:servers+ option when connecting to a JDBC database, each hash value in
277
+ the +servers+ option should contain a +:uri+ key with a JDBC connection string
278
+ for that shard as the value. Example:
279
+
280
+ DB=Sequel.connect('jdbc:postgresql://primary_server/database',
281
+ servers: {read_only: {uri: 'jdbc:postgresql://replica_server/database'}})
282
+
@@ -169,6 +169,44 @@ If you want the current savepoint and potentially enclosing savepoints to be rol
169
169
  end # ROLLBACK TO SAVEPOINT
170
170
  end # ROLLBACK
171
171
 
172
+ === Savepoint Hooks
173
+
174
+ When using savepoints, you can use the +:savepoint+ option to +after_commit+ or +after_rollback+ to use a savepoint hook. For +after_commit+, this will only run the hook after transaction commit if all enclosing savepoints are released (not rolled back). For +after_rollback+, this will run the hook after any enclosing savepoint is rolled back (before transaction commit), or after the transaction is rolled back if all enclosing savepoints are released:
175
+
176
+ x = nil
177
+ DB.transaction do # BEGIN
178
+ DB.transaction(savepoint: true) do # SAVEPOINT
179
+ DB.after_commit(savepoint: true){x = 1}
180
+ DB.after_rollback(savepoint: true){x = 2}
181
+ x # nil
182
+ end # RELEASE SAVEPOINT
183
+ x # nil
184
+ end # COMMIT
185
+ x # 1
186
+
187
+ x = nil
188
+ DB.transaction do # BEGIN
189
+ DB.transaction(savepoint: true) do # SAVEPOINT
190
+ DB.after_commit(savepoint: true){x = 1}
191
+ DB.after_rollback(savepoint: true){x = 2}
192
+ x # nil
193
+ raise Sequel::Rollback
194
+ end # ROLLBACK TO SAVEPOINT
195
+ x # 2
196
+ end # COMMIT
197
+ x # 2
198
+
199
+ x = nil
200
+ DB.transaction do # BEGIN
201
+ DB.transaction(savepoint: true) do # SAVEPOINT
202
+ DB.after_commit(savepoint: true){x = 1}
203
+ DB.after_rollback(savepoint: true){x = 2}
204
+ end # RELEASE SAVEPOINT
205
+ x # nil
206
+ raise Sequel::Rollback
207
+ end
208
+ x # 2
209
+
172
210
  == Prepared Transactions / Two-Phase Commit
173
211
 
174
212
  Sequel supports database prepared transactions on PostgreSQL, MySQL, and H2. With prepared transactions, at the end of the transaction, the transaction is not immediately committed (it acts like a rollback). Later, you can call +commit_prepared_transaction+ to commit the transaction or +rollback_prepared_transaction+ to roll the transaction back. Prepared transactions are usually used with distributed databases to make sure all databases commit the same transaction or none of them do.
@@ -254,7 +254,9 @@ module Sequel
254
254
  log_connection_yield(sql, conn){stmt.execute(sql)}
255
255
  when :insert
256
256
  log_connection_yield(sql, conn){execute_statement_insert(stmt, sql)}
257
- last_insert_id(conn, Hash[opts].merge!(:stmt=>stmt))
257
+ opts = Hash[opts]
258
+ opts[:stmt] = stmt
259
+ last_insert_id(conn, opts)
258
260
  else
259
261
  log_connection_yield(sql, conn){stmt.executeUpdate(sql)}
260
262
  end
@@ -449,7 +451,10 @@ module Sequel
449
451
  log_connection_yield(msg, conn, args){cps.execute}
450
452
  when :insert
451
453
  log_connection_yield(msg, conn, args){execute_prepared_statement_insert(cps)}
452
- last_insert_id(conn, Hash[opts].merge!(:prepared=>true, :stmt=>cps))
454
+ opts = Hash[opts]
455
+ opts[:prepared] = true
456
+ opts[:stmt] = cps
457
+ last_insert_id(conn, opts)
453
458
  else
454
459
  log_connection_yield(msg, conn, args){cps.executeUpdate}
455
460
  end
@@ -78,8 +78,8 @@ module Sequel
78
78
  end
79
79
 
80
80
  # Return the version of the MySQL server to which we are connecting.
81
- def server_version(server=nil)
82
- @server_version ||= (synchronize(server){|conn| conn.server_info[:id]} || super)
81
+ def server_version(_server=nil)
82
+ @server_version ||= super()
83
83
  end
84
84
 
85
85
  private
@@ -220,24 +220,22 @@ module Sequel
220
220
  # A hash of metadata for CHECK constraints on the table.
221
221
  # Keys are CHECK constraint name symbols. Values are hashes with the following keys:
222
222
  # :definition :: An SQL fragment for the definition of the constraint
223
- # :columns :: An array of column symbols for the columns referenced in the constraint
223
+ # :columns :: An array of column symbols for the columns referenced in the constraint,
224
+ # can be an empty array if the database cannot deteremine the column symbols.
224
225
  def check_constraints(table)
225
226
  m = output_identifier_meth
226
227
 
227
228
  rows = metadata_dataset.
228
229
  from{pg_constraint.as(:co)}.
229
- join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
230
+ left_join(Sequel[:pg_attribute].as(:att), :attrelid=>:conrelid, :attnum=>SQL::Function.new(:ANY, Sequel[:co][:conkey])).
230
231
  where(:conrelid=>regclass_oid(table), :contype=>'c').
231
232
  select{[co[:conname].as(:constraint), att[:attname].as(:column), pg_get_constraintdef(co[:oid]).as(:definition)]}
232
233
 
233
234
  hash = {}
234
235
  rows.each do |row|
235
236
  constraint = m.call(row[:constraint])
236
- if entry = hash[constraint]
237
- entry[:columns] << m.call(row[:column])
238
- else
239
- hash[constraint] = {:definition=>row[:definition], :columns=>[m.call(row[:column])]}
240
- end
237
+ entry = hash[constraint] ||= {:definition=>row[:definition], :columns=>[]}
238
+ entry[:columns] << m.call(row[:column]) if row[:column]
241
239
  end
242
240
 
243
241
  hash
@@ -1733,7 +1731,9 @@ module Sequel
1733
1731
  def _import(columns, values, opts=OPTS)
1734
1732
  if @opts[:returning]
1735
1733
  statements = multi_insert_sql(columns, values)
1736
- @db.transaction(Hash[opts].merge!(:server=>@opts[:server])) do
1734
+ trans_opts = Hash[opts]
1735
+ trans_opts[:server] = @opts[:server]
1736
+ @db.transaction(trans_opts) do
1737
1737
  statements.map{|st| returning_fetch_rows(st)}
1738
1738
  end.first.map{|v| v.length == 1 ? v.values.first : v}
1739
1739
  elsif opts[:return] == :primary_key
@@ -323,7 +323,7 @@ module Sequel
323
323
  case sqlite_error_code(exception)
324
324
  when 1299
325
325
  NotNullConstraintViolation
326
- when 2067
326
+ when 1555, 2067, 2579
327
327
  UniqueConstraintViolation
328
328
  when 787
329
329
  ForeignKeyConstraintViolation
@@ -331,6 +331,8 @@ module Sequel
331
331
  CheckConstraintViolation
332
332
  when 19
333
333
  ConstraintViolation
334
+ when 517
335
+ SerializationFailure
334
336
  else
335
337
  super
336
338
  end
@@ -17,21 +17,34 @@ module Sequel
17
17
  end
18
18
  end
19
19
 
20
- tt = Class.new do
21
- def blob(s) ::Sequel::SQL::Blob.new(s) end
22
- def boolean(s) s.to_i != 0 end
23
- def date(s) ::Date.strptime(s) end
24
- def decimal(s) BigDecimal(s) end
25
- def time(s) ::Sequel.string_to_time(s) end
26
- end.new
20
+ boolean = Object.new
21
+ def boolean.call(s)
22
+ s.to_i != 0
23
+ end
24
+
25
+ date = Object.new
26
+ def date.call(s)
27
+ ::Date.strptime(s)
28
+ end
29
+
30
+ decimal = Object.new
31
+ class << decimal
32
+ alias call BigDecimal
33
+ public :call
34
+ end
35
+
36
+ time = Object.new
37
+ def time.call(s)
38
+ ::Sequel.string_to_time(s)
39
+ end
27
40
 
28
41
  SQLANYWHERE_TYPES = {}
29
42
  {
30
- [0, 484] => tt.method(:decimal),
31
- [384] => tt.method(:date),
32
- [388] => tt.method(:time),
33
- [500] => tt.method(:boolean),
34
- [524, 528] => tt.method(:blob)
43
+ [0, 484] => decimal,
44
+ [384] => date,
45
+ [388] => time,
46
+ [500] => boolean,
47
+ [524, 528] => ::Sequel::SQL::Blob
35
48
  }.each do |k,v|
36
49
  k.each{|n| SQLANYWHERE_TYPES[n] = v}
37
50
  end
@@ -153,17 +166,20 @@ module Sequel
153
166
  else
154
167
  cps[type]
155
168
  end
156
- col_infos << [i, output_identifier(name), cp]
169
+ col_infos << [output_identifier(name), cp]
157
170
  end
158
171
 
159
- self.columns = col_infos.map{|a| a[1]}
172
+ self.columns = col_infos.map(&:first)
173
+ max = col_infos.length
160
174
 
161
175
  if rs
162
176
  while api.sqlany_fetch_next(rs) == 1
177
+ i = -1
163
178
  h = {}
164
- col_infos.each do |i, name, cp|
165
- _, v = api.sqlany_get_column(rs, i)
166
- h[name] = cp && v ? cp[v] : v
179
+ while (i+=1) < max
180
+ name, cp = col_infos[i]
181
+ v = api.sqlany_get_column(rs, i)[1]
182
+ h[name] = cp && v ? cp.call(v) : v
167
183
  end
168
184
  yield h
169
185
  end
@@ -5,7 +5,7 @@ require_relative 'shared/sqlite'
5
5
 
6
6
  module Sequel
7
7
  module SQLite
8
- FALSE_VALUES = (%w'0 false f no n' + [0]).freeze
8
+ FALSE_VALUES = (%w'0 false f no n'.each(&:freeze) + [0]).freeze
9
9
 
10
10
  blob = Object.new
11
11
  def blob.call(s)
@@ -77,6 +77,10 @@ module Sequel
77
77
  k.each{|n| SQLITE_TYPES[n] = v}
78
78
  end
79
79
  SQLITE_TYPES.freeze
80
+
81
+ sqlite_version = SQLite3::VERSION.split('.').map(&:to_i)[0..1]
82
+ sqlite_version = sqlite_version[0] * 100 + sqlite_version[1]
83
+ USE_EXTENDED_RESULT_CODES = sqlite_version >= 104
80
84
 
81
85
  class Database < Sequel::Database
82
86
  include ::Sequel::SQLite::DatabaseMethods
@@ -109,6 +113,10 @@ module Sequel
109
113
  sqlite3_opts[:readonly] = typecast_value_boolean(opts[:readonly]) if opts.has_key?(:readonly)
110
114
  db = ::SQLite3::Database.new(opts[:database].to_s, sqlite3_opts)
111
115
  db.busy_timeout(opts.fetch(:timeout, 5000))
116
+
117
+ if USE_EXTENDED_RESULT_CODES
118
+ db.extended_result_codes = true
119
+ end
112
120
 
113
121
  connection_pragmas.each{|s| log_connection_yield(s, db){db.execute_batch(s)}}
114
122
 
@@ -279,13 +287,12 @@ module Sequel
279
287
  Dataset
280
288
  end
281
289
 
282
- # Support SQLite exception codes if ruby-sqlite3 supports them.
283
- # This is disabled by default because ruby-sqlite3 doesn't currently
284
- # support them (returning nil), and even if it did, it doesn't support
285
- # extended error codes, which would lead to worse behavior.
286
- #def sqlite_error_code(exception)
287
- # exception.code if exception.respond_to?(:code)
288
- #end
290
+ if USE_EXTENDED_RESULT_CODES
291
+ # Support SQLite exception codes if ruby-sqlite3 supports them.
292
+ def sqlite_error_code(exception)
293
+ exception.code if exception.respond_to?(:code)
294
+ end
295
+ end
289
296
  end
290
297
 
291
298
  class Dataset < Sequel::Dataset
@@ -318,18 +325,18 @@ module Sequel
318
325
 
319
326
  def fetch_rows(sql)
320
327
  execute(sql) do |result|
321
- i = -1
322
328
  cps = db.conversion_procs
323
329
  type_procs = result.types.map{|t| cps[base_type_name(t)]}
324
- cols = result.columns.map{|c| i+=1; [output_identifier(c), i, type_procs[i]]}
325
- max = i+1
330
+ j = -1
331
+ cols = result.columns.map{|c| [output_identifier(c), type_procs[(j+=1)]]}
326
332
  self.columns = cols.map(&:first)
333
+ max = cols.length
327
334
  result.each do |values|
328
335
  row = {}
329
336
  i = -1
330
337
  while (i += 1) < max
331
- name, id, type_proc = cols[i]
332
- v = values[id]
338
+ name, type_proc = cols[i]
339
+ v = values[i]
333
340
  if type_proc && v
334
341
  v = type_proc.call(v)
335
342
  end