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
@@ -1,25 +0,0 @@
1
- require "bigdecimal"
2
-
3
- unless BigDecimal.method_defined?(:to_d)
4
- BigDecimal.class_eval do
5
- def to_d #:nodoc:
6
- self
7
- end
8
- end
9
- end
10
-
11
- unless Bignum.method_defined?(:to_d)
12
- Bignum.class_eval do
13
- def to_d #:nodoc:
14
- BigDecimal.new(self.to_s)
15
- end
16
- end
17
- end
18
-
19
- unless Fixnum.method_defined?(:to_d)
20
- Fixnum.class_eval do
21
- def to_d #:nodoc:
22
- BigDecimal.new(self.to_s)
23
- end
24
- end
25
- end
@@ -1,21 +0,0 @@
1
- module ActiveRecord #:nodoc:
2
- module ConnectionAdapters #:nodoc:
3
- module OracleEnhancedCpk #:nodoc:
4
-
5
- # This mightn't be in Core, but count(distinct x,y) doesn't work for me.
6
- # Return that not supported if composite_primary_keys gem is required.
7
- def supports_count_distinct? #:nodoc:
8
- @supports_count_distinct ||= ! defined?(CompositePrimaryKeys)
9
- end
10
-
11
- def concat(*columns) #:nodoc:
12
- "(#{columns.join('||')})"
13
- end
14
-
15
- end
16
- end
17
- end
18
-
19
- ActiveRecord::ConnectionAdapters::OracleEnhancedAdapter.class_eval do
20
- include ActiveRecord::ConnectionAdapters::OracleEnhancedCpk
21
- end
@@ -1,46 +0,0 @@
1
- module ActiveRecord #:nodoc:
2
- module ConnectionAdapters #:nodoc:
3
- module OracleEnhancedDirty #:nodoc:
4
-
5
- module InstanceMethods #:nodoc:
6
- private
7
-
8
- def _field_changed?(attr, old, value)
9
- if column = column_for_attribute(attr)
10
- # Added also :decimal type
11
- if (column.type == :integer || column.type == :decimal) && column.null && (old.nil? || old == 0) && value.blank?
12
- # For nullable integer columns, NULL gets stored in database for blank (i.e. '') values.
13
- # Hence we don't record it as a change if the value changes from nil to ''.
14
- # If an old value of 0 is set to '' we want this to get changed to nil as otherwise it'll
15
- # be typecast back to 0 (''.to_i => 0)
16
- value = nil
17
- # Oracle stores empty string '' as NULL
18
- # therefore need to convert empty string value to nil if old value is nil
19
- elsif column.type == :string && column.null && old.nil?
20
- value = nil if value == ''
21
- elsif old == 0 && value.is_a?(String) && value.present? && value != '0'
22
- value = nil
23
- else
24
- value = column.type_cast(value)
25
- end
26
- end
27
-
28
- old != value
29
- end
30
-
31
- end
32
-
33
- end
34
- end
35
- end
36
-
37
- if ActiveRecord::Base.method_defined?(:changed?)
38
- ActiveRecord::Base.class_eval do
39
- include ActiveRecord::ConnectionAdapters::OracleEnhancedDirty::InstanceMethods
40
- # Starting with rails 3.2.9 the method #field_changed?
41
- # was renamed to #_field_changed?
42
- if private_method_defined?(:field_changed?)
43
- alias_method :field_changed?, :_field_changed?
44
- end
45
- end
46
- end
@@ -1,572 +0,0 @@
1
- begin
2
- require "java"
3
- require "jruby"
4
-
5
- # ojdbc6.jar or ojdbc5.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
6
-
7
- java_version = java.lang.System.getProperty("java.version")
8
- ojdbc_jar = if java_version =~ /^1.5/
9
- "ojdbc5.jar"
10
- elsif java_version =~ /^1.6/
11
- "ojdbc6.jar"
12
- elsif java_version =~ /^1.7/
13
- "ojdbc7.jar"
14
- else
15
- nil
16
- end
17
-
18
- unless ojdbc_jar.nil? || ENV_JAVA['java.class.path'] =~ Regexp.new(ojdbc_jar)
19
- # On Unix environment variable should be PATH, on Windows it is sometimes Path
20
- env_path = (ENV["PATH"] || ENV["Path"] || '').split(File::PATH_SEPARATOR)
21
- # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
22
- # then in Ruby load path and finally in environment PATH
23
- if ojdbc_jar_path = ['./lib'].concat($LOAD_PATH).concat(env_path).find{|d| File.exists?(File.join(d,ojdbc_jar))}
24
- require File.join(ojdbc_jar_path,ojdbc_jar)
25
- end
26
- end
27
-
28
- ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
29
- java.sql.DriverManager.registerDriver ORACLE_DRIVER
30
-
31
- # set tns_admin property from TNS_ADMIN environment variable
32
- if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
33
- java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
34
- end
35
-
36
- rescue LoadError, NameError
37
- # JDBC driver is unavailable.
38
- raise LoadError, "ERROR: ActiveRecord oracle_enhanced adapter could not load Oracle JDBC driver. Please install #{ojdbc_jar || "Oracle JDBC"} library."
39
- end
40
-
41
-
42
- module ActiveRecord
43
- module ConnectionAdapters
44
-
45
- # JDBC database interface for JRuby
46
- class OracleEnhancedJDBCConnection < OracleEnhancedConnection #:nodoc:
47
-
48
- attr_accessor :active
49
- alias :active? :active
50
-
51
- attr_accessor :auto_retry
52
- alias :auto_retry? :auto_retry
53
- @auto_retry = false
54
-
55
- def initialize(config)
56
- @active = true
57
- @config = config
58
- new_connection(@config)
59
- end
60
-
61
- # modified method to support JNDI connections
62
- def new_connection(config)
63
- username = nil
64
-
65
- if config[:jndi]
66
- jndi = config[:jndi].to_s
67
- ctx = javax.naming.InitialContext.new
68
- ds = nil
69
-
70
- # tomcat needs first lookup method, oc4j (and maybe other application servers) need second method
71
- begin
72
- env = ctx.lookup('java:/comp/env')
73
- ds = env.lookup(jndi)
74
- rescue
75
- ds = ctx.lookup(jndi)
76
- end
77
-
78
- # check if datasource supports pooled connections, otherwise use default
79
- if ds.respond_to?(:pooled_connection)
80
- @raw_connection = ds.pooled_connection
81
- else
82
- @raw_connection = ds.connection
83
- end
84
-
85
- # get Oracle JDBC connection when using DBCP in Tomcat or jBoss
86
- if @raw_connection.respond_to?(:getInnermostDelegate)
87
- @pooled_connection = @raw_connection
88
- @raw_connection = @raw_connection.innermost_delegate
89
- elsif @raw_connection.respond_to?(:getUnderlyingConnection)
90
- @pooled_connection = @raw_connection
91
- @raw_connection = @raw_connection.underlying_connection
92
- end
93
-
94
- config[:driver] ||= @raw_connection.meta_data.connection.java_class.name
95
- username = @raw_connection.meta_data.user_name
96
- else
97
- # to_s needed if username, password or database is specified as number in database.yml file
98
- username = config[:username] && config[:username].to_s
99
- password = config[:password] && config[:password].to_s
100
- database = config[:database] && config[:database].to_s || 'XE'
101
- host, port = config[:host], config[:port]
102
- privilege = config[:privilege] && config[:privilege].to_s
103
-
104
- # connection using TNS alias
105
- if database && !host && !config[:url] && ENV['TNS_ADMIN']
106
- url = "jdbc:oracle:thin:@#{database}"
107
- else
108
- unless database.match(/^(\:|\/)/)
109
- # assume database is a SID if no colon or slash are supplied (backward-compatibility)
110
- database = ":#{database}"
111
- end
112
- url = config[:url] || "jdbc:oracle:thin:@#{host || 'localhost'}:#{port || 1521}#{database}"
113
- end
114
-
115
- prefetch_rows = config[:prefetch_rows] || 100
116
- # get session time_zone from configuration or from TZ environment variable
117
- time_zone = config[:time_zone] || ENV['TZ'] || java.util.TimeZone.default.getID
118
-
119
- properties = java.util.Properties.new
120
- properties.put("user", username)
121
- properties.put("password", password)
122
- properties.put("defaultRowPrefetch", "#{prefetch_rows}") if prefetch_rows
123
- properties.put("internal_logon", privilege) if privilege
124
-
125
- begin
126
- @raw_connection = java.sql.DriverManager.getConnection(url, properties)
127
- rescue
128
- # bypass DriverManager to work in cases where ojdbc*.jar
129
- # is added to the load path at runtime and not on the
130
- # system classpath
131
- @raw_connection = ORACLE_DRIVER.connect(url, properties)
132
- end
133
-
134
- # Set session time zone to current time zone
135
- @raw_connection.setSessionTimeZone(time_zone)
136
-
137
- # Set default number of rows to prefetch
138
- # @raw_connection.setDefaultRowPrefetch(prefetch_rows) if prefetch_rows
139
- end
140
-
141
- cursor_sharing = config[:cursor_sharing] || 'force'
142
- exec "alter session set cursor_sharing = #{cursor_sharing}"
143
-
144
- # Initialize NLS parameters
145
- OracleEnhancedAdapter::DEFAULT_NLS_PARAMETERS.each do |key, default_value|
146
- value = config[key] || ENV[key.to_s.upcase] || default_value
147
- if value
148
- exec "alter session set #{key} = '#{value}'"
149
- end
150
- end
151
-
152
- self.autocommit = true
153
-
154
- # default schema owner
155
- @owner = username.upcase unless username.nil?
156
-
157
- @raw_connection
158
- end
159
-
160
- def logoff
161
- @active = false
162
- if defined?(@pooled_connection)
163
- @pooled_connection.close
164
- else
165
- @raw_connection.close
166
- end
167
- true
168
- rescue
169
- false
170
- end
171
-
172
- def commit
173
- @raw_connection.commit
174
- end
175
-
176
- def rollback
177
- @raw_connection.rollback
178
- end
179
-
180
- def autocommit?
181
- @raw_connection.getAutoCommit
182
- end
183
-
184
- def autocommit=(value)
185
- @raw_connection.setAutoCommit(value)
186
- end
187
-
188
- # Checks connection, returns true if active. Note that ping actively
189
- # checks the connection, while #active? simply returns the last
190
- # known state.
191
- def ping
192
- exec_no_retry("select 1 from dual")
193
- @active = true
194
- rescue NativeException => e
195
- @active = false
196
- if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
197
- raise OracleEnhancedConnectionException, e.message
198
- else
199
- raise
200
- end
201
- end
202
-
203
- # Resets connection, by logging off and creating a new connection.
204
- def reset!
205
- logoff rescue nil
206
- begin
207
- new_connection(@config)
208
- @active = true
209
- rescue NativeException => e
210
- @active = false
211
- if e.message =~ /^java\.sql\.SQL(Recoverable)?Exception/
212
- raise OracleEnhancedConnectionException, e.message
213
- else
214
- raise
215
- end
216
- end
217
- end
218
-
219
- # mark connection as dead if connection lost
220
- def with_retry(&block)
221
- should_retry = auto_retry? && autocommit?
222
- begin
223
- yield if block_given?
224
- rescue NativeException => e
225
- raise unless e.message =~ /^java\.sql\.SQL(Recoverable)?Exception: (Closed Connection|Io exception:|No more data to read from socket|IO Error:)/
226
- @active = false
227
- raise unless should_retry
228
- should_retry = false
229
- reset! rescue nil
230
- retry
231
- end
232
- end
233
-
234
- def exec(sql)
235
- with_retry do
236
- exec_no_retry(sql)
237
- end
238
- end
239
-
240
- def exec_no_retry(sql)
241
- case sql
242
- when /\A\s*(UPDATE|INSERT|DELETE)/i
243
- s = @raw_connection.prepareStatement(sql)
244
- s.executeUpdate
245
- # it is safer for CREATE and DROP statements not to use PreparedStatement
246
- # as it does not allow creation of triggers with :NEW in their definition
247
- when /\A\s*(CREATE|DROP)/i
248
- s = @raw_connection.createStatement()
249
- # this disables SQL92 syntax processing of {...} which can result in statement execution errors
250
- # if sql contains {...} in strings or comments
251
- s.setEscapeProcessing(false)
252
- s.execute(sql)
253
- true
254
- else
255
- s = @raw_connection.prepareStatement(sql)
256
- s.execute
257
- true
258
- end
259
- ensure
260
- s.close rescue nil
261
- end
262
-
263
- def returning_clause(quoted_pk)
264
- " RETURNING #{quoted_pk} INTO ?"
265
- end
266
-
267
- # execute sql with RETURNING ... INTO :insert_id
268
- # and return :insert_id value
269
- def exec_with_returning(sql)
270
- with_retry do
271
- begin
272
- # it will always be INSERT statement
273
-
274
- # TODO: need to investigate why PreparedStatement is giving strange exception "Protocol violation"
275
- # s = @raw_connection.prepareStatement(sql)
276
- # s.registerReturnParameter(1, ::Java::oracle.jdbc.OracleTypes::NUMBER)
277
- # count = s.executeUpdate
278
- # if count > 0
279
- # rs = s.getReturnResultSet
280
- # if rs.next
281
- # # Assuming that primary key will not be larger as long max value
282
- # insert_id = rs.getLong(1)
283
- # rs.wasNull ? nil : insert_id
284
- # else
285
- # nil
286
- # end
287
- # else
288
- # nil
289
- # end
290
-
291
- # Workaround with CallableStatement
292
- s = @raw_connection.prepareCall("BEGIN #{sql}; END;")
293
- s.registerOutParameter(1, java.sql.Types::BIGINT)
294
- s.execute
295
- insert_id = s.getLong(1)
296
- s.wasNull ? nil : insert_id
297
- ensure
298
- # rs.close rescue nil
299
- s.close rescue nil
300
- end
301
- end
302
- end
303
-
304
- def prepare(sql)
305
- Cursor.new(self, @raw_connection.prepareStatement(sql))
306
- end
307
-
308
- class Cursor
309
- def initialize(connection, raw_statement)
310
- @connection = connection
311
- @raw_statement = raw_statement
312
- end
313
-
314
- def bind_param(position, value, column = nil)
315
- col_type = column && column.type
316
- java_value = ruby_to_java_value(value, col_type)
317
- case value
318
- when Integer
319
- @raw_statement.setLong(position, java_value)
320
- when Float
321
- @raw_statement.setFloat(position, java_value)
322
- when BigDecimal
323
- @raw_statement.setBigDecimal(position, java_value)
324
- when String
325
- case col_type
326
- when :text
327
- @raw_statement.setClob(position, java_value)
328
- when :binary
329
- @raw_statement.setBlob(position, java_value)
330
- when :raw
331
- @raw_statement.setString(position, OracleEnhancedAdapter.encode_raw(java_value))
332
- when :decimal
333
- @raw_statement.setBigDecimal(position, java_value)
334
- else
335
- @raw_statement.setString(position, java_value)
336
- end
337
- when Date, DateTime
338
- @raw_statement.setDATE(position, java_value)
339
- when Time
340
- @raw_statement.setTimestamp(position, java_value)
341
- when NilClass
342
- if column && column.object_type?
343
- @raw_statement.setNull(position, java.sql.Types::STRUCT, column.sql_type)
344
- else
345
- # TODO: currently nil is always bound as NULL with VARCHAR type.
346
- # When nils will actually be used by ActiveRecord as bound parameters
347
- # then need to pass actual column type.
348
- @raw_statement.setNull(position, java.sql.Types::VARCHAR)
349
- end
350
- else
351
- raise ArgumentError, "Don't know how to bind variable with type #{value.class}"
352
- end
353
- end
354
-
355
- def bind_returning_param(position, bind_type)
356
- @returning_positions ||= []
357
- @returning_positions << position
358
- if bind_type == Integer
359
- @raw_statement.registerReturnParameter(position, java.sql.Types::BIGINT)
360
- end
361
- end
362
-
363
- def exec
364
- @raw_result_set = @raw_statement.executeQuery
365
- true
366
- end
367
-
368
- def exec_update
369
- @raw_statement.executeUpdate
370
- end
371
-
372
- def metadata
373
- @metadata ||= @raw_result_set.getMetaData
374
- end
375
-
376
- def column_types
377
- @column_types ||= (1..metadata.getColumnCount).map{|i| metadata.getColumnTypeName(i).to_sym}
378
- end
379
-
380
- def column_names
381
- @column_names ||= (1..metadata.getColumnCount).map{|i| metadata.getColumnName(i)}
382
- end
383
- alias :get_col_names :column_names
384
-
385
- def fetch(options={})
386
- if @raw_result_set.next
387
- get_lob_value = options[:get_lob_value]
388
- row_values = []
389
- column_types.each_with_index do |column_type, i|
390
- row_values <<
391
- @connection.get_ruby_value_from_result_set(@raw_result_set, i+1, column_type, get_lob_value)
392
- end
393
- row_values
394
- else
395
- @raw_result_set.close
396
- nil
397
- end
398
- end
399
-
400
- def get_returning_param(position, type)
401
- rs_position = @returning_positions.index(position) + 1
402
- rs = @raw_statement.getReturnResultSet
403
- if rs.next
404
- # Assuming that primary key will not be larger as long max value
405
- returning_id = rs.getLong(rs_position)
406
- rs.wasNull ? nil : returning_id
407
- else
408
- nil
409
- end
410
- end
411
-
412
- def close
413
- @raw_statement.close
414
- end
415
-
416
- private
417
-
418
- def ruby_to_java_value(value, col_type = nil)
419
- case value
420
- when Fixnum, Float
421
- value
422
- when String
423
- case col_type
424
- when :text
425
- clob = Java::OracleSql::CLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::CLOB::DURATION_SESSION)
426
- clob.setString(1, value)
427
- clob
428
- when :binary
429
- blob = Java::OracleSql::BLOB.createTemporary(@connection.raw_connection, false, Java::OracleSql::BLOB::DURATION_SESSION)
430
- blob.setBytes(1, value.to_java_bytes)
431
- blob
432
- when :decimal
433
- java.math.BigDecimal.new(value.to_s)
434
- else
435
- value
436
- end
437
- when BigDecimal
438
- java.math.BigDecimal.new(value.to_s)
439
- when Date, DateTime
440
- Java::oracle.sql.DATE.new(value.strftime("%Y-%m-%d %H:%M:%S"))
441
- when Time
442
- Java::java.sql.Timestamp.new(value.year-1900, value.month-1, value.day, value.hour, value.min, value.sec, value.usec * 1000)
443
- else
444
- value
445
- end
446
- end
447
-
448
- end
449
-
450
- def select(sql, name = nil, return_column_names = false)
451
- with_retry do
452
- select_no_retry(sql, name, return_column_names)
453
- end
454
- end
455
-
456
- def select_no_retry(sql, name = nil, return_column_names = false)
457
- stmt = @raw_connection.prepareStatement(sql)
458
- rset = stmt.executeQuery
459
-
460
- # Reuse the same hash for all rows
461
- column_hash = {}
462
-
463
- metadata = rset.getMetaData
464
- column_count = metadata.getColumnCount
465
-
466
- cols_types_index = (1..column_count).map do |i|
467
- col_name = oracle_downcase(metadata.getColumnName(i))
468
- next if col_name == 'raw_rnum_'
469
- column_hash[col_name] = nil
470
- [col_name, metadata.getColumnTypeName(i).to_sym, i]
471
- end
472
- cols_types_index.delete(nil)
473
-
474
- rows = []
475
- get_lob_value = !(name == 'Writable Large Object')
476
-
477
- while rset.next
478
- hash = column_hash.dup
479
- cols_types_index.each do |col, column_type, i|
480
- hash[col] = get_ruby_value_from_result_set(rset, i, column_type, get_lob_value)
481
- end
482
- rows << hash
483
- end
484
-
485
- return_column_names ? [rows, cols_types_index.map(&:first)] : rows
486
- ensure
487
- rset.close rescue nil
488
- stmt.close rescue nil
489
- end
490
-
491
- def write_lob(lob, value, is_binary = false)
492
- if is_binary
493
- lob.setBytes(1, value.to_java_bytes)
494
- else
495
- lob.setString(1,value)
496
- end
497
- end
498
-
499
- # Return NativeException / java.sql.SQLException error code
500
- def error_code(exception)
501
- case exception
502
- when NativeException
503
- exception.cause.getErrorCode
504
- else
505
- nil
506
- end
507
- end
508
-
509
- def get_ruby_value_from_result_set(rset, i, type_name, get_lob_value = true)
510
- case type_name
511
- when :NUMBER
512
- d = rset.getNUMBER(i)
513
- if d.nil?
514
- nil
515
- elsif d.isInt
516
- Integer(d.stringValue)
517
- else
518
- BigDecimal.new(d.stringValue)
519
- end
520
- when :VARCHAR2, :CHAR, :LONG, :NVARCHAR2, :NCHAR
521
- rset.getString(i)
522
- when :DATE
523
- if dt = rset.getDATE(i)
524
- d = dt.dateValue
525
- t = dt.timeValue
526
- if OracleEnhancedAdapter.emulate_dates && t.hours == 0 && t.minutes == 0 && t.seconds == 0
527
- Date.new(d.year + 1900, d.month + 1, d.date)
528
- else
529
- Time.send(Base.default_timezone, d.year + 1900, d.month + 1, d.date, t.hours, t.minutes, t.seconds)
530
- end
531
- else
532
- nil
533
- end
534
- when :TIMESTAMP, :TIMESTAMPTZ, :TIMESTAMPLTZ, :"TIMESTAMP WITH TIME ZONE", :"TIMESTAMP WITH LOCAL TIME ZONE"
535
- ts = rset.getTimestamp(i)
536
- ts && Time.send(Base.default_timezone, ts.year + 1900, ts.month + 1, ts.date, ts.hours, ts.minutes, ts.seconds,
537
- ts.nanos / 1000)
538
- when :CLOB
539
- get_lob_value ? lob_to_ruby_value(rset.getClob(i)) : rset.getClob(i)
540
- when :BLOB
541
- get_lob_value ? lob_to_ruby_value(rset.getBlob(i)) : rset.getBlob(i)
542
- when :RAW
543
- raw_value = rset.getRAW(i)
544
- raw_value && raw_value.getBytes.to_a.pack('C*')
545
- else
546
- nil
547
- end
548
- end
549
-
550
- private
551
-
552
- def lob_to_ruby_value(val)
553
- case val
554
- when ::Java::OracleSql::CLOB
555
- if val.isEmptyLob
556
- nil
557
- else
558
- val.getSubString(1, val.length)
559
- end
560
- when ::Java::OracleSql::BLOB
561
- if val.isEmptyLob
562
- nil
563
- else
564
- String.from_java_bytes(val.getBytes(1, val.length))
565
- end
566
- end
567
- end
568
-
569
- end
570
-
571
- end
572
- end