ruby-plsql 0.8.0 → 0.9.9

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 (58) hide show
  1. checksums.yaml +4 -4
  2. data/History.txt +2 -0
  3. data/README.md +19 -3
  4. data/VERSION +1 -1
  5. data/lib/plsql/connection.rb +14 -14
  6. data/lib/plsql/helpers.rb +3 -3
  7. data/lib/plsql/jdbc_connection.rb +62 -22
  8. data/lib/plsql/oci_connection.rb +10 -10
  9. data/lib/plsql/package.rb +3 -3
  10. data/lib/plsql/procedure.rb +57 -22
  11. data/lib/plsql/procedure_call.rb +15 -15
  12. data/lib/plsql/schema.rb +17 -10
  13. data/lib/plsql/sequence.rb +2 -2
  14. data/lib/plsql/table.rb +6 -6
  15. data/lib/plsql/type.rb +7 -7
  16. data/lib/plsql/variable.rb +6 -5
  17. data/lib/plsql/version.rb +1 -1
  18. data/lib/plsql/view.rb +2 -2
  19. metadata +14 -140
  20. data/.github/stale.yml +0 -37
  21. data/.github/workflows/rubocop.yml +0 -37
  22. data/.github/workflows/test.yml +0 -69
  23. data/.rubocop.yml +0 -147
  24. data/.travis/oracle/download.sh +0 -15
  25. data/.travis/oracle/install.sh +0 -32
  26. data/.travis/setup_accounts.sh +0 -9
  27. data/.travis.yml +0 -88
  28. data/Gemfile +0 -24
  29. data/Rakefile +0 -53
  30. data/Vagrantfile +0 -38
  31. data/ci/network/admin/tnsnames.ora +0 -7
  32. data/ci/setup_accounts.sh +0 -9
  33. data/gemfiles/Gemfile.activerecord-5.0 +0 -21
  34. data/gemfiles/Gemfile.activerecord-5.1 +0 -21
  35. data/gemfiles/Gemfile.activerecord-5.2 +0 -21
  36. data/gemfiles/Gemfile.activerecord-6.0 +0 -21
  37. data/gemfiles/Gemfile.activerecord-6.1 +0 -21
  38. data/gemfiles/Gemfile.activerecord-main +0 -21
  39. data/lib/plsql/oci8_patches.rb +0 -25
  40. data/ruby-plsql.gemspec +0 -114
  41. data/spec/plsql/connection_spec.rb +0 -505
  42. data/spec/plsql/package_spec.rb +0 -172
  43. data/spec/plsql/procedure_spec.rb +0 -2390
  44. data/spec/plsql/schema_spec.rb +0 -364
  45. data/spec/plsql/sequence_spec.rb +0 -67
  46. data/spec/plsql/sql_statements_spec.rb +0 -91
  47. data/spec/plsql/table_spec.rb +0 -376
  48. data/spec/plsql/type_spec.rb +0 -299
  49. data/spec/plsql/variable_spec.rb +0 -497
  50. data/spec/plsql/version_spec.rb +0 -8
  51. data/spec/plsql/view_spec.rb +0 -264
  52. data/spec/spec.opts +0 -6
  53. data/spec/spec_helper.rb +0 -121
  54. data/spec/support/create_arunit_user.sql +0 -2
  55. data/spec/support/custom_config.rb.sample +0 -14
  56. data/spec/support/file_check_script.sh +0 -9
  57. data/spec/support/test_db.rb +0 -149
  58. data/spec/support/unlock_and_setup_hr_user.sql +0 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 786698aedb4dff545a44a91fda4b7529d392b6d3afaee1e5a4447ee3be81a9d6
4
- data.tar.gz: fa71bb0ec3ad558ab6805b077069401225fb1d8e949f4dc00360ecd085bae81b
3
+ metadata.gz: 8ce42b7f40a83d55130a51e1acae222dbb1361a7d4a43b712873e2cddae24571
4
+ data.tar.gz: 7cd295463301ff322ed0312cabd73f08337068a8b3b3c57e724a5dae8efb51a0
5
5
  SHA512:
6
- metadata.gz: f814a2f6b9b2b0784e41f861f0ed0965cb814aaf4e8687293f8285e4758810fd4979e1ef96d7f7b0a305e701f0426fb81d3be0ece725abdf1888270a91131691
7
- data.tar.gz: c17b7330167301d53715b4d43441acb67697a53a0b0378ea51f65824b96215ece3730db3af01be4e7442a2c3c632dc9fc14109425cfda73cfa535997d77444ba
6
+ metadata.gz: '08dabe0856244a3b1e487df17664d996a204f026ce6c19c50dec721bcc42be7936ce41b90ddecfc7d2b798351e624df068602d8da4c1e091e1096f534d8b96c6'
7
+ data.tar.gz: 5b25be049a7575a74911028db9b82986a692af4aa895d1561e1de3600bcfd418429c969bb45832536782ef84c70cf60bb8549207470d0cac962203666f7c035d
data/History.txt CHANGED
@@ -1,3 +1,5 @@
1
+ This file is no longer being updated. For the latest updates and release information, please see: https://github.com/rsim/ruby-plsql/releases
2
+
1
3
  == 0.8.0 2021-08-10
2
4
  * Improvements and fixes
3
5
  * Support Rails 6.1 [#193]
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- [![Build Status](https://travis-ci.com/rsim/ruby-plsql.svg?branch=master)](https://travis-ci.com/rsim/ruby-plsql)
1
+ [![Test](https://github.com/rsim/ruby-plsql/actions/workflows/test.yml/badge.svg)](https://github.com/rsim/ruby-plsql/actions/workflows/test.yml)
2
2
 
3
3
  ruby-plsql
4
4
  ==========
@@ -120,6 +120,22 @@ plsql.activerecord_class = ActiveRecord::Base
120
120
  and then you do not need to specify plsql.connection (this is also safer when ActiveRecord reestablishes connection to database).
121
121
 
122
122
 
123
+ ### JRuby JDBC connection:
124
+
125
+ When using JRuby, the `connect!` method with `:host` and `:database` options uses the thin-style service name syntax by default:
126
+
127
+ ```ruby
128
+ # Connects using service name syntax: jdbc:oracle:thin:@//localhost:1521/MYSERVICENAME
129
+ plsql.connect! username: "hr", password: "hr", host: "localhost", database: "MYSERVICENAME"
130
+ ```
131
+
132
+ If you need to connect using the legacy SID syntax (for Oracle databases older than 12c), prefix the database name with a colon:
133
+
134
+ ```ruby
135
+ # Connects using SID syntax: jdbc:oracle:thin:@localhost:1521:MYSID
136
+ plsql.connect! username: "hr", password: "hr", host: "localhost", database: ":MYSID"
137
+ ```
138
+
123
139
  ### Cheat Sheet:
124
140
 
125
141
  You may have a look at this [Cheat Sheet](http://cheatography.com/jgebal/cheat-sheets/ruby-plsql-cheat-sheet/) for instructions on how to use ruby-plsql
@@ -135,10 +151,10 @@ or include gem in Gemfile if using bundler.
135
151
 
136
152
  In addition install either ruby-oci8 (for MRI/YARV) or copy Oracle JDBC driver to $JRUBY_HOME/lib (for JRuby).
137
153
 
138
- If you are using MRI Ruby implementation then you need to install ruby-oci8 gem (version 2.0.x or 2.1.x)
154
+ If you are using MRI Ruby implementation then you need to install ruby-oci8 gem (version 2.1 or higher)
139
155
  as well as Oracle client, e.g. [Oracle Instant Client](http://www.oracle.com/technetwork/database/features/instant-client/index-097480.html).
140
156
 
141
- If you are using JRuby then you need to download latest [Oracle JDBC driver](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-112010-090769.html) - either ojdbc7.jar for Java 8 and 7, ojdbc6.jar for Java 6, 7, 8 or ojdbc5.jar for Java 5. You can refer [the support matrix](http://www.oracle.com/technetwork/database/enterprise-edition/jdbc-faq-090281.html#01_03) for details.
157
+ If you are using JRuby then you need to download the appropriate [Oracle JDBC driver](https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html) for your Java version - ojdbc17.jar for Java 17+, ojdbc11.jar for Java 11+, ojdbc8.jar for Java 8+, ojdbc7.jar for Java 7, ojdbc6.jar for Java 6, or ojdbc5.jar for Java 5.
142
158
 
143
159
  And copy this file to one of these locations. JDBC driver will be searched in this order:
144
160
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.8.0
1
+ 0.9.9
@@ -3,13 +3,13 @@ module PLSQL
3
3
  attr_reader :raw_driver
4
4
  attr_reader :activerecord_class
5
5
 
6
- def initialize(raw_conn, ar_class = nil) #:nodoc:
6
+ def initialize(raw_conn, ar_class = nil) # :nodoc:
7
7
  @raw_driver = self.class.driver_type
8
8
  @raw_connection = raw_conn
9
9
  @activerecord_class = ar_class
10
10
  end
11
11
 
12
- def self.create(raw_conn, ar_class = nil) #:nodoc:
12
+ def self.create(raw_conn, ar_class = nil) # :nodoc:
13
13
  if ar_class && !(defined?(::ActiveRecord) && ar_class.ancestors.include?(::ActiveRecord::Base))
14
14
  raise ArgumentError, "Wrong ActiveRecord class"
15
15
  end
@@ -23,7 +23,7 @@ module PLSQL
23
23
  end
24
24
  end
25
25
 
26
- def self.create_new(params) #:nodoc:
26
+ def self.create_new(params) # :nodoc:
27
27
  conn = case driver_type
28
28
  when :oci
29
29
  OCIConnection.create_raw(params)
@@ -36,7 +36,7 @@ module PLSQL
36
36
  conn
37
37
  end
38
38
 
39
- def self.driver_type #:nodoc:
39
+ def self.driver_type # :nodoc:
40
40
  # MRI 1.8.6 or YARV 1.9.1 or TruffleRuby
41
41
  @driver_type ||= if (!defined?(RUBY_ENGINE) || RUBY_ENGINE == "ruby" || RUBY_ENGINE == "truffleruby") && defined?(OCI8)
42
42
  :oci
@@ -67,18 +67,18 @@ module PLSQL
67
67
  @raw_driver == :jdbc
68
68
  end
69
69
 
70
- def logoff #:nodoc:
70
+ def logoff # :nodoc:
71
71
  # Rollback any uncommited transactions
72
72
  rollback
73
73
  # Common cleanup activities before logoff, should be called from particular driver method
74
74
  drop_session_ruby_temporary_tables
75
75
  end
76
76
 
77
- def commit #:nodoc:
77
+ def commit # :nodoc:
78
78
  raise NoMethodError, "Not implemented for this raw driver"
79
79
  end
80
80
 
81
- def rollback #:nodoc:
81
+ def rollback # :nodoc:
82
82
  raise NoMethodError, "Not implemented for this raw driver"
83
83
  end
84
84
 
@@ -98,21 +98,21 @@ module PLSQL
98
98
  raise NoMethodError, "Not implemented for this raw driver"
99
99
  end
100
100
 
101
- def select_first(sql, *bindvars) #:nodoc:
101
+ def select_first(sql, *bindvars) # :nodoc:
102
102
  cursor = cursor_from_query(sql, bindvars, prefetch_rows: 1)
103
103
  cursor.fetch
104
104
  ensure
105
105
  cursor.close rescue nil
106
106
  end
107
107
 
108
- def select_hash_first(sql, *bindvars) #:nodoc:
108
+ def select_hash_first(sql, *bindvars) # :nodoc:
109
109
  cursor = cursor_from_query(sql, bindvars, prefetch_rows: 1)
110
110
  cursor.fetch_hash
111
111
  ensure
112
112
  cursor.close rescue nil
113
113
  end
114
114
 
115
- def select_all(sql, *bindvars, &block) #:nodoc:
115
+ def select_all(sql, *bindvars, &block) # :nodoc:
116
116
  cursor = cursor_from_query(sql, bindvars)
117
117
  results = []
118
118
  row_count = 0
@@ -129,7 +129,7 @@ module PLSQL
129
129
  cursor.close rescue nil
130
130
  end
131
131
 
132
- def select_hash_all(sql, *bindvars, &block) #:nodoc:
132
+ def select_hash_all(sql, *bindvars, &block) # :nodoc:
133
133
  cursor = cursor_from_query(sql, bindvars)
134
134
  results = []
135
135
  row_count = 0
@@ -146,11 +146,11 @@ module PLSQL
146
146
  cursor.close rescue nil
147
147
  end
148
148
 
149
- def exec(sql, *bindvars) #:nodoc:
149
+ def exec(sql, *bindvars) # :nodoc:
150
150
  raise NoMethodError, "Not implemented for this raw driver"
151
151
  end
152
152
 
153
- def parse(sql) #:nodoc:
153
+ def parse(sql) # :nodoc:
154
154
  raise NoMethodError, "Not implemented for this raw driver"
155
155
  end
156
156
 
@@ -181,7 +181,7 @@ module PLSQL
181
181
 
182
182
  # all_synonyms view is quite slow therefore
183
183
  # this implementation is overriden in OCI connection with faster native OCI method
184
- def describe_synonym(schema_name, synonym_name) #:nodoc:
184
+ def describe_synonym(schema_name, synonym_name) # :nodoc:
185
185
  select_first(
186
186
  "SELECT table_owner, table_name FROM all_synonyms WHERE owner = :owner AND synonym_name = :synonym_name",
187
187
  schema_name.to_s.upcase, synonym_name.to_s.upcase)
data/lib/plsql/helpers.rb CHANGED
@@ -1,6 +1,6 @@
1
- module PLSQL #:nodoc:
2
- module ArrayHelpers #:nodoc:
3
- def self.to_hash(keys, values) #:nodoc:
1
+ module PLSQL # :nodoc:
2
+ module ArrayHelpers # :nodoc:
3
+ def self.to_hash(keys, values) # :nodoc:
4
4
  (0...keys.size).inject({}) { |hash, i| hash[keys[i]] = values[i]; hash }
5
5
  end
6
6
  end
@@ -1,23 +1,26 @@
1
+ ojdbc_jars = []
2
+
1
3
  begin
2
4
  require "java"
3
5
  require "jruby"
4
6
 
5
- # ojdbc6.jar or ojdbc5.jar file should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
7
+ # Oracle JDBC driver jar should be in JRUBY_HOME/lib or should be in ENV['PATH'] or load path
6
8
 
7
9
  java_version = java.lang.System.getProperty("java.version")
8
- ojdbc_jars = if java_version =~ /^1.5/
9
- %w(ojdbc5.jar)
10
- elsif java_version =~ /^1.6/
11
- %w(ojdbc6.jar)
12
- elsif java_version >= "1.7"
13
- # Oracle 11g client ojdbc6.jar is also compatible with Java 1.7
14
- # Oracle 12c client provides new ojdbc7.jar
15
- %w(ojdbc7.jar ojdbc6.jar)
10
+ java_major = if java_version =~ /^1\.(\d+)/
11
+ $1.to_i
16
12
  else
17
- []
13
+ java_version.to_i
18
14
  end
19
15
 
20
- if ENV_JAVA["java.class.path"] !~ Regexp.new(ojdbc_jars.join("|"))
16
+ ojdbc_jars << "ojdbc17.jar" if java_major >= 17
17
+ ojdbc_jars << "ojdbc11.jar" if java_major >= 11
18
+ ojdbc_jars << "ojdbc8.jar" if java_major >= 8
19
+ ojdbc_jars << "ojdbc7.jar" if java_major >= 7
20
+ ojdbc_jars << "ojdbc6.jar" if java_major >= 6
21
+ ojdbc_jars << "ojdbc5.jar" if java_major == 5
22
+
23
+ if ENV_JAVA["java.class.path"] !~ Regexp.union(ojdbc_jars)
21
24
  # On Unix environment variable should be PATH, on Windows it is sometimes Path
22
25
  env_path = (ENV["PATH"] || ENV["Path"] || "").split(File::PATH_SEPARATOR)
23
26
  # Look for JDBC driver at first in lib subdirectory (application specific JDBC file version)
@@ -25,7 +28,7 @@ begin
25
28
  ["./lib"].concat($LOAD_PATH).concat(env_path).detect do |dir|
26
29
  # check any compatible JDBC driver in the priority order
27
30
  ojdbc_jars.any? do |ojdbc_jar|
28
- if File.exists?(file_path = File.join(dir, ojdbc_jar))
31
+ if File.exist?(file_path = File.join(dir, ojdbc_jar))
29
32
  require file_path
30
33
  true
31
34
  end
@@ -33,29 +36,66 @@ begin
33
36
  end
34
37
  end
35
38
 
36
- java.sql.DriverManager.registerDriver Java::oracle.jdbc.OracleDriver.new
37
-
38
39
  # set tns_admin property from TNS_ADMIN environment variable
39
40
  if !java.lang.System.get_property("oracle.net.tns_admin") && ENV["TNS_ADMIN"]
40
41
  java.lang.System.set_property("oracle.net.tns_admin", ENV["TNS_ADMIN"])
41
42
  end
42
43
 
43
- rescue LoadError, NameError
44
+ rescue LoadError
44
45
  # JDBC driver is unavailable.
45
46
  raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. Please install #{ojdbc_jars.empty? ? "Oracle JDBC" : ojdbc_jars.join(' or ') } library."
46
47
  end
47
48
 
48
49
  module PLSQL
49
- class JDBCConnection < Connection #:nodoc:
50
+ class JDBCConnection < Connection # :nodoc:
51
+ begin
52
+ ORACLE_DRIVER = Java::oracle.jdbc.OracleDriver.new
53
+ java.sql.DriverManager.registerDriver ORACLE_DRIVER
54
+ rescue NameError
55
+ raise LoadError, "ERROR: ruby-plsql could not load Oracle JDBC driver. " \
56
+ "Please install the appropriate Oracle JDBC driver. " \
57
+ "See https://www.oracle.com/database/technologies/appdev/jdbc-downloads.html"
58
+ end
59
+
50
60
  def self.create_raw(params)
61
+ url = jdbc_connection_url(params)
62
+ conn = begin
63
+ java.sql.DriverManager.getConnection(url, params[:username], params[:password])
64
+ rescue Java::JavaSql::SQLException => e
65
+ raise unless e.message =~ /no suitable driver/i
66
+ # bypass DriverManager to work in cases where ojdbc*.jar
67
+ # is added to the load path at runtime and not on the
68
+ # system classpath
69
+ ORACLE_DRIVER.connect(url, java.util.Properties.new.tap do |props|
70
+ props.setProperty("user", params[:username])
71
+ props.setProperty("password", params[:password])
72
+ end)
73
+ end
74
+ conn.setAutoCommit(false)
75
+ new(conn)
76
+ end
77
+
78
+ def self.jdbc_connection_url(params)
51
79
  database = params[:database]
52
- url = if ENV["TNS_ADMIN"] && database && !params[:host] && !params[:url]
80
+ if ENV["TNS_ADMIN"] && database && database !~ %r{\A[:/]} && !params[:host] && !params[:url]
53
81
  "jdbc:oracle:thin:@#{database}"
54
82
  else
55
- database = ":#{database}" unless database.match(/^(\:|\/)/)
56
- params[:url] || "jdbc:oracle:thin:@#{params[:host] || 'localhost'}:#{params[:port] || 1521}#{database}"
83
+ return params[:url] if params[:url]
84
+
85
+ raise ArgumentError, "database or url option is required" if database.nil? || database.empty?
86
+
87
+ host = params[:host] || "localhost"
88
+ port = params[:port] || 1521
89
+
90
+ if database =~ /^:/
91
+ # SID syntax: jdbc:oracle:thin:@host:port:SID
92
+ "jdbc:oracle:thin:@#{host}:#{port}#{database}"
93
+ else
94
+ # service name syntax: jdbc:oracle:thin:@//host:port/service_name
95
+ database = "/#{database}" unless database =~ /^\//
96
+ "jdbc:oracle:thin:@//#{host}:#{port}#{database}"
97
+ end
57
98
  end
58
- new(java.sql.DriverManager.getConnection(url, params[:username], params[:password]))
59
99
  end
60
100
 
61
101
  def set_time_zone(time_zone = nil)
@@ -98,7 +138,7 @@ module PLSQL
98
138
  cs.close rescue nil
99
139
  end
100
140
 
101
- class CallableStatement #:nodoc:
141
+ class CallableStatement # :nodoc:
102
142
  def initialize(conn, sql)
103
143
  @sql = sql
104
144
  @connection = conn
@@ -145,7 +185,7 @@ module PLSQL
145
185
  end
146
186
  end
147
187
 
148
- class Cursor #:nodoc:
188
+ class Cursor # :nodoc:
149
189
  include Connection::CursorCommon
150
190
 
151
191
  attr_reader :result_set
@@ -14,17 +14,13 @@ rescue LoadError
14
14
  raise LoadError, "ERROR: ruby-plsql could not load ruby-oci8 library. #{msg}"
15
15
  end
16
16
 
17
- require "plsql/oci8_patches"
18
-
19
17
  # check ruby-oci8 version
20
- required_oci8_version = [2, 0, 3]
21
- oci8_version_ints = OCI8::VERSION.scan(/\d+/).map { |s| s.to_i }
22
- if (oci8_version_ints <=> required_oci8_version) < 0
23
- raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version #{required_oci8_version.join('.')} or later."
18
+ if Gem::Version.new(OCI8::VERSION) < Gem::Version.new("2.1.0")
19
+ raise LoadError, "ERROR: ruby-oci8 version #{OCI8::VERSION} is too old. Please install ruby-oci8 version 2.1.0 or later."
24
20
  end
25
21
 
26
22
  module PLSQL
27
- class OCIConnection < Connection #:nodoc:
23
+ class OCIConnection < Connection # :nodoc:
28
24
  def self.create_raw(params)
29
25
  connection_string = if params[:host]
30
26
  "//#{params[:host]}:#{params[:port] || 1521}/#{params[:database]}"
@@ -64,7 +60,7 @@ module PLSQL
64
60
  true
65
61
  end
66
62
 
67
- class Cursor #:nodoc:
63
+ class Cursor # :nodoc:
68
64
  include Connection::CursorCommon
69
65
 
70
66
  attr_reader :raw_cursor
@@ -157,7 +153,7 @@ module PLSQL
157
153
  [DateTime, nil]
158
154
  when "TIMESTAMP", "TIMESTAMP WITH TIME ZONE", "TIMESTAMP WITH LOCAL TIME ZONE"
159
155
  [Time, nil]
160
- when "TABLE", "VARRAY", "OBJECT", "XMLTYPE"
156
+ when "TABLE", "VARRAY", "OBJECT", "XMLTYPE", "OPAQUE/XMLTYPE"
161
157
  # create Ruby class for collection
162
158
  klass = OCI8::Object::Base.get_class_by_typename(metadata[:sql_type_name])
163
159
  unless klass
@@ -289,7 +285,11 @@ module PLSQL
289
285
  # ActiveRecord Oracle enhanced adapter puts OCI8EnhancedAutoRecover wrapper around OCI8
290
286
  # in this case we need to pass original OCI8 connection
291
287
  else
292
- raw_connection.instance_variable_get(:@connection)
288
+ if raw_connection.instance_variable_defined?(:@raw_connection)
289
+ raw_connection.instance_variable_get(:@raw_connection)
290
+ else
291
+ raw_connection.instance_variable_get(:@connection)
292
+ end
293
293
  end
294
294
  end
295
295
 
data/lib/plsql/package.rb CHANGED
@@ -1,5 +1,5 @@
1
1
  module PLSQL
2
- module PackageClassMethods #:nodoc:
2
+ module PackageClassMethods # :nodoc:
3
3
  def find(schema, package)
4
4
  package_name = package.to_s.upcase
5
5
  find_in_schema(schema, package_name) || find_by_synonym(schema, package_name)
@@ -32,7 +32,7 @@ module PLSQL
32
32
  end
33
33
  end
34
34
 
35
- class Package #:nodoc:
35
+ class Package # :nodoc:
36
36
  extend PackageClassMethods
37
37
 
38
38
  def initialize(schema, package, override_schema_name = nil)
@@ -56,7 +56,7 @@ module PLSQL
56
56
  private
57
57
 
58
58
  def method_missing(method, *args, &block)
59
- method = method.to_s
59
+ method = +method.to_s
60
60
  method.chop! if (assignment = method[/=$/])
61
61
 
62
62
  case (object = self[method])
@@ -1,5 +1,5 @@
1
1
  module PLSQL
2
- module ProcedureClassMethods #:nodoc:
2
+ module ProcedureClassMethods # :nodoc:
3
3
  def find(schema, procedure, package = nil, override_schema_name = nil)
4
4
  if package.nil?
5
5
  if (row = schema.select_first(
@@ -53,12 +53,12 @@ module PLSQL
53
53
  end
54
54
  end
55
55
 
56
- module ProcedureCommon #:nodoc:
56
+ module ProcedureCommon # :nodoc:
57
57
  attr_reader :arguments, :argument_list, :out_list, :return
58
58
  attr_reader :schema, :schema_name, :package, :procedure
59
59
 
60
60
  # return type string from metadata that can be used in DECLARE block or table definition
61
- def self.type_to_sql(metadata) #:nodoc:
61
+ def self.type_to_sql(metadata) # :nodoc:
62
62
  case metadata[:data_type]
63
63
  when "NUMBER"
64
64
  precision, scale = metadata[:data_precision], metadata[:data_scale]
@@ -82,7 +82,7 @@ module PLSQL
82
82
  end
83
83
 
84
84
  # get procedure argument metadata from data dictionary
85
- def get_argument_metadata #:nodoc:
85
+ def get_argument_metadata # :nodoc:
86
86
  if (@schema.connection.database_version <=> [18, 0, 0, 0]) >= 0
87
87
  get_argument_metadata_from_18c
88
88
  else
@@ -90,7 +90,7 @@ module PLSQL
90
90
  end
91
91
  end
92
92
 
93
- def get_argument_metadata_below_18c #:nodoc:
93
+ def get_argument_metadata_below_18c # :nodoc:
94
94
  @arguments = {}
95
95
  @argument_list = {}
96
96
  @out_list = {}
@@ -126,6 +126,9 @@ module PLSQL
126
126
  data_type, in_out, data_length, data_precision, data_scale, char_used,
127
127
  char_length, type_owner, type_name, type_subname, defaulted = r
128
128
 
129
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
130
+ data_type = "PL/SQL BOOLEAN" if data_type == "BOOLEAN"
131
+
129
132
  @overloaded ||= !overload.nil?
130
133
  # if not overloaded then store arguments at key 0
131
134
  overload ||= 0
@@ -206,7 +209,7 @@ module PLSQL
206
209
  end
207
210
 
208
211
  # get procedure argument metadata from data dictionary
209
- def get_argument_metadata_from_18c #:nodoc:
212
+ def get_argument_metadata_from_18c # :nodoc:
210
213
  @arguments = {}
211
214
  @argument_list = {}
212
215
  @out_list = {}
@@ -231,10 +234,13 @@ module PLSQL
231
234
  @object_id, @schema_name, @procedure
232
235
  ) do |r|
233
236
 
234
- subprogram_id, object_name, overload, argument_name, position,
237
+ subprogram_id, _object_name, overload, argument_name, position,
235
238
  data_type, in_out, data_length, data_precision, data_scale, char_used,
236
239
  char_length, type_owner, type_name, type_package, type_object_type, defaulted = r
237
240
 
241
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
242
+ data_type = "PL/SQL BOOLEAN" if data_type == "BOOLEAN"
243
+
238
244
  @overloaded ||= !overload.nil?
239
245
  # if not overloaded then store arguments at key 0
240
246
  overload ||= 0
@@ -298,7 +304,7 @@ module PLSQL
298
304
  construct_argument_list_for_overloads
299
305
  end
300
306
 
301
- def construct_argument_list_for_overloads #:nodoc:
307
+ def construct_argument_list_for_overloads # :nodoc:
302
308
  @overloads = @arguments.keys.sort
303
309
  @overloads.each do |overload|
304
310
  @argument_list[overload] = @arguments[overload].keys.sort { |k1, k2| @arguments[overload][k1][:position] <=> @arguments[overload][k2][:position] }
@@ -306,10 +312,10 @@ module PLSQL
306
312
  end
307
313
  end
308
314
 
309
- def ensure_tmp_tables_created(overload) #:nodoc:
315
+ def ensure_tmp_tables_created(overload) # :nodoc:
310
316
  return if @tmp_tables_created.nil? || @tmp_tables_created[overload]
311
317
  @tmp_table_names[overload] && @tmp_table_names[overload].each do |table_name, argument_metadata|
312
- sql = "CREATE GLOBAL TEMPORARY TABLE #{table_name} (\n"
318
+ sql = +"CREATE GLOBAL TEMPORARY TABLE #{table_name} (\n"
313
319
  element_metadata = argument_metadata[:element]
314
320
  case element_metadata[:data_type]
315
321
  when "PL/SQL RECORD"
@@ -330,7 +336,7 @@ module PLSQL
330
336
  @tmp_tables_created[overload] = true
331
337
  end
332
338
 
333
- def build_sql_type_name(type_owner, type_package, type_name) #:nodoc:
339
+ def build_sql_type_name(type_owner, type_package, type_name) # :nodoc:
334
340
  if type_owner == nil || type_owner == "PUBLIC"
335
341
  type_owner_res = ""
336
342
  else
@@ -345,7 +351,7 @@ module PLSQL
345
351
  type_name_res && "#{type_owner_res}#{type_name_res}"
346
352
  end
347
353
 
348
- def get_field_definitions(argument_metadata) #:nodoc:
354
+ def get_field_definitions(argument_metadata) # :nodoc:
349
355
  fields = {}
350
356
  case argument_metadata[:type_object_type]
351
357
  when "PACKAGE"
@@ -355,10 +361,13 @@ module PLSQL
355
361
  WHERE t.OWNER = :owner AND t.type_name = :type_name AND t.package_name = :package_name
356
362
  AND ta.OWNER = t.owner AND ta.TYPE_NAME = t.TYPE_NAME AND ta.PACKAGE_NAME = t.PACKAGE_NAME
357
363
  ORDER BY attr_no",
358
- @schema_name, argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
364
+ argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname]) do |r|
359
365
 
360
366
  attr_no, attr_name, attr_type_owner, attr_type_name, attr_type_package, attr_length, attr_precision, attr_scale, attr_char_used = r
361
367
 
368
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
369
+ attr_type_name = "PL/SQL BOOLEAN" if attr_type_name == "BOOLEAN"
370
+
362
371
  fields[attr_name.downcase.to_sym] = {
363
372
  position: attr_no.to_i,
364
373
  data_type: attr_type_owner == nil ? attr_type_name : get_composite_type(attr_type_owner, attr_type_name, attr_type_package),
@@ -382,12 +391,15 @@ module PLSQL
382
391
  when "TABLE", "VIEW"
383
392
  @schema.select_all(
384
393
  "SELECT column_id, column_name, data_type, data_length, data_precision, data_scale, char_length, char_used
385
- FROM ALL_TAB_COLS WHERE OWNER = :owner AND TABLE_NAME = :type_name
394
+ FROM ALL_TAB_COLUMNS WHERE OWNER = :owner AND TABLE_NAME = :type_name
386
395
  ORDER BY column_id",
387
- @schema_name, argument_metadata[:type_name]) do |r|
396
+ argument_metadata[:type_owner], argument_metadata[:type_name]) do |r|
388
397
 
389
398
  col_no, col_name, col_type_name, col_length, col_precision, col_scale, col_char_length, col_char_used = r
390
399
 
400
+ # remove precision (n) from data_type (returned for TIMESTAMPs and INTERVALs)
401
+ col_type_name = col_type_name.sub(/\(\d+\)/, "")
402
+
391
403
  fields[col_name.downcase.to_sym] = {
392
404
  position: col_no.to_i,
393
405
  data_type: col_type_name,
@@ -408,7 +420,7 @@ module PLSQL
408
420
  fields
409
421
  end
410
422
 
411
- def get_element_definition(argument_metadata) #:nodoc:
423
+ def get_element_definition(argument_metadata) # :nodoc:
412
424
  element_metadata = {}
413
425
  if collection_type?(argument_metadata[:data_type])
414
426
  case argument_metadata[:type_object_type]
@@ -417,10 +429,13 @@ module PLSQL
417
429
  "SELECT elem_type_owner, elem_type_name, elem_type_package, length, precision, scale, char_used, index_by
418
430
  FROM ALL_PLSQL_COLL_TYPES t
419
431
  WHERE t.OWNER = :owner AND t.TYPE_NAME = :type_name AND t.PACKAGE_NAME = :package_name",
420
- @schema_name, argument_metadata[:type_name], argument_metadata[:type_subname])
432
+ argument_metadata[:type_owner], argument_metadata[:type_name], argument_metadata[:type_subname])
421
433
 
422
434
  elem_type_owner, elem_type_name, elem_type_package, elem_length, elem_precision, elem_scale, elem_char_used, index_by = r
423
435
 
436
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
437
+ elem_type_name = "PL/SQL BOOLEAN" if elem_type_name == "BOOLEAN"
438
+
424
439
  if index_by == "VARCHAR2"
425
440
  raise ArgumentError, "Index-by Varchar-Table (associative array) #{argument_metadata[:type_name]} is not supported"
426
441
  end
@@ -448,16 +463,36 @@ module PLSQL
448
463
 
449
464
  if elem_type_package != nil
450
465
  element_metadata[:fields] = get_field_definitions(element_metadata)
466
+ elsif elem_type_name && elem_type_name =~ /\A(.+)%ROWTYPE\z/
467
+ # TABLE OF table%ROWTYPE: Oracle stores elem_type_name as "TABLE_NAME%ROWTYPE"
468
+ rowtype_table_name = $1
469
+ check_owner = elem_type_owner || @schema_name
470
+ object_type_row = @schema.select_first(
471
+ "SELECT object_type FROM ALL_OBJECTS WHERE owner = :owner AND object_name = :name AND object_type IN ('TABLE', 'VIEW')",
472
+ check_owner, rowtype_table_name)
473
+ if object_type_row
474
+ element_metadata[:type_owner] ||= check_owner
475
+ element_metadata[:type_name] = rowtype_table_name
476
+ element_metadata[:sql_type_name] = build_sql_type_name(check_owner, nil, rowtype_table_name)
477
+ element_metadata[:data_type] = "PL/SQL RECORD"
478
+ element_metadata[:type_object_type] = object_type_row[0]
479
+ element_metadata[:fields] = get_field_definitions(element_metadata)
480
+ else
481
+ raise ArgumentError, "Could not resolve #{check_owner}.#{rowtype_table_name} to a table or view for #{elem_type_name}"
482
+ end
451
483
  end
452
484
  when "TYPE"
453
485
  r = @schema.select_first(
454
486
  "SELECT elem_type_owner, elem_type_name, length, precision, scale, char_used
455
487
  FROM ALL_COLL_TYPES t
456
488
  WHERE t.owner = :owner AND t.TYPE_NAME = :type_name",
457
- @schema_name, argument_metadata[:type_name]
489
+ argument_metadata[:type_owner], argument_metadata[:type_name]
458
490
  )
459
491
  elem_type_owner, elem_type_name, elem_length, elem_precision, elem_scale, elem_char_used = r
460
492
 
493
+ # Oracle 23c reports BOOLEAN as "BOOLEAN" instead of "PL/SQL BOOLEAN"
494
+ elem_type_name = "PL/SQL BOOLEAN" if elem_type_name == "BOOLEAN"
495
+
461
496
  element_metadata = {
462
497
  position: 1,
463
498
  data_type: elem_type_owner == nil ? elem_type_name : "OBJECT",
@@ -510,21 +545,21 @@ module PLSQL
510
545
  end
511
546
 
512
547
  PLSQL_COMPOSITE_TYPES = ["PL/SQL RECORD", "PL/SQL TABLE", "TABLE", "VARRAY", "REF CURSOR"].freeze
513
- def composite_type?(data_type) #:nodoc:
548
+ def composite_type?(data_type) # :nodoc:
514
549
  PLSQL_COMPOSITE_TYPES.include? data_type
515
550
  end
516
551
 
517
552
  PLSQL_COLLECTION_TYPES = ["PL/SQL TABLE", "TABLE", "VARRAY"].freeze
518
- def collection_type?(data_type) #:nodoc:
553
+ def collection_type?(data_type) # :nodoc:
519
554
  PLSQL_COLLECTION_TYPES.include? data_type
520
555
  end
521
556
 
522
- def overloaded? #:nodoc:
557
+ def overloaded? # :nodoc:
523
558
  @overloaded
524
559
  end
525
560
  end
526
561
 
527
- class Procedure #:nodoc:
562
+ class Procedure # :nodoc:
528
563
  extend ProcedureClassMethods
529
564
  include ProcedureCommon
530
565