While setting up a new environment, I learned something new regarding how Oracle handles synonyms. First, I will demonstrate the behaviour I encountered.
SQL> show user
USER is "PDBADMIN"
SQL> CREATE PUBLIC SYNONYM alpha FOR pdbadmin.alpha;
SYNONYM ALPHA created.
SQL> CREATE PUBLIC SYNONYM gamma FOR pdbadmin.beta;
SYNONYM GAMMA created.
SQL> SELECT * FROM dba_synonyms WHERE synonym_name IN ('ALPHA', 'GAMMA');
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK ORIGIN_CON_ID
_________ _______________ ______________ _____________ __________ ________________
PUBLIC GAMMA PDBADMIN BETA 3
PUBLIC ALPHA PDBADMIN ALPHA 3
SQL> SELECT * FROM pdbadmin.alpha;
SQL Error: ORA-00942: table or view "PDBADMIN"."ALPHA" does not exist
SQL> SELECT * FROM pdbadmin.beta;
SQL Error: ORA-00942: table or view "PDBADMIN"."BETA" does not exist
SQL> SELECT * FROM alpha;
SQL Error: ORA-01775: synonym ALPHA resolves to itself due to circular translation
SQL> SELECT * FROM gamma;
SQL Error: ORA-00980: synonym translation is no longer valid
SQL>
We create global synonyms called ALPHA and GAMMA, which point, respectively, to the qualified objects PDBADMIN.ALPHA and PDBADMIN.BETA, neither of which exists. When I reference ALPHA, I get ORA-01775, which indicates that there is a loop in the resolution of the synonym. When I reference GAMMA, I get ORA-00980, which indicates that the synonym translation is invalid.
My confusion was that, since the public synonym ALPHA points to the fully qualified object PDBADMIN.ALPHA, how can it resolve to itself and create a circular reference?
What I learned was that, while a fully qualified reference to a non-existent object normally fails to resolve—even if a public synonym exists—Oracle will, when resolving a synonym (public or not), also resolve it using a public synonym.
SQL> CREATE TABLE delta AS SELECT * FROM dual;
Table DELTA created.
SQL> CREATE PUBLIC SYNONYM epsilon FOR pdbadmin.delta;
SYNONYM EPSILON created.
SQL> CREATE PUBLIC SYNONYM zeta FOR pdbadmin.epsilon;
SYNONYM ZETA created.
SQL> create synonym eta for pdbadmin.epsilon;
Synonym ETA created.
SQL> SELECT * FROM dba_synonyms WHERE synonym_name IN ('EPSILON', 'ZETA', 'ETA');
OWNER SYNONYM_NAME TABLE_OWNER TABLE_NAME DB_LINK ORIGIN_CON_ID
___________ _______________ ______________ _____________ __________ ________________
PUBLIC EPSILON PDBADMIN DELTA 3
PUBLIC ZETA PDBADMIN EPSILON 3
PDBADMIN ETA PDBADMIN EPSILON 3
SQL> SELECT * FROM pdbadmin.epsilon;
SQL Error: ORA-00942: table or view "PDBADMIN"."EPSILON" does not exist
SQL> SELECT * FROM zeta;
DUMMY
________
X
SQL> SELECT * FROM eta;
DUMMY
________
X
Thanks to Neil Chandler for pointing me in the right direction on this.