Skip to content

Commit 3be6115

Browse files
committed
Merge pull request rsim#2521 from yahonda/add-describe-regression-test
Replace slow UNION ALL query with optimized all_objects query
1 parent a9949f2 commit 3be6115

3 files changed

Lines changed: 54 additions & 36 deletions

File tree

lib/active_record/connection_adapters/oracle_enhanced/schema_statements.rb

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -756,32 +756,39 @@ def resolve_data_source_name(name)
756756
end
757757

758758
binds = [
759-
bind_string("table_owner", owner),
760-
bind_string("table_name", identifier),
761-
bind_string("table_owner", owner),
762-
bind_string("table_name", identifier),
763759
bind_string("table_owner", owner),
764760
bind_string("table_name", identifier),
765761
bind_string("real_name", real_name),
766762
]
763+
# Single-pass lookup against all_objects, ordered so the first row
764+
# is the one the legacy 4-way UNION ALL (all_tables, all_views,
765+
# owner synonym, public synonym) would have returned: prefer a
766+
# match in the caller's schema over PUBLIC, and within a schema
767+
# prefer TABLE over VIEW over SYNONYM.
767768
result = select_one(<<~SQL.squish, "SCHEMA", binds)
768-
SELECT owner, table_name, 'TABLE' name_type
769-
FROM all_tables WHERE owner = :table_owner AND table_name = :table_name
770-
UNION ALL
771-
SELECT owner, view_name table_name, 'VIEW' name_type
772-
FROM all_views WHERE owner = :table_owner AND view_name = :table_name
773-
UNION ALL
774-
SELECT table_owner, table_name, 'SYNONYM' name_type
775-
FROM all_synonyms WHERE owner = :table_owner AND synonym_name = :table_name
776-
UNION ALL
777-
SELECT table_owner, table_name, 'SYNONYM' name_type
778-
FROM all_synonyms WHERE owner = 'PUBLIC' AND synonym_name = :real_name
769+
SELECT owner, object_name table_name, object_type name_type
770+
FROM all_objects
771+
WHERE ((owner = :table_owner AND object_name = :table_name)
772+
OR (owner = 'PUBLIC' AND object_name = :real_name))
773+
AND object_type IN ('TABLE', 'VIEW', 'SYNONYM')
774+
ORDER BY DECODE(owner, 'PUBLIC', 2, 1),
775+
DECODE(object_type, 'TABLE', 1, 'VIEW', 2, 'SYNONYM', 3)
779776
SQL
780777

781778
raise OracleEnhanced::ConnectionException, %Q{"DESC #{name}" failed; does it exist?} unless result
782779

783780
if result["name_type"] == "SYNONYM"
784-
name = "#{result['owner'] && "#{result['owner']}."}#{result['table_name']}"
781+
synonym_binds = [
782+
bind_string("owner", result["owner"]),
783+
bind_string("synonym_name", result["table_name"]),
784+
]
785+
syn = select_one(<<~SQL.squish, "SCHEMA", synonym_binds)
786+
SELECT table_owner, table_name
787+
FROM all_synonyms
788+
WHERE owner = :owner AND synonym_name = :synonym_name
789+
SQL
790+
raise OracleEnhanced::ConnectionException, %Q{"DESC #{name}" failed; does it exist?} unless syn
791+
name = "#{syn['table_owner'] && "#{syn['table_owner']}."}#{syn['table_name']}"
785792
else
786793
return [result["owner"], result["table_name"]]
787794
end

spec/active_record/connection_adapters/oracle_enhanced/connection_spec.rb

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -590,6 +590,33 @@ def resolve(name)
590590
expect(resolve("all_tables")).to eq(["SYS", "ALL_TABLES"])
591591
end
592592

593+
# Exercises all five catalog paths (table, view, materialized view,
594+
# private synonym, public synonym) for one underlying table in a single
595+
# run. The individual cases above use disjoint fixtures; this one proves
596+
# the DECODE-ordered all_objects lookup + synonym follow-through stays
597+
# consistent when a private and a public synonym to the same table
598+
# coexist, and that a materialized view created on the same base table
599+
# resolves to the MV name (not the base table) as a sibling data source.
600+
it "resolves table, view, materialized view, private synonym and public synonym for the same underlying table" do
601+
@conn.execute "CREATE TABLE test_describe_all (id NUMBER)" rescue nil
602+
@conn.execute "CREATE VIEW test_describe_all_v AS SELECT * FROM test_describe_all" rescue nil
603+
@conn.execute "CREATE MATERIALIZED VIEW test_describe_all_mv AS SELECT * FROM test_describe_all" rescue nil
604+
@conn.execute "CREATE SYNONYM test_describe_all_syn FOR test_describe_all" rescue nil
605+
@conn.execute "CREATE PUBLIC SYNONYM test_describe_all_pub FOR #{@owner}.test_describe_all" rescue nil
606+
607+
expect(resolve("test_describe_all")).to eq([@owner, "TEST_DESCRIBE_ALL"])
608+
expect(resolve("test_describe_all_v")).to eq([@owner, "TEST_DESCRIBE_ALL_V"])
609+
expect(resolve("test_describe_all_mv")).to eq([@owner, "TEST_DESCRIBE_ALL_MV"])
610+
expect(resolve("test_describe_all_syn")).to eq([@owner, "TEST_DESCRIBE_ALL"])
611+
expect(resolve("test_describe_all_pub")).to eq([@owner, "TEST_DESCRIBE_ALL"])
612+
ensure
613+
@conn.execute "DROP PUBLIC SYNONYM test_describe_all_pub" rescue nil
614+
@conn.execute "DROP SYNONYM test_describe_all_syn" rescue nil
615+
@conn.execute "DROP MATERIALIZED VIEW test_describe_all_mv" rescue nil
616+
@conn.execute "DROP VIEW test_describe_all_v" rescue nil
617+
@conn.execute "DROP TABLE test_describe_all" rescue nil
618+
end
619+
593620
it "raises when synonym resolution produces a looping chain" do
594621
@conn.execute "CREATE SYNONYM test_cycle_a FOR test_cycle_b" rescue nil
595622
@conn.execute "CREATE SYNONYM test_cycle_b FOR test_cycle_a" rescue nil

spec/support/create_oracle_enhanced_users.sql

Lines changed: 4 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,28 +4,12 @@ CREATE USER oracle_enhanced IDENTIFIED BY oracle_enhanced;
44

55
GRANT unlimited tablespace, create session, create table, create sequence,
66
create procedure, create trigger, create view, create materialized view,
7-
create database link, create synonym, create type, ctxapp TO oracle_enhanced;
7+
create database link, create synonym, create type, ctxapp,
8+
create public synonym, drop public synonym TO oracle_enhanced;
89

910
CREATE USER oracle_enhanced_schema IDENTIFIED BY oracle_enhanced_schema;
1011

1112
GRANT unlimited tablespace, create session, create table, create sequence,
1213
create procedure, create trigger, create view, create materialized view,
13-
create database link, create synonym, create type, ctxapp TO oracle_enhanced_schema;
14-
15-
CREATE USER arunit IDENTIFIED BY arunit;
16-
17-
GRANT unlimited tablespace, create session, create table, create sequence,
18-
create procedure, create trigger, create view, create materialized view,
19-
create database link, create synonym, create type, ctxapp TO arunit;
20-
21-
CREATE USER arunit2 IDENTIFIED BY arunit2;
22-
23-
GRANT unlimited tablespace, create session, create table, create sequence,
24-
create procedure, create trigger, create view, create materialized view,
25-
create database link, create synonym, create type, ctxapp TO arunit2;
26-
27-
CREATE USER ruby IDENTIFIED BY oci8;
28-
GRANT connect, resource, create view,create synonym TO ruby;
29-
GRANT EXECUTE ON dbms_lock TO ruby;
30-
GRANT CREATE VIEW TO ruby;
31-
GRANT unlimited tablespace to ruby;
14+
create database link, create synonym, create type, ctxapp,
15+
create public synonym, drop public synonym TO oracle_enhanced_schema;

0 commit comments

Comments
 (0)