activerecord-oracle_enhanced-adapter 1.4.3 → 5.2.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 (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