epugh-sequel 0.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (134) hide show
  1. data/README.rdoc +652 -0
  2. data/VERSION.yml +4 -0
  3. data/bin/sequel +104 -0
  4. data/lib/sequel.rb +1 -0
  5. data/lib/sequel/adapters/ado.rb +85 -0
  6. data/lib/sequel/adapters/db2.rb +132 -0
  7. data/lib/sequel/adapters/dbi.rb +101 -0
  8. data/lib/sequel/adapters/do.rb +197 -0
  9. data/lib/sequel/adapters/do/mysql.rb +38 -0
  10. data/lib/sequel/adapters/do/postgres.rb +92 -0
  11. data/lib/sequel/adapters/do/sqlite.rb +31 -0
  12. data/lib/sequel/adapters/firebird.rb +307 -0
  13. data/lib/sequel/adapters/informix.rb +75 -0
  14. data/lib/sequel/adapters/jdbc.rb +485 -0
  15. data/lib/sequel/adapters/jdbc/h2.rb +62 -0
  16. data/lib/sequel/adapters/jdbc/mysql.rb +56 -0
  17. data/lib/sequel/adapters/jdbc/oracle.rb +23 -0
  18. data/lib/sequel/adapters/jdbc/postgresql.rb +101 -0
  19. data/lib/sequel/adapters/jdbc/sqlite.rb +43 -0
  20. data/lib/sequel/adapters/mysql.rb +370 -0
  21. data/lib/sequel/adapters/odbc.rb +184 -0
  22. data/lib/sequel/adapters/openbase.rb +57 -0
  23. data/lib/sequel/adapters/oracle.rb +140 -0
  24. data/lib/sequel/adapters/postgres.rb +453 -0
  25. data/lib/sequel/adapters/shared/mssql.rb +93 -0
  26. data/lib/sequel/adapters/shared/mysql.rb +341 -0
  27. data/lib/sequel/adapters/shared/oracle.rb +62 -0
  28. data/lib/sequel/adapters/shared/postgres.rb +743 -0
  29. data/lib/sequel/adapters/shared/progress.rb +34 -0
  30. data/lib/sequel/adapters/shared/sqlite.rb +263 -0
  31. data/lib/sequel/adapters/sqlite.rb +243 -0
  32. data/lib/sequel/adapters/utils/date_format.rb +21 -0
  33. data/lib/sequel/adapters/utils/stored_procedures.rb +75 -0
  34. data/lib/sequel/adapters/utils/unsupported.rb +62 -0
  35. data/lib/sequel/connection_pool.rb +258 -0
  36. data/lib/sequel/core.rb +204 -0
  37. data/lib/sequel/core_sql.rb +185 -0
  38. data/lib/sequel/database.rb +687 -0
  39. data/lib/sequel/database/schema_generator.rb +324 -0
  40. data/lib/sequel/database/schema_methods.rb +164 -0
  41. data/lib/sequel/database/schema_sql.rb +324 -0
  42. data/lib/sequel/dataset.rb +422 -0
  43. data/lib/sequel/dataset/convenience.rb +237 -0
  44. data/lib/sequel/dataset/prepared_statements.rb +220 -0
  45. data/lib/sequel/dataset/sql.rb +1105 -0
  46. data/lib/sequel/deprecated.rb +529 -0
  47. data/lib/sequel/exceptions.rb +44 -0
  48. data/lib/sequel/extensions/blank.rb +42 -0
  49. data/lib/sequel/extensions/inflector.rb +288 -0
  50. data/lib/sequel/extensions/pagination.rb +96 -0
  51. data/lib/sequel/extensions/pretty_table.rb +78 -0
  52. data/lib/sequel/extensions/query.rb +48 -0
  53. data/lib/sequel/extensions/string_date_time.rb +47 -0
  54. data/lib/sequel/metaprogramming.rb +44 -0
  55. data/lib/sequel/migration.rb +212 -0
  56. data/lib/sequel/model.rb +142 -0
  57. data/lib/sequel/model/association_reflection.rb +263 -0
  58. data/lib/sequel/model/associations.rb +1024 -0
  59. data/lib/sequel/model/base.rb +911 -0
  60. data/lib/sequel/model/deprecated.rb +188 -0
  61. data/lib/sequel/model/deprecated_hooks.rb +103 -0
  62. data/lib/sequel/model/deprecated_inflector.rb +335 -0
  63. data/lib/sequel/model/deprecated_validations.rb +384 -0
  64. data/lib/sequel/model/errors.rb +37 -0
  65. data/lib/sequel/model/exceptions.rb +7 -0
  66. data/lib/sequel/model/inflections.rb +230 -0
  67. data/lib/sequel/model/plugins.rb +74 -0
  68. data/lib/sequel/object_graph.rb +230 -0
  69. data/lib/sequel/plugins/caching.rb +122 -0
  70. data/lib/sequel/plugins/hook_class_methods.rb +122 -0
  71. data/lib/sequel/plugins/schema.rb +53 -0
  72. data/lib/sequel/plugins/single_table_inheritance.rb +63 -0
  73. data/lib/sequel/plugins/validation_class_methods.rb +373 -0
  74. data/lib/sequel/sql.rb +854 -0
  75. data/lib/sequel/version.rb +11 -0
  76. data/lib/sequel_core.rb +1 -0
  77. data/lib/sequel_model.rb +1 -0
  78. data/spec/adapters/ado_spec.rb +46 -0
  79. data/spec/adapters/firebird_spec.rb +376 -0
  80. data/spec/adapters/informix_spec.rb +96 -0
  81. data/spec/adapters/mysql_spec.rb +875 -0
  82. data/spec/adapters/oracle_spec.rb +272 -0
  83. data/spec/adapters/postgres_spec.rb +692 -0
  84. data/spec/adapters/spec_helper.rb +10 -0
  85. data/spec/adapters/sqlite_spec.rb +550 -0
  86. data/spec/core/connection_pool_spec.rb +526 -0
  87. data/spec/core/core_ext_spec.rb +156 -0
  88. data/spec/core/core_sql_spec.rb +528 -0
  89. data/spec/core/database_spec.rb +1214 -0
  90. data/spec/core/dataset_spec.rb +3513 -0
  91. data/spec/core/expression_filters_spec.rb +363 -0
  92. data/spec/core/migration_spec.rb +261 -0
  93. data/spec/core/object_graph_spec.rb +280 -0
  94. data/spec/core/pretty_table_spec.rb +58 -0
  95. data/spec/core/schema_generator_spec.rb +167 -0
  96. data/spec/core/schema_spec.rb +778 -0
  97. data/spec/core/spec_helper.rb +82 -0
  98. data/spec/core/version_spec.rb +7 -0
  99. data/spec/extensions/blank_spec.rb +67 -0
  100. data/spec/extensions/caching_spec.rb +201 -0
  101. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  102. data/spec/extensions/inflector_spec.rb +122 -0
  103. data/spec/extensions/pagination_spec.rb +99 -0
  104. data/spec/extensions/pretty_table_spec.rb +91 -0
  105. data/spec/extensions/query_spec.rb +85 -0
  106. data/spec/extensions/schema_spec.rb +111 -0
  107. data/spec/extensions/single_table_inheritance_spec.rb +53 -0
  108. data/spec/extensions/spec_helper.rb +90 -0
  109. data/spec/extensions/string_date_time_spec.rb +93 -0
  110. data/spec/extensions/validation_class_methods_spec.rb +1054 -0
  111. data/spec/integration/dataset_test.rb +160 -0
  112. data/spec/integration/eager_loader_test.rb +683 -0
  113. data/spec/integration/prepared_statement_test.rb +130 -0
  114. data/spec/integration/schema_test.rb +183 -0
  115. data/spec/integration/spec_helper.rb +75 -0
  116. data/spec/integration/type_test.rb +96 -0
  117. data/spec/model/association_reflection_spec.rb +93 -0
  118. data/spec/model/associations_spec.rb +1780 -0
  119. data/spec/model/base_spec.rb +494 -0
  120. data/spec/model/caching_spec.rb +217 -0
  121. data/spec/model/dataset_methods_spec.rb +78 -0
  122. data/spec/model/eager_loading_spec.rb +1165 -0
  123. data/spec/model/hooks_spec.rb +472 -0
  124. data/spec/model/inflector_spec.rb +126 -0
  125. data/spec/model/model_spec.rb +588 -0
  126. data/spec/model/plugins_spec.rb +142 -0
  127. data/spec/model/record_spec.rb +1243 -0
  128. data/spec/model/schema_spec.rb +92 -0
  129. data/spec/model/spec_helper.rb +124 -0
  130. data/spec/model/validations_spec.rb +1080 -0
  131. data/spec/rcov.opts +6 -0
  132. data/spec/spec.opts +0 -0
  133. data/spec/spec_config.rb.example +10 -0
  134. metadata +202 -0
@@ -0,0 +1,75 @@
1
+ Sequel.require 'adapters/utils/unsupported'
2
+ require 'informix'
3
+
4
+ module Sequel
5
+ module Informix
6
+ class Database < Sequel::Database
7
+ set_adapter_scheme :informix
8
+
9
+ def connect(server)
10
+ opts = server_opts(server)
11
+ ::Informix.connect(opts[:database], opts[:user], opts[:password])
12
+ end
13
+
14
+ def dataset(opts = nil)
15
+ Sequel::Informix::Dataset.new(self, opts)
16
+ end
17
+
18
+ # Returns number of rows affected
19
+ def execute_dui(sql, opts={})
20
+ log_info(sql)
21
+ synchronize(opts[:server]){|c| c.immediate(sql)}
22
+ end
23
+ alias_method :do, :execute_dui
24
+
25
+ def execute(sql, opts={})
26
+ log_info(sql)
27
+ synchronize(opts[:server]){|c| yield c.cursor(sql)}
28
+ end
29
+ alias_method :query, :execute
30
+
31
+ private
32
+
33
+ def disconnect_connection(c)
34
+ c.close
35
+ end
36
+ end
37
+
38
+ class Dataset < Sequel::Dataset
39
+ include UnsupportedIntersectExcept
40
+
41
+ SELECT_CLAUSE_ORDER = %w'limit distinct columns from join where having group compounds order'.freeze
42
+
43
+ def fetch_rows(sql, &block)
44
+ execute(sql) do |cursor|
45
+ begin
46
+ col_map = nil
47
+ cursor.open.each_hash do |h|
48
+ unless col_map
49
+ col_map = {}
50
+ @columns = h.keys.map{|k| col_map[k] = output_identifier(k)}
51
+ end
52
+ h2 = {}
53
+ h.each{|k,v| h2[col_map[k]||k] = v}
54
+ yield h2
55
+ end
56
+ ensure
57
+ cursor.drop
58
+ end
59
+ end
60
+ self
61
+ end
62
+
63
+ private
64
+
65
+ def select_clause_order
66
+ SELECT_CLAUSE_ORDER
67
+ end
68
+
69
+ def select_limit_sql(sql, opts)
70
+ sql << " SKIP #{opts[:offset]}" if opts[:offset]
71
+ sql << " FIRST #{opts[:limit]}" if opts[:limit]
72
+ end
73
+ end
74
+ end
75
+ end
@@ -0,0 +1,485 @@
1
+ require 'java'
2
+ Sequel.require 'adapters/utils/stored_procedures'
3
+
4
+ module Sequel
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
+ module JDBC
25
+ # Make it accesing the java.lang hierarchy more ruby friendly.
26
+ module JavaLang
27
+ include_package 'java.lang'
28
+ end
29
+
30
+ # Make it accesing the java.sql hierarchy more ruby friendly.
31
+ module JavaSQL
32
+ include_package 'java.sql'
33
+ end
34
+
35
+ # Contains procs keyed on sub adapter type that extend the
36
+ # given database object so it supports the correct database type.
37
+ DATABASE_SETUP = {:postgresql=>proc do |db|
38
+ Sequel.require 'adapters/jdbc/postgresql'
39
+ db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
40
+ JDBC.load_gem('postgres')
41
+ org.postgresql.Driver
42
+ end,
43
+ :mysql=>proc do |db|
44
+ Sequel.require 'adapters/jdbc/mysql'
45
+ db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
46
+ JDBC.load_gem('mysql')
47
+ com.mysql.jdbc.Driver
48
+ end,
49
+ :sqlite=>proc do |db|
50
+ Sequel.require 'adapters/jdbc/sqlite'
51
+ db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
52
+ JDBC.load_gem('sqlite3')
53
+ org.sqlite.JDBC
54
+ end,
55
+ :oracle=>proc do |db|
56
+ Sequel.require 'adapters/jdbc/oracle'
57
+ db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
58
+ Java::oracle.jdbc.driver.OracleDriver
59
+ end,
60
+ :sqlserver=>proc do |db|
61
+ Sequel.require 'adapters/shared/mssql'
62
+ db.extend(Sequel::MSSQL::DatabaseMethods)
63
+ com.microsoft.sqlserver.jdbc.SQLServerDriver
64
+ end,
65
+ :h2=>proc do |db|
66
+ Sequel.require 'adapters/jdbc/h2'
67
+ db.extend(Sequel::JDBC::H2::DatabaseMethods)
68
+ JDBC.load_gem('h2')
69
+ org.h2.Driver
70
+ end
71
+ }
72
+
73
+ # Allowing loading the necessary JDBC support via a gem, which
74
+ # works for PostgreSQL, MySQL, and SQLite.
75
+ def self.load_gem(name)
76
+ begin
77
+ require "jdbc/#{name}"
78
+ rescue LoadError
79
+ # jdbc gem not used, hopefully the user has the .jar in their CLASSPATH
80
+ end
81
+ end
82
+
83
+ # JDBC Databases offer a fairly uniform interface that does not change
84
+ # much based on the sub adapter.
85
+ class Database < Sequel::Database
86
+ set_adapter_scheme :jdbc
87
+
88
+ # The type of database we are connecting to
89
+ attr_reader :database_type
90
+
91
+ # Call the DATABASE_SETUP proc directly after initialization,
92
+ # so the object always uses sub adapter specific code. Also,
93
+ # raise an error immediately if the connection doesn't have a
94
+ # uri, since JDBC requires one.
95
+ def initialize(opts)
96
+ @opts = opts
97
+ raise(Error, "No connection string specified") unless uri
98
+ if match = /\Ajdbc:([^:]+)/.match(uri) and prok = DATABASE_SETUP[match[1].to_sym]
99
+ prok.call(self)
100
+ end
101
+ super(opts)
102
+ end
103
+
104
+ # Execute the given stored procedure with the give name. If a block is
105
+ # given, the stored procedure should return rows.
106
+ def call_sproc(name, opts = {})
107
+ args = opts[:args] || []
108
+ sql = "{call #{name}(#{args.map{'?'}.join(',')})}"
109
+ synchronize(opts[:server]) do |conn|
110
+ cps = conn.prepareCall(sql)
111
+
112
+ i = 0
113
+ args.each{|arg| set_ps_arg(cps, arg, i+=1)}
114
+
115
+ begin
116
+ if block_given?
117
+ yield cps.executeQuery
118
+ else
119
+ case opts[:type]
120
+ when :insert
121
+ cps.executeUpdate
122
+ last_insert_id(conn, opts)
123
+ else
124
+ cps.executeUpdate
125
+ end
126
+ end
127
+ rescue NativeException, JavaSQL::SQLException => e
128
+ raise_error(e)
129
+ ensure
130
+ cps.close
131
+ end
132
+ end
133
+ end
134
+
135
+ # Connect to the database using JavaSQL::DriverManager.getConnection.
136
+ def connect(server)
137
+ setup_connection(JavaSQL::DriverManager.getConnection(uri(server_opts(server))))
138
+ end
139
+
140
+ # Return instances of JDBC::Dataset with the given opts.
141
+ def dataset(opts = nil)
142
+ JDBC::Dataset.new(self, opts)
143
+ end
144
+
145
+ # Execute the given SQL. If a block is given, if should be a SELECT
146
+ # statement or something else that returns rows.
147
+ def execute(sql, opts={}, &block)
148
+ return call_sproc(sql, opts, &block) if opts[:sproc]
149
+ return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
150
+ log_info(sql)
151
+ synchronize(opts[:server]) do |conn|
152
+ stmt = conn.createStatement
153
+ begin
154
+ if block_given?
155
+ yield stmt.executeQuery(sql)
156
+ else
157
+ case opts[:type]
158
+ when :ddl
159
+ stmt.execute(sql)
160
+ when :insert
161
+ stmt.executeUpdate(sql)
162
+ last_insert_id(conn, opts)
163
+ else
164
+ stmt.executeUpdate(sql)
165
+ end
166
+ end
167
+ rescue NativeException, JavaSQL::SQLException => e
168
+ raise_error(e)
169
+ ensure
170
+ stmt.close
171
+ end
172
+ end
173
+ end
174
+ alias execute_dui execute
175
+
176
+ # Execute the given DDL SQL, which should not return any
177
+ # values or rows.
178
+ def execute_ddl(sql, opts={})
179
+ execute(sql, {:type=>:ddl}.merge(opts))
180
+ end
181
+
182
+ # Execute the given INSERT SQL, returning the last inserted
183
+ # row id.
184
+ def execute_insert(sql, opts={})
185
+ execute(sql, {:type=>:insert}.merge(opts))
186
+ end
187
+
188
+ # All tables in this database
189
+ def tables
190
+ ts = []
191
+ ds = dataset
192
+ metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << ds.send(:output_identifier, h[:table_name])}
193
+ ts
194
+ end
195
+
196
+ # Default transaction method that should work on most JDBC
197
+ # databases. Does not use the JDBC transaction methods, uses
198
+ # SQL BEGIN/ROLLBACK/COMMIT statements instead.
199
+ def transaction(opts={})
200
+ unless opts.is_a?(Hash)
201
+ Deprecation.deprecate('Passing an argument other than a Hash to Database#transaction', "Use DB.transaction(:server=>#{opts.inspect})")
202
+ opts = {:server=>opts}
203
+ end
204
+ synchronize(opts[:server]) do |conn|
205
+ return yield(conn) if @transactions.include?(Thread.current)
206
+ stmt = conn.createStatement
207
+ begin
208
+ log_info(begin_transaction_sql)
209
+ stmt.execute(begin_transaction_sql)
210
+ @transactions << Thread.current
211
+ yield(conn)
212
+ rescue Exception => e
213
+ log_info(rollback_transaction_sql)
214
+ stmt.execute(rollback_transaction_sql)
215
+ transaction_error(e)
216
+ ensure
217
+ unless e
218
+ log_info(commit_transaction_sql)
219
+ stmt.execute(commit_transaction_sql)
220
+ end
221
+ stmt.close
222
+ @transactions.delete(Thread.current)
223
+ end
224
+ end
225
+ end
226
+
227
+ # The uri for this connection. You can specify the uri
228
+ # using the :uri, :url, or :database options. You don't
229
+ # need to worry about this if you use Sequel.connect
230
+ # with the JDBC connectrion strings.
231
+ def uri(opts={})
232
+ opts = @opts.merge(opts)
233
+ ur = opts[:uri] || opts[:url] || opts[:database]
234
+ ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
235
+ end
236
+
237
+ private
238
+
239
+ # The JDBC adapter should not need the pool to convert exceptions.
240
+ def connection_pool_default_options
241
+ super.merge(:pool_convert_exceptions=>false)
242
+ end
243
+
244
+ # Close given adapter connections
245
+ def disconnect_connection(c)
246
+ c.close
247
+ end
248
+
249
+ # Execute the prepared statement. If the provided name is a
250
+ # dataset, use that as the prepared statement, otherwise use
251
+ # it as a key to look it up in the prepared_statements hash.
252
+ # If the connection we are using has already prepared an identical
253
+ # statement, use that statement instead of creating another.
254
+ # Otherwise, prepare a new statement for the connection, bind the
255
+ # variables, and execute it.
256
+ def execute_prepared_statement(name, opts={})
257
+ args = opts[:arguments]
258
+ if Dataset === name
259
+ ps = name
260
+ name = ps.prepared_statement_name
261
+ else
262
+ ps = prepared_statements[name]
263
+ end
264
+ sql = ps.prepared_sql
265
+ synchronize(opts[:server]) do |conn|
266
+ if name and cps = conn.prepared_statements[name] and cps[0] == sql
267
+ cps = cps[1]
268
+ else
269
+ if cps
270
+ log_info("Closing #{name}")
271
+ cps[1].close
272
+ end
273
+ log_info("Preparing#{" #{name}:" if name} #{sql}")
274
+ cps = conn.prepareStatement(sql)
275
+ conn.prepared_statements[name] = [sql, cps] if name
276
+ end
277
+ i = 0
278
+ args.each{|arg| set_ps_arg(cps, arg, i+=1)}
279
+ log_info("Executing#{" #{name}" if name}", args)
280
+ begin
281
+ if block_given?
282
+ yield cps.executeQuery
283
+ else
284
+ case opts[:type]
285
+ when :ddl
286
+ cps.execute
287
+ when :insert
288
+ cps.executeUpdate
289
+ last_insert_id(conn, opts)
290
+ else
291
+ cps.executeUpdate
292
+ end
293
+ end
294
+ rescue NativeException, JavaSQL::SQLException => e
295
+ raise_error(e)
296
+ ensure
297
+ cps.close unless name
298
+ end
299
+ end
300
+ end
301
+
302
+ # By default, there is no support for determining the last inserted
303
+ # id, so return nil. This method should be overridden in
304
+ # sub adapters.
305
+ def last_insert_id(conn, opts)
306
+ nil
307
+ end
308
+
309
+ # Yield the metadata for this database
310
+ def metadata(*args, &block)
311
+ ds = dataset
312
+ ds.identifier_output_method = :downcase
313
+ synchronize{|c| ds.send(:process_result_set, c.getMetaData.send(*args), &block)}
314
+ end
315
+
316
+ # Java being java, you need to specify the type of each argument
317
+ # for the prepared statement, and bind it individually. This
318
+ # guesses which JDBC method to use, and hopefully JRuby will convert
319
+ # things properly for us.
320
+ def set_ps_arg(cps, arg, i)
321
+ case arg
322
+ when Integer
323
+ cps.setInt(i, arg)
324
+ when String
325
+ cps.setString(i, arg)
326
+ when Date, Java::JavaSql::Date
327
+ cps.setDate(i, arg)
328
+ when Time, DateTime, Java::JavaSql::Timestamp
329
+ cps.setTimestamp(i, arg)
330
+ when Float
331
+ cps.setDouble(i, arg)
332
+ when nil
333
+ cps.setNull(i, JavaSQL::Types::NULL)
334
+ end
335
+ end
336
+
337
+ # Add a prepared_statements accessor to the connection,
338
+ # and set it to an empty hash. This is used to store
339
+ # adapter specific prepared statements.
340
+ def setup_connection(conn)
341
+ class << conn
342
+ attr_accessor :prepared_statements
343
+ end
344
+ conn.prepared_statements = {}
345
+ conn
346
+ end
347
+
348
+ # All tables in this database
349
+ def schema_parse_table(table, opts={})
350
+ ds = dataset
351
+ schema, table = schema_and_table(table)
352
+ schema = ds.send(:input_identifier, schema) if schema
353
+ table = ds.send(:input_identifier, table)
354
+ pks, ts = [], []
355
+ metadata(:getPrimaryKeys, nil, schema, table) do |h|
356
+ pks << h[:column_name]
357
+ end
358
+ metadata(:getColumns, nil, schema, table, nil) do |h|
359
+ ts << [ds.send(:output_identifier, 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])}]
360
+ end
361
+ ts
362
+ end
363
+ end
364
+
365
+ class Dataset < Sequel::Dataset
366
+ include StoredProcedures
367
+
368
+ # Use JDBC PreparedStatements instead of emulated ones. Statements
369
+ # created using #prepare are cached at the connection level to allow
370
+ # reuse. This also supports bind variables by using unnamed
371
+ # prepared statements created using #call.
372
+ module PreparedStatementMethods
373
+ include Sequel::Dataset::UnnumberedArgumentMapper
374
+
375
+ private
376
+
377
+ # Execute the prepared SQL using the stored type and
378
+ # arguments derived from the hash passed to call.
379
+ def execute(sql, opts={}, &block)
380
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
381
+ end
382
+
383
+ # Same as execute, explicit due to intricacies of alias and super.
384
+ def execute_dui(sql, opts={}, &block)
385
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
386
+ end
387
+
388
+ # Same as execute, explicit due to intricacies of alias and super.
389
+ def execute_insert(sql, opts={}, &block)
390
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
391
+ end
392
+ end
393
+
394
+ # Use JDBC CallableStatements to execute stored procedures. Only supported
395
+ # if the underlying database has stored procedure support.
396
+ module StoredProcedureMethods
397
+ include Sequel::Dataset::StoredProcedureMethods
398
+
399
+ private
400
+
401
+ # Execute the database stored procedure with the stored arguments.
402
+ def execute(sql, opts={}, &block)
403
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
404
+ end
405
+
406
+ # Same as execute, explicit due to intricacies of alias and super.
407
+ def execute_dui(sql, opts={}, &block)
408
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
409
+ end
410
+
411
+ # Same as execute, explicit due to intricacies of alias and super.
412
+ def execute_insert(sql, opts={}, &block)
413
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
414
+ end
415
+ end
416
+
417
+ # Correctly return rows from the database and return them as hashes.
418
+ def fetch_rows(sql, &block)
419
+ execute(sql){|result| process_result_set(result, &block)}
420
+ self
421
+ end
422
+
423
+ # Create a named prepared statement that is stored in the
424
+ # database (and connection) for reuse.
425
+ def prepare(type, name=nil, values=nil)
426
+ ps = to_prepared_statement(type, values)
427
+ ps.extend(PreparedStatementMethods)
428
+ if name
429
+ ps.prepared_statement_name = name
430
+ db.prepared_statements[name] = ps
431
+ end
432
+ ps
433
+ end
434
+
435
+ private
436
+
437
+ # Convert the type. Used for converting Java types to ruby types.
438
+ def convert_type(v)
439
+ case v
440
+ when Java::JavaSQL::Timestamp, Java::JavaSQL::Time
441
+ Sequel.string_to_datetime(v.to_string)
442
+ when Java::JavaSQL::Date
443
+ Sequel.string_to_date(v.to_string)
444
+ when Java::JavaIo::BufferedReader
445
+ lines = []
446
+ while(line = v.read_line) do lines << line end
447
+ lines.join("\n")
448
+ when Java::JavaMath::BigDecimal
449
+ BigDecimal.new(v.to_string)
450
+ else
451
+ v
452
+ end
453
+ end
454
+
455
+ # Extend the dataset with the JDBC stored procedure methods.
456
+ def prepare_extend_sproc(ds)
457
+ ds.extend(StoredProcedureMethods)
458
+ end
459
+
460
+ # Split out from fetch rows to allow processing of JDBC result sets
461
+ # that don't come from issuing an SQL string.
462
+ def process_result_set(result)
463
+ # get column names
464
+ meta = result.getMetaData
465
+ column_count = meta.getColumnCount
466
+ @columns = []
467
+ column_count.times{|i| @columns << output_identifier(meta.getColumnName(i+1))}
468
+
469
+ # get rows
470
+ while result.next
471
+ row = {}
472
+ @columns.each_with_index{|v, i| row[v] = convert_type(result.getObject(i+1))}
473
+ yield row
474
+ end
475
+ end
476
+ end
477
+ end
478
+ end
479
+
480
+ class Java::JavaSQL::Timestamp
481
+ # Add a usec method in order to emulate Time values.
482
+ def usec
483
+ getNanos/1000
484
+ end
485
+ end