epugh-sequel 0.0.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 (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