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.
Files changed (158) hide show
  1. data/CHANGELOG +294 -0
  2. data/README.rdoc +20 -6
  3. data/Rakefile +20 -15
  4. data/doc/association_basics.rdoc +210 -43
  5. data/doc/dataset_basics.rdoc +4 -4
  6. data/doc/mass_assignment.rdoc +54 -0
  7. data/doc/migration.rdoc +15 -538
  8. data/doc/model_hooks.rdoc +64 -27
  9. data/doc/opening_databases.rdoc +37 -10
  10. data/doc/prepared_statements.rdoc +16 -10
  11. data/doc/reflection.rdoc +8 -2
  12. data/doc/release_notes/3.22.0.txt +39 -0
  13. data/doc/release_notes/3.23.0.txt +172 -0
  14. data/doc/release_notes/3.24.0.txt +420 -0
  15. data/doc/release_notes/3.25.0.txt +88 -0
  16. data/doc/release_notes/3.26.0.txt +88 -0
  17. data/doc/release_notes/3.27.0.txt +82 -0
  18. data/doc/release_notes/3.28.0.txt +304 -0
  19. data/doc/schema_modification.rdoc +547 -0
  20. data/doc/testing.rdoc +106 -0
  21. data/doc/transactions.rdoc +97 -0
  22. data/doc/virtual_rows.rdoc +2 -2
  23. data/lib/sequel/adapters/ado.rb +12 -1
  24. data/lib/sequel/adapters/amalgalite.rb +4 -0
  25. data/lib/sequel/adapters/db2.rb +95 -58
  26. data/lib/sequel/adapters/do.rb +12 -0
  27. data/lib/sequel/adapters/firebird.rb +25 -203
  28. data/lib/sequel/adapters/ibmdb.rb +440 -0
  29. data/lib/sequel/adapters/informix.rb +4 -19
  30. data/lib/sequel/adapters/jdbc/as400.rb +0 -7
  31. data/lib/sequel/adapters/jdbc/db2.rb +49 -0
  32. data/lib/sequel/adapters/jdbc/firebird.rb +34 -0
  33. data/lib/sequel/adapters/jdbc/h2.rb +16 -5
  34. data/lib/sequel/adapters/jdbc/informix.rb +31 -0
  35. data/lib/sequel/adapters/jdbc/jtds.rb +34 -0
  36. data/lib/sequel/adapters/jdbc/mssql.rb +0 -32
  37. data/lib/sequel/adapters/jdbc/mysql.rb +9 -0
  38. data/lib/sequel/adapters/jdbc/oracle.rb +2 -27
  39. data/lib/sequel/adapters/jdbc/postgresql.rb +6 -0
  40. data/lib/sequel/adapters/jdbc/sqlserver.rb +46 -0
  41. data/lib/sequel/adapters/jdbc/transactions.rb +34 -0
  42. data/lib/sequel/adapters/jdbc.rb +62 -29
  43. data/lib/sequel/adapters/mysql.rb +22 -139
  44. data/lib/sequel/adapters/mysql2.rb +9 -14
  45. data/lib/sequel/adapters/odbc/db2.rb +21 -0
  46. data/lib/sequel/adapters/odbc.rb +15 -3
  47. data/lib/sequel/adapters/oracle.rb +17 -1
  48. data/lib/sequel/adapters/postgres.rb +111 -16
  49. data/lib/sequel/adapters/shared/access.rb +21 -0
  50. data/lib/sequel/adapters/shared/db2.rb +290 -0
  51. data/lib/sequel/adapters/shared/firebird.rb +214 -0
  52. data/lib/sequel/adapters/shared/informix.rb +45 -0
  53. data/lib/sequel/adapters/shared/mssql.rb +85 -47
  54. data/lib/sequel/adapters/shared/mysql.rb +50 -7
  55. data/lib/sequel/adapters/shared/mysql_prepared_statements.rb +143 -0
  56. data/lib/sequel/adapters/shared/oracle.rb +0 -4
  57. data/lib/sequel/adapters/shared/postgres.rb +75 -43
  58. data/lib/sequel/adapters/shared/sqlite.rb +56 -8
  59. data/lib/sequel/adapters/sqlite.rb +12 -11
  60. data/lib/sequel/adapters/swift/mysql.rb +9 -0
  61. data/lib/sequel/adapters/tinytds.rb +139 -7
  62. data/lib/sequel/adapters/utils/emulate_offset_with_row_number.rb +55 -0
  63. data/lib/sequel/ast_transformer.rb +190 -0
  64. data/lib/sequel/connection_pool/threaded.rb +3 -2
  65. data/lib/sequel/connection_pool.rb +1 -1
  66. data/lib/sequel/core.rb +6 -5
  67. data/lib/sequel/database/connecting.rb +5 -5
  68. data/lib/sequel/database/dataset.rb +1 -1
  69. data/lib/sequel/database/dataset_defaults.rb +1 -1
  70. data/lib/sequel/database/logging.rb +1 -1
  71. data/lib/sequel/database/misc.rb +38 -17
  72. data/lib/sequel/database/query.rb +50 -19
  73. data/lib/sequel/database/schema_generator.rb +8 -5
  74. data/lib/sequel/database/schema_methods.rb +52 -27
  75. data/lib/sequel/dataset/actions.rb +167 -48
  76. data/lib/sequel/dataset/features.rb +57 -8
  77. data/lib/sequel/dataset/graph.rb +1 -1
  78. data/lib/sequel/dataset/misc.rb +39 -20
  79. data/lib/sequel/dataset/mutation.rb +3 -3
  80. data/lib/sequel/dataset/prepared_statements.rb +29 -14
  81. data/lib/sequel/dataset/query.rb +182 -32
  82. data/lib/sequel/dataset/sql.rb +31 -58
  83. data/lib/sequel/dataset.rb +8 -0
  84. data/lib/sequel/exceptions.rb +4 -0
  85. data/lib/sequel/extensions/columns_introspection.rb +61 -0
  86. data/lib/sequel/extensions/migration.rb +6 -4
  87. data/lib/sequel/extensions/to_dot.rb +95 -83
  88. data/lib/sequel/model/associations.rb +893 -309
  89. data/lib/sequel/model/base.rb +302 -105
  90. data/lib/sequel/model/errors.rb +1 -1
  91. data/lib/sequel/model/exceptions.rb +5 -1
  92. data/lib/sequel/model.rb +13 -7
  93. data/lib/sequel/plugins/association_pks.rb +22 -4
  94. data/lib/sequel/plugins/defaults_setter.rb +58 -0
  95. data/lib/sequel/plugins/identity_map.rb +113 -6
  96. data/lib/sequel/plugins/many_through_many.rb +67 -5
  97. data/lib/sequel/plugins/prepared_statements.rb +140 -0
  98. data/lib/sequel/plugins/prepared_statements_associations.rb +84 -0
  99. data/lib/sequel/plugins/prepared_statements_safe.rb +82 -0
  100. data/lib/sequel/plugins/prepared_statements_with_pk.rb +59 -0
  101. data/lib/sequel/plugins/serialization_modification_detection.rb +51 -0
  102. data/lib/sequel/plugins/sharding.rb +12 -20
  103. data/lib/sequel/plugins/single_table_inheritance.rb +2 -0
  104. data/lib/sequel/plugins/update_primary_key.rb +1 -1
  105. data/lib/sequel/plugins/xml_serializer.rb +3 -3
  106. data/lib/sequel/sql.rb +107 -51
  107. data/lib/sequel/timezones.rb +12 -3
  108. data/lib/sequel/version.rb +1 -1
  109. data/spec/adapters/db2_spec.rb +146 -0
  110. data/spec/adapters/mssql_spec.rb +36 -0
  111. data/spec/adapters/mysql_spec.rb +36 -19
  112. data/spec/adapters/postgres_spec.rb +115 -28
  113. data/spec/adapters/spec_helper.rb +6 -0
  114. data/spec/adapters/sqlite_spec.rb +11 -0
  115. data/spec/core/connection_pool_spec.rb +62 -77
  116. data/spec/core/database_spec.rb +244 -287
  117. data/spec/core/dataset_spec.rb +383 -34
  118. data/spec/core/expression_filters_spec.rb +159 -41
  119. data/spec/core/schema_spec.rb +326 -3
  120. data/spec/core/spec_helper.rb +45 -0
  121. data/spec/extensions/association_pks_spec.rb +38 -0
  122. data/spec/extensions/columns_introspection_spec.rb +91 -0
  123. data/spec/extensions/defaults_setter_spec.rb +64 -0
  124. data/spec/extensions/identity_map_spec.rb +162 -0
  125. data/spec/extensions/many_through_many_spec.rb +195 -20
  126. data/spec/extensions/migration_spec.rb +17 -17
  127. data/spec/extensions/nested_attributes_spec.rb +1 -0
  128. data/spec/extensions/prepared_statements_associations_spec.rb +126 -0
  129. data/spec/extensions/prepared_statements_safe_spec.rb +79 -0
  130. data/spec/extensions/prepared_statements_spec.rb +72 -0
  131. data/spec/extensions/prepared_statements_with_pk_spec.rb +38 -0
  132. data/spec/extensions/schema_dumper_spec.rb +2 -2
  133. data/spec/extensions/schema_spec.rb +12 -20
  134. data/spec/extensions/serialization_modification_detection_spec.rb +36 -0
  135. data/spec/extensions/single_table_inheritance_spec.rb +11 -0
  136. data/spec/extensions/spec_helper.rb +3 -1
  137. data/spec/extensions/to_dot_spec.rb +3 -5
  138. data/spec/extensions/xml_serializer_spec.rb +16 -4
  139. data/spec/integration/associations_test.rb +405 -15
  140. data/spec/integration/database_test.rb +4 -2
  141. data/spec/integration/dataset_test.rb +240 -20
  142. data/spec/integration/plugin_test.rb +142 -5
  143. data/spec/integration/prepared_statement_test.rb +174 -95
  144. data/spec/integration/schema_test.rb +128 -16
  145. data/spec/integration/spec_helper.rb +15 -0
  146. data/spec/integration/transaction_test.rb +40 -0
  147. data/spec/integration/type_test.rb +16 -2
  148. data/spec/model/association_reflection_spec.rb +91 -0
  149. data/spec/model/associations_spec.rb +476 -5
  150. data/spec/model/base_spec.rb +91 -1
  151. data/spec/model/eager_loading_spec.rb +519 -31
  152. data/spec/model/hooks_spec.rb +161 -0
  153. data/spec/model/model_spec.rb +89 -2
  154. data/spec/model/plugins_spec.rb +17 -0
  155. data/spec/model/record_spec.rb +184 -12
  156. data/spec/model/spec_helper.rb +5 -0
  157. data/spec/model/validations_spec.rb +11 -0
  158. 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
- TRANSACTION_BEGIN = 'Transaction.begin'.freeze
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
+
@@ -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/mssql'
57
- db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
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/mssql'
62
- db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
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
- ts = []
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
- # Whether or not JNDI is being used for this connection.
268
- def jndi?
269
- !!(uri =~ JNDI_URI_REGEXP)
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
- # Gets the connection from JNDI.
337
- def get_connection_from_jndi
338
- jndi_name = JNDI_URI_REGEXP.match(uri)[1]
339
- JavaxNaming::InitialContext.new.lookup(jndi_name).connection
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 nil
409
- cps.setNull(i, JavaSQL::Types::NULL)
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, Java::JavaSQL::Time
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/mysql utils/stored_procedures', 'adapters'
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 decimal(s) ::BigDecimal.new(s) end
19
- def date(s) ::Sequel.string_to_date(s) end
20
- def time(s) ::Sequel.string_to_time(s) end
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] => tt.method(:decimal),
31
- [2, 3, 8, 9, 13, 247, 248] => tt.method(:integer),
32
- [4, 5] => tt.method(:float),
33
- [249, 250, 251, 252] => tt.method(:blob)
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] = TYPE_TRANSLATOR.method(v == false ? :time : :time_conv)
58
- m = TYPE_TRANSLATOR.method(v == false ? :date : :date_conv)
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 = TYPE_TRANSLATOR.method(v == false ? :timestamp : :timestamp_conv)
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
- class << conn
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, :disconnect=>MYSQL_DATABASE_DISCONNECT_ERRORS.match(e.message))
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 StoredProcedures
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)