activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (100) hide show
  1. checksums.yaml +5 -5
  2. data/History.md +1162 -2
  3. data/README.md +567 -155
  4. data/VERSION +1 -1
  5. data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +3 -1
  6. data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +19 -0
  7. data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +132 -0
  8. data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +345 -0
  9. data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +52 -0
  10. data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +280 -0
  11. data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +64 -0
  12. data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +59 -0
  13. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +538 -0
  14. data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
  15. data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +46 -0
  16. data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +435 -0
  17. data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
  18. data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +196 -0
  19. data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +164 -0
  20. data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
  21. data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +79 -0
  22. data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +194 -0
  23. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +709 -0
  24. data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +28 -0
  25. data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +353 -0
  26. data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +33 -0
  27. data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +3 -0
  28. data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +385 -1083
  29. data/lib/active_record/type/oracle_enhanced/boolean.rb +20 -0
  30. data/lib/active_record/type/oracle_enhanced/integer.rb +15 -0
  31. data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
  32. data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
  33. data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
  34. data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
  35. data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
  36. data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
  37. data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
  38. data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
  39. data/lib/activerecord-oracle_enhanced-adapter.rb +5 -13
  40. data/spec/active_record/connection_adapters/{oracle_enhanced_emulate_oracle_adapter_spec.rb → emulation/oracle_adapter_spec.rb} +5 -4
  41. data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +469 -0
  42. data/spec/active_record/connection_adapters/{oracle_enhanced_context_index_spec.rb → oracle_enhanced/context_index_spec.rb} +140 -128
  43. data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +112 -0
  44. data/spec/active_record/connection_adapters/{oracle_enhanced_dbms_output_spec.rb → oracle_enhanced/dbms_output_spec.rb} +13 -13
  45. data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +365 -0
  46. data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +196 -0
  47. data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
  48. data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1433 -0
  49. data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +478 -0
  50. data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +385 -550
  51. data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +92 -1249
  52. data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
  53. data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +208 -0
  54. data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +139 -0
  55. data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
  56. data/spec/active_record/oracle_enhanced/type/integer_spec.rb +91 -0
  57. data/spec/active_record/oracle_enhanced/type/json_spec.rb +57 -0
  58. data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
  59. data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
  60. data/spec/active_record/oracle_enhanced/type/raw_spec.rb +122 -0
  61. data/spec/active_record/oracle_enhanced/type/text_spec.rb +229 -0
  62. data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +75 -0
  63. data/spec/spec_config.yaml.template +11 -0
  64. data/spec/spec_helper.rb +100 -93
  65. data/spec/support/alter_system_set_open_cursors.sql +1 -0
  66. data/spec/support/alter_system_user_password.sql +2 -0
  67. data/spec/support/create_oracle_enhanced_users.sql +31 -0
  68. metadata +105 -152
  69. data/.rspec +0 -2
  70. data/Gemfile +0 -52
  71. data/RUNNING_TESTS.md +0 -45
  72. data/Rakefile +0 -59
  73. data/activerecord-oracle_enhanced-adapter.gemspec +0 -130
  74. data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
  75. data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
  76. data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -121
  77. data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +0 -151
  78. data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +0 -119
  79. data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -359
  80. data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
  81. data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +0 -21
  82. data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -46
  83. data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +0 -572
  84. data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +0 -497
  85. data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +0 -260
  86. data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -227
  87. data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +0 -260
  88. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -428
  89. data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
  90. data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +0 -294
  91. data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
  92. data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
  93. data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +0 -334
  94. data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
  95. data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +0 -113
  96. data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +0 -141
  97. data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +0 -378
  98. data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +0 -440
  99. data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +0 -1400
  100. data/spec/active_record/connection_adapters/oracle_enhanced_structure_dump_spec.rb +0 -339
@@ -0,0 +1,538 @@
1
+ # frozen_string_literal: true
2
+
3
+ begin
4
+ require "java"
5
+ require "jruby"
6
+
7
+ # ojdbc7.jar or ojdbc6.jar file should be in application ./lib directory or in load path or in ENV['PATH']
8
+
9
+ java_version = java.lang.System.getProperty("java.version")
10
+ # Dropping Java SE 6(1.6) or older version without deprecation cycle.
11
+ # Rails 5.0 already requires CRuby 2.2.2 or higher and JRuby 9.0 supporging CRuby 2.2 requires Java SE 7.
12
+ if java_version < "1.7"
13
+ raise "ERROR: Java SE 6 or older version is not supported. Upgrade Java version to Java SE 7 or higher"
14
+ end
15
+
16
+ # Oracle 11g client ojdbc6.jar is also compatible with Java 1.7
17
+ # Oracle 12c Release 1 client provides ojdbc7.jar
18
+ # Oracle 12c Release 2 client provides ojdbc8.jar
19
+ ojdbc_jars = %w(ojdbc8.jar ojdbc7.jar ojdbc6.jar)
20
+
21
+ if ENV_JAVA["java.class.path"] !~ Regexp.new(ojdbc_jars.join("|"))
22
+ # On Unix environment variable should be PATH, on Windows it is sometimes Path
23
+ env_path = (ENV["PATH"] || ENV["Path"] || "").split(File::PATH_SEPARATOR)
24
+ # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
25
+ # then in Ruby load path and finally in environment PATH
26
+ ["./lib"].concat($LOAD_PATH).concat(env_path).detect do |dir|
27
+ # check any compatible JDBC driver in the priority order
28
+ ojdbc_jars.any? do |ojdbc_jar|
29
+ if File.exists?(file_path = File.join(dir, ojdbc_jar))
30
+ require file_path
31
+ true
32
+ end
33
+ end
34
+ end
35
+ end
36
+
37
+ ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
38
+ java.sql.DriverManager.registerDriver ORACLE_DRIVER
39
+
40
+ # set tns_admin property from TNS_ADMIN environment variable
41
+ if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
42
+ java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
43
+ end
44
+
45
+ rescue LoadError, NameError
46
+ # JDBC driver is unavailable.
47
+ raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install #{ojdbc_jars.join(' or ') } library."
48
+ end
49
+
50
+ module ActiveRecord
51
+ module ConnectionAdapters
52
+ # JDBC database interface for JRuby
53
+ module OracleEnhanced
54
+ class JDBCConnection < OracleEnhanced::Connection #:nodoc:
55
+ attr_accessor :active
56
+ alias :active? :active
57
+
58
+ attr_accessor :auto_retry
59
+ alias :auto_retry? :auto_retry
60
+ @auto_retry = false
61
+
62
+ def initialize(config)
63
+ @active = true
64
+ @config = config
65
+ new_connection(@config)
66
+ end
67
+
68
+ # modified method to support JNDI connections
69
+ def new_connection(config)
70
+ username = nil
71
+
72
+ if config[:jndi]
73
+ jndi = config[:jndi].to_s
74
+ ctx = javax.naming.InitialContext.new
75
+ ds = nil
76
+
77
+ # tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
78
+ begin
79
+ env = ctx.lookup("java:/comp/env")
80
+ ds = env.lookup(jndi)
81
+ rescue
82
+ ds = ctx.lookup(jndi)
83
+ end
84
+
85
+ # check if datasource supports pooled connections, otherwise use default
86
+ if ds.respond_to?(:pooled_connection)
87
+ @raw_connection = ds.pooled_connection
88
+ else
89
+ @raw_connection = ds.connection
90
+ end
91
+
92
+ # get Oracle JDBC connection when using DBCP in Tomcat or jBoss
93
+ if @raw_connection.respond_to?(:getInnermostDelegate)
94
+ @pooled_connection = @raw_connection
95
+ @raw_connection = @raw_connection.innermost_delegate
96
+ elsif @raw_connection.respond_to?(:getUnderlyingConnection)
97
+ @pooled_connection = @raw_connection
98
+ @raw_connection = @raw_connection.underlying_connection
99
+ end
100
+
101
+ config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
102
+ username = @raw_connection.meta_data.user_name
103
+ else
104
+ # to_s needed if username, password or database is specified as number in database.yml file
105
+ username = config[:username] && config[:username].to_s
106
+ password = config[:password] && config[:password].to_s
107
+ database = config[:database] && config[:database].to_s || "XE"
108
+ host, port = config[:host], config[:port]
109
+ privilege = config[:privilege] && config[:privilege].to_s
110
+
111
+ # connection using TNS alias, or connection-string from DATABASE_URL
112
+ using_tns_alias = !host && !config[:url] && ENV["TNS_ADMIN"]
113
+ if database && (using_tns_alias || host == "connection-string")
114
+ url = "jdbc:oracle:thin:@#{database}"
115
+ else
116
+ unless database.match(/^(\:|\/)/)
117
+ # assume database is a SID if no colon or slash are supplied (backward-compatibility)
118
+ database = ":#{database}"
119
+ end
120
+ url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}#{database}"
121
+ end
122
+
123
+ prefetch_rows = config[:prefetch_rows] || 100
124
+ # get session time_zone from configuration or from TZ environment variable
125
+ time_zone = config[:time_zone] || ENV["TZ"] || java.util.TimeZone.default.getID
126
+
127
+ properties = java.util.Properties.new
128
+ properties.put("user", username)
129
+ properties.put("password", password)
130
+ properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
131
+ properties.put("internal_logon", privilege) if privilege
132
+
133
+ begin
134
+ @raw_connection = java.sql.DriverManager.getConnection(url, properties)
135
+ rescue
136
+ # bypass DriverManager to work in cases where ojdbc*.jar
137
+ # is added to the load path at runtime and not on the
138
+ # system classpath
139
+ @raw_connection = ORACLE_DRIVER.connect(url, properties)
140
+ end
141
+
142
+ # Set session time zone to current time zone
143
+ if ActiveRecord::Base.default_timezone == :local
144
+ @raw_connection.setSessionTimeZone(time_zone)
145
+ elsif ActiveRecord::Base.default_timezone == :utc
146
+ @raw_connection.setSessionTimeZone("UTC")
147
+ end
148
+
149
+ # Set default number of rows to prefetch
150
+ # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
151
+ end
152
+
153
+ cursor_sharing = config[:cursor_sharing]
154
+ exec "alter session set cursor_sharing = #{cursor_sharing}" if cursor_sharing
155
+
156
+ # Initialize NLS parameters
157
+ OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
158
+ value = config[key] || ENV[key.to_s.upcase] || default_value
159
+ if value
160
+ exec "alter session set #{key} = '#{value}'"
161
+ end
162
+ end
163
+
164
+ self.autocommit = true
165
+
166
+ schema = config[:schema] && config[:schema].to_s
167
+ if schema.blank?
168
+ # default schema owner
169
+ @owner = username.upcase unless username.nil?
170
+ else
171
+ exec "alter session set current_schema = #{schema}"
172
+ @owner = schema
173
+ end
174
+
175
+ @raw_connection
176
+ end
177
+
178
+ def logoff
179
+ @active = false
180
+ if defined?(@pooled_connection)
181
+ @pooled_connection.close
182
+ else
183
+ @raw_connection.close
184
+ end
185
+ true
186
+ rescue
187
+ false
188
+ end
189
+
190
+ def commit
191
+ @raw_connection.commit
192
+ end
193
+
194
+ def rollback
195
+ @raw_connection.rollback
196
+ end
197
+
198
+ def autocommit?
199
+ @raw_connection.getAutoCommit
200
+ end
201
+
202
+ def autocommit=(value)
203
+ @raw_connection.setAutoCommit(value)
204
+ end
205
+
206
+ # Checks connection, returns true if active. Note that ping actively
207
+ # checks the connection, while #active? simply returns the last
208
+ # known state.
209
+ def ping
210
+ exec_no_retry("select 1 from dual")
211
+ @active = true
212
+ rescue NativeException => e
213
+ @active = false
214
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
215
+ raise OracleEnhanced::ConnectionException, e.message
216
+ else
217
+ raise
218
+ end
219
+ end
220
+
221
+ # Resets connection, by logging off and creating a new connection.
222
+ def reset!
223
+ logoff rescue nil
224
+ begin
225
+ new_connection(@config)
226
+ @active = true
227
+ rescue NativeException => e
228
+ @active = false
229
+ if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
230
+ raise OracleEnhanced::ConnectionException, e.message
231
+ else
232
+ raise
233
+ end
234
+ end
235
+ end
236
+
237
+ # mark connection as dead if connection lost
238
+ def with_retry(&block)
239
+ should_retry = auto_retry? && autocommit?
240
+ begin
241
+ yield if block_given?
242
+ rescue NativeException => e
243
+ raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
244
+ @active = false
245
+ raise unless should_retry
246
+ should_retry = false
247
+ reset! rescue nil
248
+ retry
249
+ end
250
+ end
251
+
252
+ def exec(sql)
253
+ with_retry do
254
+ exec_no_retry(sql)
255
+ end
256
+ end
257
+
258
+ def exec_no_retry(sql)
259
+ case sql
260
+ when /\A\s*(UPDATE|INSERT|DELETE)/i
261
+ s = @raw_connection.prepareStatement(sql)
262
+ s.executeUpdate
263
+ # it is safer for CREATE and DROP statements not to use PreparedStatement
264
+ # as it does not allow creation of triggers with :NEW in their definition
265
+ when /\A\s*(CREATE|DROP)/i
266
+ s = @raw_connection.createStatement()
267
+ # this disables SQL92 syntax processing of {...} which can result in statement execution errors
268
+ # if sql contains {...} in strings or comments
269
+ s.setEscapeProcessing(false)
270
+ s.execute(sql)
271
+ true
272
+ else
273
+ s = @raw_connection.prepareStatement(sql)
274
+ s.execute
275
+ true
276
+ end
277
+ ensure
278
+ s.close rescue nil
279
+ end
280
+
281
+ def prepare(sql)
282
+ Cursor.new(self, @raw_connection.prepareStatement(sql))
283
+ end
284
+
285
+ def database_version
286
+ @database_version ||= (md = raw_connection.getMetaData) && [md.getDatabaseMajorVersion, md.getDatabaseMinorVersion]
287
+ end
288
+
289
+ class Cursor
290
+ def initialize(connection, raw_statement)
291
+ @connection = connection
292
+ @raw_statement = raw_statement
293
+ end
294
+
295
+ def bind_params(*bind_vars)
296
+ index = 1
297
+ bind_vars.flatten.each do |var|
298
+ if Hash === var
299
+ var.each { |key, val| bind_param key, val }
300
+ else
301
+ bind_param index, var
302
+ index += 1
303
+ end
304
+ end
305
+ end
306
+
307
+ def bind_param(position, value)
308
+ case value
309
+ when Integer
310
+ @raw_statement.setLong(position, value)
311
+ when Float
312
+ @raw_statement.setFloat(position, value)
313
+ when BigDecimal
314
+ @raw_statement.setBigDecimal(position, value)
315
+ when Java::OracleSql::BLOB
316
+ @raw_statement.setBlob(position, value)
317
+ when Java::OracleSql::CLOB
318
+ @raw_statement.setClob(position, value)
319
+ when Java::OracleSql::NCLOB
320
+ @raw_statement.setClob(position, value)
321
+ when Type::OracleEnhanced::Raw
322
+ @raw_statement.setString(position, OracleEnhanced::Quoting.encode_raw(value))
323
+ when String
324
+ @raw_statement.setString(position, value)
325
+ when Java::OracleSql::DATE
326
+ @raw_statement.setDATE(position, value)
327
+ when Java::JavaSql::Timestamp
328
+ @raw_statement.setTimestamp(position, value)
329
+ when Time
330
+ new_value = Java::java.sql.Timestamp.new(value.year - 1900, value.month - 1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
331
+ @raw_statement.setTimestamp(position, new_value)
332
+ when NilClass
333
+ # TODO: currently nil is always bound as NULL with VARCHAR type.
334
+ # When nils will actually be used by ActiveRecord as bound parameters
335
+ # then need to pass actual column type.
336
+ @raw_statement.setNull(position, java.sql.Types::VARCHAR)
337
+ else
338
+ raise ArgumentError, "Don't know how to bind variable with type #{value.class}"
339
+ end
340
+ end
341
+
342
+ def bind_returning_param(position, bind_type)
343
+ @returning_positions ||= []
344
+ @returning_positions << position
345
+ if bind_type == Integer
346
+ @raw_statement.registerReturnParameter(position, java.sql.Types::BIGINT)
347
+ end
348
+ end
349
+
350
+ def exec
351
+ @raw_result_set = @raw_statement.executeQuery
352
+ true
353
+ end
354
+
355
+ def exec_update
356
+ @raw_statement.executeUpdate
357
+ end
358
+
359
+ def metadata
360
+ @metadata ||= @raw_result_set.getMetaData
361
+ end
362
+
363
+ def column_types
364
+ @column_types ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnTypeName(i).to_sym }
365
+ end
366
+
367
+ def column_names
368
+ @column_names ||= (1..metadata.getColumnCount).map { |i| metadata.getColumnName(i) }
369
+ end
370
+ alias :get_col_names :column_names
371
+
372
+ def fetch(options = {})
373
+ if @raw_result_set.next
374
+ get_lob_value = options[:get_lob_value]
375
+ row_values = []
376
+ column_types.each_with_index do |column_type, i|
377
+ row_values <<
378
+ @connection.get_ruby_value_from_result_set(@raw_result_set, i + 1, column_type, get_lob_value)
379
+ end
380
+ row_values
381
+ else
382
+ @raw_result_set.close
383
+ nil
384
+ end
385
+ end
386
+
387
+ def get_returning_param(position, type)
388
+ rs_position = @returning_positions.index(position) + 1
389
+ rs = @raw_statement.getReturnResultSet
390
+ if rs.next
391
+ # Assuming that primary key will not be larger as long max value
392
+ returning_id = rs.getLong(rs_position)
393
+ rs.wasNull ? nil : returning_id
394
+ else
395
+ nil
396
+ end
397
+ end
398
+
399
+ def close
400
+ @raw_statement.close
401
+ end
402
+ end
403
+
404
+ def select(sql, name = nil, return_column_names = false)
405
+ with_retry do
406
+ select_no_retry(sql, name, return_column_names)
407
+ end
408
+ end
409
+
410
+ def select_no_retry(sql, name = nil, return_column_names = false)
411
+ stmt = @raw_connection.prepareStatement(sql)
412
+ rset = stmt.executeQuery
413
+
414
+ # Reuse the same hash for all rows
415
+ column_hash = {}
416
+
417
+ metadata = rset.getMetaData
418
+ column_count = metadata.getColumnCount
419
+
420
+ cols_types_index = (1..column_count).map do |i|
421
+ col_name = _oracle_downcase(metadata.getColumnName(i))
422
+ next if col_name == "raw_rnum_"
423
+ column_hash[col_name] = nil
424
+ [col_name, metadata.getColumnTypeName(i).to_sym, i]
425
+ end
426
+ cols_types_index.delete(nil)
427
+
428
+ rows = []
429
+ get_lob_value = !(name == "Writable Large Object")
430
+
431
+ while rset.next
432
+ hash = column_hash.dup
433
+ cols_types_index.each do |col, column_type, i|
434
+ hash[col] = get_ruby_value_from_result_set(rset, i, column_type, get_lob_value)
435
+ end
436
+ rows << hash
437
+ end
438
+
439
+ return_column_names ? [rows, cols_types_index.map(&:first)] : rows
440
+ ensure
441
+ rset.close rescue nil
442
+ stmt.close rescue nil
443
+ end
444
+
445
+ def write_lob(lob, value, is_binary = false)
446
+ if is_binary
447
+ lob.setBytes(1, value.to_java_bytes)
448
+ else
449
+ lob.setString(1, value)
450
+ end
451
+ end
452
+
453
+ # To allow private method called from `JDBCConnection`
454
+ def describe(name)
455
+ super
456
+ end
457
+
458
+ # Return NativeException / java.sql.SQLException error code
459
+ def error_code(exception)
460
+ case exception
461
+ when NativeException
462
+ exception.cause.getErrorCode
463
+ when Java::JavaSql::SQLException
464
+ exception.getErrorCode
465
+ else
466
+ nil
467
+ end
468
+ end
469
+
470
+ def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
471
+ case type_name
472
+ when :NUMBER
473
+ d = rset.getNUMBER(i)
474
+ if d.nil?
475
+ nil
476
+ elsif d.isInt
477
+ Integer(d.stringValue)
478
+ else
479
+ BigDecimal.new(d.stringValue)
480
+ end
481
+ when :BINARY_FLOAT
482
+ rset.getFloat(i)
483
+ when :VARCHAR2, :CHAR, :LONG, :NVARCHAR2, :NCHAR
484
+ rset.getString(i)
485
+ when :DATE
486
+ if dt = rset.getDATE(i)
487
+ d = dt.dateValue
488
+ t = dt.timeValue
489
+ Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
490
+ else
491
+ nil
492
+ end
493
+ when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
494
+ ts = rset.getTimestamp(i)
495
+ ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
496
+ ts.nanos / 1000)
497
+ when :CLOB
498
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
499
+ when :NCLOB
500
+ get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
501
+ when :BLOB
502
+ get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
503
+ when :RAW
504
+ raw_value = rset.getRAW(i)
505
+ raw_value && raw_value.getBytes.to_a.pack("C*")
506
+ else
507
+ nil
508
+ end
509
+ end
510
+
511
+ private
512
+
513
+ def lob_to_ruby_value(val)
514
+ case val
515
+ when ::Java::OracleSql::CLOB
516
+ if val.isEmptyLob
517
+ nil
518
+ else
519
+ val.getSubString(1, val.length)
520
+ end
521
+ when ::Java::OracleSql::NCLOB
522
+ if val.isEmptyLob
523
+ nil
524
+ else
525
+ val.getSubString(1, val.length)
526
+ end
527
+ when ::Java::OracleSql::BLOB
528
+ if val.isEmptyLob
529
+ nil
530
+ else
531
+ String.from_java_bytes(val.getBytes(1, val.length))
532
+ end
533
+ end
534
+ end
535
+ end
536
+ end
537
+ end
538
+ end