sequel 3.12.1 → 3.13.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (150) hide show
  1. data/CHANGELOG +42 -0
  2. data/README.rdoc +137 -118
  3. data/Rakefile +21 -66
  4. data/doc/active_record.rdoc +9 -9
  5. data/doc/advanced_associations.rdoc +59 -188
  6. data/doc/association_basics.rdoc +15 -2
  7. data/doc/cheat_sheet.rdoc +38 -33
  8. data/doc/dataset_filtering.rdoc +16 -7
  9. data/doc/prepared_statements.rdoc +7 -7
  10. data/doc/querying.rdoc +5 -4
  11. data/doc/release_notes/3.13.0.txt +210 -0
  12. data/doc/sharding.rdoc +1 -1
  13. data/doc/sql.rdoc +5 -5
  14. data/doc/validations.rdoc +11 -11
  15. data/lib/sequel/adapters/ado.rb +1 -1
  16. data/lib/sequel/adapters/do.rb +3 -3
  17. data/lib/sequel/adapters/firebird.rb +3 -3
  18. data/lib/sequel/adapters/jdbc/h2.rb +39 -0
  19. data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
  20. data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
  21. data/lib/sequel/adapters/mysql.rb +7 -4
  22. data/lib/sequel/adapters/oracle.rb +3 -3
  23. data/lib/sequel/adapters/shared/mssql.rb +10 -1
  24. data/lib/sequel/adapters/shared/mysql.rb +63 -0
  25. data/lib/sequel/adapters/shared/postgres.rb +61 -3
  26. data/lib/sequel/adapters/sqlite.rb +105 -18
  27. data/lib/sequel/connection_pool.rb +31 -30
  28. data/lib/sequel/core.rb +58 -58
  29. data/lib/sequel/core_sql.rb +52 -43
  30. data/lib/sequel/database/misc.rb +11 -0
  31. data/lib/sequel/database/query.rb +55 -17
  32. data/lib/sequel/dataset/actions.rb +2 -1
  33. data/lib/sequel/dataset/query.rb +2 -3
  34. data/lib/sequel/dataset/sql.rb +24 -11
  35. data/lib/sequel/extensions/schema_dumper.rb +1 -1
  36. data/lib/sequel/metaprogramming.rb +4 -0
  37. data/lib/sequel/model.rb +37 -19
  38. data/lib/sequel/model/associations.rb +33 -25
  39. data/lib/sequel/model/base.rb +2 -2
  40. data/lib/sequel/model/plugins.rb +7 -2
  41. data/lib/sequel/plugins/active_model.rb +1 -1
  42. data/lib/sequel/plugins/association_pks.rb +2 -2
  43. data/lib/sequel/plugins/association_proxies.rb +1 -1
  44. data/lib/sequel/plugins/boolean_readers.rb +2 -2
  45. data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
  46. data/lib/sequel/plugins/identity_map.rb +3 -3
  47. data/lib/sequel/plugins/instance_hooks.rb +1 -1
  48. data/lib/sequel/plugins/json_serializer.rb +212 -0
  49. data/lib/sequel/plugins/lazy_attributes.rb +1 -1
  50. data/lib/sequel/plugins/list.rb +174 -0
  51. data/lib/sequel/plugins/many_through_many.rb +2 -2
  52. data/lib/sequel/plugins/rcte_tree.rb +6 -7
  53. data/lib/sequel/plugins/tree.rb +118 -0
  54. data/lib/sequel/plugins/xml_serializer.rb +321 -0
  55. data/lib/sequel/sql.rb +315 -206
  56. data/lib/sequel/timezones.rb +40 -17
  57. data/lib/sequel/version.rb +8 -2
  58. data/spec/adapters/firebird_spec.rb +2 -2
  59. data/spec/adapters/informix_spec.rb +1 -1
  60. data/spec/adapters/mssql_spec.rb +2 -2
  61. data/spec/adapters/mysql_spec.rb +2 -2
  62. data/spec/adapters/oracle_spec.rb +1 -1
  63. data/spec/adapters/postgres_spec.rb +36 -6
  64. data/spec/adapters/spec_helper.rb +2 -2
  65. data/spec/adapters/sqlite_spec.rb +1 -1
  66. data/spec/core/connection_pool_spec.rb +3 -3
  67. data/spec/core/core_sql_spec.rb +31 -13
  68. data/spec/core/database_spec.rb +39 -2
  69. data/spec/core/dataset_spec.rb +24 -12
  70. data/spec/core/expression_filters_spec.rb +5 -1
  71. data/spec/core/object_graph_spec.rb +1 -1
  72. data/spec/core/schema_generator_spec.rb +1 -1
  73. data/spec/core/schema_spec.rb +1 -1
  74. data/spec/core/spec_helper.rb +1 -1
  75. data/spec/core/version_spec.rb +1 -1
  76. data/spec/extensions/active_model_spec.rb +82 -67
  77. data/spec/extensions/association_dependencies_spec.rb +1 -1
  78. data/spec/extensions/association_pks_spec.rb +1 -1
  79. data/spec/extensions/association_proxies_spec.rb +1 -1
  80. data/spec/extensions/blank_spec.rb +1 -1
  81. data/spec/extensions/boolean_readers_spec.rb +1 -1
  82. data/spec/extensions/caching_spec.rb +1 -1
  83. data/spec/extensions/class_table_inheritance_spec.rb +3 -2
  84. data/spec/extensions/composition_spec.rb +2 -5
  85. data/spec/extensions/force_encoding_spec.rb +3 -1
  86. data/spec/extensions/hook_class_methods_spec.rb +1 -1
  87. data/spec/extensions/identity_map_spec.rb +1 -1
  88. data/spec/extensions/inflector_spec.rb +1 -1
  89. data/spec/extensions/instance_filters_spec.rb +1 -1
  90. data/spec/extensions/instance_hooks_spec.rb +1 -1
  91. data/spec/extensions/json_serializer_spec.rb +154 -0
  92. data/spec/extensions/lazy_attributes_spec.rb +1 -2
  93. data/spec/extensions/list_spec.rb +251 -0
  94. data/spec/extensions/looser_typecasting_spec.rb +1 -1
  95. data/spec/extensions/many_through_many_spec.rb +3 -3
  96. data/spec/extensions/migration_spec.rb +1 -1
  97. data/spec/extensions/named_timezones_spec.rb +5 -6
  98. data/spec/extensions/nested_attributes_spec.rb +1 -1
  99. data/spec/extensions/optimistic_locking_spec.rb +1 -1
  100. data/spec/extensions/pagination_spec.rb +1 -1
  101. data/spec/extensions/pretty_table_spec.rb +1 -1
  102. data/spec/extensions/query_spec.rb +1 -1
  103. data/spec/extensions/rcte_tree_spec.rb +1 -1
  104. data/spec/extensions/schema_dumper_spec.rb +3 -2
  105. data/spec/extensions/schema_spec.rb +1 -1
  106. data/spec/extensions/serialization_spec.rb +6 -2
  107. data/spec/extensions/sharding_spec.rb +1 -1
  108. data/spec/extensions/single_table_inheritance_spec.rb +1 -1
  109. data/spec/extensions/skip_create_refresh_spec.rb +1 -1
  110. data/spec/extensions/spec_helper.rb +7 -3
  111. data/spec/extensions/sql_expr_spec.rb +1 -1
  112. data/spec/extensions/string_date_time_spec.rb +1 -1
  113. data/spec/extensions/string_stripper_spec.rb +1 -1
  114. data/spec/extensions/subclasses_spec.rb +1 -1
  115. data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
  116. data/spec/extensions/thread_local_timezones_spec.rb +1 -1
  117. data/spec/extensions/timestamps_spec.rb +1 -1
  118. data/spec/extensions/touch_spec.rb +1 -1
  119. data/spec/extensions/tree_spec.rb +119 -0
  120. data/spec/extensions/typecast_on_load_spec.rb +1 -1
  121. data/spec/extensions/update_primary_key_spec.rb +1 -1
  122. data/spec/extensions/validation_class_methods_spec.rb +1 -1
  123. data/spec/extensions/validation_helpers_spec.rb +1 -1
  124. data/spec/extensions/xml_serializer_spec.rb +142 -0
  125. data/spec/integration/associations_test.rb +1 -1
  126. data/spec/integration/database_test.rb +1 -1
  127. data/spec/integration/dataset_test.rb +29 -14
  128. data/spec/integration/eager_loader_test.rb +1 -1
  129. data/spec/integration/migrator_test.rb +1 -1
  130. data/spec/integration/model_test.rb +1 -1
  131. data/spec/integration/plugin_test.rb +316 -1
  132. data/spec/integration/prepared_statement_test.rb +1 -1
  133. data/spec/integration/schema_test.rb +8 -8
  134. data/spec/integration/spec_helper.rb +1 -1
  135. data/spec/integration/timezone_test.rb +1 -1
  136. data/spec/integration/transaction_test.rb +35 -20
  137. data/spec/integration/type_test.rb +1 -1
  138. data/spec/model/association_reflection_spec.rb +1 -1
  139. data/spec/model/associations_spec.rb +49 -34
  140. data/spec/model/base_spec.rb +1 -1
  141. data/spec/model/dataset_methods_spec.rb +4 -4
  142. data/spec/model/eager_loading_spec.rb +1 -1
  143. data/spec/model/hooks_spec.rb +1 -1
  144. data/spec/model/inflector_spec.rb +1 -1
  145. data/spec/model/model_spec.rb +7 -1
  146. data/spec/model/plugins_spec.rb +1 -1
  147. data/spec/model/record_spec.rb +1 -3
  148. data/spec/model/spec_helper.rb +2 -2
  149. data/spec/model/validations_spec.rb +1 -1
  150. metadata +29 -5
data/doc/sharding.rdoc CHANGED
@@ -4,7 +4,7 @@ Sequel has support for read only slave databases
4
4
  with a writable master database, as well as database sharding (where you can
5
5
  pick a server to use for a given dataset). Support for both
6
6
  features is database independent, and should work for all database adapters
7
- included with Sequel.
7
+ that ship with Sequel.
8
8
 
9
9
  == The :servers Database option
10
10
 
data/doc/sql.rdoc CHANGED
@@ -1,6 +1,6 @@
1
1
  = Sequel for SQL Users
2
2
 
3
- One of the main benefits of Sequel is that it doesn't require the user know SQL in order to use it, though SQL knowledge is certainly helpful. Unlike most other Sequel documentation, this guide assumes you know SQL, and provides an easy way to discover how to do something in Sequel given the knowledge of how to do so in SQL.
3
+ One of the main benefits of Sequel is that it doesn't require the user to know SQL in order to use it, though SQL knowledge is certainly helpful. Unlike most other Sequel documentation, this guide assumes you know SQL, and provides an easy way to discover how to do something in Sequel given the knowledge of how to do so in SQL.
4
4
 
5
5
  == You Can Just Use SQL
6
6
 
@@ -32,7 +32,7 @@ What Sequel actually does internally is two separate things. It first creates a
32
32
 
33
33
  ds = DB.fetch("SELECT * FROM albums")
34
34
 
35
- Then when you want to retrieve the rows later, you can call +each+ on the dataset to retrieve the rows:
35
+ Then, when you want to retrieve the rows later, you can call +each+ on the dataset to retrieve the rows:
36
36
 
37
37
  ds.each{|r| puts r[:name]}
38
38
 
@@ -78,7 +78,7 @@ Almost everywhere in Sequel, you can drop down to literal SQL by providing a lit
78
78
  DB[:albums].select('name') # SELECT 'name' FROM albums
79
79
  DB[:albums].select('name'.lit) # SELECT name FROM albums
80
80
 
81
- So you can use Sequel's DSL for everywhere you find it helpful, and fallback to literal SQL if the DSL can't do what you want or you just find literal SQL easier.
81
+ So you can use Sequel's DSL everywhere you find it helpful, and fallback to literal SQL if the DSL can't do what you want or you just find literal SQL easier.
82
82
 
83
83
  == Translating SQL Expressions into Sequel
84
84
 
@@ -315,7 +315,7 @@ Note that the following does not work:
315
315
 
316
316
  1 + :column # raises TypeError
317
317
 
318
- For commutative operates such as + and *, this isn't a problem as you can just reorder, but non-commutative operators such as - and * cannot be expressed directly. However, Sequel comes with an +sql_expr+ extension that adds an +sql_expr+ method to all objects, allowing you to do:
318
+ For commutative operates such as + and *, this isn't a problem as you can just reorder, but non-commutative operators such as - and / cannot be expressed directly. However, Sequel comes with an +sql_expr+ extension that adds an +sql_expr+ method to all objects, allowing you to do:
319
319
 
320
320
  Sequel.extension :sql_expr
321
321
  1.sql_expr / :column # (1 / "column")
@@ -413,7 +413,7 @@ Just like for the mathematical operators, you can use the +sql_expr+ extension t
413
413
  Sequel.extension :sql_expr
414
414
  'Name - '.sql_expr + :name # ('Name - ' || "name")
415
415
 
416
- Sequel also adds an <tt>Array#sql_string_join</tt> method, which concatentates all of the elements in the array:
416
+ Sequel also adds an <tt>Array#sql_string_join</tt> method, which concatenates all of the elements in the array:
417
417
 
418
418
  ['Name', :name].sql_string_join # ('Name' || "name")
419
419
 
data/doc/validations.rdoc CHANGED
@@ -20,7 +20,7 @@ database-level constraints.
20
20
 
21
21
  == Data Integrity
22
22
 
23
- Data Integrity is best handled by the database itself. For example, if you have
23
+ Data integrity is best handled by the database itself. For example, if you have
24
24
  a date column that should never contain a NULL value, the column should be
25
25
  specified in the database as NOT NULL. If you have an integer column that should
26
26
  only have values from 1 to 10, there should be a CHECK constraint that ensures
@@ -52,12 +52,12 @@ instance, before sending the INSERT or UPDATE query to the database,
52
52
  <tt>Sequel::Model</tt> will attempt to validate the instance by calling
53
53
  +validate+. If +validate+ does not add any errors to the object, the object is
54
54
  considered valid, and <tt>valid?</tt> will return true. If +validate+ adds any errors
55
- to the object, <tt>valid?</tt> will return false, and the save will either return raise
55
+ to the object, <tt>valid?</tt> will return false, and the save will either raise
56
56
  a <tt>Sequel::ValidationFailed</tt> exception (the default), or return nil (if +raise_on_save_failure+
57
57
  is false).
58
58
 
59
59
  By validating the object before sending the database query, Sequel attempts to
60
- ensure that invalidate objects are not saved in the database. However, if you
60
+ ensure that invalid objects are not saved in the database. However, if you
61
61
  are not enforcing the same validations in the database via constraints, it's
62
62
  possible that invalid data can get added to the database via some other method.
63
63
  This leads to odd cases such as retrieving a model object from the database,
@@ -128,7 +128,7 @@ will be covered later in this guide.
128
128
 
129
129
  While <tt>Sequel::Model</tt> does provide a validations framework, it does not define
130
130
  any built-in validation helper methods that you can call. However, Sequel ships with a
131
- plugin call +validation_helpers+ that handles most basic validation needs. So instead of
131
+ plugin called +validation_helpers+ that handles most basic validation needs. So instead of
132
132
  specifying validations like this:
133
133
 
134
134
  class Album < Sequel::Model
@@ -168,7 +168,7 @@ The following methods are provided by +validation_helpers+:
168
168
 
169
169
  === +validates_presence+
170
170
 
171
- This is probably the most commonly used helper method, which checks if the specified attributes are not blank. In general, if an object responds to <tt>blank?</tt>, it calls the method to determine if the object is blank. Otherwise, nil is considered blank, empty strings or strings that just contain whitespace are blank, and objects that respond to <tt>empty?</tt> and return a true valid are considered blank. All other objects are considered non-blank for the purposes of +validates_presence+. This means that +validates_presence+ is safe to use on boolean columns where you want to ensure that either true or false is used, but not NULL.
171
+ This is probably the most commonly used helper method, which checks if the specified attributes are not blank. In general, if an object responds to <tt>blank?</tt>, it calls the method to determine if the object is blank. Otherwise, nil is considered blank, empty strings or strings that just contain whitespace are blank, and objects that respond to <tt>empty?</tt> and return true are considered blank. All other objects are considered non-blank for the purposes of +validates_presence+. This means that +validates_presence+ is safe to use on boolean columns where you want to ensure that either true or false is used, but not NULL.
172
172
 
173
173
  class Album < Sequel::Model
174
174
  def validate
@@ -247,7 +247,7 @@ The <tt>raise_on_typecast_failure = false</tt> setting tells Sequel to attempt t
247
247
  album.copies_sold = 'banana'
248
248
  album.copies_sold # => 'banana'
249
249
 
250
- +validates_not_string+ is designed to be used in web applications where all user input comes in as strings. When the <tt>raise_on_typecast_failure = false</tt> setting is used in such an application, you can call +validates_not_string+ with all non-string columns. If any of those columns has a string value, it's because the column was set and Sequel was not able to typecast it correctly, which means it probably isn't valid. For example, let's say that you want to check that a couple columns contain valid dates:
250
+ +validates_not_string+ is designed to be used in web applications where all user input comes in as strings. When the <tt>raise_on_typecast_failure = false</tt> setting is used in such an application, you can call +validates_not_string+ with all non-string columns. If any of those columns has a string value, it's because the column was set and Sequel was not able to typecast it correctly, which means it probably isn't valid. For example, let's say that you want to check that a couple of columns contain valid dates:
251
251
 
252
252
  class Album < Sequel::Model
253
253
  self.raise_on_typecast_failure = false
@@ -264,7 +264,7 @@ The <tt>raise_on_typecast_failure = false</tt> setting tells Sequel to attempt t
264
264
  album.valid? # => false
265
265
  album.errors # => {:release_date=>["is not a valid date"]}
266
266
 
267
- For web applications, you usually want the <tt>raise_on_typecast_failure = false</tt> setting, so that you can accept all of the input without raising an error, and then present the user with all error messages. Without the setting, if the user submits any invalid data, Sequel will immediately raise an error. +validates_not_string+ is helpful because it allows you to check for typecasting errors on non-string columns, and provides a good default error message stating that it the attribute is not of the expected type.
267
+ For web applications, you usually want the <tt>raise_on_typecast_failure = false</tt> setting, so that you can accept all of the input without raising an error, and then present the user with all error messages. Without the setting, if the user submits any invalid data, Sequel will immediately raise an error. +validates_not_string+ is helpful because it allows you to check for typecasting errors on non-string columns, and provides a good default error message stating that the attribute is not of the expected type.
268
268
 
269
269
  === +validates_unique+
270
270
 
@@ -293,7 +293,7 @@ Additionally, you can also include an optional options hash as the last argument
293
293
  :message :: The message to use
294
294
  :only_if_modified :: Only check the uniqueness if the object is new or one of the columns has been modified.
295
295
 
296
- +validates_unique+ is the only method in +validation_helpers+ that checks with the database. Attempting to validate uniqueness outside of the database suffers from a race condition, so any time you want to add a uniqueness validation, you should make sure to add a uniqueness constraint or unique index on the underlying database table. See the the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html] for details on how to do that.
296
+ +validates_unique+ is the only method in +validation_helpers+ that checks with the database. Attempting to validate uniqueness outside of the database suffers from a race condition, so any time you want to add a uniqueness validation, you should make sure to add a uniqueness constraint or unique index on the underlying database table. See the {"Migrations and Schema Modification" guide}[link:files/doc/migration_rdoc.html] for details on how to do that.
297
297
 
298
298
  == +validation_helpers+ Options
299
299
 
@@ -301,7 +301,7 @@ All +validation_helpers+ methods except +validates_unique+ accept the following
301
301
 
302
302
  === <tt>:message</tt>
303
303
 
304
- The most commonly used option, used to override the default validation message. Can be either a string or proc. If a string, it is used directly. If a proc, the proc is called and should return a string. If the validation method takes a argument before the array of attributes, that argument is passed as an argument to the proc. The exception is the +validates_not_string+ method, which doesn�t take an argument, but passes the schema type symbol as the argument to the proc.
304
+ The most commonly used option, used to override the default validation message. Can be either a string or a proc. If a string, it is used directly. If a proc, the proc is called and should return a string. If the validation method takes an argument before the array of attributes, that argument is passed as an argument to the proc. The exception is the +validates_not_string+ method, which doesn�t take an argument, but passes the schema type symbol as the argument to the proc.
305
305
 
306
306
  class Album < Sequel::Model
307
307
  def validate
@@ -460,7 +460,7 @@ This will make sure that all string columns in the model are validated to make s
460
460
  If you forget to call +super+, the validations that you defined in <tt>Sequel::Model</tt> will not be enforced. It's a good idea to call super whenever you override one of <tt>Sequel::Model</tt>'s methods, unless you specifically do not want the default behavior.
461
461
 
462
462
  == <tt>Sequel::Model::Errors</tt>
463
-
463
+ '
464
464
  As mentioned earlier, <tt>Sequel::Model::Errors</tt> is a subclass of Hash with a few special methods, the most common of which are described here:
465
465
 
466
466
  === +add+
@@ -483,7 +483,7 @@ Here, you don't care about validating the release date if there were validation
483
483
 
484
484
  === +full_messages+
485
485
 
486
- +full_messages+ returns an area of error messages for the object. It's commonly called after validation to get a list of error messages to display to the user:
486
+ +full_messages+ returns an array of error messages for the object. It's commonly called after validation to get a list of error messages to display to the user:
487
487
 
488
488
  album.errors
489
489
  # => {:name=>["cannot be empty"]}
@@ -68,7 +68,7 @@ module Sequel
68
68
  # The ADO adapter's default provider doesn't support transactions, since it
69
69
  # creates a new native connection for each query. So Sequel only attempts
70
70
  # to use transactions if an explicit :provider is given.
71
- def _transaction(conn)
71
+ def _transaction(conn, o={})
72
72
  return super if opts[:provider]
73
73
  th = Thread.current
74
74
  begin
@@ -118,7 +118,7 @@ module Sequel
118
118
  # transactions. Unfortunately, it tries to create a new connection
119
119
  # to do a transaction. So we close the connection created and
120
120
  # substitute our own.
121
- def begin_transaction(conn)
121
+ def begin_transaction(conn, opts={})
122
122
  return super if supports_savepoints?
123
123
  log_yield(TRANSACTION_BEGIN) do
124
124
  t = ::DataObjects::Transaction.create_for_uri(uri)
@@ -131,7 +131,7 @@ module Sequel
131
131
 
132
132
  # DataObjects requires transactions be prepared before being
133
133
  # committed, so we do that.
134
- def commit_transaction(t)
134
+ def commit_transaction(t, opts={})
135
135
  return super if supports_savepoints?
136
136
  log_yield(TRANSACTION_ROLLBACK) do
137
137
  t.prepare
@@ -156,7 +156,7 @@ module Sequel
156
156
  end
157
157
 
158
158
  # We use the transactions rollback method to rollback.
159
- def rollback_transaction(t)
159
+ def rollback_transaction(t, opts={})
160
160
  return super if supports_savepoints?
161
161
  log_yield(TRANSACTION_COMMIT){t.rollback}
162
162
  end
@@ -102,12 +102,12 @@ module Sequel
102
102
  AUTO_INCREMENT
103
103
  end
104
104
 
105
- def begin_transaction(conn)
105
+ def begin_transaction(conn, opts={})
106
106
  log_yield(TRANSACTION_BEGIN){conn.transaction}
107
107
  conn
108
108
  end
109
109
 
110
- def commit_transaction(conn)
110
+ def commit_transaction(conn, opts={})
111
111
  log_yield(TRANSACTION_COMMIT){conn.commit}
112
112
  end
113
113
 
@@ -182,7 +182,7 @@ module Sequel
182
182
  "ALTER SEQUENCE #{seq_name} RESTART WITH #{opts[:restart_position]}"
183
183
  end
184
184
 
185
- def rollback_transaction(conn)
185
+ def rollback_transaction(conn, opts={})
186
186
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
187
187
  end
188
188
 
@@ -6,6 +6,12 @@ module Sequel
6
6
  module DatabaseMethods
7
7
  PRIMARY_KEY_INDEX_RE = /\Aprimary_key/i.freeze
8
8
 
9
+ # Commit an existing prepared transaction with the given transaction
10
+ # identifier string.
11
+ def commit_prepared_transaction(transaction_id)
12
+ run("COMMIT TRANSACTION #{transaction_id}")
13
+ end
14
+
9
15
  # H2 uses the :h2 database type.
10
16
  def database_type
11
17
  :h2
@@ -16,13 +22,39 @@ module Sequel
16
22
  Sequel::JDBC::H2::Dataset.new(self, opts)
17
23
  end
18
24
 
25
+ # Rollback an existing prepared transaction with the given transaction
26
+ # identifier string.
27
+ def rollback_prepared_transaction(transaction_id)
28
+ run("ROLLBACK TRANSACTION #{transaction_id}")
29
+ end
30
+
19
31
  # H2 uses an IDENTITY type
20
32
  def serial_primary_key_options
21
33
  {:primary_key => true, :type => :identity}
22
34
  end
35
+
36
+ # H2 supports prepared transactions
37
+ def supports_prepared_transactions?
38
+ true
39
+ end
40
+
41
+ # H2 supports savepoints
42
+ def supports_savepoints?
43
+ true
44
+ end
23
45
 
24
46
  private
25
47
 
48
+ # If the :prepare option is given and we aren't in a savepoint,
49
+ # prepare the transaction for a two-phase commit.
50
+ def commit_transaction(conn, opts={})
51
+ if opts[:prepare] && Thread.current[:sequel_transaction_depth] <= 1
52
+ log_connection_execute(conn, "PREPARE COMMIT #{opts[:prepare]}")
53
+ else
54
+ super
55
+ end
56
+ end
57
+
26
58
  # H2 needs to add a primary key column as a constraint
27
59
  def alter_table_sql(table, op)
28
60
  case op[:op]
@@ -75,6 +107,7 @@ module Sequel
75
107
  # Dataset class for H2 datasets accessed via JDBC.
76
108
  class Dataset < JDBC::Dataset
77
109
  SELECT_CLAUSE_METHODS = clause_methods(:select, %w'distinct columns from join where group having compounds order limit')
110
+ BITWISE_METHOD_MAP = {:& =>:BITAND, :| => :BITOR, :^ => :BITXOR}
78
111
 
79
112
  # Work around H2's lack of a case insensitive LIKE operator
80
113
  def complex_expression_sql(op, args)
@@ -83,6 +116,12 @@ module Sequel
83
116
  super(:LIKE, [SQL::PlaceholderLiteralString.new("CAST(? AS VARCHAR_IGNORECASE)", [args.at(0)]), args.at(1)])
84
117
  when :"NOT ILIKE"
85
118
  super(:"NOT LIKE", [SQL::PlaceholderLiteralString.new("CAST(? AS VARCHAR_IGNORECASE)", [args.at(0)]), args.at(1)])
119
+ when :&, :|, :^
120
+ literal(SQL::Function.new(BITWISE_METHOD_MAP[op], *args))
121
+ when :<<
122
+ "(#{literal(args[0])} * POWER(2, #{literal(args[1])}))"
123
+ when :>>
124
+ "(#{literal(args[0])} / POWER(2, #{literal(args[1])}))"
86
125
  else
87
126
  super(op, args)
88
127
  end
@@ -54,6 +54,11 @@ module Sequel
54
54
  def requires_return_generated_keys?
55
55
  true
56
56
  end
57
+
58
+ # Convert tinyint(1) type to boolean
59
+ def schema_column_type(db_type)
60
+ db_type == 'tinyint(1)' ? :boolean : super
61
+ end
57
62
  end
58
63
 
59
64
  # Dataset class for MySQL datasets accessed via JDBC.
@@ -19,13 +19,13 @@ module Sequel
19
19
  private
20
20
 
21
21
  # Use JDBC connection's setAutoCommit to false to start transactions
22
- def begin_transaction(conn)
22
+ def begin_transaction(conn, opts={})
23
23
  log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
24
24
  conn
25
25
  end
26
26
 
27
27
  # Use JDBC connection's commit method to commit transactions
28
- def commit_transaction(conn)
28
+ def commit_transaction(conn, opts={})
29
29
  log_yield(TRANSACTION_COMMIT){conn.commit}
30
30
  end
31
31
 
@@ -36,7 +36,7 @@ module Sequel
36
36
  end
37
37
 
38
38
  # Use JDBC connection's rollback method to rollback transactions
39
- def rollback_transaction(conn)
39
+ def rollback_transaction(conn, opts={})
40
40
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
41
41
  end
42
42
  end
@@ -115,18 +115,21 @@ module Sequel
115
115
  Mysql::CLIENT_MULTI_STATEMENTS +
116
116
  (opts[:compress] == false ? 0 : Mysql::CLIENT_COMPRESS)
117
117
  )
118
+ sqls = []
118
119
  # Set encoding a slightly different way after connecting,
119
120
  # in case the READ_DEFAULT_GROUP overrode the provided encoding.
120
121
  # Doesn't work across implicit reconnects, but Sequel doesn't turn on
121
122
  # that feature.
122
- conn.query("set names #{literal(encoding.to_s)}") if encoding
123
+ sqls << "SET NAMES #{literal(encoding.to_s)}" if encoding
123
124
 
124
125
  # increase timeout so mysql server doesn't disconnect us
125
- conn.query("set @@wait_timeout = #{opts[:timeout] || 2592000}")
126
+ sqls << "SET @@wait_timeout = #{opts[:timeout] || 2592000}"
126
127
 
127
128
  # By default, MySQL 'where id is null' selects the last inserted id
128
- conn.query("set SQL_AUTO_IS_NULL=0") unless opts[:auto_is_null]
129
-
129
+ sqls << "SET SQL_AUTO_IS_NULL=0" unless opts[:auto_is_null]
130
+
131
+ sqls.each{|sql| log_yield(sql){conn.query(sql)}}
132
+
130
133
  class << conn
131
134
  attr_accessor :prepared_statements
132
135
  end
@@ -74,12 +74,12 @@ module Sequel
74
74
 
75
75
  private
76
76
 
77
- def begin_transaction(conn)
77
+ def begin_transaction(conn, opts={})
78
78
  log_yield(TRANSACTION_BEGIN){conn.autocommit = false}
79
79
  conn
80
80
  end
81
81
 
82
- def commit_transaction(conn)
82
+ def commit_transaction(conn, opts={})
83
83
  log_yield(TRANSACTION_COMMIT){conn.commit}
84
84
  end
85
85
 
@@ -92,7 +92,7 @@ module Sequel
92
92
  super
93
93
  end
94
94
 
95
- def rollback_transaction(conn)
95
+ def rollback_transaction(conn, opts={})
96
96
  log_yield(TRANSACTION_ROLLBACK){conn.rollback}
97
97
  end
98
98
  end
@@ -39,6 +39,11 @@ module Sequel
39
39
  true
40
40
  end
41
41
 
42
+ # MSSQL supports transaction isolation levels
43
+ def supports_transaction_isolation_levels?
44
+ true
45
+ end
46
+
42
47
  # Microsoft SQL Server supports using the INFORMATION_SCHEMA to get
43
48
  # information on tables.
44
49
  def tables(opts={})
@@ -91,7 +96,7 @@ module Sequel
91
96
 
92
97
  # Commit the active transaction on the connection, does not commit/release
93
98
  # savepoints.
94
- def commit_transaction(conn)
99
+ def commit_transaction(conn, opts={})
95
100
  log_connection_execute(conn, commit_transaction_sql) unless Thread.current[:sequel_transaction_depth] > 1
96
101
  end
97
102
 
@@ -215,6 +220,10 @@ module Sequel
215
220
  super(:LIKE, args)
216
221
  when :"NOT ILIKE"
217
222
  super(:"NOT LIKE", args)
223
+ when :<<
224
+ "(#{literal(args[0])} * POWER(2, #{literal(args[1])}))"
225
+ when :>>
226
+ "(#{literal(args[0])} / POWER(2, #{literal(args[1])}))"
218
227
  else
219
228
  super(op, args)
220
229
  end
@@ -30,6 +30,12 @@ module Sequel
30
30
  CAST_TYPES[type] || super
31
31
  end
32
32
 
33
+ # Commit an existing prepared transaction with the given transaction
34
+ # identifier string.
35
+ def commit_prepared_transaction(transaction_id)
36
+ run("XA COMMIT #{literal(transaction_id)}")
37
+ end
38
+
33
39
  # MySQL uses the :mysql database type
34
40
  def database_type
35
41
  :mysql
@@ -52,6 +58,12 @@ module Sequel
52
58
  indexes.reject{|k,v| remove_indexes.include?(k)}
53
59
  end
54
60
 
61
+ # Rollback an existing prepared transaction with the given transaction
62
+ # identifier string.
63
+ def rollback_prepared_transaction(transaction_id)
64
+ run("XA ROLLBACK #{literal(transaction_id)}")
65
+ end
66
+
55
67
  # Get version of MySQL server, used for determined capabilities.
56
68
  def server_version
57
69
  m = /(\d+)\.(\d+)\.(\d+)/.match(get(SQL::Function.new(:version)))
@@ -67,11 +79,21 @@ module Sequel
67
79
  metadata_dataset.with_sql('SHOW TABLES').server(opts[:server]).map{|r| m.call(r.values.first)}
68
80
  end
69
81
 
82
+ # MySQL supports prepared transactions (two-phase commit) using XA
83
+ def supports_prepared_transactions?
84
+ true
85
+ end
86
+
70
87
  # MySQL supports savepoints
71
88
  def supports_savepoints?
72
89
  true
73
90
  end
74
91
 
92
+ # MySQL supports transaction isolation levels
93
+ def supports_transaction_isolation_levels?
94
+ true
95
+ end
96
+
75
97
  # Changes the database in use by issuing a USE statement. I would be
76
98
  # very careful if I used this.
77
99
  def use(db_name)
@@ -117,6 +139,23 @@ module Sequel
117
139
  AUTO_INCREMENT
118
140
  end
119
141
 
142
+ # MySQL needs to set transaction isolation before begining a transaction
143
+ def begin_new_transaction(conn, opts)
144
+ set_transaction_isolation(conn, opts)
145
+ log_connection_execute(conn, begin_transaction_sql)
146
+ end
147
+
148
+ # Use XA START to start a new prepared transaction if the :prepare
149
+ # option is given.
150
+ def begin_transaction(conn, opts={})
151
+ if s = opts[:prepare]
152
+ log_connection_execute(conn, "XA START #{literal(s)}")
153
+ conn
154
+ else
155
+ super
156
+ end
157
+ end
158
+
120
159
  # MySQL doesn't allow default values on text columns, so ignore if it the
121
160
  # generic text type is used
122
161
  def column_definition_sql(column)
@@ -124,6 +163,17 @@ module Sequel
124
163
  super
125
164
  end
126
165
 
166
+ # Prepare the XA transaction for a two-phase commit if the
167
+ # :prepare option is given.
168
+ def commit_transaction(conn, opts={})
169
+ if s = opts[:prepare]
170
+ log_connection_execute(conn, "XA END #{literal(s)}")
171
+ log_connection_execute(conn, "XA PREPARE #{literal(s)}")
172
+ else
173
+ super
174
+ end
175
+ end
176
+
127
177
  # Use MySQL specific syntax for engine type and character encoding
128
178
  def create_table_sql(name, generator, options = {})
129
179
  engine = options.fetch(:engine, Sequel::MySQL.default_engine)
@@ -162,6 +212,17 @@ module Sequel
162
212
  "CREATE #{index_type}INDEX #{index_name}#{using} ON #{quote_schema_table(table_name)} #{literal(index[:columns])}"
163
213
  end
164
214
 
215
+ # Rollback the currently open XA transaction
216
+ def rollback_transaction(conn, opts={})
217
+ if s = opts[:prepare]
218
+ log_connection_execute(conn, "XA END #{literal(s)}")
219
+ log_connection_execute(conn, "XA PREPARE #{literal(s)}")
220
+ log_connection_execute(conn, "XA ROLLBACK #{literal(s)}")
221
+ else
222
+ super
223
+ end
224
+ end
225
+
165
226
  # MySQL treats integer primary keys as autoincrementing.
166
227
  def schema_autoincrementing_primary_key?(schema)
167
228
  super and schema[:db_type] =~ /int/io
@@ -240,6 +301,8 @@ module Sequel
240
301
  else
241
302
  literal(args.at(0))
242
303
  end
304
+ when :'B~'
305
+ "CAST(~#{literal(args.at(0))} AS SIGNED INTEGER)"
243
306
  else
244
307
  super(op, args)
245
308
  end