sequel 5.39.0 → 5.44.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG +52 -0
  3. data/MIT-LICENSE +1 -1
  4. data/doc/release_notes/5.40.0.txt +40 -0
  5. data/doc/release_notes/5.41.0.txt +25 -0
  6. data/doc/release_notes/5.42.0.txt +136 -0
  7. data/doc/release_notes/5.43.0.txt +98 -0
  8. data/doc/release_notes/5.44.0.txt +32 -0
  9. data/doc/sql.rdoc +1 -1
  10. data/doc/testing.rdoc +3 -0
  11. data/lib/sequel/adapters/ado.rb +16 -16
  12. data/lib/sequel/adapters/jdbc.rb +2 -2
  13. data/lib/sequel/adapters/shared/postgres.rb +4 -2
  14. data/lib/sequel/adapters/shared/sqlite.rb +37 -3
  15. data/lib/sequel/core.rb +11 -0
  16. data/lib/sequel/database/misc.rb +1 -2
  17. data/lib/sequel/database/schema_generator.rb +35 -47
  18. data/lib/sequel/database/schema_methods.rb +4 -0
  19. data/lib/sequel/dataset/features.rb +10 -0
  20. data/lib/sequel/dataset/prepared_statements.rb +2 -0
  21. data/lib/sequel/dataset/sql.rb +32 -10
  22. data/lib/sequel/extensions/async_thread_pool.rb +438 -0
  23. data/lib/sequel/extensions/blank.rb +8 -0
  24. data/lib/sequel/extensions/date_arithmetic.rb +36 -24
  25. data/lib/sequel/extensions/eval_inspect.rb +2 -0
  26. data/lib/sequel/extensions/inflector.rb +8 -0
  27. data/lib/sequel/extensions/migration.rb +2 -0
  28. data/lib/sequel/extensions/named_timezones.rb +5 -1
  29. data/lib/sequel/extensions/pg_array.rb +1 -0
  30. data/lib/sequel/extensions/pg_enum.rb +1 -1
  31. data/lib/sequel/extensions/pg_interval.rb +34 -8
  32. data/lib/sequel/extensions/pg_row.rb +1 -0
  33. data/lib/sequel/extensions/query.rb +2 -0
  34. data/lib/sequel/model/associations.rb +68 -13
  35. data/lib/sequel/model/base.rb +23 -6
  36. data/lib/sequel/model/plugins.rb +5 -0
  37. data/lib/sequel/plugins/association_proxies.rb +2 -0
  38. data/lib/sequel/plugins/async_thread_pool.rb +39 -0
  39. data/lib/sequel/plugins/auto_validations.rb +15 -1
  40. data/lib/sequel/plugins/column_encryption.rb +728 -0
  41. data/lib/sequel/plugins/composition.rb +7 -2
  42. data/lib/sequel/plugins/concurrent_eager_loading.rb +174 -0
  43. data/lib/sequel/plugins/constraint_validations.rb +2 -1
  44. data/lib/sequel/plugins/dataset_associations.rb +4 -1
  45. data/lib/sequel/plugins/json_serializer.rb +37 -22
  46. data/lib/sequel/plugins/nested_attributes.rb +8 -3
  47. data/lib/sequel/plugins/pg_array_associations.rb +10 -4
  48. data/lib/sequel/plugins/pg_auto_constraint_validations.rb +2 -0
  49. data/lib/sequel/plugins/rcte_tree.rb +27 -19
  50. data/lib/sequel/plugins/serialization.rb +8 -3
  51. data/lib/sequel/plugins/serialization_modification_detection.rb +1 -1
  52. data/lib/sequel/plugins/validation_helpers.rb +6 -2
  53. data/lib/sequel/version.rb +1 -1
  54. metadata +36 -22
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: b995589f8bb51611b490ae4cd1d84c2ecfbb5889cb4f940dd786a841fe9ecf9f
4
- data.tar.gz: afa86ae9dfc39cd21138aff35955fb4194c359a1ea56c9a80026f04b85b79a4a
3
+ metadata.gz: 84b971e8db174387861ce5ccbb36f43ba2ffa39a8b5f88359c1246eac5ff1539
4
+ data.tar.gz: d90034f07d752fc45f6359d9b38708e865200c1a1f0c9a1ea77c9d24d681e0a3
5
5
  SHA512:
6
- metadata.gz: 553d5c2b92a49d079503c9e8e0f8dfecef1a4abf2ed2fe242c8aec19d53c918912de4f5d1453626a05c1b654c5582c508199df10aa7540ec590331b3ecb355da
7
- data.tar.gz: f0fc7bceadccc74651573f82ed957ab91061ecb05f5f30050d0814a0142b915a79175d146b3bcac6f7269cd4f3b3b69eb58a92a4df439a944c081b45f1deaa2c
6
+ metadata.gz: 194b8b093ecc8c18b228a19b1c27b21535590de8ac5d5002af1038ee28add4a8afdf31741bd33a5838c94535ec272cbb594e6674d76dcdd99518f5e3cbca4dda
7
+ data.tar.gz: cb54772e671e6c82422e6f4b076b3c7748165837d3501834ddc374cf939c4ea08cc581876150be72c3532a0f3591aaef18e4858e00493db0b49724a1257da957
data/CHANGELOG CHANGED
@@ -1,3 +1,55 @@
1
+ === 5.44.0 (2021-05-01)
2
+
3
+ * Add concurrent_eager_loading plugin, for eager loading multiple associations concurrently using separate threads (jeremyevans)
4
+
5
+ * Support :weeks as a interval unit in the date_arithmetic extension (jeremyevans) (#1759)
6
+
7
+ * Raise an exception if an interval hash with an unsupported key is passed in the date_arithmetic extension (jeremyevans) (#1759)
8
+
9
+ * Support dropping non-composite unique constraints on SQLite (jeremyevans) (#1755)
10
+
11
+ === 5.43.0 (2021-04-01)
12
+
13
+ * Add column_encryption plugin, for encrypting column values (jeremyevans)
14
+
15
+ === 5.42.0 (2021-03-01)
16
+
17
+ * Make the ado timestamp conversion proc a normal conversion proc that can be overridden similar to other conversion procs (jeremyevans)
18
+
19
+ * Add :reject_nil option to the nested_attributes method, to ignore calls where nil is passed as the associated object data (jeremyevans)
20
+
21
+ * Add async_thread_pool plugin for easier async usage with model classes and support for async destroy, with_pk, and with_pk! methods (jeremyevans)
22
+
23
+ * Add async_thread_pool Database extension for executing queries asynchronously using a thread pool (jeremyevans)
24
+
25
+ * Fix possible thread safety issue in Database#extension that could allow Module#extended to be called twice with the same Database instance (jeremyevans)
26
+
27
+ * Support cases where validations make modifications beyond setting errors in Model#freeze (jeremyevans)
28
+
29
+ * Add Model#to_json_data to the json_serializer plugin, returning a JSON data structure (jeremyevans)
30
+
31
+ === 5.41.0 (2021-02-01)
32
+
33
+ * Have explicit :text option for a String column take priority over :size option on PostgreSQL (jeremyevans) (#1750)
34
+
35
+ * Support a :skip_invalid option in auto_validations plugin for not adding errors to a column that already has an error (jeremyevans)
36
+
37
+ * Support a :skip_invalid option in validation_helpers for not adding an error to a column that already has an error (jeremyevans)
38
+
39
+ * Support :adder, :remover, and :clearer association options that use keyword arguments in Ruby 2.7+ (jeremyevans)
40
+
41
+ * Make pg_interval use the same number of seconds per year and per month as ActiveSupport::Duration when using ActiveSupport 5.1+ (jeremyevans)
42
+
43
+ === 5.40.0 (2021-01-01)
44
+
45
+ * Support UPDATE FROM syntax in SQLite 3.33.0+ (jeremyevans)
46
+
47
+ * Have pg_interval extension work with ActiveSupport 6.1 (jeremyevans)
48
+
49
+ * Have date_arithmetic extension work with ActiveSupport 6.1 (jeremyevans)
50
+
51
+ * Avoid method redefinition warnings in verbose warning mode (jeremyevans)
52
+
1
53
  === 5.39.0 (2020-12-01)
2
54
 
3
55
  * Support :clustered option for primary key and unique constraints on Microsoft SQL Server (jeremyevans)
data/MIT-LICENSE CHANGED
@@ -1,5 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
- Copyright (c) 2008-2020 Jeremy Evans
2
+ Copyright (c) 2008-2021 Jeremy Evans
3
3
 
4
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
5
5
  of this software and associated documentation files (the "Software"), to
@@ -0,0 +1,40 @@
1
+ = New Features
2
+
3
+ * On SQLite 3.33.0+, the UPDATE FROM syntax is now supported. This
4
+ allows you to update one table based on a join to another table.
5
+ The SQLite syntax is based on the PostgreSQL syntax, and the
6
+ Sequel API is the same for both. You need to pass multiple tables
7
+ to Dataset#from. The first table is the table to update, and the
8
+ remaining tables are used to construct the UPDATE FROM clause:
9
+
10
+ DB[:a, :b].where{{a[:c]=>b[:d]}}.update(:e=>'f')
11
+ # UPDATE a SET e = 'f' FROM b WHERE (a.c = b.d)
12
+
13
+ Unlike PostgreSQL, SQLite does not support the deletion of joined
14
+ datasets. Related to this, the following methods for testing
15
+ database support for modifying joined datasets have been added:
16
+
17
+ * supports_updating_joins?
18
+ * supports_deleting_joins?
19
+
20
+ = Other Improvements
21
+
22
+ * The pg_interval and date_arithmetic extensions now support
23
+ ActiveSupport 6.1.
24
+
25
+ * Sequel no longer issues method redefinition warnings in verbose
26
+ mode. As Ruby 3 has dropped uninitialized instance variable
27
+ warnings, Sequel is now verbose warning free on Ruby 3.
28
+
29
+ = Backwards Compatibility
30
+
31
+ * Trying to truncate or insert into a joined dataset now correctly
32
+ raises an exception even if the joined dataset supports updates.
33
+
34
+ * The private Dataset#check_modification_allowed! method is now
35
+ deprecated, and users (custom adapters) should now switch to one
36
+ of the more specific methods introduced in this version:
37
+
38
+ * check_insert_allowed!
39
+ * check_update_allowed!
40
+ * check_delete_allowed!
@@ -0,0 +1,25 @@
1
+ = New Features
2
+
3
+ * The validation methods added by the validation_helpers plugin now
4
+ support the :skip_invalid option, which will not add a validation
5
+ error on a column if it already has a validation error. This can
6
+ be useful if you want to avoid having duplicate errors.
7
+
8
+ * The auto_validations plugin now supports a :skip_invalid plugin
9
+ option, which will pass the :skip_invalid option when calling
10
+ validation methods.
11
+
12
+ = Other Improvements
13
+
14
+ * The :adder, :remover, and :clearer association options now
15
+ support keyword arguments in Ruby 2.7+.
16
+
17
+ * In the pg_interval extension, Sequel now uses the same number of
18
+ seconds per month and seconds per year as active_support. It
19
+ originally used the same number, but active_support changed the
20
+ values in active_support 5.1. Sequel now uses the active_support
21
+ values if they are available.
22
+
23
+ * When adding a String column on PostgreSQL, an explicit text: true
24
+ option now takes precedence over an explicit :size option, as it
25
+ does in Sequel's default behavior.
@@ -0,0 +1,136 @@
1
+ = New Features
2
+
3
+ * An async_thread_pool Database extension has been added, which
4
+ executes queries and processes results using a separate thread
5
+ pool. This allows you do do things like:
6
+
7
+ foos = DB[:foos].async.all
8
+ bars = DB[:bars].async.select_map(:name)
9
+ foo_bars = DB[:foo_bars].async.each{|x| p x}
10
+
11
+ and have the three method calls (all, select_map, and each)
12
+ execute concurrently. On Ruby implementations without a global
13
+ VM lock, such as JRuby, it will allow for parallel execution of
14
+ the method calls. On CRuby, the main benefit will be for cases
15
+ where query execution takes a long time or there is significant
16
+ latency between the application and the database.
17
+
18
+ When you call a method on foos, bars, or foo_bars, if the thread
19
+ pool hasn't finished processing the method, the calling code will
20
+ block until the method call has finished.
21
+
22
+ By default, for consistency, calling code will not preempt the
23
+ async thread pool. For example, if you do:
24
+
25
+ DB[:foos].async.all.size
26
+
27
+ The calling code will always wait for the async thread pool to
28
+ run the all method, and then the calling code will call size on
29
+ the result. This ensures that async queries will not use the
30
+ same connection as the the calling thread, even if calling thread
31
+ has a connection checked out.
32
+
33
+ In some cases, such as when the async thread pool is very busy,
34
+ preemption is desired for performance reasons. If you set the
35
+ :preempt_async_thread Database option before loading the
36
+ async_thread_pool extension, preemption will be allowed. With
37
+ preemption allowed, if the async thread pool has not started the
38
+ processing of the method at the time the calling code needs the
39
+ results of the method, the calling code will preempt the async
40
+ thread pool, and run the method on the current thread.
41
+
42
+ By default, the async thread pool uses the same number of threads as
43
+ the Database objects :max_connections attribute (the default for
44
+ that is 4). You can modify the number of async threads by setting
45
+ the :num_async_threads Database option before loading the Database
46
+ async_thread_pool extension.
47
+
48
+ Most Dataset methods that execute queries on the database and return
49
+ results will operate asynchronously if the the dataset is set to be
50
+ asynchronous via the Dataset#async method. This includes most
51
+ methods available due to the inclusion in Enumerable, even if not
52
+ defined by Dataset itself.
53
+
54
+ There are multiple caveats when using the async_thread_pool
55
+ extension:
56
+
57
+ * Asynchronous behavior is harder to understand and harder to
58
+ debug. It would be wise to only use this support in cases where
59
+ it provides is significant performance benefit.
60
+
61
+ * Dataset methods executed asynchronously will use a separate
62
+ database connection than the calling thread, so they will not
63
+ respect transactions in the calling thread, or other cases where
64
+ the calling thread checks out a connection directly using
65
+ Database#synchronize. They will also not respect the use of
66
+ Database#with_server (from the server_block extension) in the
67
+ calling thread.
68
+
69
+ * Dataset methods executed asynchronously should never ignore their
70
+ return value. Code such as:
71
+
72
+ DB[:table].async.insert(1)
73
+
74
+ is probablematic because without storing the return value, you
75
+ have no way to block until the insert has been completed.
76
+
77
+ * The returned object for Dataset methods executed asynchronously is
78
+ a proxy object (promise). So you should never do:
79
+
80
+ row = DB[:table].async.first
81
+ # ...
82
+ if row
83
+ end
84
+
85
+ # or:
86
+
87
+ bool = DB[:table].async.get(:boolean_column)
88
+ # ...
89
+ if bool
90
+ end
91
+
92
+ because the if branches will always be taken as row and bool will
93
+ never be nil or false. If you want to get the underlying value,
94
+ call itself on the proxy object (or __value if using Ruby <2.2).
95
+
96
+ For the same reason, you should not use the proxy objects directly
97
+ in case expressions or as arguments to Class#===. Use itself or
98
+ __value in those cases.
99
+
100
+ * Dataset methods executed asynchronously that include blocks have the
101
+ block executed asynchronously as well, assuming that the method
102
+ calls the block. Because these blocks are executed in a separate
103
+ thread, you cannot use control flow modifiers such as break or
104
+ return in them.
105
+
106
+ * An async_thread_pool model plugin has been added. This requires the
107
+ async_thread_pool extension has been loaded into the model's Database
108
+ object, and allows you to call Model.async instead of
109
+ Model.dataset.async. It also adds async support to the destroy,
110
+ with_pk, and with_pk! model dataset methods.
111
+
112
+ * Model#to_json_data has been added to the json_serializer plugin, for
113
+ returning a hash of data that can be converted to JSON, instead of
114
+ a JSON string.
115
+
116
+ * A :reject_nil option has been added to the nested_attributes method
117
+ in the nested_attributes plugin. This will ignore calls to the
118
+ nested attributes setter method where nil is passed as the setter
119
+ method argument.
120
+
121
+ = Other Improvements
122
+
123
+ * Model#freeze now works in case where model validation modifies the
124
+ object beyond adding errors.
125
+
126
+ * Model#freeze in the composition, serialization, and
127
+ serialization_modification_detection plugins now works in cases
128
+ where validation would end up loading the composed or
129
+ serialized values.
130
+
131
+ * Database#extension now avoids a possible thread safety issue that
132
+ could result in the extension being loaded into the Database twice.
133
+
134
+ * The ado adapter now supports overriding the timestamp conversion
135
+ proc. Previously, unlike other conversion procs, the timestamp
136
+ conversion proc was hard coded and could not be overridden.
@@ -0,0 +1,98 @@
1
+ = New Features
2
+
3
+ * A column_encryption plugin has been added to support encrypting the
4
+ content of individual columns in a table.
5
+
6
+ Column values are encrypted with AES-256-GCM using a per-value
7
+ cipher key derived from a key provided in the configuration using
8
+ HMAC-SHA256.
9
+
10
+ If you would like to support encryption of columns in more than one
11
+ model, you should probably load the plugin into the parent class of
12
+ your models and specify the keys:
13
+
14
+ Sequel::Model.plugin :column_encryption do |enc|
15
+ enc.key 0, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
16
+ end
17
+
18
+ This specifies a single master encryption key. Unless you are
19
+ actively rotating keys, it is best to use a single master key.
20
+
21
+ In the above call, 0 is the id of the key, and
22
+ ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"] is the content of the key, which
23
+ must be a string with exactly 32 bytes. As indicated, this key
24
+ should not be hardcoded or otherwise committed to the source control
25
+ repository.
26
+
27
+ For models that need encrypted columns, you load the plugin again,
28
+ but specify the columns to encrypt:
29
+
30
+ ConfidentialModel.plugin :column_encryption do |enc|
31
+ enc.column :encrypted_column_name
32
+ enc.column :searchable_column_name, searchable: true
33
+ enc.column :ci_searchable_column_name, searchable: :case_insensitive
34
+ end
35
+
36
+ With this, all three specified columns (encrypted_column_name,
37
+ searchable_column_name, and ci_searchable_column_name) will be
38
+ marked as encrypted columns. When you run the following code:
39
+
40
+ ConfidentialModel.create(
41
+ encrypted_column_name: 'These',
42
+ searchable_column_name: 'will be',
43
+ ci_searchable_column_name: 'Encrypted'
44
+ )
45
+
46
+ It will save encrypted versions to the database.
47
+ encrypted_column_name will not be searchable, searchable_column_name
48
+ will be searchable with an exact match, and
49
+ ci_searchable_column_name will be searchable with a case insensitive
50
+ match.
51
+
52
+ To search searchable encrypted columns, use with_encrypted_value.
53
+ This example code will return the model instance created in the code
54
+ example in the previous section:
55
+
56
+ ConfidentialModel.
57
+ with_encrypted_value(:searchable_column_name, "will be")
58
+ with_encrypted_value(:ci_searchable_column_name, "encrypted").
59
+ first
60
+
61
+ To rotate encryption keys, add a new key above the existing key,
62
+ with a new key ID:
63
+
64
+ Sequel::Model.plugin :column_encryption do |enc|
65
+ enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
66
+ enc.key 0, ENV["SEQUEL_OLD_COLUMN_ENCRYPTION_KEY"]
67
+ end
68
+
69
+ Newly encrypted data will then use the new key. Records encrypted
70
+ with the older key will still be decrypted correctly.
71
+
72
+ To force reencryption for existing records that are using the older
73
+ key, you can use the needing_reencryption dataset method and the
74
+ reencrypt instance method. For a small number of records, you can
75
+ probably do:
76
+
77
+ ConfidentialModel.needing_reencryption.all(&:reencrypt)
78
+
79
+ With more than a small number of records, you'll want to do this in
80
+ batches. It's possible you could use an approach such as:
81
+
82
+ ds = ConfidentialModel.needing_reencryption.limit(100)
83
+ true until ds.all(&:reencrypt).empty?
84
+
85
+ After all values have been reencrypted for all models, and no models
86
+ use the older encryption key, you can remove it from the
87
+ configuration:
88
+
89
+ Sequel::Model.plugin :column_encryption do |enc|
90
+ enc.key 1, ENV["SEQUEL_COLUMN_ENCRYPTION_KEY"]
91
+ end
92
+
93
+ The column_encryption plugin supports encrypting serialized data,
94
+ as well as enforcing uniquenss of searchable encrypted columns
95
+ (in the absence of key rotation). By design, it does not support
96
+ compression, mixing encrypted and unencrypted data in the same
97
+ column, or support arbitrary encryption ciphers. See the plugin
98
+ documentation for more details.
@@ -0,0 +1,32 @@
1
+ = New Features
2
+
3
+ * A concurrent_eager_loading plugin has been added. This plugin
4
+ builds on top of the async_thread_pool Database extension and
5
+ allows eager loading multiple associations concurrently in
6
+ separate threads. With this plugin, you can mark datasets for
7
+ concurrent eager loading using eager_load_concurrently:
8
+
9
+ Album.eager_load_concurrently.eager(:artist, :genre, :tracks).all
10
+
11
+ Datasets that are marked for concurrent eager loading will use
12
+ concurrent eager loading if they are eager loading more than one
13
+ association. If you would like to make concurrent eager loading
14
+ the default, you can load the plugin with the :always option.
15
+
16
+ All of the association types that ship with Sequel now support
17
+ concurrent eager loading when using this plugin. For custom eager
18
+ loaders using the :eager_loader association option, please see the
19
+ documentation for the plugin for how to enable custom eager loading
20
+ for them.
21
+
22
+ = Other Improvements
23
+
24
+ * The date_arithmetic extension now handles ActiveSupport::Duration
25
+ values with weeks, as well as :weeks as a key in a hash value. Weeks
26
+ are converted into 7 days internally.
27
+
28
+ * The shared SQLite adapter now emulates the dropping of non-composite
29
+ unique constraints. Non-composite unique constraints are now
30
+ treated similarly to composite unique constraints, in that dropping
31
+ any unique constraints on a table will drop all unique constraints
32
+ on that table.
data/doc/sql.rdoc CHANGED
@@ -544,7 +544,7 @@ On some databases, you can specify null ordering:
544
544
 
545
545
  === All Columns (.*)
546
546
 
547
- To select all columns in a table, Sequel supports the * method on identifiers and qualified without an argument:
547
+ To select all columns in a table, Sequel supports the * method on identifiers and qualified identifiers without an argument:
548
548
 
549
549
  Sequel[:table].* # "table".*
550
550
  Sequel[:schema][:table].* # "schema"."table".*
data/doc/testing.rdoc CHANGED
@@ -157,9 +157,12 @@ The SEQUEL_INTEGRATION_URL environment variable specifies the Database connectio
157
157
 
158
158
  === Other
159
159
 
160
+ SEQUEL_ASYNC_THREAD_POOL :: Use the async_thread_pool extension when running the specs
161
+ SEQUEL_ASYNC_THREAD_POOL_PREEMPT :: Use the async_thread_pool extension when running the specs, with the :preempt_async_thread option
160
162
  SEQUEL_COLUMNS_INTROSPECTION :: Use the columns_introspection extension when running the specs
161
163
  SEQUEL_CONNECTION_VALIDATOR :: Use the connection validator extension when running the specs
162
164
  SEQUEL_DUPLICATE_COLUMNS_HANDLER :: Use the duplicate columns handler extension with value given when running the specs
165
+ SEQUEL_CONCURRENT_EAGER_LOADING :: Use the async_thread_pool extension and concurrent_eager_loading plugin when running the specs
163
166
  SEQUEL_ERROR_SQL :: Use the error_sql extension when running the specs
164
167
  SEQUEL_INDEX_CACHING :: Use the index_caching extension when running the specs
165
168
  SEQUEL_FIBER_CONCURRENCY :: Use the fiber_concurrency extension when running the adapter and integration specs
@@ -195,10 +195,25 @@ module Sequel
195
195
  end
196
196
 
197
197
  @conversion_procs = CONVERSION_PROCS.dup
198
+ @conversion_procs[AdDBTimeStamp] = method(:adb_timestamp_to_application_timestamp)
198
199
 
199
200
  super
200
201
  end
201
202
 
203
+ def adb_timestamp_to_application_timestamp(v)
204
+ # This hard codes a timestamp_precision of 6 when converting.
205
+ # That is the default timestamp_precision, but the ado/mssql adapter uses a timestamp_precision
206
+ # of 3. However, timestamps returned by ado/mssql have nsec values that end up rounding to a
207
+ # the same value as if a timestamp_precision of 3 was hard coded (either xxx999yzz, where y is
208
+ # 5-9 or xxx000yzz where y is 0-4).
209
+ #
210
+ # ADO subadapters should override this they would like a different timestamp precision and the
211
+ # this code does not work for them (for example, if they provide full nsec precision).
212
+ #
213
+ # Note that fractional second handling for WIN32OLE objects is not correct on ruby <2.2
214
+ to_application_timestamp([v.year, v.month, v.day, v.hour, v.min, v.sec, (v.nsec/1000.0).round * 1000])
215
+ end
216
+
202
217
  def dataset_class_default
203
218
  Dataset
204
219
  end
@@ -233,23 +248,8 @@ module Sequel
233
248
  cols = []
234
249
  conversion_procs = db.conversion_procs
235
250
 
236
- ts_cp = nil
237
251
  recordset.Fields.each do |field|
238
- type = field.Type
239
- cp = if type == AdDBTimeStamp
240
- ts_cp ||= begin
241
- nsec_div = 1000000000.0/(10**(timestamp_precision))
242
- nsec_mul = 10**(timestamp_precision+3)
243
- meth = db.method(:to_application_timestamp)
244
- lambda do |v|
245
- # Fractional second handling is not correct on ruby <2.2
246
- meth.call([v.year, v.month, v.day, v.hour, v.min, v.sec, (v.nsec/nsec_div).round * nsec_mul])
247
- end
248
- end
249
- else
250
- conversion_procs[type]
251
- end
252
- cols << [output_identifier(field.Name), cp]
252
+ cols << [output_identifier(field.Name), conversion_procs[field.Type]]
253
253
  end
254
254
 
255
255
  self.columns = cols.map(&:first)