sequel 3.10.0 → 3.11.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 (87) hide show
  1. data/CHANGELOG +68 -0
  2. data/COPYING +1 -1
  3. data/README.rdoc +87 -27
  4. data/bin/sequel +2 -4
  5. data/doc/association_basics.rdoc +1383 -0
  6. data/doc/dataset_basics.rdoc +106 -0
  7. data/doc/opening_databases.rdoc +45 -16
  8. data/doc/querying.rdoc +210 -0
  9. data/doc/release_notes/3.11.0.txt +254 -0
  10. data/doc/virtual_rows.rdoc +217 -31
  11. data/lib/sequel/adapters/ado.rb +28 -12
  12. data/lib/sequel/adapters/ado/mssql.rb +33 -1
  13. data/lib/sequel/adapters/amalgalite.rb +13 -8
  14. data/lib/sequel/adapters/db2.rb +1 -2
  15. data/lib/sequel/adapters/dbi.rb +7 -4
  16. data/lib/sequel/adapters/do.rb +14 -15
  17. data/lib/sequel/adapters/do/postgres.rb +4 -5
  18. data/lib/sequel/adapters/do/sqlite.rb +9 -0
  19. data/lib/sequel/adapters/firebird.rb +5 -10
  20. data/lib/sequel/adapters/informix.rb +2 -4
  21. data/lib/sequel/adapters/jdbc.rb +111 -49
  22. data/lib/sequel/adapters/jdbc/mssql.rb +1 -2
  23. data/lib/sequel/adapters/jdbc/mysql.rb +11 -0
  24. data/lib/sequel/adapters/jdbc/oracle.rb +4 -7
  25. data/lib/sequel/adapters/jdbc/postgresql.rb +8 -1
  26. data/lib/sequel/adapters/jdbc/sqlite.rb +12 -0
  27. data/lib/sequel/adapters/mysql.rb +14 -5
  28. data/lib/sequel/adapters/odbc.rb +2 -4
  29. data/lib/sequel/adapters/odbc/mssql.rb +2 -4
  30. data/lib/sequel/adapters/openbase.rb +1 -2
  31. data/lib/sequel/adapters/oracle.rb +4 -8
  32. data/lib/sequel/adapters/postgres.rb +4 -11
  33. data/lib/sequel/adapters/shared/mssql.rb +22 -9
  34. data/lib/sequel/adapters/shared/mysql.rb +33 -30
  35. data/lib/sequel/adapters/shared/oracle.rb +0 -5
  36. data/lib/sequel/adapters/shared/postgres.rb +13 -11
  37. data/lib/sequel/adapters/shared/sqlite.rb +56 -10
  38. data/lib/sequel/adapters/sqlite.rb +16 -9
  39. data/lib/sequel/connection_pool.rb +6 -1
  40. data/lib/sequel/connection_pool/single.rb +1 -0
  41. data/lib/sequel/core.rb +6 -1
  42. data/lib/sequel/database.rb +52 -23
  43. data/lib/sequel/database/schema_generator.rb +6 -0
  44. data/lib/sequel/database/schema_methods.rb +5 -5
  45. data/lib/sequel/database/schema_sql.rb +1 -1
  46. data/lib/sequel/dataset.rb +4 -190
  47. data/lib/sequel/dataset/actions.rb +323 -1
  48. data/lib/sequel/dataset/features.rb +18 -2
  49. data/lib/sequel/dataset/graph.rb +7 -0
  50. data/lib/sequel/dataset/misc.rb +119 -0
  51. data/lib/sequel/dataset/mutation.rb +64 -0
  52. data/lib/sequel/dataset/prepared_statements.rb +6 -0
  53. data/lib/sequel/dataset/query.rb +272 -6
  54. data/lib/sequel/dataset/sql.rb +186 -394
  55. data/lib/sequel/model.rb +4 -2
  56. data/lib/sequel/model/associations.rb +31 -14
  57. data/lib/sequel/model/base.rb +32 -13
  58. data/lib/sequel/model/exceptions.rb +8 -4
  59. data/lib/sequel/model/plugins.rb +3 -13
  60. data/lib/sequel/plugins/active_model.rb +26 -7
  61. data/lib/sequel/plugins/instance_filters.rb +98 -0
  62. data/lib/sequel/plugins/many_through_many.rb +1 -1
  63. data/lib/sequel/plugins/optimistic_locking.rb +25 -9
  64. data/lib/sequel/version.rb +1 -1
  65. data/spec/adapters/mssql_spec.rb +26 -0
  66. data/spec/adapters/mysql_spec.rb +33 -4
  67. data/spec/adapters/postgres_spec.rb +24 -1
  68. data/spec/adapters/spec_helper.rb +6 -0
  69. data/spec/adapters/sqlite_spec.rb +28 -0
  70. data/spec/core/connection_pool_spec.rb +17 -5
  71. data/spec/core/database_spec.rb +101 -1
  72. data/spec/core/dataset_spec.rb +42 -4
  73. data/spec/core/schema_spec.rb +13 -0
  74. data/spec/extensions/active_model_spec.rb +34 -11
  75. data/spec/extensions/caching_spec.rb +2 -0
  76. data/spec/extensions/instance_filters_spec.rb +55 -0
  77. data/spec/extensions/spec_helper.rb +2 -0
  78. data/spec/integration/dataset_test.rb +12 -1
  79. data/spec/integration/model_test.rb +12 -0
  80. data/spec/integration/plugin_test.rb +61 -1
  81. data/spec/integration/schema_test.rb +14 -3
  82. data/spec/model/base_spec.rb +27 -0
  83. data/spec/model/plugins_spec.rb +0 -22
  84. data/spec/model/record_spec.rb +32 -1
  85. data/spec/model/spec_helper.rb +2 -0
  86. metadata +14 -3
  87. data/lib/sequel/dataset/convenience.rb +0 -326
@@ -26,6 +26,11 @@ module Sequel
26
26
  when 'mssql'
27
27
  Sequel.ts_require 'adapters/shared/mssql'
28
28
  extend Sequel::MSSQL::DatabaseMethods
29
+ def self.dataset(*args)
30
+ ds = super
31
+ ds.extend Sequel::MSSQL::DatasetMethods
32
+ ds
33
+ end
29
34
  end
30
35
  end
31
36
 
@@ -63,17 +68,15 @@ module Sequel
63
68
  end
64
69
 
65
70
  def execute(sql, opts={})
66
- log_info(sql)
67
71
  synchronize(opts[:server]) do |conn|
68
- r = conn.execute(sql)
72
+ r = log_yield(sql){conn.execute(sql)}
69
73
  yield(r) if block_given?
70
74
  r
71
75
  end
72
76
  end
73
77
 
74
78
  def do(sql, opts={})
75
- log_info(sql)
76
- synchronize(opts[:server]){|conn| conn.do(sql)}
79
+ synchronize(opts[:server]){|conn| log_yield(sql){conn.do(sql)}}
77
80
  end
78
81
  alias_method :execute_dui, :do
79
82
 
@@ -66,11 +66,10 @@ module Sequel
66
66
  # Otherwise, the return value is the insert id if opts[:type] is :insert,
67
67
  # or the number of affected rows, otherwise.
68
68
  def execute(sql, opts={})
69
- log_info(sql)
70
69
  synchronize(opts[:server]) do |conn|
71
70
  begin
72
71
  command = conn.create_command(sql)
73
- res = block_given? ? command.execute_reader : command.execute_non_query
72
+ res = log_yield(sql){block_given? ? command.execute_reader : command.execute_non_query}
74
73
  rescue ::DataObjects::Error => e
75
74
  raise_error(e)
76
75
  end
@@ -121,21 +120,23 @@ module Sequel
121
120
  # substitute our own.
122
121
  def begin_transaction(conn)
123
122
  return super if supports_savepoints?
124
- log_info(TRANSACTION_BEGIN)
125
- t = ::DataObjects::Transaction.create_for_uri(uri)
126
- t.instance_variable_get(:@connection).close
127
- t.instance_variable_set(:@connection, conn)
128
- t.begin
129
- t
123
+ log_yield(TRANSACTION_BEGIN) do
124
+ t = ::DataObjects::Transaction.create_for_uri(uri)
125
+ t.instance_variable_get(:@connection).close
126
+ t.instance_variable_set(:@connection, conn)
127
+ t.begin
128
+ t
129
+ end
130
130
  end
131
131
 
132
132
  # DataObjects requires transactions be prepared before being
133
133
  # committed, so we do that.
134
134
  def commit_transaction(t)
135
135
  return super if supports_savepoints?
136
- log_info(TRANSACTION_ROLLBACK)
137
- t.prepare
138
- t.commit
136
+ log_yield(TRANSACTION_ROLLBACK) do
137
+ t.prepare
138
+ t.commit
139
+ end
139
140
  end
140
141
 
141
142
  # Method to call on a statement object to execute SQL that does
@@ -151,15 +152,13 @@ module Sequel
151
152
 
152
153
  # Execute SQL on the connection by creating a command first
153
154
  def log_connection_execute(conn, sql)
154
- log_info(sql)
155
- conn.create_command(sql).execute_non_query
155
+ log_yield(sql){conn.create_command(sql).execute_non_query}
156
156
  end
157
157
 
158
158
  # We use the transactions rollback method to rollback.
159
159
  def rollback_transaction(t)
160
160
  return super if supports_savepoints?
161
- log_info(TRANSACTION_COMMIT)
162
- t.rollback
161
+ log_yield(TRANSACTION_COMMIT){t.rollback}
163
162
  end
164
163
 
165
164
  # Allow extending the given connection when it is first created.
@@ -19,13 +19,12 @@ module Sequel
19
19
  begin
20
20
  if block_given?
21
21
  begin
22
- reader = command.execute_reader
23
- yield(reader)
22
+ yield(reader = @db.log_yield(sql){command.execute_reader})
24
23
  ensure
25
24
  reader.close if reader
26
25
  end
27
26
  else
28
- command.execute_non_query
27
+ @db.log_yield(sql){command.execute_non_query}
29
28
  end
30
29
  rescue ::DataObjects::Error => e
31
30
  raise_error(e)
@@ -64,9 +63,9 @@ module Sequel
64
63
  # Run the INSERT sql on the database and return the primary key
65
64
  # for the record.
66
65
  def execute_insert(sql, opts={})
67
- log_info(sql)
68
66
  synchronize(opts[:server]) do |conn|
69
- conn.create_command(sql).execute_non_query
67
+ com = conn.create_command(sql)
68
+ log_yield(sql){com.execute_non_query}
70
69
  insert_result(conn, opts[:table], opts[:values])
71
70
  end
72
71
  end
@@ -20,6 +20,15 @@ module Sequel
20
20
  o = super
21
21
  uri == 'sqlite3::memory:' ? o.merge(:max_connections=>1) : o
22
22
  end
23
+
24
+ # Execute the connection pragmas on the connection
25
+ def setup_connection(conn)
26
+ connection_pragmas.each do |s|
27
+ com = conn.create_command(s)
28
+ log_yield(s){com.execute_non_query}
29
+ end
30
+ super
31
+ end
23
32
  end
24
33
 
25
34
  # Dataset class for SQLite datasets accessed via DataObjects.
@@ -43,15 +43,13 @@ module Sequel
43
43
  end
44
44
 
45
45
  def execute(sql, opts={})
46
- log_info(sql)
47
46
  begin
48
47
  synchronize(opts[:server]) do |conn|
49
- r = conn.execute(sql)
48
+ r = log_yield(sql){conn.execute(sql)}
50
49
  yield(r) if block_given?
51
50
  r
52
51
  end
53
52
  rescue => e
54
- log_info(e.message)
55
53
  raise_error(e, :classes=>[Fb::Error])
56
54
  end
57
55
  end
@@ -105,14 +103,12 @@ module Sequel
105
103
  end
106
104
 
107
105
  def begin_transaction(conn)
108
- log_info(TRANSACTION_BEGIN)
109
- conn.transaction
106
+ log_yield(TRANSACTION_BEGIN){conn.transaction}
110
107
  conn
111
108
  end
112
109
 
113
110
  def commit_transaction(conn)
114
- log_info(TRANSACTION_COMMIT)
115
- conn.commit
111
+ log_yield(TRANSACTION_COMMIT){conn.commit}
116
112
  end
117
113
 
118
114
  def create_sequence_sql(name, opts={})
@@ -160,7 +156,7 @@ module Sequel
160
156
  events = opts[:events] ? Array(opts[:events]) : [:insert, :update, :delete]
161
157
  whence = opts[:after] ? 'AFTER' : 'BEFORE'
162
158
  inactive = opts[:inactive] ? 'INACTIVE' : 'ACTIVE'
163
- position = opts[:position] ? opts[:position] : 0
159
+ position = opts.fetch(:position, 0)
164
160
  sql = <<-end_sql
165
161
  CREATE TRIGGER #{quote_identifier(name)} for #{quote_identifier(table)}
166
162
  #{inactive} #{whence} #{events.map{|e| e.to_s.upcase}.join(' OR ')} position #{position}
@@ -187,8 +183,7 @@ module Sequel
187
183
  end
188
184
 
189
185
  def rollback_transaction(conn)
190
- log_info(TRANSACTION_ROLLBACK)
191
- conn.rollback
186
+ log_yield(TRANSACTION_ROLLBACK){conn.rollback}
192
187
  end
193
188
 
194
189
  def type_literal_generic_string(column)
@@ -18,14 +18,12 @@ module Sequel
18
18
 
19
19
  # Returns number of rows affected
20
20
  def execute_dui(sql, opts={})
21
- log_info(sql)
22
- synchronize(opts[:server]){|c| c.immediate(sql)}
21
+ synchronize(opts[:server]){|c| log_yield(sql){c.immediate(sql)}}
23
22
  end
24
23
  alias_method :do, :execute_dui
25
24
 
26
25
  def execute(sql, opts={})
27
- log_info(sql)
28
- synchronize(opts[:server]){|c| yield c.cursor(sql)}
26
+ synchronize(opts[:server]){|c| yield log_yield(sql){c.cursor(sql)}}
29
27
  end
30
28
  alias_method :query, :execute
31
29
 
@@ -3,24 +3,6 @@ Sequel.require 'adapters/utils/stored_procedures'
3
3
 
4
4
  module Sequel
5
5
  # Houses Sequel's JDBC support when running on JRuby.
6
- # Support for individual database types is done using sub adapters.
7
- # PostgreSQL, MySQL, SQLite, Oracle, and MSSQL all have relatively good support,
8
- # close the the level supported by the native adapter.
9
- # PostgreSQL, MySQL, SQLite can load necessary support using
10
- # the jdbc-* gem, if it is installed, though they will work if you
11
- # have the correct .jar in your CLASSPATH. Oracle and MSSQL should
12
- # load the necessary support if you have the .jar in your CLASSPATH.
13
- # For all other databases, the Java class should be loaded manually
14
- # before calling Sequel.connect.
15
- #
16
- # Note that when using a JDBC adapter, the best way to use Sequel
17
- # is via Sequel.connect, NOT Sequel.jdbc. Use the JDBC connection
18
- # string when connecting, which will be in a different format than
19
- # the native connection string. The connection string should start
20
- # with 'jdbc:'. For PostgreSQL, use 'jdbc:postgresql:', and for
21
- # SQLite you do not need 2 preceding slashes for the database name
22
- # (use no preceding slashes for a relative path, and one preceding
23
- # slash for an absolute path).
24
6
  module JDBC
25
7
  # Make it accesing the java.lang hierarchy more ruby friendly.
26
8
  module JavaLang
@@ -31,6 +13,19 @@ module Sequel
31
13
  module JavaSQL
32
14
  include_package 'java.sql'
33
15
  end
16
+
17
+ # Make it accesing the javax.naming hierarchy more ruby friendly.
18
+ module JavaxNaming
19
+ include_package 'javax.naming'
20
+ end
21
+
22
+ # Used to identify a jndi connection and to extract the jndi
23
+ # resource name.
24
+ JNDI_URI_REGEXP = /\Ajdbc:jndi:(.+)/
25
+
26
+ # The types to check for 0 scale to transform :decimal types
27
+ # to :integer.
28
+ DECIMAL_TYPE_RE = /number|numeric|decimal/io
34
29
 
35
30
  # Contains procs keyed on sub adapter type that extend the
36
31
  # given database object so it supports the correct database type.
@@ -62,6 +57,12 @@ module Sequel
62
57
  db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
63
58
  com.microsoft.sqlserver.jdbc.SQLServerDriver
64
59
  end,
60
+ :jtds=>proc do |db|
61
+ Sequel.ts_require 'adapters/jdbc/mssql'
62
+ db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
63
+ JDBC.load_gem('jtds')
64
+ Java::net.sourceforge.jtds.jdbc.Driver
65
+ end,
65
66
  :h2=>proc do |db|
66
67
  Sequel.ts_require 'adapters/jdbc/h2'
67
68
  db.extend(Sequel::JDBC::H2::DatabaseMethods)
@@ -93,6 +94,9 @@ module Sequel
93
94
  # The type of database we are connecting to
94
95
  attr_reader :database_type
95
96
 
97
+ # The Java database driver we are using
98
+ attr_reader :driver
99
+
96
100
  # Whether to convert some Java types to ruby types when retrieving rows.
97
101
  # True by default, can be set to false to roughly double performance when
98
102
  # fetching rows.
@@ -104,11 +108,14 @@ module Sequel
104
108
  # uri, since JDBC requires one.
105
109
  def initialize(opts)
106
110
  super
107
- @convert_types = @opts.include?(:convert_types) ? typecast_value_boolean(@opts[:convert_types]) : true
111
+ @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
108
112
  raise(Error, "No connection string specified") unless uri
109
- if match = /\Ajdbc:([^:]+)/.match(uri) and prok = DATABASE_SETUP[match[1].to_sym]
110
- prok.call(self)
111
- end
113
+
114
+ resolved_uri = jndi? ? get_uri_from_jndi : uri
115
+
116
+ if match = /\Ajdbc:([^:]+)/.match(resolved_uri) and prok = DATABASE_SETUP[match[1].to_sym]
117
+ @driver = prok.call(self)
118
+ end
112
119
  end
113
120
 
114
121
  # Execute the given stored procedure with the give name. If a block is
@@ -124,14 +131,14 @@ module Sequel
124
131
 
125
132
  begin
126
133
  if block_given?
127
- yield cps.executeQuery
134
+ yield log_yield(sql){cps.executeQuery}
128
135
  else
129
136
  case opts[:type]
130
137
  when :insert
131
- cps.executeUpdate
138
+ log_yield(sql){cps.executeUpdate}
132
139
  last_insert_id(conn, opts)
133
140
  else
134
- cps.executeUpdate
141
+ log_yield(sql){cps.executeUpdate}
135
142
  end
136
143
  end
137
144
  rescue NativeException, JavaSQL::SQLException => e
@@ -141,12 +148,29 @@ module Sequel
141
148
  end
142
149
  end
143
150
  end
144
-
151
+
145
152
  # Connect to the database using JavaSQL::DriverManager.getConnection.
146
153
  def connect(server)
147
- args = [uri(server_opts(server))]
148
- args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
149
- setup_connection(JavaSQL::DriverManager.getConnection(*args))
154
+ opts = server_opts(server)
155
+ conn = if jndi?
156
+ get_connection_from_jndi
157
+ else
158
+ args = [uri(opts)]
159
+ args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
160
+ begin
161
+ JavaSQL::DriverManager.getConnection(*args)
162
+ rescue
163
+ # If the DriverManager can't get the connection - use the connect
164
+ # method of the driver. (This happens under Tomcat for instance)
165
+ props = java.util.Properties.new
166
+ if opts && opts[:user] && opts[:password]
167
+ props.setProperty("user", opts[:user])
168
+ props.setProperty("password", opts[:password])
169
+ end
170
+ driver.new.connect(args[0], props)
171
+ end
172
+ end
173
+ setup_connection(conn)
150
174
  end
151
175
 
152
176
  # Return instances of JDBC::Dataset with the given opts.
@@ -159,21 +183,26 @@ module Sequel
159
183
  def execute(sql, opts={}, &block)
160
184
  return call_sproc(sql, opts, &block) if opts[:sproc]
161
185
  return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
162
- log_info(sql)
163
186
  synchronize(opts[:server]) do |conn|
164
187
  stmt = conn.createStatement
165
188
  begin
166
189
  if block_given?
167
- yield stmt.executeQuery(sql)
190
+ yield log_yield(sql){stmt.executeQuery(sql)}
168
191
  else
169
192
  case opts[:type]
170
193
  when :ddl
171
- stmt.execute(sql)
194
+ log_yield(sql){stmt.execute(sql)}
172
195
  when :insert
173
- stmt.executeUpdate(sql)
196
+ log_yield(sql) do
197
+ if requires_return_generated_keys?
198
+ stmt.executeUpdate(sql, JavaSQL::Statement.RETURN_GENERATED_KEYS)
199
+ else
200
+ stmt.executeUpdate(sql)
201
+ end
202
+ end
174
203
  last_insert_id(conn, opts.merge(:stmt=>stmt))
175
204
  else
176
- stmt.executeUpdate(sql)
205
+ log_yield(sql){stmt.executeUpdate(sql)}
177
206
  end
178
207
  end
179
208
  rescue NativeException, JavaSQL::SQLException => e
@@ -235,9 +264,14 @@ module Sequel
235
264
  ur = opts[:uri] || opts[:url] || opts[:database]
236
265
  ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
237
266
  end
267
+
268
+ # Whether or not JNDI is being used for this connection.
269
+ def jndi?
270
+ !!(uri =~ JNDI_URI_REGEXP)
271
+ end
238
272
 
239
273
  private
240
-
274
+
241
275
  # JDBC uses a statement object to execute SQL on the database
242
276
  def begin_transaction(conn)
243
277
  conn = conn.createStatement unless supports_savepoints?
@@ -269,29 +303,25 @@ module Sequel
269
303
  if name and cps = conn.prepared_statements[name] and cps[0] == sql
270
304
  cps = cps[1]
271
305
  else
272
- if cps
273
- log_info("Closing #{name}")
274
- cps[1].close
275
- end
276
- log_info("Preparing#{" #{name}:" if name} #{sql}")
277
- cps = conn.prepareStatement(sql)
306
+ log_yield("Closing #{name}"){cps[1].close} if cps
307
+ cps = log_yield("Preparing#{" #{name}:" if name} #{sql}"){conn.prepareStatement(sql)}
278
308
  conn.prepared_statements[name] = [sql, cps] if name
279
309
  end
280
310
  i = 0
281
311
  args.each{|arg| set_ps_arg(cps, arg, i+=1)}
282
- log_info("Executing#{" #{name}" if name}", args)
312
+ msg = "Executing#{" #{name}" if name}"
283
313
  begin
284
314
  if block_given?
285
- yield cps.executeQuery
315
+ yield log_yield(msg, args){cps.executeQuery}
286
316
  else
287
317
  case opts[:type]
288
318
  when :ddl
289
- cps.execute
319
+ log_yield(msg, args){cps.execute}
290
320
  when :insert
291
- cps.executeUpdate
321
+ log_yield(msg, args){cps.executeUpdate}
292
322
  last_insert_id(conn, opts.merge(:prepared=>true))
293
323
  else
294
- cps.executeUpdate
324
+ log_yield(msg, args){cps.executeUpdate}
295
325
  end
296
326
  end
297
327
  rescue NativeException, JavaSQL::SQLException => e
@@ -301,7 +331,21 @@ module Sequel
301
331
  end
302
332
  end
303
333
  end
334
+
335
+ # Gets the JDBC connection uri from the JNDI resource.
336
+ def get_uri_from_jndi
337
+ conn = get_connection_from_jndi
338
+ conn.meta_data.url
339
+ ensure
340
+ conn.close if conn
341
+ end
304
342
 
343
+ # Gets the connection from JNDI.
344
+ def get_connection_from_jndi
345
+ jndi_name = JNDI_URI_REGEXP.match(uri)[1]
346
+ JavaxNaming::InitialContext.new.lookup(jndi_name).connection
347
+ end
348
+
305
349
  # Support fractional seconds for Time objects used in bound variables
306
350
  def java_sql_timestamp(time)
307
351
  millis = time.to_i * 1000
@@ -319,7 +363,14 @@ module Sequel
319
363
 
320
364
  # Yield the metadata for this database
321
365
  def metadata(*args, &block)
322
- synchronize{|c| metadata_dataset.send(:process_result_set, c.getMetaData.send(*args), &block)}
366
+ synchronize do |c|
367
+ result = c.getMetaData.send(*args)
368
+ begin
369
+ metadata_dataset.send(:process_result_set, result, &block)
370
+ ensure
371
+ result.close
372
+ end
373
+ end
323
374
  end
324
375
 
325
376
  # Treat SQLExceptions with a "Connection Error" SQLState as disconnects
@@ -388,7 +439,11 @@ module Sequel
388
439
  pks << h[:column_name]
389
440
  end
390
441
  metadata(:getColumns, nil, schema, table, nil) do |h|
391
- ts << [m.call(h[:column_name]), {: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]}]
442
+ 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]}
443
+ if s[:db_type] =~ DECIMAL_TYPE_RE && s[:scale] == 0
444
+ s[:type] = :integer
445
+ end
446
+ ts << [m.call(h[:column_name]), s]
392
447
  end
393
448
  ts
394
449
  end
@@ -397,6 +452,13 @@ module Sequel
397
452
  def transaction_statement_object(conn)
398
453
  conn.createStatement
399
454
  end
455
+
456
+ # This method determines whether or not to add
457
+ # Statement.RETURN_GENERATED_KEYS as an argument when inserting rows.
458
+ # Sub-adapters that require this should override this method.
459
+ def requires_return_generated_keys?
460
+ false
461
+ end
400
462
  end
401
463
 
402
464
  class Dataset < Sequel::Dataset
@@ -498,7 +560,7 @@ module Sequel
498
560
  when Java::byte[]
499
561
  Sequel::SQL::Blob.new(String.from_java_bytes(v))
500
562
  when Java::JavaSQL::Blob
501
- convert_type(v.getBytes(0, v.length))
563
+ convert_type(v.getBytes(1, v.length))
502
564
  else
503
565
  v
504
566
  end