This one is about bad practices.

The use of synonyms.

If you have felt a cold sweat, you probably experienced something similar.

Many users want to access application tables “transparently.” Note that I put it in quotes because calling “transparent” “without prefixing the owner user” seems more appropriate to me.

Yes, in production the user is called PEPE, and in development he is called JUAN, and the code must work whatever the user’s name is, but when we start to “universalize” access to objects, the following happens to us .

CASE 1:

  • JUAN has the TEST table. Create a public synonym that everyone has access to.
  • JUAN deletes the TEST table. Because it’s his and he wants it. But he does not erase the synonym.
  • JUAN tries to access the TEST table that he just deleted, and the error that appears is this:
SQL> connect juan/juan
Conected.

SQL> create table test (id number);
Table created.
SQL> create public synonym test for juan.test;
Synonym created.

SQL> drop table test;
Table dropped.

SQL> desc test
SP2-0749: No se puede resolver la ruta de acceso circular del sinonimo "test"

SQL> select * from test;
select * from test
*
ERROR in line 1:
ORA-01775: looping chain of synonyms

CASE 2:

  • JUAN wants to access the TEST table of the user PEPE.
  • JUAN creates a private synonym to view PEPE.TEST “transparently”.
  • MARIA wants to access JUAN.TEST, without knowing that it is not actually a table, but rather a synonym for another user, also transparently.
  • MARIA creates a public synonym to access JUAN.TEST.
SQL> connect pepe/pepe
Conected.
SQL> create table test (id number);

Table created.

SQL> connect juan/juan
Conected.
SQL> create synonym test for pepe.test;

Synonym created.

SQL> connect maria/maria
Conected.
SQL> create public synonym test for juan.test;

Synonym created.

 

Everyone sees the TEST table.

 

SQL> connect maria/maria
Conected.

SQL> desc test
Nombre ┐Nulo? Tipo
----------------------------------------------------- -------- ------------------------------------
ID NUMBER

SQL> connect juan/juan
Conected.
SQL> desc test
Nombre ┐Nulo? Tipo
----------------------------------------------------- -------- ------------------------------------
ID NUMBER

SQL> connect pepe/pepe
Conected.
SQL> desc test
Nombre ┐Nulo? Tipo
----------------------------------------------------- -------- ------------------------------------
ID NUMBER
  • PEPE deletes his table, and when he tries to access TEST it resolves the public synonym of MARIA, which points to the private synonym of JUAN, which points to the table that PEPE just deleted.
SQL> select count(*) from test;
select count(*) from test
*
ERROR in line 1:
ORA-01775: looping chain of synonyms

 

And to solve these problems, here I leave you a home made procedure recipe, so you can identify the problematic synonyms. You have the project on the Café Database GitHub and, as always, since it’s open source and all, feel free to modify and extend as you consider.

https://github.com/CafeDatabase/Check-Synonyms

-- Ensure user has DBA privileges.
create or replace procedure CHECK_SYNONYMS (p_owner varchar2) 
is
  usuario varchar2(30);
  bucle_sinonimos exception;
	sinonimo_no_valido exception;
	PRAGMA EXCEPTION_INIT(bucle_sinonimos,-1775);
	PRAGMA EXCEPTION_INIT(sinonimo_no_valido,-980);
	filas number;
begin 
  dbms_output.enable;
  for x in (select owner, synonym_name,table_owner,table_name,NVL2(db_link,'@'||db_link,null) db_link 
              from dba_synonyms where owner IN (p_owner,'PUBLIC'))
			  -- It takes time to check SYS synonyms, but I think it's worth it.
	loop
	   begin 
		     select DECODE(x.owner,'PUBLIC','',x.owner||'.') into usuario from dual;
			 execute immediate 'select count(*) into :filas from '||usuario||x.synonym_name||' where 1=0' into filas;
	   exception 
		   when bucle_sinonimos then 
			   dbms_output.put_line('Synonym in a loop chain '||x.owner||'.'||x.synonym_name||' pointing to '
				            ||x.table_owner||'.'||x.table_name||x.db_link);
									
		   when sinonimo_no_valido then 
			   dbms_output.put_line('Synonym not valid '||x.owner||'.'||x.synonym_name||'  pointing to '
				            ||x.table_owner||'.'||x.table_name||x.db_link);
			 when others then null;
	   end;
	end loop;
end;
/
Share This