viking-sequel 3.10.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 (237) hide show
  1. data/CHANGELOG +3134 -0
  2. data/COPYING +19 -0
  3. data/README.rdoc +723 -0
  4. data/Rakefile +193 -0
  5. data/bin/sequel +196 -0
  6. data/doc/advanced_associations.rdoc +644 -0
  7. data/doc/cheat_sheet.rdoc +218 -0
  8. data/doc/dataset_basics.rdoc +106 -0
  9. data/doc/dataset_filtering.rdoc +158 -0
  10. data/doc/opening_databases.rdoc +296 -0
  11. data/doc/prepared_statements.rdoc +104 -0
  12. data/doc/reflection.rdoc +84 -0
  13. data/doc/release_notes/1.0.txt +38 -0
  14. data/doc/release_notes/1.1.txt +143 -0
  15. data/doc/release_notes/1.3.txt +101 -0
  16. data/doc/release_notes/1.4.0.txt +53 -0
  17. data/doc/release_notes/1.5.0.txt +155 -0
  18. data/doc/release_notes/2.0.0.txt +298 -0
  19. data/doc/release_notes/2.1.0.txt +271 -0
  20. data/doc/release_notes/2.10.0.txt +328 -0
  21. data/doc/release_notes/2.11.0.txt +215 -0
  22. data/doc/release_notes/2.12.0.txt +534 -0
  23. data/doc/release_notes/2.2.0.txt +253 -0
  24. data/doc/release_notes/2.3.0.txt +88 -0
  25. data/doc/release_notes/2.4.0.txt +106 -0
  26. data/doc/release_notes/2.5.0.txt +137 -0
  27. data/doc/release_notes/2.6.0.txt +157 -0
  28. data/doc/release_notes/2.7.0.txt +166 -0
  29. data/doc/release_notes/2.8.0.txt +171 -0
  30. data/doc/release_notes/2.9.0.txt +97 -0
  31. data/doc/release_notes/3.0.0.txt +221 -0
  32. data/doc/release_notes/3.1.0.txt +406 -0
  33. data/doc/release_notes/3.10.0.txt +286 -0
  34. data/doc/release_notes/3.2.0.txt +268 -0
  35. data/doc/release_notes/3.3.0.txt +192 -0
  36. data/doc/release_notes/3.4.0.txt +325 -0
  37. data/doc/release_notes/3.5.0.txt +510 -0
  38. data/doc/release_notes/3.6.0.txt +366 -0
  39. data/doc/release_notes/3.7.0.txt +179 -0
  40. data/doc/release_notes/3.8.0.txt +151 -0
  41. data/doc/release_notes/3.9.0.txt +233 -0
  42. data/doc/schema.rdoc +36 -0
  43. data/doc/sharding.rdoc +113 -0
  44. data/doc/virtual_rows.rdoc +205 -0
  45. data/lib/sequel.rb +1 -0
  46. data/lib/sequel/adapters/ado.rb +90 -0
  47. data/lib/sequel/adapters/ado/mssql.rb +30 -0
  48. data/lib/sequel/adapters/amalgalite.rb +176 -0
  49. data/lib/sequel/adapters/db2.rb +139 -0
  50. data/lib/sequel/adapters/dbi.rb +113 -0
  51. data/lib/sequel/adapters/do.rb +188 -0
  52. data/lib/sequel/adapters/do/mysql.rb +49 -0
  53. data/lib/sequel/adapters/do/postgres.rb +91 -0
  54. data/lib/sequel/adapters/do/sqlite.rb +40 -0
  55. data/lib/sequel/adapters/firebird.rb +283 -0
  56. data/lib/sequel/adapters/informix.rb +77 -0
  57. data/lib/sequel/adapters/jdbc.rb +587 -0
  58. data/lib/sequel/adapters/jdbc/as400.rb +58 -0
  59. data/lib/sequel/adapters/jdbc/h2.rb +133 -0
  60. data/lib/sequel/adapters/jdbc/mssql.rb +57 -0
  61. data/lib/sequel/adapters/jdbc/mysql.rb +78 -0
  62. data/lib/sequel/adapters/jdbc/oracle.rb +50 -0
  63. data/lib/sequel/adapters/jdbc/postgresql.rb +108 -0
  64. data/lib/sequel/adapters/jdbc/sqlite.rb +55 -0
  65. data/lib/sequel/adapters/mysql.rb +421 -0
  66. data/lib/sequel/adapters/odbc.rb +143 -0
  67. data/lib/sequel/adapters/odbc/mssql.rb +42 -0
  68. data/lib/sequel/adapters/openbase.rb +64 -0
  69. data/lib/sequel/adapters/oracle.rb +131 -0
  70. data/lib/sequel/adapters/postgres.rb +504 -0
  71. data/lib/sequel/adapters/shared/mssql.rb +490 -0
  72. data/lib/sequel/adapters/shared/mysql.rb +498 -0
  73. data/lib/sequel/adapters/shared/oracle.rb +195 -0
  74. data/lib/sequel/adapters/shared/postgres.rb +830 -0
  75. data/lib/sequel/adapters/shared/progress.rb +44 -0
  76. data/lib/sequel/adapters/shared/sqlite.rb +389 -0
  77. data/lib/sequel/adapters/sqlite.rb +224 -0
  78. data/lib/sequel/adapters/utils/stored_procedures.rb +84 -0
  79. data/lib/sequel/connection_pool.rb +99 -0
  80. data/lib/sequel/connection_pool/sharded_single.rb +84 -0
  81. data/lib/sequel/connection_pool/sharded_threaded.rb +211 -0
  82. data/lib/sequel/connection_pool/single.rb +29 -0
  83. data/lib/sequel/connection_pool/threaded.rb +150 -0
  84. data/lib/sequel/core.rb +293 -0
  85. data/lib/sequel/core_sql.rb +241 -0
  86. data/lib/sequel/database.rb +1079 -0
  87. data/lib/sequel/database/schema_generator.rb +327 -0
  88. data/lib/sequel/database/schema_methods.rb +203 -0
  89. data/lib/sequel/database/schema_sql.rb +320 -0
  90. data/lib/sequel/dataset.rb +32 -0
  91. data/lib/sequel/dataset/actions.rb +441 -0
  92. data/lib/sequel/dataset/features.rb +86 -0
  93. data/lib/sequel/dataset/graph.rb +254 -0
  94. data/lib/sequel/dataset/misc.rb +119 -0
  95. data/lib/sequel/dataset/mutation.rb +64 -0
  96. data/lib/sequel/dataset/prepared_statements.rb +227 -0
  97. data/lib/sequel/dataset/query.rb +709 -0
  98. data/lib/sequel/dataset/sql.rb +996 -0
  99. data/lib/sequel/exceptions.rb +51 -0
  100. data/lib/sequel/extensions/blank.rb +43 -0
  101. data/lib/sequel/extensions/inflector.rb +242 -0
  102. data/lib/sequel/extensions/looser_typecasting.rb +21 -0
  103. data/lib/sequel/extensions/migration.rb +239 -0
  104. data/lib/sequel/extensions/named_timezones.rb +61 -0
  105. data/lib/sequel/extensions/pagination.rb +100 -0
  106. data/lib/sequel/extensions/pretty_table.rb +82 -0
  107. data/lib/sequel/extensions/query.rb +52 -0
  108. data/lib/sequel/extensions/schema_dumper.rb +271 -0
  109. data/lib/sequel/extensions/sql_expr.rb +122 -0
  110. data/lib/sequel/extensions/string_date_time.rb +46 -0
  111. data/lib/sequel/extensions/thread_local_timezones.rb +48 -0
  112. data/lib/sequel/metaprogramming.rb +9 -0
  113. data/lib/sequel/model.rb +120 -0
  114. data/lib/sequel/model/associations.rb +1514 -0
  115. data/lib/sequel/model/base.rb +1069 -0
  116. data/lib/sequel/model/default_inflections.rb +45 -0
  117. data/lib/sequel/model/errors.rb +39 -0
  118. data/lib/sequel/model/exceptions.rb +21 -0
  119. data/lib/sequel/model/inflections.rb +162 -0
  120. data/lib/sequel/model/plugins.rb +70 -0
  121. data/lib/sequel/plugins/active_model.rb +59 -0
  122. data/lib/sequel/plugins/association_dependencies.rb +103 -0
  123. data/lib/sequel/plugins/association_proxies.rb +41 -0
  124. data/lib/sequel/plugins/boolean_readers.rb +53 -0
  125. data/lib/sequel/plugins/caching.rb +141 -0
  126. data/lib/sequel/plugins/class_table_inheritance.rb +214 -0
  127. data/lib/sequel/plugins/composition.rb +138 -0
  128. data/lib/sequel/plugins/force_encoding.rb +72 -0
  129. data/lib/sequel/plugins/hook_class_methods.rb +126 -0
  130. data/lib/sequel/plugins/identity_map.rb +116 -0
  131. data/lib/sequel/plugins/instance_filters.rb +98 -0
  132. data/lib/sequel/plugins/instance_hooks.rb +57 -0
  133. data/lib/sequel/plugins/lazy_attributes.rb +77 -0
  134. data/lib/sequel/plugins/many_through_many.rb +208 -0
  135. data/lib/sequel/plugins/nested_attributes.rb +206 -0
  136. data/lib/sequel/plugins/optimistic_locking.rb +81 -0
  137. data/lib/sequel/plugins/rcte_tree.rb +281 -0
  138. data/lib/sequel/plugins/schema.rb +66 -0
  139. data/lib/sequel/plugins/serialization.rb +166 -0
  140. data/lib/sequel/plugins/single_table_inheritance.rb +74 -0
  141. data/lib/sequel/plugins/subclasses.rb +45 -0
  142. data/lib/sequel/plugins/tactical_eager_loading.rb +61 -0
  143. data/lib/sequel/plugins/timestamps.rb +87 -0
  144. data/lib/sequel/plugins/touch.rb +118 -0
  145. data/lib/sequel/plugins/typecast_on_load.rb +72 -0
  146. data/lib/sequel/plugins/validation_class_methods.rb +405 -0
  147. data/lib/sequel/plugins/validation_helpers.rb +223 -0
  148. data/lib/sequel/sql.rb +1020 -0
  149. data/lib/sequel/timezones.rb +161 -0
  150. data/lib/sequel/version.rb +12 -0
  151. data/lib/sequel_core.rb +1 -0
  152. data/lib/sequel_model.rb +1 -0
  153. data/spec/adapters/firebird_spec.rb +407 -0
  154. data/spec/adapters/informix_spec.rb +97 -0
  155. data/spec/adapters/mssql_spec.rb +403 -0
  156. data/spec/adapters/mysql_spec.rb +1019 -0
  157. data/spec/adapters/oracle_spec.rb +286 -0
  158. data/spec/adapters/postgres_spec.rb +969 -0
  159. data/spec/adapters/spec_helper.rb +51 -0
  160. data/spec/adapters/sqlite_spec.rb +432 -0
  161. data/spec/core/connection_pool_spec.rb +808 -0
  162. data/spec/core/core_sql_spec.rb +417 -0
  163. data/spec/core/database_spec.rb +1662 -0
  164. data/spec/core/dataset_spec.rb +3827 -0
  165. data/spec/core/expression_filters_spec.rb +595 -0
  166. data/spec/core/object_graph_spec.rb +296 -0
  167. data/spec/core/schema_generator_spec.rb +159 -0
  168. data/spec/core/schema_spec.rb +830 -0
  169. data/spec/core/spec_helper.rb +56 -0
  170. data/spec/core/version_spec.rb +7 -0
  171. data/spec/extensions/active_model_spec.rb +76 -0
  172. data/spec/extensions/association_dependencies_spec.rb +127 -0
  173. data/spec/extensions/association_proxies_spec.rb +50 -0
  174. data/spec/extensions/blank_spec.rb +67 -0
  175. data/spec/extensions/boolean_readers_spec.rb +92 -0
  176. data/spec/extensions/caching_spec.rb +250 -0
  177. data/spec/extensions/class_table_inheritance_spec.rb +252 -0
  178. data/spec/extensions/composition_spec.rb +194 -0
  179. data/spec/extensions/force_encoding_spec.rb +117 -0
  180. data/spec/extensions/hook_class_methods_spec.rb +470 -0
  181. data/spec/extensions/identity_map_spec.rb +202 -0
  182. data/spec/extensions/inflector_spec.rb +181 -0
  183. data/spec/extensions/instance_filters_spec.rb +55 -0
  184. data/spec/extensions/instance_hooks_spec.rb +133 -0
  185. data/spec/extensions/lazy_attributes_spec.rb +153 -0
  186. data/spec/extensions/looser_typecasting_spec.rb +39 -0
  187. data/spec/extensions/many_through_many_spec.rb +884 -0
  188. data/spec/extensions/migration_spec.rb +332 -0
  189. data/spec/extensions/named_timezones_spec.rb +72 -0
  190. data/spec/extensions/nested_attributes_spec.rb +396 -0
  191. data/spec/extensions/optimistic_locking_spec.rb +100 -0
  192. data/spec/extensions/pagination_spec.rb +99 -0
  193. data/spec/extensions/pretty_table_spec.rb +91 -0
  194. data/spec/extensions/query_spec.rb +85 -0
  195. data/spec/extensions/rcte_tree_spec.rb +205 -0
  196. data/spec/extensions/schema_dumper_spec.rb +357 -0
  197. data/spec/extensions/schema_spec.rb +127 -0
  198. data/spec/extensions/serialization_spec.rb +209 -0
  199. data/spec/extensions/single_table_inheritance_spec.rb +96 -0
  200. data/spec/extensions/spec_helper.rb +91 -0
  201. data/spec/extensions/sql_expr_spec.rb +89 -0
  202. data/spec/extensions/string_date_time_spec.rb +93 -0
  203. data/spec/extensions/subclasses_spec.rb +52 -0
  204. data/spec/extensions/tactical_eager_loading_spec.rb +65 -0
  205. data/spec/extensions/thread_local_timezones_spec.rb +45 -0
  206. data/spec/extensions/timestamps_spec.rb +150 -0
  207. data/spec/extensions/touch_spec.rb +155 -0
  208. data/spec/extensions/typecast_on_load_spec.rb +69 -0
  209. data/spec/extensions/validation_class_methods_spec.rb +984 -0
  210. data/spec/extensions/validation_helpers_spec.rb +438 -0
  211. data/spec/integration/associations_test.rb +281 -0
  212. data/spec/integration/database_test.rb +26 -0
  213. data/spec/integration/dataset_test.rb +963 -0
  214. data/spec/integration/eager_loader_test.rb +734 -0
  215. data/spec/integration/model_test.rb +130 -0
  216. data/spec/integration/plugin_test.rb +814 -0
  217. data/spec/integration/prepared_statement_test.rb +213 -0
  218. data/spec/integration/schema_test.rb +361 -0
  219. data/spec/integration/spec_helper.rb +73 -0
  220. data/spec/integration/timezone_test.rb +55 -0
  221. data/spec/integration/transaction_test.rb +122 -0
  222. data/spec/integration/type_test.rb +96 -0
  223. data/spec/model/association_reflection_spec.rb +175 -0
  224. data/spec/model/associations_spec.rb +2633 -0
  225. data/spec/model/base_spec.rb +418 -0
  226. data/spec/model/dataset_methods_spec.rb +78 -0
  227. data/spec/model/eager_loading_spec.rb +1391 -0
  228. data/spec/model/hooks_spec.rb +240 -0
  229. data/spec/model/inflector_spec.rb +26 -0
  230. data/spec/model/model_spec.rb +593 -0
  231. data/spec/model/plugins_spec.rb +236 -0
  232. data/spec/model/record_spec.rb +1500 -0
  233. data/spec/model/spec_helper.rb +97 -0
  234. data/spec/model/validations_spec.rb +153 -0
  235. data/spec/rcov.opts +6 -0
  236. data/spec/spec_config.rb.example +10 -0
  237. metadata +346 -0
@@ -0,0 +1,587 @@
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
+ # Make it accesing the javax.naming hierarchy more ruby friendly.
36
+ module JavaxNaming
37
+ include_package 'javax.naming'
38
+ end
39
+
40
+ # Used to identify a jndi connection and to extract the jndi
41
+ # resource name.
42
+ JNDI_URI_REGEXP = /\Ajdbc:jndi:(.+)/
43
+
44
+ # Contains procs keyed on sub adapter type that extend the
45
+ # given database object so it supports the correct database type.
46
+ DATABASE_SETUP = {:postgresql=>proc do |db|
47
+ Sequel.ts_require 'adapters/jdbc/postgresql'
48
+ db.extend(Sequel::JDBC::Postgres::DatabaseMethods)
49
+ JDBC.load_gem('postgres')
50
+ org.postgresql.Driver
51
+ end,
52
+ :mysql=>proc do |db|
53
+ Sequel.ts_require 'adapters/jdbc/mysql'
54
+ db.extend(Sequel::JDBC::MySQL::DatabaseMethods)
55
+ JDBC.load_gem('mysql')
56
+ com.mysql.jdbc.Driver
57
+ end,
58
+ :sqlite=>proc do |db|
59
+ Sequel.ts_require 'adapters/jdbc/sqlite'
60
+ db.extend(Sequel::JDBC::SQLite::DatabaseMethods)
61
+ JDBC.load_gem('sqlite3')
62
+ org.sqlite.JDBC
63
+ end,
64
+ :oracle=>proc do |db|
65
+ Sequel.ts_require 'adapters/jdbc/oracle'
66
+ db.extend(Sequel::JDBC::Oracle::DatabaseMethods)
67
+ Java::oracle.jdbc.driver.OracleDriver
68
+ end,
69
+ :sqlserver=>proc do |db|
70
+ Sequel.ts_require 'adapters/jdbc/mssql'
71
+ db.extend(Sequel::JDBC::MSSQL::DatabaseMethods)
72
+ com.microsoft.sqlserver.jdbc.SQLServerDriver
73
+ end,
74
+ :h2=>proc do |db|
75
+ Sequel.ts_require 'adapters/jdbc/h2'
76
+ db.extend(Sequel::JDBC::H2::DatabaseMethods)
77
+ JDBC.load_gem('h2')
78
+ org.h2.Driver
79
+ end,
80
+ :as400=>proc do |db|
81
+ Sequel.ts_require 'adapters/jdbc/as400'
82
+ db.extend(Sequel::JDBC::AS400::DatabaseMethods)
83
+ com.ibm.as400.access.AS400JDBCDriver
84
+ end
85
+ }
86
+
87
+ # Allowing loading the necessary JDBC support via a gem, which
88
+ # works for PostgreSQL, MySQL, and SQLite.
89
+ def self.load_gem(name)
90
+ begin
91
+ Sequel.tsk_require "jdbc/#{name}"
92
+ rescue LoadError
93
+ # jdbc gem not used, hopefully the user has the .jar in their CLASSPATH
94
+ end
95
+ end
96
+
97
+ # JDBC Databases offer a fairly uniform interface that does not change
98
+ # much based on the sub adapter.
99
+ class Database < Sequel::Database
100
+ set_adapter_scheme :jdbc
101
+
102
+ # The type of database we are connecting to
103
+ attr_reader :database_type
104
+
105
+ # Whether to convert some Java types to ruby types when retrieving rows.
106
+ # True by default, can be set to false to roughly double performance when
107
+ # fetching rows.
108
+ attr_accessor :convert_types
109
+
110
+ # Call the DATABASE_SETUP proc directly after initialization,
111
+ # so the object always uses sub adapter specific code. Also,
112
+ # raise an error immediately if the connection doesn't have a
113
+ # uri, since JDBC requires one.
114
+ def initialize(opts)
115
+ super
116
+ @convert_types = typecast_value_boolean(@opts.fetch(:convert_types, true))
117
+ raise(Error, "No connection string specified") unless uri
118
+
119
+ resolved_uri = jndi? ? get_uri_from_jndi : uri
120
+
121
+ if match = /\Ajdbc:([^:]+)/.match(resolved_uri) and prok = DATABASE_SETUP[match[1].to_sym]
122
+ prok.call(self)
123
+ end
124
+ end
125
+
126
+ # Execute the given stored procedure with the give name. If a block is
127
+ # given, the stored procedure should return rows.
128
+ def call_sproc(name, opts = {})
129
+ args = opts[:args] || []
130
+ sql = "{call #{name}(#{args.map{'?'}.join(',')})}"
131
+ synchronize(opts[:server]) do |conn|
132
+ cps = conn.prepareCall(sql)
133
+
134
+ i = 0
135
+ args.each{|arg| set_ps_arg(cps, arg, i+=1)}
136
+
137
+ begin
138
+ if block_given?
139
+ yield log_yield(sql){cps.executeQuery}
140
+ else
141
+ case opts[:type]
142
+ when :insert
143
+ log_yield(sql){cps.executeUpdate}
144
+ last_insert_id(conn, opts)
145
+ else
146
+ log_yield(sql){cps.executeUpdate}
147
+ end
148
+ end
149
+ rescue NativeException, JavaSQL::SQLException => e
150
+ raise_error(e)
151
+ ensure
152
+ cps.close
153
+ end
154
+ end
155
+ end
156
+
157
+ # Connect to the database using JavaSQL::DriverManager.getConnection.
158
+ def connect(server)
159
+ conn = if jndi?
160
+ get_connection_from_jndi
161
+ else
162
+ args = [uri(server_opts(server))]
163
+ args.concat([opts[:user], opts[:password]]) if opts[:user] && opts[:password]
164
+ JavaSQL::DriverManager.getConnection(*args)
165
+ end
166
+ setup_connection(conn)
167
+ end
168
+
169
+ # Return instances of JDBC::Dataset with the given opts.
170
+ def dataset(opts = nil)
171
+ JDBC::Dataset.new(self, opts)
172
+ end
173
+
174
+ # Execute the given SQL. If a block is given, if should be a SELECT
175
+ # statement or something else that returns rows.
176
+ def execute(sql, opts={}, &block)
177
+ return call_sproc(sql, opts, &block) if opts[:sproc]
178
+ return execute_prepared_statement(sql, opts, &block) if [Symbol, Dataset].any?{|c| sql.is_a?(c)}
179
+ synchronize(opts[:server]) do |conn|
180
+ stmt = conn.createStatement
181
+ begin
182
+ if block_given?
183
+ yield log_yield(sql){stmt.executeQuery(sql)}
184
+ else
185
+ case opts[:type]
186
+ when :ddl
187
+ log_yield(sql){stmt.execute(sql)}
188
+ when :insert
189
+ log_yield(sql) do
190
+ if requires_return_generated_keys?
191
+ stmt.executeUpdate(sql, JavaSQL::Statement.RETURN_GENERATED_KEYS)
192
+ else
193
+ stmt.executeUpdate(sql)
194
+ end
195
+ end
196
+ last_insert_id(conn, opts.merge(:stmt=>stmt))
197
+ else
198
+ log_yield(sql){stmt.executeUpdate(sql)}
199
+ end
200
+ end
201
+ rescue NativeException, JavaSQL::SQLException => e
202
+ raise_error(e)
203
+ ensure
204
+ stmt.close
205
+ end
206
+ end
207
+ end
208
+ alias execute_dui execute
209
+
210
+ # Execute the given DDL SQL, which should not return any
211
+ # values or rows.
212
+ def execute_ddl(sql, opts={})
213
+ execute(sql, {:type=>:ddl}.merge(opts))
214
+ end
215
+
216
+ # Execute the given INSERT SQL, returning the last inserted
217
+ # row id.
218
+ def execute_insert(sql, opts={})
219
+ execute(sql, {:type=>:insert}.merge(opts))
220
+ end
221
+
222
+ # Return a hash containing index information. Hash keys are index name symbols.
223
+ # Values are subhashes with two keys, :columns and :unique. The value of :columns
224
+ # is an array of symbols of column names. The value of :unique is true or false
225
+ # depending on if the index is unique.
226
+ def indexes(table, opts={})
227
+ m = output_identifier_meth
228
+ im = input_identifier_meth
229
+ schema, table = schema_and_table(table)
230
+ schema ||= opts[:schema]
231
+ schema = im.call(schema) if schema
232
+ table = im.call(table)
233
+ indexes = {}
234
+ metadata(:getIndexInfo, nil, schema, table, false, true) do |r|
235
+ next unless name = r[:column_name]
236
+ next if respond_to?(:primary_key_index_re, true) and r[:index_name] =~ primary_key_index_re
237
+ i = indexes[m.call(r[:index_name])] ||= {:columns=>[], :unique=>[false, 0].include?(r[:non_unique])}
238
+ i[:columns] << m.call(name)
239
+ end
240
+ indexes
241
+ end
242
+
243
+ # All tables in this database
244
+ def tables(opts={})
245
+ ts = []
246
+ m = output_identifier_meth
247
+ metadata(:getTables, nil, nil, nil, ['TABLE'].to_java(:string)){|h| ts << m.call(h[:table_name])}
248
+ ts
249
+ end
250
+
251
+ # The uri for this connection. You can specify the uri
252
+ # using the :uri, :url, or :database options. You don't
253
+ # need to worry about this if you use Sequel.connect
254
+ # with the JDBC connectrion strings.
255
+ def uri(opts={})
256
+ opts = @opts.merge(opts)
257
+ ur = opts[:uri] || opts[:url] || opts[:database]
258
+ ur =~ /^\Ajdbc:/ ? ur : "jdbc:#{ur}"
259
+ end
260
+
261
+ # Whether or not JNDI is being used for this connection.
262
+ def jndi?
263
+ !!(uri =~ JNDI_URI_REGEXP)
264
+ end
265
+
266
+ private
267
+
268
+ # JDBC uses a statement object to execute SQL on the database
269
+ def begin_transaction(conn)
270
+ conn = conn.createStatement unless supports_savepoints?
271
+ super
272
+ end
273
+
274
+ # Close given adapter connections
275
+ def disconnect_connection(c)
276
+ c.close
277
+ end
278
+
279
+ # Execute the prepared statement. If the provided name is a
280
+ # dataset, use that as the prepared statement, otherwise use
281
+ # it as a key to look it up in the prepared_statements hash.
282
+ # If the connection we are using has already prepared an identical
283
+ # statement, use that statement instead of creating another.
284
+ # Otherwise, prepare a new statement for the connection, bind the
285
+ # variables, and execute it.
286
+ def execute_prepared_statement(name, opts={})
287
+ args = opts[:arguments]
288
+ if Dataset === name
289
+ ps = name
290
+ name = ps.prepared_statement_name
291
+ else
292
+ ps = prepared_statements[name]
293
+ end
294
+ sql = ps.prepared_sql
295
+ synchronize(opts[:server]) do |conn|
296
+ if name and cps = conn.prepared_statements[name] and cps[0] == sql
297
+ cps = cps[1]
298
+ else
299
+ log_yield("Closing #{name}"){cps[1].close} if cps
300
+ cps = log_yield("Preparing#{" #{name}:" if name} #{sql}"){conn.prepareStatement(sql)}
301
+ conn.prepared_statements[name] = [sql, cps] if name
302
+ end
303
+ i = 0
304
+ args.each{|arg| set_ps_arg(cps, arg, i+=1)}
305
+ msg = "Executing#{" #{name}" if name}"
306
+ begin
307
+ if block_given?
308
+ yield log_yield(msg, args){cps.executeQuery}
309
+ else
310
+ case opts[:type]
311
+ when :ddl
312
+ log_yield(msg, args){cps.execute}
313
+ when :insert
314
+ log_yield(msg, args){cps.executeUpdate}
315
+ last_insert_id(conn, opts.merge(:prepared=>true))
316
+ else
317
+ log_yield(msg, args){cps.executeUpdate}
318
+ end
319
+ end
320
+ rescue NativeException, JavaSQL::SQLException => e
321
+ raise_error(e)
322
+ ensure
323
+ cps.close unless name
324
+ end
325
+ end
326
+ end
327
+
328
+ # Gets the JDBC connection uri from the JNDI resource.
329
+ def get_uri_from_jndi
330
+ conn = get_connection_from_jndi
331
+ conn.meta_data.url
332
+ ensure
333
+ conn.close if conn
334
+ end
335
+
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
340
+ end
341
+
342
+ # Support fractional seconds for Time objects used in bound variables
343
+ def java_sql_timestamp(time)
344
+ millis = time.to_i * 1000
345
+ ts = java.sql.Timestamp.new(millis)
346
+ ts.setNanos(time.usec * 1000)
347
+ ts
348
+ end
349
+
350
+ # By default, there is no support for determining the last inserted
351
+ # id, so return nil. This method should be overridden in
352
+ # sub adapters.
353
+ def last_insert_id(conn, opts)
354
+ nil
355
+ end
356
+
357
+ # Yield the metadata for this database
358
+ def metadata(*args, &block)
359
+ synchronize do |c|
360
+ result = c.getMetaData.send(*args)
361
+ begin
362
+ metadata_dataset.send(:process_result_set, result, &block)
363
+ ensure
364
+ result.close
365
+ end
366
+ end
367
+ end
368
+
369
+ # Treat SQLExceptions with a "Connection Error" SQLState as disconnects
370
+ def raise_error(exception, opts={})
371
+ cause = exception.respond_to?(:cause) ? exception.cause : exception
372
+ super(exception, {:disconnect => cause.respond_to?(:getSQLState) && cause.getSQLState =~ /^08/}.merge(opts))
373
+ end
374
+
375
+ # Close the given statement when removing the transaction
376
+ def remove_transaction(stmt)
377
+ stmt.close if stmt && !supports_savepoints?
378
+ super
379
+ end
380
+
381
+ # Java being java, you need to specify the type of each argument
382
+ # for the prepared statement, and bind it individually. This
383
+ # guesses which JDBC method to use, and hopefully JRuby will convert
384
+ # things properly for us.
385
+ def set_ps_arg(cps, arg, i)
386
+ case arg
387
+ when Integer
388
+ cps.setLong(i, arg)
389
+ when Sequel::SQL::Blob
390
+ cps.setBytes(i, arg.to_java_bytes)
391
+ when String
392
+ cps.setString(i, arg)
393
+ when Date, Java::JavaSql::Date
394
+ cps.setDate(i, arg)
395
+ when DateTime, Java::JavaSql::Timestamp
396
+ cps.setTimestamp(i, arg)
397
+ when Time
398
+ cps.setTimestamp(i, java_sql_timestamp(arg))
399
+ when Float
400
+ cps.setDouble(i, arg)
401
+ when TrueClass, FalseClass
402
+ cps.setBoolean(i, arg)
403
+ when nil
404
+ cps.setNull(i, JavaSQL::Types::NULL)
405
+ else
406
+ cps.setObject(i, arg)
407
+ end
408
+ end
409
+
410
+ # Add a prepared_statements accessor to the connection,
411
+ # and set it to an empty hash. This is used to store
412
+ # adapter specific prepared statements.
413
+ def setup_connection(conn)
414
+ class << conn
415
+ attr_accessor :prepared_statements
416
+ end
417
+ conn.prepared_statements = {}
418
+ conn
419
+ end
420
+
421
+ # Parse the table schema for the given table.
422
+ def schema_parse_table(table, opts={})
423
+ m = output_identifier_meth
424
+ im = input_identifier_meth
425
+ ds = dataset
426
+ schema, table = schema_and_table(table)
427
+ schema ||= opts[:schema]
428
+ schema = im.call(schema) if schema
429
+ table = im.call(table)
430
+ pks, ts = [], []
431
+ metadata(:getPrimaryKeys, nil, schema, table) do |h|
432
+ pks << h[:column_name]
433
+ end
434
+ metadata(:getColumns, nil, schema, table, nil) do |h|
435
+ 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], :scale=>h[:decimal_digits]}]
436
+ end
437
+ ts
438
+ end
439
+
440
+ # Create a statement object to execute transaction statements.
441
+ def transaction_statement_object(conn)
442
+ conn.createStatement
443
+ end
444
+
445
+ # This method determines whether or not to add
446
+ # Statement.RETURN_GENERATED_KEYS as an argument when inserting rows.
447
+ # Sub-adapters that require this should override this method.
448
+ def requires_return_generated_keys?
449
+ false
450
+ end
451
+ end
452
+
453
+ class Dataset < Sequel::Dataset
454
+ include StoredProcedures
455
+
456
+ # Use JDBC PreparedStatements instead of emulated ones. Statements
457
+ # created using #prepare are cached at the connection level to allow
458
+ # reuse. This also supports bind variables by using unnamed
459
+ # prepared statements created using #call.
460
+ module PreparedStatementMethods
461
+ include Sequel::Dataset::UnnumberedArgumentMapper
462
+
463
+ private
464
+
465
+ # Execute the prepared SQL using the stored type and
466
+ # arguments derived from the hash passed to call.
467
+ def execute(sql, opts={}, &block)
468
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
469
+ end
470
+
471
+ # Same as execute, explicit due to intricacies of alias and super.
472
+ def execute_dui(sql, opts={}, &block)
473
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
474
+ end
475
+
476
+ # Same as execute, explicit due to intricacies of alias and super.
477
+ def execute_insert(sql, opts={}, &block)
478
+ super(self, {:arguments=>bind_arguments, :type=>sql_query_type}.merge(opts), &block)
479
+ end
480
+ end
481
+
482
+ # Use JDBC CallableStatements to execute stored procedures. Only supported
483
+ # if the underlying database has stored procedure support.
484
+ module StoredProcedureMethods
485
+ include Sequel::Dataset::StoredProcedureMethods
486
+
487
+ private
488
+
489
+ # Execute the database stored procedure with the stored arguments.
490
+ def execute(sql, opts={}, &block)
491
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
492
+ end
493
+
494
+ # Same as execute, explicit due to intricacies of alias and super.
495
+ def execute_dui(sql, opts={}, &block)
496
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
497
+ end
498
+
499
+ # Same as execute, explicit due to intricacies of alias and super.
500
+ def execute_insert(sql, opts={}, &block)
501
+ super(@sproc_name, {:args=>@sproc_args, :sproc=>true, :type=>sql_query_type}.merge(opts), &block)
502
+ end
503
+ end
504
+
505
+ # Whether to convert some Java types to ruby types when retrieving rows.
506
+ # Uses the database's setting by default, can be set to false to roughly
507
+ # double performance when fetching rows.
508
+ attr_accessor :convert_types
509
+
510
+ # Use the convert_types default setting from the database
511
+ def initialize(db, opts={})
512
+ @convert_types = db.convert_types
513
+ super
514
+ end
515
+
516
+ # Correctly return rows from the database and return them as hashes.
517
+ def fetch_rows(sql, &block)
518
+ execute(sql){|result| process_result_set(result, &block)}
519
+ self
520
+ end
521
+
522
+ # Create a named prepared statement that is stored in the
523
+ # database (and connection) for reuse.
524
+ def prepare(type, name=nil, *values)
525
+ ps = to_prepared_statement(type, values)
526
+ ps.extend(PreparedStatementMethods)
527
+ if name
528
+ ps.prepared_statement_name = name
529
+ db.prepared_statements[name] = ps
530
+ end
531
+ ps
532
+ end
533
+
534
+ private
535
+
536
+ # Convert the type. Used for converting Java types to ruby types.
537
+ def convert_type(v)
538
+ case v
539
+ when Java::JavaSQL::Timestamp, Java::JavaSQL::Time
540
+ Sequel.database_to_application_timestamp(v.to_string)
541
+ when Java::JavaSQL::Date
542
+ Sequel.string_to_date(v.to_string)
543
+ when Java::JavaIo::BufferedReader
544
+ lines = []
545
+ while(line = v.read_line) do lines << line end
546
+ lines.join("\n")
547
+ when Java::JavaMath::BigDecimal
548
+ BigDecimal.new(v.to_string)
549
+ when Java::byte[]
550
+ Sequel::SQL::Blob.new(String.from_java_bytes(v))
551
+ when Java::JavaSQL::Blob
552
+ convert_type(v.getBytes(0, v.length))
553
+ else
554
+ v
555
+ end
556
+ end
557
+
558
+ # Extend the dataset with the JDBC stored procedure methods.
559
+ def prepare_extend_sproc(ds)
560
+ ds.extend(StoredProcedureMethods)
561
+ end
562
+
563
+ # Split out from fetch rows to allow processing of JDBC result sets
564
+ # that don't come from issuing an SQL string.
565
+ def process_result_set(result)
566
+ # get column names
567
+ meta = result.getMetaData
568
+ cols = []
569
+ i = 0
570
+ meta.getColumnCount.times{cols << [output_identifier(meta.getColumnLabel(i+=1)), i]}
571
+ @columns = cols.map{|c| c.at(0)}
572
+ row = {}
573
+ blk = if @convert_types
574
+ lambda{|n, i| row[n] = convert_type(result.getObject(i))}
575
+ else
576
+ lambda{|n, i| row[n] = result.getObject(i)}
577
+ end
578
+ # get rows
579
+ while result.next
580
+ row = {}
581
+ cols.each(&blk)
582
+ yield row
583
+ end
584
+ end
585
+ end
586
+ end
587
+ end