sequel 3.21.0 → 3.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/CHANGELOG +294 -0
- data/README.rdoc +20 -6
- data/Rakefile +20 -15
- data/doc/association_basics.rdoc +210 -43
- data/doc/dataset_basics.rdoc +4 -4
- data/doc/mass_assignment.rdoc +54 -0
- data/doc/migration.rdoc +15 -538
- data/doc/model_hooks.rdoc +64 -27
- data/doc/opening_databases.rdoc +37 -10
- data/doc/prepared_statements.rdoc +16 -10
- data/doc/reflection.rdoc +8 -2
- data/doc/release_notes/3.22.0.txt +39 -0
- data/doc/release_notes/3.23.0.txt +172 -0
- data/doc/release_notes/3.24.0.txt +420 -0
- data/doc/release_notes/3.25.0.txt +88 -0
- data/doc/release_notes/3.26.0.txt +88 -0
- data/doc/release_notes/3.27.0.txt +82 -0
- data/doc/release_notes/3.28.0.txt +304 -0
- data/doc/schema_modification.rdoc +547 -0
- data/doc/testing.rdoc +106 -0
- data/doc/transactions.rdoc +97 -0
- data/doc/virtual_rows.rdoc +2 -2
- data/lib/sequel/adapters/ado.rb +12 -1
- data/lib/sequel/adapters/amalgalite.rb +4 -0
- data/lib/sequel/adapters/db2.rb +95 -58
- data/lib/sequel/adapters/do.rb +12 -0
- data/lib/sequel/adapters/firebird.rb +25 -203
- data/lib/sequel/adapters/ibmdb.rb +440 -0
- data/lib/sequel/adapters/informix.rb +4 -19
- data/lib/sequel/adapters/jdbc/as400.rb +0 -7
- data/lib/sequel/adapters/jdbc/db2.rb +49 -0
- data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
- data/lib/sequel/adapters/jdbc/h2.rb +16 -5
- data/lib/sequel/adapters/jdbc/informix.rb +31 -0
- data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
- data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
- data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
- data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
- data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
- data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
- data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
- data/lib/sequel/adapters/jdbc.rb +62 -29
- data/lib/sequel/adapters/mysql.rb +22 -139
- data/lib/sequel/adapters/mysql2.rb +9 -14
- data/lib/sequel/adapters/odbc/db2.rb +21 -0
- data/lib/sequel/adapters/odbc.rb +15 -3
- data/lib/sequel/adapters/oracle.rb +17 -1
- data/lib/sequel/adapters/postgres.rb +111 -16
- data/lib/sequel/adapters/shared/access.rb +21 -0
- data/lib/sequel/adapters/shared/db2.rb +290 -0
- data/lib/sequel/adapters/shared/firebird.rb +214 -0
- data/lib/sequel/adapters/shared/informix.rb +45 -0
- data/lib/sequel/adapters/shared/mssql.rb +85 -47
- data/lib/sequel/adapters/shared/mysql.rb +50 -7
- data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
- data/lib/sequel/adapters/shared/oracle.rb +0 -4
- data/lib/sequel/adapters/shared/postgres.rb +75 -43
- data/lib/sequel/adapters/shared/sqlite.rb +56 -8
- data/lib/sequel/adapters/sqlite.rb +12 -11
- data/lib/sequel/adapters/swift/mysql.rb +9 -0
- data/lib/sequel/adapters/tinytds.rb +139 -7
- data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
- data/lib/sequel/ast_transformer.rb +190 -0
- data/lib/sequel/connection_pool/threaded.rb +3 -2
- data/lib/sequel/connection_pool.rb +1 -1
- data/lib/sequel/core.rb +6 -5
- data/lib/sequel/database/connecting.rb +5 -5
- data/lib/sequel/database/dataset.rb +1 -1
- data/lib/sequel/database/dataset_defaults.rb +1 -1
- data/lib/sequel/database/logging.rb +1 -1
- data/lib/sequel/database/misc.rb +38 -17
- data/lib/sequel/database/query.rb +50 -19
- data/lib/sequel/database/schema_generator.rb +8 -5
- data/lib/sequel/database/schema_methods.rb +52 -27
- data/lib/sequel/dataset/actions.rb +167 -48
- data/lib/sequel/dataset/features.rb +57 -8
- data/lib/sequel/dataset/graph.rb +1 -1
- data/lib/sequel/dataset/misc.rb +39 -20
- data/lib/sequel/dataset/mutation.rb +3 -3
- data/lib/sequel/dataset/prepared_statements.rb +29 -14
- data/lib/sequel/dataset/query.rb +182 -32
- data/lib/sequel/dataset/sql.rb +31 -58
- data/lib/sequel/dataset.rb +8 -0
- data/lib/sequel/exceptions.rb +4 -0
- data/lib/sequel/extensions/columns_introspection.rb +61 -0
- data/lib/sequel/extensions/migration.rb +6 -4
- data/lib/sequel/extensions/to_dot.rb +95 -83
- data/lib/sequel/model/associations.rb +893 -309
- data/lib/sequel/model/base.rb +302 -105
- data/lib/sequel/model/errors.rb +1 -1
- data/lib/sequel/model/exceptions.rb +5 -1
- data/lib/sequel/model.rb +13 -7
- data/lib/sequel/plugins/association_pks.rb +22 -4
- data/lib/sequel/plugins/defaults_setter.rb +58 -0
- data/lib/sequel/plugins/identity_map.rb +113 -6
- data/lib/sequel/plugins/many_through_many.rb +67 -5
- data/lib/sequel/plugins/prepared_statements.rb +140 -0
- data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
- data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
- data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
- data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
- data/lib/sequel/plugins/sharding.rb +12 -20
- data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
- data/lib/sequel/plugins/update_primary_key.rb +1 -1
- data/lib/sequel/plugins/xml_serializer.rb +3 -3
- data/lib/sequel/sql.rb +107 -51
- data/lib/sequel/timezones.rb +12 -3
- data/lib/sequel/version.rb +1 -1
- data/spec/adapters/db2_spec.rb +146 -0
- data/spec/adapters/mssql_spec.rb +36 -0
- data/spec/adapters/mysql_spec.rb +36 -19
- data/spec/adapters/postgres_spec.rb +115 -28
- data/spec/adapters/spec_helper.rb +6 -0
- data/spec/adapters/sqlite_spec.rb +11 -0
- data/spec/core/connection_pool_spec.rb +62 -77
- data/spec/core/database_spec.rb +244 -287
- data/spec/core/dataset_spec.rb +383 -34
- data/spec/core/expression_filters_spec.rb +159 -41
- data/spec/core/schema_spec.rb +326 -3
- data/spec/core/spec_helper.rb +45 -0
- data/spec/extensions/association_pks_spec.rb +38 -0
- data/spec/extensions/columns_introspection_spec.rb +91 -0
- data/spec/extensions/defaults_setter_spec.rb +64 -0
- data/spec/extensions/identity_map_spec.rb +162 -0
- data/spec/extensions/many_through_many_spec.rb +195 -20
- data/spec/extensions/migration_spec.rb +17 -17
- data/spec/extensions/nested_attributes_spec.rb +1 -0
- data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
- data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
- data/spec/extensions/prepared_statements_spec.rb +72 -0
- data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
- data/spec/extensions/schema_dumper_spec.rb +2 -2
- data/spec/extensions/schema_spec.rb +12 -20
- data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
- data/spec/extensions/single_table_inheritance_spec.rb +11 -0
- data/spec/extensions/spec_helper.rb +3 -1
- data/spec/extensions/to_dot_spec.rb +3 -5
- data/spec/extensions/xml_serializer_spec.rb +16 -4
- data/spec/integration/associations_test.rb +405 -15
- data/spec/integration/database_test.rb +4 -2
- data/spec/integration/dataset_test.rb +240 -20
- data/spec/integration/plugin_test.rb +142 -5
- data/spec/integration/prepared_statement_test.rb +174 -95
- data/spec/integration/schema_test.rb +128 -16
- data/spec/integration/spec_helper.rb +15 -0
- data/spec/integration/transaction_test.rb +40 -0
- data/spec/integration/type_test.rb +16 -2
- data/spec/model/association_reflection_spec.rb +91 -0
- data/spec/model/associations_spec.rb +476 -5
- data/spec/model/base_spec.rb +91 -1
- data/spec/model/eager_loading_spec.rb +519 -31
- data/spec/model/hooks_spec.rb +161 -0
- data/spec/model/model_spec.rb +89 -2
- data/spec/model/plugins_spec.rb +17 -0
- data/spec/model/record_spec.rb +184 -12
- data/spec/model/spec_helper.rb +5 -0
- data/spec/model/validations_spec.rb +11 -0
- metadata +85 -34
|
@@ -16,11 +16,6 @@ module Sequel
|
|
|
16
16
|
|
|
17
17
|
include Sequel::MSSQL::DatabaseMethods
|
|
18
18
|
|
|
19
|
-
# Return instance of Sequel::JDBC::MSSQL::Dataset with the given opts.
|
|
20
|
-
def dataset(opts=nil)
|
|
21
|
-
Sequel::JDBC::MSSQL::Dataset.new(self, opts)
|
|
22
|
-
end
|
|
23
|
-
|
|
24
19
|
private
|
|
25
20
|
|
|
26
21
|
# Get the last inserted id using SCOPE_IDENTITY().
|
|
@@ -43,33 +38,6 @@ module Sequel
|
|
|
43
38
|
def primary_key_index_re
|
|
44
39
|
PRIMARY_KEY_INDEX_RE
|
|
45
40
|
end
|
|
46
|
-
|
|
47
|
-
def metadata_dataset
|
|
48
|
-
ds = super
|
|
49
|
-
# Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
|
|
50
|
-
# for the getColumns result set specifies an incorrect type for the
|
|
51
|
-
# IS_AUTOINCREMENT column. The column is a string, but the type is
|
|
52
|
-
# specified as a short. This causes getObject() to throw a
|
|
53
|
-
# com.microsoft.sqlserver.jdbc.SQLServerException: "The conversion
|
|
54
|
-
# from char to SMALLINT is unsupported." Using getString() rather
|
|
55
|
-
# than getObject() for this column avoids the problem.
|
|
56
|
-
# Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
|
|
57
|
-
def ds.result_set_object_getter
|
|
58
|
-
lambda do |result, n, i|
|
|
59
|
-
if n == :is_autoincrement
|
|
60
|
-
@convert_types ? convert_type(result.getString(i)) : result.getString(i)
|
|
61
|
-
else
|
|
62
|
-
@convert_types ? convert_type(result.getObject(i)) : result.getObject(i)
|
|
63
|
-
end
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
ds
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
# Dataset class for MSSQL datasets accessed via JDBC.
|
|
71
|
-
class Dataset < JDBC::Dataset
|
|
72
|
-
include Sequel::MSSQL::DatasetMethods
|
|
73
41
|
end
|
|
74
42
|
end
|
|
75
43
|
end
|
|
@@ -56,6 +56,15 @@ module Sequel
|
|
|
56
56
|
def schema_column_type(db_type)
|
|
57
57
|
db_type == 'tinyint(1)' ? :boolean : super
|
|
58
58
|
end
|
|
59
|
+
|
|
60
|
+
# By default, MySQL 'where id is null' selects the last inserted id.
|
|
61
|
+
# Turn that off unless explicitly enabled.
|
|
62
|
+
def setup_connection(conn)
|
|
63
|
+
super
|
|
64
|
+
sql = "SET SQL_AUTO_IS_NULL=0"
|
|
65
|
+
statement(conn){|s| log_yield(sql){s.execute(sql)}} unless opts[:auto_is_null]
|
|
66
|
+
conn
|
|
67
|
+
end
|
|
59
68
|
end
|
|
60
69
|
|
|
61
70
|
# Dataset class for MySQL datasets accessed via JDBC.
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
Sequel.require 'adapters/shared/oracle'
|
|
2
|
+
Sequel.require 'adapters/jdbc/transactions'
|
|
2
3
|
|
|
3
4
|
module Sequel
|
|
4
5
|
module JDBC
|
|
@@ -7,38 +8,12 @@ module Sequel
|
|
|
7
8
|
# Instance methods for Oracle Database objects accessed via JDBC.
|
|
8
9
|
module DatabaseMethods
|
|
9
10
|
include Sequel::Oracle::DatabaseMethods
|
|
10
|
-
|
|
11
|
-
TRANSACTION_COMMIT = 'Transaction.commit'.freeze
|
|
12
|
-
TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
|
|
11
|
+
include Sequel::JDBC::Transactions
|
|
13
12
|
|
|
14
13
|
# Return Sequel::JDBC::Oracle::Dataset object with the given opts.
|
|
15
14
|
def dataset(opts=nil)
|
|
16
15
|
Sequel::JDBC::Oracle::Dataset.new(self, opts)
|
|
17
16
|
end
|
|
18
|
-
|
|
19
|
-
private
|
|
20
|
-
|
|
21
|
-
# Use JDBC connection's setAutoCommit to false to start transactions
|
|
22
|
-
def begin_transaction(conn, opts={})
|
|
23
|
-
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
|
24
|
-
conn
|
|
25
|
-
end
|
|
26
|
-
|
|
27
|
-
# Use JDBC connection's commit method to commit transactions
|
|
28
|
-
def commit_transaction(conn, opts={})
|
|
29
|
-
log_yield(TRANSACTION_COMMIT){conn.commit}
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
|
|
33
|
-
def remove_transaction(conn)
|
|
34
|
-
conn.setAutoCommit(true) if conn
|
|
35
|
-
@transactions.delete(Thread.current)
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
# Use JDBC connection's rollback method to rollback transactions
|
|
39
|
-
def rollback_transaction(conn, opts={})
|
|
40
|
-
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
41
|
-
end
|
|
42
17
|
end
|
|
43
18
|
|
|
44
19
|
# Dataset class for Oracle datasets accessed via JDBC.
|
|
@@ -60,6 +60,12 @@ module Sequel
|
|
|
60
60
|
|
|
61
61
|
private
|
|
62
62
|
|
|
63
|
+
# Use setNull for nil arguments as the default behavior of setString
|
|
64
|
+
# with nil doesn't appear to work correctly on PostgreSQL.
|
|
65
|
+
def set_ps_arg(cps, arg, i)
|
|
66
|
+
arg.nil? ? cps.setNull(i, JavaSQL::Types::NULL) : super
|
|
67
|
+
end
|
|
68
|
+
|
|
63
69
|
# Extend the adapter with the JDBC PostgreSQL AdapterMethods
|
|
64
70
|
def setup_connection(conn)
|
|
65
71
|
conn = super(conn)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
Sequel.require 'adapters/jdbc/mssql'
|
|
2
|
+
|
|
3
|
+
module Sequel
|
|
4
|
+
module JDBC
|
|
5
|
+
# Database and Dataset instance methods for SQLServer specific
|
|
6
|
+
# support via JDBC.
|
|
7
|
+
module SQLServer
|
|
8
|
+
# Database instance methods for SQLServer databases accessed via JDBC.
|
|
9
|
+
module DatabaseMethods
|
|
10
|
+
include Sequel::JDBC::MSSQL::DatabaseMethods
|
|
11
|
+
|
|
12
|
+
# Return instance of Sequel::JDBC::SQLServer::Dataset with the given opts.
|
|
13
|
+
def dataset(opts=nil)
|
|
14
|
+
Sequel::JDBC::SQLServer::Dataset.new(self, opts)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def metadata_dataset
|
|
18
|
+
ds = super
|
|
19
|
+
# Work around a bug in SQL Server JDBC Driver 3.0, where the metadata
|
|
20
|
+
# for the getColumns result set specifies an incorrect type for the
|
|
21
|
+
# IS_AUTOINCREMENT column. The column is a string, but the type is
|
|
22
|
+
# specified as a short. This causes getObject() to throw a
|
|
23
|
+
# com.microsoft.sqlserver.jdbc.SQLServerException: "The conversion
|
|
24
|
+
# from char to SMALLINT is unsupported." Using getString() rather
|
|
25
|
+
# than getObject() for this column avoids the problem.
|
|
26
|
+
# Reference: http://social.msdn.microsoft.com/Forums/en/sqldataaccess/thread/20df12f3-d1bf-4526-9daa-239a83a8e435
|
|
27
|
+
def ds.result_set_object_getter
|
|
28
|
+
lambda do |result, n, i|
|
|
29
|
+
if n == :is_autoincrement
|
|
30
|
+
@convert_types ? convert_type(result.getString(i)) : result.getString(i)
|
|
31
|
+
else
|
|
32
|
+
@convert_types ? convert_type(result.getObject(i)) : result.getObject(i)
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
ds
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# Dataset class for SQLServer datasets accessed via JDBC.
|
|
41
|
+
class Dataset < JDBC::Dataset
|
|
42
|
+
include Sequel::MSSQL::DatasetMethods
|
|
43
|
+
end
|
|
44
|
+
end
|
|
45
|
+
end
|
|
46
|
+
end
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
module Sequel
|
|
2
|
+
module JDBC
|
|
3
|
+
module Transactions
|
|
4
|
+
TRANSACTION_BEGIN = 'Transaction.begin'.freeze
|
|
5
|
+
TRANSACTION_COMMIT = 'Transaction.commit'.freeze
|
|
6
|
+
TRANSACTION_ROLLBACK = 'Transaction.rollback'.freeze
|
|
7
|
+
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
# Use JDBC connection's setAutoCommit to false to start transactions
|
|
11
|
+
def begin_transaction(conn, opts={})
|
|
12
|
+
log_yield(TRANSACTION_BEGIN){conn.setAutoCommit(false)}
|
|
13
|
+
conn
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
# Use JDBC connection's commit method to commit transactions
|
|
17
|
+
def commit_transaction(conn, opts={})
|
|
18
|
+
log_yield(TRANSACTION_COMMIT){conn.commit}
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# Use JDBC connection's setAutoCommit to true to enable non-transactional behavior
|
|
22
|
+
def remove_transaction(conn)
|
|
23
|
+
conn.setAutoCommit(true) if conn
|
|
24
|
+
@transactions.delete(Thread.current)
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
# Use JDBC connection's rollback method to rollback transactions
|
|
28
|
+
def rollback_transaction(conn, opts={})
|
|
29
|
+
log_yield(TRANSACTION_ROLLBACK){conn.rollback}
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
data/lib/sequel/adapters/jdbc.rb
CHANGED
|
@@ -53,13 +53,15 @@ module Sequel
|
|
|
53
53
|
Java::oracle.jdbc.driver.OracleDriver
|
|
54
54
|
end,
|
|
55
55
|
:sqlserver=>proc do |db|
|
|
56
|
-
Sequel.ts_require 'adapters/jdbc/
|
|
57
|
-
db.extend(Sequel::JDBC::
|
|
56
|
+
Sequel.ts_require 'adapters/jdbc/sqlserver'
|
|
57
|
+
db.extend(Sequel::JDBC::SQLServer::DatabaseMethods)
|
|
58
|
+
db.send(:set_mssql_unicode_strings)
|
|
58
59
|
com.microsoft.sqlserver.jdbc.SQLServerDriver
|
|
59
60
|
end,
|
|
60
61
|
:jtds=>proc do |db|
|
|
61
|
-
Sequel.ts_require 'adapters/jdbc/
|
|
62
|
-
db.extend(Sequel::JDBC::
|
|
62
|
+
Sequel.ts_require 'adapters/jdbc/jtds'
|
|
63
|
+
db.extend(Sequel::JDBC::JTDS::DatabaseMethods)
|
|
64
|
+
db.send(:set_mssql_unicode_strings)
|
|
63
65
|
JDBC.load_gem('jtds')
|
|
64
66
|
Java::net.sourceforge.jtds.jdbc.Driver
|
|
65
67
|
end,
|
|
@@ -73,6 +75,21 @@ module Sequel
|
|
|
73
75
|
Sequel.ts_require 'adapters/jdbc/as400'
|
|
74
76
|
db.extend(Sequel::JDBC::AS400::DatabaseMethods)
|
|
75
77
|
com.ibm.as400.access.AS400JDBCDriver
|
|
78
|
+
end,
|
|
79
|
+
:"informix-sqli"=>proc do |db|
|
|
80
|
+
Sequel.ts_require 'adapters/jdbc/informix'
|
|
81
|
+
db.extend(Sequel::JDBC::Informix::DatabaseMethods)
|
|
82
|
+
com.informix.jdbc.IfxDriver
|
|
83
|
+
end,
|
|
84
|
+
:db2=>proc do |db|
|
|
85
|
+
Sequel.ts_require 'adapters/jdbc/db2'
|
|
86
|
+
db.extend(Sequel::JDBC::DB2::DatabaseMethods)
|
|
87
|
+
com.ibm.db2.jcc.DB2Driver
|
|
88
|
+
end,
|
|
89
|
+
:firebirdsql=>proc do |db|
|
|
90
|
+
Sequel.ts_require 'adapters/jdbc/firebird'
|
|
91
|
+
db.extend(Sequel::JDBC::Firebird::DatabaseMethods)
|
|
92
|
+
org.firebirdsql.jdbc.FBDriver
|
|
76
93
|
end
|
|
77
94
|
}
|
|
78
95
|
|
|
@@ -101,7 +118,7 @@ module Sequel
|
|
|
101
118
|
# True by default, can be set to false to roughly double performance when
|
|
102
119
|
# fetching rows.
|
|
103
120
|
attr_accessor :convert_types
|
|
104
|
-
|
|
121
|
+
|
|
105
122
|
# Call the DATABASE_SETUP proc directly after initialization,
|
|
106
123
|
# so the object always uses sub adapter specific code. Also,
|
|
107
124
|
# raise an error immediately if the connection doesn't have a
|
|
@@ -246,12 +263,14 @@ module Sequel
|
|
|
246
263
|
indexes
|
|
247
264
|
end
|
|
248
265
|
|
|
266
|
+
# Whether or not JNDI is being used for this connection.
|
|
267
|
+
def jndi?
|
|
268
|
+
!!(uri =~ JNDI_URI_REGEXP)
|
|
269
|
+
end
|
|
270
|
+
|
|
249
271
|
# All tables in this database
|
|
250
272
|
def tables(opts={})
|
|
251
|
-
|
|
252
|
-
m = output_identifier_meth
|
|
253
|
-
metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << m.call(h[:table_name])}
|
|
254
|
-
ts
|
|
273
|
+
get_tables('TABLE', opts)
|
|
255
274
|
end
|
|
256
275
|
|
|
257
276
|
# The uri for this connection. You can specify the uri
|
|
@@ -264,11 +283,11 @@ module Sequel
|
|
|
264
283
|
ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
|
|
265
284
|
end
|
|
266
285
|
|
|
267
|
-
#
|
|
268
|
-
def
|
|
269
|
-
|
|
286
|
+
# All views in this database
|
|
287
|
+
def views(opts={})
|
|
288
|
+
get_tables('VIEW', opts)
|
|
270
289
|
end
|
|
271
|
-
|
|
290
|
+
|
|
272
291
|
private
|
|
273
292
|
|
|
274
293
|
# Close given adapter connections
|
|
@@ -276,6 +295,12 @@ module Sequel
|
|
|
276
295
|
c.close
|
|
277
296
|
end
|
|
278
297
|
|
|
298
|
+
# Raise a disconnect error if the SQL state of the cause of the exception indicates so.
|
|
299
|
+
def disconnect_error?(exception, opts)
|
|
300
|
+
cause = exception.respond_to?(:cause) ? exception.cause : exception
|
|
301
|
+
super || (cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/)
|
|
302
|
+
end
|
|
303
|
+
|
|
279
304
|
# Execute the prepared statement. If the provided name is a
|
|
280
305
|
# dataset, use that as the prepared statement, otherwise use
|
|
281
306
|
# it as a key to look it up in the prepared_statements hash.
|
|
@@ -325,6 +350,12 @@ module Sequel
|
|
|
325
350
|
end
|
|
326
351
|
end
|
|
327
352
|
|
|
353
|
+
# Gets the connection from JNDI.
|
|
354
|
+
def get_connection_from_jndi
|
|
355
|
+
jndi_name = JNDI_URI_REGEXP.match(uri)[1]
|
|
356
|
+
JavaxNaming::InitialContext.new.lookup(jndi_name).connection
|
|
357
|
+
end
|
|
358
|
+
|
|
328
359
|
# Gets the JDBC connection uri from the JNDI resource.
|
|
329
360
|
def get_uri_from_jndi
|
|
330
361
|
conn = get_connection_from_jndi
|
|
@@ -333,12 +364,14 @@ module Sequel
|
|
|
333
364
|
conn.close if conn
|
|
334
365
|
end
|
|
335
366
|
|
|
336
|
-
#
|
|
337
|
-
def
|
|
338
|
-
|
|
339
|
-
|
|
367
|
+
# Backbone of the tables and views support.
|
|
368
|
+
def get_tables(type, opts)
|
|
369
|
+
ts = []
|
|
370
|
+
m = output_identifier_meth
|
|
371
|
+
metadata(:getTables, nil, nil, nil, [type].to_java(:string)){|h| ts << m.call(h[:table_name])}
|
|
372
|
+
ts
|
|
340
373
|
end
|
|
341
|
-
|
|
374
|
+
|
|
342
375
|
# Support Date objects used in bound variables
|
|
343
376
|
def java_sql_date(date)
|
|
344
377
|
java.sql.Date.new(Time.local(date.year, date.month, date.day).to_i * 1000)
|
|
@@ -383,12 +416,6 @@ module Sequel
|
|
|
383
416
|
end
|
|
384
417
|
end
|
|
385
418
|
|
|
386
|
-
# Treat SQLExceptions with a "Connection Error" SQLState as disconnects
|
|
387
|
-
def raise_error(exception, opts={})
|
|
388
|
-
cause = exception.respond_to?(:cause) ? exception.cause : exception
|
|
389
|
-
super(exception, {:disconnect => cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/}.merge(opts))
|
|
390
|
-
end
|
|
391
|
-
|
|
392
419
|
# Java being java, you need to specify the type of each argument
|
|
393
420
|
# for the prepared statement, and bind it individually. This
|
|
394
421
|
# guesses which JDBC method to use, and hopefully JRuby will convert
|
|
@@ -405,8 +432,8 @@ module Sequel
|
|
|
405
432
|
cps.setDouble(i, arg)
|
|
406
433
|
when TrueClass, FalseClass
|
|
407
434
|
cps.setBoolean(i, arg)
|
|
408
|
-
when
|
|
409
|
-
cps.
|
|
435
|
+
when NilClass
|
|
436
|
+
cps.setString(i, nil)
|
|
410
437
|
when DateTime
|
|
411
438
|
cps.setTimestamp(i, java_sql_datetime(arg))
|
|
412
439
|
when Date
|
|
@@ -444,9 +471,11 @@ module Sequel
|
|
|
444
471
|
table = im.call(table)
|
|
445
472
|
pks, ts = [], []
|
|
446
473
|
metadata(:getPrimaryKeys, nil, schema, table) do |h|
|
|
474
|
+
next if h[:table_schem] == 'INFORMATION_SCHEMA'
|
|
447
475
|
pks << h[:column_name]
|
|
448
476
|
end
|
|
449
477
|
metadata(:getColumns, nil, schema, table, nil) do |h|
|
|
478
|
+
next if h[:table_schem] == 'INFORMATION_SCHEMA'
|
|
450
479
|
s = {:type=>schema_column_type(h[:type_name]), :db_type=>h[:type_name], :default=>(h[:column_def] == '' ? nil : h[:column_def]), :allow_null=>(h[:nullable] != 0), :primary_key=>pks.include?(h[:column_name]), :column_size=>h[:column_size], :scale=>h[:decimal_digits]}
|
|
451
480
|
if s[:db_type] =~ DECIMAL_TYPE_RE && s[:scale] == 0
|
|
452
481
|
s[:type] = :integer
|
|
@@ -530,8 +559,8 @@ module Sequel
|
|
|
530
559
|
# Uses the database's setting by default, can be set to false to roughly
|
|
531
560
|
# double performance when fetching rows.
|
|
532
561
|
attr_accessor :convert_types
|
|
533
|
-
|
|
534
|
-
# Use the convert_types default setting from the database
|
|
562
|
+
|
|
563
|
+
# Use the convert_types default setting from the database.
|
|
535
564
|
def initialize(db, opts={})
|
|
536
565
|
@convert_types = db.convert_types
|
|
537
566
|
super
|
|
@@ -560,8 +589,10 @@ module Sequel
|
|
|
560
589
|
# Convert the type. Used for converting Java types to ruby types.
|
|
561
590
|
def convert_type(v)
|
|
562
591
|
case v
|
|
563
|
-
when Java::JavaSQL::Timestamp
|
|
592
|
+
when Java::JavaSQL::Timestamp
|
|
564
593
|
Sequel.database_to_application_timestamp(v.to_string)
|
|
594
|
+
when Java::JavaSQL::Time
|
|
595
|
+
Sequel.string_to_time(v.to_string)
|
|
565
596
|
when Java::JavaSQL::Date
|
|
566
597
|
Sequel.string_to_date(v.to_string)
|
|
567
598
|
when Java::JavaIo::BufferedReader
|
|
@@ -574,6 +605,8 @@ module Sequel
|
|
|
574
605
|
Sequel::SQL::Blob.new(String.from_java_bytes(v))
|
|
575
606
|
when Java::JavaSQL::Blob
|
|
576
607
|
convert_type(v.getBytes(1, v.length))
|
|
608
|
+
when Java::JavaSQL::Clob
|
|
609
|
+
Sequel::SQL::Blob.new(v.getSubString(1, v.length))
|
|
577
610
|
else
|
|
578
611
|
v
|
|
579
612
|
end
|
|
@@ -5,32 +5,27 @@ rescue LoadError
|
|
|
5
5
|
end
|
|
6
6
|
raise(LoadError, "require 'mysql' did not define Mysql::CLIENT_MULTI_RESULTS!\n You are probably using the pure ruby mysql.rb driver,\n which Sequel does not support. You need to install\n the C based adapter, and make sure that the mysql.so\n file is loaded instead of the mysql.rb file.\n") unless defined?(Mysql::CLIENT_MULTI_RESULTS)
|
|
7
7
|
|
|
8
|
-
Sequel.require %w'shared/
|
|
8
|
+
Sequel.require %w'shared/mysql_prepared_statements', 'adapters'
|
|
9
9
|
|
|
10
10
|
module Sequel
|
|
11
11
|
# Module for holding all MySQL-related classes and modules for Sequel.
|
|
12
12
|
module MySQL
|
|
13
13
|
TYPE_TRANSLATOR = tt = Class.new do
|
|
14
14
|
def boolean(s) s.to_i != 0 end
|
|
15
|
-
def blob(s) ::Sequel::SQL::Blob.new(s) end
|
|
16
15
|
def integer(s) s.to_i end
|
|
17
16
|
def float(s) s.to_f end
|
|
18
|
-
def
|
|
19
|
-
def
|
|
20
|
-
def
|
|
21
|
-
def timestamp(s) ::Sequel.database_to_application_timestamp(s) end
|
|
22
|
-
def date_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
|
|
23
|
-
def time_conv(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
|
|
24
|
-
def timestamp_conv(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
|
|
17
|
+
def date(s) ::Sequel::MySQL.convert_date_time(:string_to_date, s) end
|
|
18
|
+
def time(s) ::Sequel::MySQL.convert_date_time(:string_to_time, s) end
|
|
19
|
+
def timestamp(s) ::Sequel::MySQL.convert_date_time(:database_to_application_timestamp, s) end
|
|
25
20
|
end.new
|
|
26
21
|
|
|
27
22
|
# Hash with integer keys and callable values for converting MySQL types.
|
|
28
23
|
MYSQL_TYPES = {}
|
|
29
24
|
{
|
|
30
|
-
[0, 246]
|
|
31
|
-
[2, 3, 8, 9, 13, 247, 248]
|
|
32
|
-
[4, 5]
|
|
33
|
-
[249, 250, 251, 252]
|
|
25
|
+
[0, 246] => ::BigDecimal.method(:new),
|
|
26
|
+
[2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
|
|
27
|
+
[4, 5] => tt.method(:float),
|
|
28
|
+
[249, 250, 251, 252] => ::Sequel::SQL::Blob.method(:new)
|
|
34
29
|
}.each do |k,v|
|
|
35
30
|
k.each{|n| MYSQL_TYPES[n] = v}
|
|
36
31
|
end
|
|
@@ -54,10 +49,10 @@ module Sequel
|
|
|
54
49
|
# Modify the type translators for the date, time, and timestamp types
|
|
55
50
|
# depending on the value given.
|
|
56
51
|
def self.convert_invalid_date_time=(v)
|
|
57
|
-
MYSQL_TYPES[11] =
|
|
58
|
-
m =
|
|
52
|
+
MYSQL_TYPES[11] = (v != false) ? TYPE_TRANSLATOR.method(:time) : ::Sequel.method(:string_to_time)
|
|
53
|
+
m = (v != false) ? TYPE_TRANSLATOR.method(:date) : ::Sequel.method(:string_to_date)
|
|
59
54
|
[10, 14].each{|i| MYSQL_TYPES[i] = m}
|
|
60
|
-
m =
|
|
55
|
+
m = (v != false) ? TYPE_TRANSLATOR.method(:timestamp) : ::Sequel.method(:database_to_application_timestamp)
|
|
61
56
|
[7, 12].each{|i| MYSQL_TYPES[i] = m}
|
|
62
57
|
@convert_invalid_date_time = v
|
|
63
58
|
end
|
|
@@ -84,18 +79,13 @@ module Sequel
|
|
|
84
79
|
# Database class for MySQL databases used with Sequel.
|
|
85
80
|
class Database < Sequel::Database
|
|
86
81
|
include Sequel::MySQL::DatabaseMethods
|
|
82
|
+
include Sequel::MySQL::PreparedStatements::DatabaseMethods
|
|
87
83
|
|
|
88
84
|
# Mysql::Error messages that indicate the current connection should be disconnected
|
|
89
85
|
MYSQL_DATABASE_DISCONNECT_ERRORS = /\A(Commands out of sync; you can't run this command now|Can't connect to local MySQL server through socket|MySQL server has gone away|Lost connection to MySQL server during query)/
|
|
90
86
|
|
|
91
87
|
set_adapter_scheme :mysql
|
|
92
88
|
|
|
93
|
-
# Support stored procedures on MySQL
|
|
94
|
-
def call_sproc(name, opts={}, &block)
|
|
95
|
-
args = opts[:args] || []
|
|
96
|
-
execute("CALL #{name}#{args.empty? ? '()' : literal(args)}", opts.merge(:sproc=>false), &block)
|
|
97
|
-
end
|
|
98
|
-
|
|
99
89
|
# Connect to the database. In addition to the usual database options,
|
|
100
90
|
# the following options have effect:
|
|
101
91
|
#
|
|
@@ -139,7 +129,7 @@ module Sequel
|
|
|
139
129
|
opts[:user],
|
|
140
130
|
opts[:password],
|
|
141
131
|
opts[:database],
|
|
142
|
-
opts[:port],
|
|
132
|
+
(opts[:port].to_i if opts[:port]),
|
|
143
133
|
opts[:socket],
|
|
144
134
|
Mysql::CLIENT_MULTI_RESULTS +
|
|
145
135
|
Mysql::CLIENT_MULTI_STATEMENTS +
|
|
@@ -161,10 +151,7 @@ module Sequel
|
|
|
161
151
|
|
|
162
152
|
sqls.each{|sql| log_yield(sql){conn.query(sql)}}
|
|
163
153
|
|
|
164
|
-
|
|
165
|
-
attr_accessor :prepared_statements
|
|
166
|
-
end
|
|
167
|
-
conn.prepared_statements = {}
|
|
154
|
+
add_prepared_statements_cache(conn)
|
|
168
155
|
conn
|
|
169
156
|
end
|
|
170
157
|
|
|
@@ -173,18 +160,6 @@ module Sequel
|
|
|
173
160
|
MySQL::Dataset.new(self, opts)
|
|
174
161
|
end
|
|
175
162
|
|
|
176
|
-
# Executes the given SQL using an available connection, yielding the
|
|
177
|
-
# connection if the block is given.
|
|
178
|
-
def execute(sql, opts={}, &block)
|
|
179
|
-
if opts[:sproc]
|
|
180
|
-
call_sproc(sql, opts, &block)
|
|
181
|
-
elsif sql.is_a?(Symbol)
|
|
182
|
-
execute_prepared_statement(sql, opts, &block)
|
|
183
|
-
else
|
|
184
|
-
synchronize(opts[:server]){|conn| _execute(conn, sql, opts, &block)}
|
|
185
|
-
end
|
|
186
|
-
end
|
|
187
|
-
|
|
188
163
|
# Return the version of the MySQL server two which we are connecting.
|
|
189
164
|
def server_version(server=nil)
|
|
190
165
|
@server_version ||= (synchronize(server){|conn| conn.server_version if conn.respond_to?(:server_version)} || super)
|
|
@@ -220,7 +195,7 @@ module Sequel
|
|
|
220
195
|
end
|
|
221
196
|
end
|
|
222
197
|
rescue Mysql::Error => e
|
|
223
|
-
raise_error(e
|
|
198
|
+
raise_error(e)
|
|
224
199
|
ensure
|
|
225
200
|
r.free if r
|
|
226
201
|
# Use up all results to avoid a commands out of sync message.
|
|
@@ -248,6 +223,12 @@ module Sequel
|
|
|
248
223
|
def database_error_classes
|
|
249
224
|
[Mysql::Error]
|
|
250
225
|
end
|
|
226
|
+
|
|
227
|
+
# Raise a disconnect error if the exception message matches the list
|
|
228
|
+
# of recognized exceptions.
|
|
229
|
+
def disconnect_error?(e, opts)
|
|
230
|
+
super || (e.is_a?(::Mysql::Error) && MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
|
|
231
|
+
end
|
|
251
232
|
|
|
252
233
|
# The database name when using the native adapter is always stored in
|
|
253
234
|
# the :database option.
|
|
@@ -262,27 +243,6 @@ module Sequel
|
|
|
262
243
|
nil
|
|
263
244
|
end
|
|
264
245
|
|
|
265
|
-
# Executes a prepared statement on an available connection. If the
|
|
266
|
-
# prepared statement already exists for the connection and has the same
|
|
267
|
-
# SQL, reuse it, otherwise, prepare the new statement. Because of the
|
|
268
|
-
# usual MySQL stupidity, we are forced to name arguments via separate
|
|
269
|
-
# SET queries. Use @sequel_arg_N (for N starting at 1) for these
|
|
270
|
-
# arguments.
|
|
271
|
-
def execute_prepared_statement(ps_name, opts, &block)
|
|
272
|
-
args = opts[:arguments]
|
|
273
|
-
ps = prepared_statements[ps_name]
|
|
274
|
-
sql = ps.prepared_sql
|
|
275
|
-
synchronize(opts[:server]) do |conn|
|
|
276
|
-
unless conn.prepared_statements[ps_name] == sql
|
|
277
|
-
conn.prepared_statements[ps_name] = sql
|
|
278
|
-
_execute(conn, "PREPARE #{ps_name} FROM '#{::Mysql.quote(sql)}'", opts)
|
|
279
|
-
end
|
|
280
|
-
i = 0
|
|
281
|
-
_execute(conn, "SET " + args.map {|arg| "@sequel_arg_#{i+=1} = #{literal(arg)}"}.join(", "), opts) unless args.empty?
|
|
282
|
-
_execute(conn, "EXECUTE #{ps_name}#{" USING #{(1..i).map{|j| "@sequel_arg_#{j}"}.join(', ')}" unless i == 0}", opts, &block)
|
|
283
|
-
end
|
|
284
|
-
end
|
|
285
|
-
|
|
286
246
|
# Convert tinyint(1) type to boolean if convert_tinyint_to_bool is true
|
|
287
247
|
def schema_column_type(db_type)
|
|
288
248
|
Sequel::MySQL.convert_tinyint_to_bool && db_type == 'tinyint(1)' ? :boolean : super
|
|
@@ -292,67 +252,7 @@ module Sequel
|
|
|
292
252
|
# Dataset class for MySQL datasets accessed via the native driver.
|
|
293
253
|
class Dataset < Sequel::Dataset
|
|
294
254
|
include Sequel::MySQL::DatasetMethods
|
|
295
|
-
include
|
|
296
|
-
|
|
297
|
-
# Methods to add to MySQL prepared statement calls without using a
|
|
298
|
-
# real database prepared statement and bound variables.
|
|
299
|
-
module CallableStatementMethods
|
|
300
|
-
# Extend given dataset with this module so subselects inside subselects in
|
|
301
|
-
# prepared statements work.
|
|
302
|
-
def subselect_sql(ds)
|
|
303
|
-
ps = ds.to_prepared_statement(:select)
|
|
304
|
-
ps.extend(CallableStatementMethods)
|
|
305
|
-
ps = ps.bind(@opts[:bind_vars]) if @opts[:bind_vars]
|
|
306
|
-
ps.prepared_args = prepared_args
|
|
307
|
-
ps.prepared_sql
|
|
308
|
-
end
|
|
309
|
-
end
|
|
310
|
-
|
|
311
|
-
# Methods for MySQL prepared statements using the native driver.
|
|
312
|
-
module PreparedStatementMethods
|
|
313
|
-
include Sequel::Dataset::UnnumberedArgumentMapper
|
|
314
|
-
|
|
315
|
-
private
|
|
316
|
-
|
|
317
|
-
# Execute the prepared statement with the bind arguments instead of
|
|
318
|
-
# the given SQL.
|
|
319
|
-
def execute(sql, opts={}, &block)
|
|
320
|
-
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
321
|
-
end
|
|
322
|
-
|
|
323
|
-
# Same as execute, explicit due to intricacies of alias and super.
|
|
324
|
-
def execute_dui(sql, opts={}, &block)
|
|
325
|
-
super(prepared_statement_name, {:arguments=>bind_arguments}.merge(opts), &block)
|
|
326
|
-
end
|
|
327
|
-
end
|
|
328
|
-
|
|
329
|
-
# Methods for MySQL stored procedures using the native driver.
|
|
330
|
-
module StoredProcedureMethods
|
|
331
|
-
include Sequel::Dataset::StoredProcedureMethods
|
|
332
|
-
|
|
333
|
-
private
|
|
334
|
-
|
|
335
|
-
# Execute the database stored procedure with the stored arguments.
|
|
336
|
-
def execute(sql, opts={}, &block)
|
|
337
|
-
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
338
|
-
end
|
|
339
|
-
|
|
340
|
-
# Same as execute, explicit due to intricacies of alias and super.
|
|
341
|
-
def execute_dui(sql, opts={}, &block)
|
|
342
|
-
super(@sproc_name, {:args=>@sproc_args, :sproc=>true}.merge(opts), &block)
|
|
343
|
-
end
|
|
344
|
-
end
|
|
345
|
-
|
|
346
|
-
# MySQL is different in that it supports prepared statements but not bound
|
|
347
|
-
# variables outside of prepared statements. The default implementation
|
|
348
|
-
# breaks the use of subselects in prepared statements, so extend the
|
|
349
|
-
# temporary prepared statement that this creates with a module that
|
|
350
|
-
# fixes it.
|
|
351
|
-
def call(type, bind_arguments={}, *values, &block)
|
|
352
|
-
ps = to_prepared_statement(type, values)
|
|
353
|
-
ps.extend(CallableStatementMethods)
|
|
354
|
-
ps.call(bind_arguments, &block)
|
|
355
|
-
end
|
|
255
|
+
include Sequel::MySQL::PreparedStatements::DatasetMethods
|
|
356
256
|
|
|
357
257
|
# Delete rows matching this dataset
|
|
358
258
|
def delete
|
|
@@ -395,18 +295,6 @@ module Sequel
|
|
|
395
295
|
execute_dui(insert_sql(*values)){|c| return c.insert_id}
|
|
396
296
|
end
|
|
397
297
|
|
|
398
|
-
# Store the given type of prepared statement in the associated database
|
|
399
|
-
# with the given name.
|
|
400
|
-
def prepare(type, name=nil, *values)
|
|
401
|
-
ps = to_prepared_statement(type, values)
|
|
402
|
-
ps.extend(PreparedStatementMethods)
|
|
403
|
-
if name
|
|
404
|
-
ps.prepared_statement_name = name
|
|
405
|
-
db.prepared_statements[name] = ps
|
|
406
|
-
end
|
|
407
|
-
ps
|
|
408
|
-
end
|
|
409
|
-
|
|
410
298
|
# Replace (update or insert) the matching row.
|
|
411
299
|
def replace(*args)
|
|
412
300
|
execute_dui(replace_sql(*args)){|c| return c.insert_id}
|
|
@@ -450,11 +338,6 @@ module Sequel
|
|
|
450
338
|
"'#{::Mysql.quote(v)}'"
|
|
451
339
|
end
|
|
452
340
|
|
|
453
|
-
# Extend the dataset with the MySQL stored procedure methods.
|
|
454
|
-
def prepare_extend_sproc(ds)
|
|
455
|
-
ds.extend(StoredProcedureMethods)
|
|
456
|
-
end
|
|
457
|
-
|
|
458
341
|
# Yield each row of the given result set r with columns cols
|
|
459
342
|
# as a hash with symbol keys
|
|
460
343
|
def yield_rows(r, cols)
|