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.
- checksums.yaml +5 -5
- data/History.md +1162 -2
- data/README.md +567 -155
- data/VERSION +1 -1
- data/lib/active_record/connection_adapters/emulation/oracle_adapter.rb +3 -1
- data/lib/active_record/connection_adapters/oracle_enhanced/column.rb +19 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/connection.rb +132 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/context_index.rb +345 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_limits.rb +52 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_statements.rb +280 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/database_tasks.rb +64 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/dbms_output.rb +59 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_connection.rb +538 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/jdbc_quoting.rb +38 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/lob.rb +46 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_connection.rb +435 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/oci_quoting.rb +44 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/procedures.rb +196 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/quoting.rb +164 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_creation.rb +95 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_definitions.rb +79 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_dumper.rb +194 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb +709 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/schema_statements_ext.rb +28 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/structure_dump.rb +353 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/type_metadata.rb +33 -0
- data/lib/active_record/connection_adapters/oracle_enhanced/version.rb +3 -0
- data/lib/active_record/connection_adapters/oracle_enhanced_adapter.rb +385 -1083
- data/lib/active_record/type/oracle_enhanced/boolean.rb +20 -0
- data/lib/active_record/type/oracle_enhanced/integer.rb +15 -0
- data/lib/active_record/type/oracle_enhanced/json.rb +10 -0
- data/lib/active_record/type/oracle_enhanced/national_character_string.rb +26 -0
- data/lib/active_record/type/oracle_enhanced/national_character_text.rb +36 -0
- data/lib/active_record/type/oracle_enhanced/raw.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/string.rb +29 -0
- data/lib/active_record/type/oracle_enhanced/text.rb +32 -0
- data/lib/active_record/type/oracle_enhanced/timestampltz.rb +25 -0
- data/lib/active_record/type/oracle_enhanced/timestamptz.rb +25 -0
- data/lib/activerecord-oracle_enhanced-adapter.rb +5 -13
- data/spec/active_record/connection_adapters/{oracle_enhanced_emulate_oracle_adapter_spec.rb → emulation/oracle_adapter_spec.rb} +5 -4
- data/spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb +469 -0
- data/spec/active_record/connection_adapters/{oracle_enhanced_context_index_spec.rb → oracle_enhanced/context_index_spec.rb} +140 -128
- data/spec/active_record/connection_adapters/oracle_enhanced/database_tasks_spec.rb +112 -0
- data/spec/active_record/connection_adapters/{oracle_enhanced_dbms_output_spec.rb → oracle_enhanced/dbms_output_spec.rb} +13 -13
- data/spec/active_record/connection_adapters/oracle_enhanced/procedures_spec.rb +365 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/quoting_spec.rb +196 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_dumper_spec.rb +492 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/schema_statements_spec.rb +1433 -0
- data/spec/active_record/connection_adapters/oracle_enhanced/structure_dump_spec.rb +478 -0
- data/spec/active_record/connection_adapters/oracle_enhanced_adapter_spec.rb +385 -550
- data/spec/active_record/connection_adapters/oracle_enhanced_data_types_spec.rb +92 -1249
- data/spec/active_record/oracle_enhanced/type/binary_spec.rb +119 -0
- data/spec/active_record/oracle_enhanced/type/boolean_spec.rb +208 -0
- data/spec/active_record/oracle_enhanced/type/dirty_spec.rb +139 -0
- data/spec/active_record/oracle_enhanced/type/float_spec.rb +48 -0
- data/spec/active_record/oracle_enhanced/type/integer_spec.rb +91 -0
- data/spec/active_record/oracle_enhanced/type/json_spec.rb +57 -0
- data/spec/active_record/oracle_enhanced/type/national_character_string_spec.rb +55 -0
- data/spec/active_record/oracle_enhanced/type/national_character_text_spec.rb +230 -0
- data/spec/active_record/oracle_enhanced/type/raw_spec.rb +122 -0
- data/spec/active_record/oracle_enhanced/type/text_spec.rb +229 -0
- data/spec/active_record/oracle_enhanced/type/timestamp_spec.rb +75 -0
- data/spec/spec_config.yaml.template +11 -0
- data/spec/spec_helper.rb +100 -93
- data/spec/support/alter_system_set_open_cursors.sql +1 -0
- data/spec/support/alter_system_user_password.sql +2 -0
- data/spec/support/create_oracle_enhanced_users.sql +31 -0
- metadata +105 -152
- data/.rspec +0 -2
- data/Gemfile +0 -52
- data/RUNNING_TESTS.md +0 -45
- data/Rakefile +0 -59
- data/activerecord-oracle_enhanced-adapter.gemspec +0 -130
- data/lib/active_record/connection_adapters/oracle_enhanced.rake +0 -105
- data/lib/active_record/connection_adapters/oracle_enhanced_activerecord_patches.rb +0 -41
- data/lib/active_record/connection_adapters/oracle_enhanced_base_ext.rb +0 -121
- data/lib/active_record/connection_adapters/oracle_enhanced_column.rb +0 -151
- data/lib/active_record/connection_adapters/oracle_enhanced_connection.rb +0 -119
- data/lib/active_record/connection_adapters/oracle_enhanced_context_index.rb +0 -359
- data/lib/active_record/connection_adapters/oracle_enhanced_core_ext.rb +0 -25
- data/lib/active_record/connection_adapters/oracle_enhanced_cpk.rb +0 -21
- data/lib/active_record/connection_adapters/oracle_enhanced_dirty.rb +0 -46
- data/lib/active_record/connection_adapters/oracle_enhanced_jdbc_connection.rb +0 -572
- data/lib/active_record/connection_adapters/oracle_enhanced_oci_connection.rb +0 -497
- data/lib/active_record/connection_adapters/oracle_enhanced_procedures.rb +0 -260
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_definitions.rb +0 -227
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_dumper.rb +0 -260
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements.rb +0 -428
- data/lib/active_record/connection_adapters/oracle_enhanced_schema_statements_ext.rb +0 -258
- data/lib/active_record/connection_adapters/oracle_enhanced_structure_dump.rb +0 -294
- data/lib/active_record/connection_adapters/oracle_enhanced_tasks.rb +0 -17
- data/lib/active_record/connection_adapters/oracle_enhanced_version.rb +0 -1
- data/spec/active_record/connection_adapters/oracle_enhanced_connection_spec.rb +0 -334
- data/spec/active_record/connection_adapters/oracle_enhanced_core_ext_spec.rb +0 -19
- data/spec/active_record/connection_adapters/oracle_enhanced_cpk_spec.rb +0 -113
- data/spec/active_record/connection_adapters/oracle_enhanced_dirty_spec.rb +0 -141
- data/spec/active_record/connection_adapters/oracle_enhanced_procedures_spec.rb +0 -378
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_dump_spec.rb +0 -440
- data/spec/active_record/connection_adapters/oracle_enhanced_schema_statements_spec.rb +0 -1400
- 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
|