sequel 3.12.1 → 3.13.0
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +42 -0
- data/README.rdoc +137 -118
- data/Rakefile +21 -66
- data/doc/active_record.rdoc +9 -9
- data/doc/advanced_associations.rdoc +59 -188
- data/doc/association_basics.rdoc +15 -2
- data/doc/cheat_sheet.rdoc +38 -33
- data/doc/dataset_filtering.rdoc +16 -7
- data/doc/prepared_statements.rdoc +7 -7
- data/doc/querying.rdoc +5 -4
- data/doc/release_notes/3.13.0.txt +210 -0
- data/doc/sharding.rdoc +1 -1
- data/doc/sql.rdoc +5 -5
- data/doc/validations.rdoc +11 -11
- data/lib/sequel/adapters/ado.rb +1 -1
- data/lib/sequel/adapters/do.rb +3 -3
- data/lib/sequel/adapters/firebird.rb +3 -3
- data/lib/sequel/adapters/jdbc/h2.rb +39 -0
- data/lib/sequel/adapters/jdbc/mysql.rb +5 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +3 -3
- data/lib/sequel/adapters/mysql.rb +7 -4
- data/lib/sequel/adapters/oracle.rb +3 -3
- data/lib/sequel/adapters/shared/mssql.rb +10 -1
- data/lib/sequel/adapters/shared/mysql.rb +63 -0
- data/lib/sequel/adapters/shared/postgres.rb +61 -3
- data/lib/sequel/adapters/sqlite.rb +105 -18
- data/lib/sequel/connection_pool.rb +31 -30
- data/lib/sequel/core.rb +58 -58
- data/lib/sequel/core_sql.rb +52 -43
- data/lib/sequel/database/misc.rb +11 -0
- data/lib/sequel/database/query.rb +55 -17
- data/lib/sequel/dataset/actions.rb +2 -1
- data/lib/sequel/dataset/query.rb +2 -3
- data/lib/sequel/dataset/sql.rb +24 -11
- data/lib/sequel/extensions/schema_dumper.rb +1 -1
- data/lib/sequel/metaprogramming.rb +4 -0
- data/lib/sequel/model.rb +37 -19
- data/lib/sequel/model/associations.rb +33 -25
- data/lib/sequel/model/base.rb +2 -2
- data/lib/sequel/model/plugins.rb +7 -2
- data/lib/sequel/plugins/active_model.rb +1 -1
- data/lib/sequel/plugins/association_pks.rb +2 -2
- data/lib/sequel/plugins/association_proxies.rb +1 -1
- data/lib/sequel/plugins/boolean_readers.rb +2 -2
- data/lib/sequel/plugins/class_table_inheritance.rb +10 -2
- data/lib/sequel/plugins/identity_map.rb +3 -3
- data/lib/sequel/plugins/instance_hooks.rb +1 -1
- data/lib/sequel/plugins/json_serializer.rb +212 -0
- data/lib/sequel/plugins/lazy_attributes.rb +1 -1
- data/lib/sequel/plugins/list.rb +174 -0
- data/lib/sequel/plugins/many_through_many.rb +2 -2
- data/lib/sequel/plugins/rcte_tree.rb +6 -7
- data/lib/sequel/plugins/tree.rb +118 -0
- data/lib/sequel/plugins/xml_serializer.rb +321 -0
- data/lib/sequel/sql.rb +315 -206
- data/lib/sequel/timezones.rb +40 -17
- data/lib/sequel/version.rb +8 -2
- data/spec/adapters/firebird_spec.rb +2 -2
- data/spec/adapters/informix_spec.rb +1 -1
- data/spec/adapters/mssql_spec.rb +2 -2
- data/spec/adapters/mysql_spec.rb +2 -2
- data/spec/adapters/oracle_spec.rb +1 -1
- data/spec/adapters/postgres_spec.rb +36 -6
- data/spec/adapters/spec_helper.rb +2 -2
- data/spec/adapters/sqlite_spec.rb +1 -1
- data/spec/core/connection_pool_spec.rb +3 -3
- data/spec/core/core_sql_spec.rb +31 -13
- data/spec/core/database_spec.rb +39 -2
- data/spec/core/dataset_spec.rb +24 -12
- data/spec/core/expression_filters_spec.rb +5 -1
- data/spec/core/object_graph_spec.rb +1 -1
- data/spec/core/schema_generator_spec.rb +1 -1
- data/spec/core/schema_spec.rb +1 -1
- data/spec/core/spec_helper.rb +1 -1
- data/spec/core/version_spec.rb +1 -1
- data/spec/extensions/active_model_spec.rb +82 -67
- data/spec/extensions/association_dependencies_spec.rb +1 -1
- data/spec/extensions/association_pks_spec.rb +1 -1
- data/spec/extensions/association_proxies_spec.rb +1 -1
- data/spec/extensions/blank_spec.rb +1 -1
- data/spec/extensions/boolean_readers_spec.rb +1 -1
- data/spec/extensions/caching_spec.rb +1 -1
- data/spec/extensions/class_table_inheritance_spec.rb +3 -2
- data/spec/extensions/composition_spec.rb +2 -5
- data/spec/extensions/force_encoding_spec.rb +3 -1
- data/spec/extensions/hook_class_methods_spec.rb +1 -1
- data/spec/extensions/identity_map_spec.rb +1 -1
- data/spec/extensions/inflector_spec.rb +1 -1
- data/spec/extensions/instance_filters_spec.rb +1 -1
- data/spec/extensions/instance_hooks_spec.rb +1 -1
- data/spec/extensions/json_serializer_spec.rb +154 -0
- data/spec/extensions/lazy_attributes_spec.rb +1 -2
- data/spec/extensions/list_spec.rb +251 -0
- data/spec/extensions/looser_typecasting_spec.rb +1 -1
- data/spec/extensions/many_through_many_spec.rb +3 -3
- data/spec/extensions/migration_spec.rb +1 -1
- data/spec/extensions/named_timezones_spec.rb +5 -6
- data/spec/extensions/nested_attributes_spec.rb +1 -1
- data/spec/extensions/optimistic_locking_spec.rb +1 -1
- data/spec/extensions/pagination_spec.rb +1 -1
- data/spec/extensions/pretty_table_spec.rb +1 -1
- data/spec/extensions/query_spec.rb +1 -1
- data/spec/extensions/rcte_tree_spec.rb +1 -1
- data/spec/extensions/schema_dumper_spec.rb +3 -2
- data/spec/extensions/schema_spec.rb +1 -1
- data/spec/extensions/serialization_spec.rb +6 -2
- data/spec/extensions/sharding_spec.rb +1 -1
- data/spec/extensions/single_table_inheritance_spec.rb +1 -1
- data/spec/extensions/skip_create_refresh_spec.rb +1 -1
- data/spec/extensions/spec_helper.rb +7 -3
- data/spec/extensions/sql_expr_spec.rb +1 -1
- data/spec/extensions/string_date_time_spec.rb +1 -1
- data/spec/extensions/string_stripper_spec.rb +1 -1
- data/spec/extensions/subclasses_spec.rb +1 -1
- data/spec/extensions/tactical_eager_loading_spec.rb +1 -1
- data/spec/extensions/thread_local_timezones_spec.rb +1 -1
- data/spec/extensions/timestamps_spec.rb +1 -1
- data/spec/extensions/touch_spec.rb +1 -1
- data/spec/extensions/tree_spec.rb +119 -0
- data/spec/extensions/typecast_on_load_spec.rb +1 -1
- data/spec/extensions/update_primary_key_spec.rb +1 -1
- data/spec/extensions/validation_class_methods_spec.rb +1 -1
- data/spec/extensions/validation_helpers_spec.rb +1 -1
- data/spec/extensions/xml_serializer_spec.rb +142 -0
- data/spec/integration/associations_test.rb +1 -1
- data/spec/integration/database_test.rb +1 -1
- data/spec/integration/dataset_test.rb +29 -14
- data/spec/integration/eager_loader_test.rb +1 -1
- data/spec/integration/migrator_test.rb +1 -1
- data/spec/integration/model_test.rb +1 -1
- data/spec/integration/plugin_test.rb +316 -1
- data/spec/integration/prepared_statement_test.rb +1 -1
- data/spec/integration/schema_test.rb +8 -8
- data/spec/integration/spec_helper.rb +1 -1
- data/spec/integration/timezone_test.rb +1 -1
- data/spec/integration/transaction_test.rb +35 -20
- data/spec/integration/type_test.rb +1 -1
- data/spec/model/association_reflection_spec.rb +1 -1
- data/spec/model/associations_spec.rb +49 -34
- data/spec/model/base_spec.rb +1 -1
- data/spec/model/dataset_methods_spec.rb +4 -4
- data/spec/model/eager_loading_spec.rb +1 -1
- data/spec/model/hooks_spec.rb +1 -1
- data/spec/model/inflector_spec.rb +1 -1
- data/spec/model/model_spec.rb +7 -1
- data/spec/model/plugins_spec.rb +1 -1
- data/spec/model/record_spec.rb +1 -3
- data/spec/model/spec_helper.rb +2 -2
- data/spec/model/validations_spec.rb +1 -1
- 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
|
-
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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
|
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"]}
|
data/lib/sequel/adapters/ado.rb
CHANGED
@@ -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
|
data/lib/sequel/adapters/do.rb
CHANGED
@@ -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
|
-
|
123
|
+
sqls << "SET NAMES #{literal(encoding.to_s)}" if encoding
|
123
124
|
|
124
125
|
# increase timeout so mysql server doesn't disconnect us
|
125
|
-
|
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
|
-
|
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
|