Administración de Sistemas Gestores de Bases de Datos — práctica sobre cómo interconectar servidores de bases de datos para que puedan consultar y modificar datos entre sí: Oracle↔Oracle, PostgreSQL↔PostgreSQL, y la interconexión heterogénea Oracle↔PostgreSQL.

Enlace entre dos servidores Oracle

Un enlace de base de datos (database link) en Oracle permite a un usuario conectarse a una base de datos remota y acceder a sus datos como si estuvieran en la base de datos local, mediante un enlace privado que almacena las credenciales de conexión.

Configuración básica de las máquinas

Dos máquinas Oracle: Oracle1 (192.168.122.222) y Oracle2 (192.168.122.237).

En Oracle1:

1sqlplus / as sysdba
2
3CREATE USER c##josemanuel1 IDENTIFIED BY josemanuel1;
4GRANT CONNECT, RESOURCE TO c##josemanuel1;
5GRANT CREATE SESSION TO c##josemanuel1;
6GRANT CREATE DATABASE LINK TO c##josemanuel1;
7GRANT UNLIMITED TABLESPACE TO c##josemanuel1;
8exit;

Creación del usuario y concesión de permisos en Oracle1

En Oracle2:

1sqlplus / as sysdba
2
3CREATE USER c##josemanuel2 IDENTIFIED BY josemanuel2;
4GRANT CONNECT, RESOURCE TO c##josemanuel2;
5GRANT CREATE SESSION TO c##josemanuel2;
6GRANT CREATE DATABASE LINK TO c##josemanuel2;
7GRANT UNLIMITED TABLESPACE TO c##josemanuel2;
8EXIT;

Creación del usuario en Oracle2

Confirmación del usuario creado en Oracle2

Usuarios comunes vs. locales: en una Container Database (CDB), los usuarios C## son comunes y existen en toda la CDB; los usuarios sin C## son locales y solo existen en una Pluggable Database (PDB) concreta. Para crear un usuario en una PDB (sin el prefijo C##) hay que conectarse a ella primero:

1SHOW PDBS;
2ALTER SESSION SET CONTAINER = ORCLPDB1;
3alter pluggable database orclpdb1 open;
4-- ya se puede crear el usuario sin problema

Listado de PDBs disponibles y creación de usuario en el PDB

Configurar listener en ambos servidores

1sudo nano /opt/oracle/product/21c/dbhome_1/network/admin/listener.ora

En Oracle1:

1LISTENER =
2  (DESCRIPTION_LIST =
3    (DESCRIPTION =
4      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.122.222)(PORT = 1521))
5    )
6  )

listener.ora configurado en Oracle1

En Oracle2 (misma estructura, con su propia IP):

1LISTENER =
2  (DESCRIPTION_LIST =
3    (DESCRIPTION =
4      (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.122.237)(PORT = 1521))
5    )
6  )

listener.ora configurado en Oracle2

Es recomendable que la IP de cada máquina sea estática (o con reserva DHCP) para que el listener no dé conflictos.

Configurar archivo tnsnames.ora

En Oracle1:

1sudo nano /opt/oracle/product/21c/dbhome_1/network/admin/tnsnames.ora
1#Interconexion con oracle2
2oracle2 =
3  (DESCRIPTION =
4    (ADDRESS = (PROTOCOL = TCP)(HOST = 192.168.122.237)(PORT = 1521))
5    (CONNECT_DATA =
6      (SERVICE_NAME = ORCLCDB)
7    )
8  )

tnsnames.ora en Oracle1 con el alias oracle2

Consulta del nombre de servicio con global_name

Esto define un alias de conexión llamado oracle2: en vez de escribir toda la cadena de conexión cada vez, basta con usar ese nombre.

  • oracle2 → nombre del alias que se usará
  • HOST → IP del servidor remoto
  • PORT → puerto donde escucha el listener
  • SERVICE_NAME → nombre del servicio de base de datos (se obtiene con SELECT * FROM global_name;)

En Oracle2 se configura el mismo archivo pero apuntando a Oracle1.

Para comprobar que ambos se comunican:

1tnsping oracle2   # desde oracle1
2tnsping oracle1   # desde oracle2

Resultado de tnsping oracle2

Resultado de tnsping oracle1 (parte 1)

Resultado de tnsping oracle1 (parte 2)

Una respuesta de “Realizado correctamente” confirma que el listener remoto está activo y accesible, y que el database link podrá establecerse sin problemas.

Verificación de conexión cruzada:

1-- Desde Oracle1
2sqlplus c##josemanuel2/josemanuel2@oracle2
3
4-- Desde Oracle2
5sqlplus c##josemanuel1/josemanuel1@oracle1

Conexión cruzada desde Oracle1 a Oracle2

Conexión cruzada desde Oracle2 a Oracle1

Creación del link en Oracle1:

1sqlplus c##josemanuel1/josemanuel1
2
3CREATE DATABASE LINK link_oracle2
4CONNECT TO C##josemanuel2 IDENTIFIED BY josemanuel2
5USING 'oracle2';

Creación del database link link_oracle2

CREATE DATABASE LINK crea el enlace (link_oracle2 es su nombre); CONNECT TO es el usuario remoto de Oracle2; USING 'oracle2' es el alias definido en tnsnames.ora.

Verificar el enlace creado:

1SELECT * FROM dba_db_links;

Verificación de los enlaces creados con dba_db_links

Tabla de ejemplo en Oracle1 para probar el link:

 1CREATE TABLE liga_espanola (
 2    id NUMBER PRIMARY KEY,
 3    equipo VARCHAR2(100) NOT NULL,
 4    ciudad VARCHAR2(50),
 5    puntos NUMBER DEFAULT 0,
 6    partidos_jugados NUMBER DEFAULT 0
 7);
 8INSERT INTO liga_espanola VALUES (1, 'Sevilla FC', 'Sevilla', 78, 30);
 9INSERT INTO liga_espanola VALUES (2, 'Real Madrid', 'Madrid', 75, 30);
10INSERT INTO liga_espanola VALUES (3, 'FC Barcelona', 'Barcelona', 73, 30);
11INSERT INTO liga_espanola VALUES (4, 'Atlético de Madrid', 'Madrid', 70, 30);
12INSERT INTO liga_espanola VALUES (5, 'Real Betis', 'Sevilla', 65, 30);
13INSERT INTO liga_espanola VALUES (6, 'Valencia CF', 'Valencia', 60, 30);

Lo mismo pero al revés (desde Oracle2, para que el enlace sea bidireccional):

 1sqlplus c##josemanuel2/josemanuel2
 2
 3CREATE DATABASE LINK link_oracle1
 4CONNECT TO c##josemanuel1 IDENTIFIED BY josemanuel1
 5USING 'oracle1';
 6
 7CREATE TABLE motos (
 8    id NUMBER PRIMARY KEY,
 9    marca VARCHAR2(100) NOT NULL,
10    modelo VARCHAR2(100),
11    cilindrada NUMBER,
12    precio NUMBER(8,2),
13    tipo VARCHAR2(50)
14);
15INSERT INTO motos VALUES (1, 'Honda', 'CBR 1000RR', 1000, 18500.00, 'Deportiva');
16INSERT INTO motos VALUES (2, 'Yamaha', 'MT-09', 890, 11500.00, 'Naked');
17INSERT INTO motos VALUES (3, 'Kawasaki', 'Ninja ZX-10R', 1000, 22500.00, 'Deportiva');
18INSERT INTO motos VALUES (4, 'Ducati', 'Panigale V4', 1103, 28000.00, 'Deportiva');
19INSERT INTO motos VALUES (5, 'Harley-Davidson', 'Street Glide', 1868, 28500.00, 'Cruiser');
20INSERT INTO motos VALUES (6, 'BMW', 'R 1250 GS', 1254, 19500.00, 'Adventure');
21COMMIT;

Creación de usuario y enlace link_oracle1 en Oracle2

Creación de la tabla motos

Consultas de datos simultáneas

Desde Oracle1 hacia Oracle2:

1SELECT * FROM motos@link_oracle2;

Consulta remota de la tabla motos desde Oracle1

Desde Oracle2 hacia Oracle1:

1SELECT * FROM liga_espanola@link_oracle1;

JOIN combinando datos de ambos servidores en una sola consulta:

1SELECT m.marca, m.modelo, m.precio, e.equipo, e.ciudad, e.puntos
2FROM motos m, liga_espanola@link_oracle1 e
3WHERE m.id = e.id
4ORDER BY m.id;

Consulta JOIN combinando motos y liga_espanola

Resultado de la consulta JOIN

Errores encontrados (Oracle↔Oracle)

Error de listener (tnsping no funcionaba): tenía otro tnsnames.ora/listener.ora escondido en otra ruta, generando conflicto. Solución — quedarme con un único fichero por servicio:

1# desde root, borro los que sobran
2rm -r /opt/oracle/homes/OraDBHome21cEE/network/admin/tnsnames.ora
3rm -r /opt/oracle/homes/OraDBHome21cEE/network/admin/listener.ora

Y dejo solo /opt/oracle/product/21c/dbhome_1/network/admin/{listener,tnsnames}.ora. Luego reinicio el listener:

1sudo -u oracle bash -c 'export ORACLE_HOME=/opt/oracle/product/21c/dbhome_1 && export PATH=$ORACLE_HOME/bin:$PATH && lsnrctl stop'
2sudo -u oracle bash -c 'export ORACLE_HOME=/opt/oracle/product/21c/dbhome_1 && export PATH=$ORACLE_HOME/bin:$PATH && lsnrctl start'

Error 2 — el listener conecta pero al intentar entrar con el usuario dice que no encuentra el servicio (ORA-12514): el listener está arrancado pero la base de datos no está registrada en él.

 1sqlplus / as sysdba
 2
 3-- en oracle1
 4ALTER SYSTEM SET LOCAL_LISTENER='(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.122.221)(PORT=1521))' SCOPE=BOTH;
 5ALTER SYSTEM REGISTER;
 6EXIT;
 7
 8-- en oracle2
 9ALTER SYSTEM SET LOCAL_LISTENER='(ADDRESS=(PROTOCOL=TCP)(HOST=192.168.122.237)(PORT=1521))' SCOPE=BOTH;
10ALTER SYSTEM REGISTER;
11EXIT;

Se verifica con lsnrctl status: si el servicio ya aparece listado, la conexión funciona. lsnrctl status mostrando el servicio ya registrado


Enlace entre dos servidores PostgreSQL

A diferencia de Oracle, donde los database links son nativos, PostgreSQL usa una extensión llamada dblink que hay que habilitar explícitamente.

Dos máquinas: Postgre1 (192.168.122.159, base bd1, tabla portatiles) y Postgre2 (192.168.122.237, base bd2, tabla coches). Se asume que PostgreSQL ya acepta conexiones remotas (configurado en la instalación).

Creación de usuarios y bases de datos

En Postgre1:

1sudo su - postgres
2psql
3
4CREATE DATABASE bd1;
5CREATE USER josemanuel1 WITH PASSWORD 'josemanuel1';
6GRANT ALL PRIVILEGES ON DATABASE bd1 TO josemanuel1;
7\c bd1
8GRANT ALL PRIVILEGES ON SCHEMA public TO josemanuel1;
9ALTER DEFAULT PRIVILEGES IN SCHEMA public GRANT ALL ON TABLES TO josemanuel1;

En Postgre2 Creación de base de datos y usuario en Postgre1

En Postgre2, lo mismo con bd2 / josemanuel2. Creación de base de datos y usuario en Postgre2

Verificación de conectividad

1# Desde Postgre1 hacia Postgre2
2psql -h 192.168.122.237 -U josemanuel2 -d bd2
3
4# Desde Postgre2 hacia Postgre1
5psql -h 192.168.122.159 -U josemanuel1 -d bd1

Conexión remota desde Postgre1 a Postgre2

Conexión remota desde Postgre2 a Postgre1

Solo los superusuarios (postgres) pueden crear links; si se quiere que un usuario normal también pueda, se le da con ALTER USER josemanuel1 WITH SUPERUSER;.

En Postgre1:

1psql -U josemanuel1 -d bd1
2CREATE EXTENSION dblink;

En Postgre2:

1psql -U josemanuel2 -d bd2
2CREATE EXTENSION dblink;

Instalación de dblink en Postgre1

Instalación de dblink en Postgre2

Creación de las tablas de ejemplo

En Postgre1 — tabla portatiles:

 1CREATE TABLE portatiles (
 2    id SERIAL PRIMARY KEY,
 3    marca VARCHAR(50),
 4    modelo VARCHAR(50),
 5    ram INT,
 6    almacenamiento INT,
 7    precio NUMERIC(10,2)
 8);
 9INSERT INTO portatiles (marca, modelo, ram, almacenamiento, precio) VALUES
10('Lenovo', 'ThinkPad X1 Carbon', 16, 512, 1500.00),
11('HP', 'Pavilion 15', 8, 256, 700.00),
12('Dell', 'XPS 13', 16, 512, 1600.00),
13('Asus', 'ROG Strix', 32, 1024, 2200.00),
14('Apple', 'MacBook Air M2', 16, 512, 1800.00);

Tabla portatiles creada en Postgre1

En Postgre2 — tabla coches:

 1CREATE TABLE coches (
 2    id SERIAL PRIMARY KEY,
 3    marca VARCHAR(50),
 4    modelo VARCHAR(50),
 5    año INT,
 6    precio NUMERIC(10,2)
 7);
 8INSERT INTO coches (marca, modelo, año, precio) VALUES
 9('Toyota', 'Corolla', 2020, 18000.00),
10('Honda', 'Civic', 2019, 17500.00),
11('Ford', 'Focus', 2018, 15000.00),
12('BMW', 'Serie 3', 2021, 35000.00),
13('Audi', 'A4', 2022, 38000.00);

Tabla coches creada en Postgre2

Funcionamiento del enlace: primera consulta remota

La función dblink sigue esta estructura:

1SELECT *
2FROM dblink(
3    'cadena_de_conexion',
4    'consulta_SQL'
5) AS alias(definicion_de_columnas);
  • cadena_de_conexion → host, base de datos, usuario y contraseña del servidor remoto
  • consulta_SQL → la consulta a ejecutar en remoto
  • alias → nombre temporal para los resultados
  • definicion_de_columnas → nombre y tipo de cada columna que devuelve la consulta

Desde Postgre1, consultando la tabla coches de Postgre2:

 1SELECT *
 2FROM dblink(
 3    'host=192.168.122.237 dbname=bd2 user=josemanuel2 password=josemanuel2',
 4    'SELECT * FROM coches'
 5) AS equipos_remotos(
 6    id VARCHAR,
 7    marca VARCHAR,
 8    modelo VARCHAR,
 9    año INT,
10    precio NUMERIC
11);

Primera consulta remota con dblink

Consulta JOIN entre bases de datos distribuidas

1SELECT p.marca, p.modelo, p.precio, c.marca, c.modelo, c.precio
2FROM portatiles p
3LEFT JOIN dblink(
4    'host=192.168.122.237 dbname=bd2 user=josemanuel2 password=josemanuel2',
5    'SELECT id, marca, modelo, precio FROM coches'
6) AS c(id INTEGER, marca VARCHAR, modelo VARCHAR, precio NUMERIC)
7ON p.id = c.id
8ORDER BY p.id;

Enlace bidireccional

Consulta JOIN combinando datos de ambos servidores

Enlace bidireccional

Los enlaces con dblink son unidireccionales por sí mismos, así que para tener consultas en ambas direcciones hay que repetir el proceso en el otro sentido. Desde Postgre2 hacia Postgre1:

1SELECT c.marca, c.modelo, c.año, c.precio, p.marca, p.modelo, p.ram, p.almacenamiento
2FROM coches c,
3dblink(
4    'host=192.168.122.159 dbname=bd1 user=josemanuel1 password=josemanuel1',
5    'SELECT id, marca, modelo, ram, almacenamiento FROM portatiles'
6) AS p(id INTEGER, marca VARCHAR, modelo VARCHAR, ram INTEGER, almacenamiento INTEGER)
7WHERE c.id = p.id
8AND p.almacenamiento >= 512
9ORDER BY c.año DESC;

Consulta bidireccional desde Postgre2 hacia Postgre1

Resultado de la consulta bidireccional

Operaciones de escritura remota

Enlace bidireccional funcionando en ambos sentidos

Operaciones de escritura remota

Para INSERT/UPDATE/DELETE (que no devuelven filas) se usa dblink_exec.

Insertar un portátil en Postgre1 desde Postgre2:

1SELECT dblink_exec(
2    'host=192.168.122.159 dbname=bd1 user=josemanuel1 password=josemanuel1',
3    'INSERT INTO portatiles (marca, modelo, ram, almacenamiento, precio)
4     VALUES (''MSI'', ''Katana GF66'', 16, 512, 1200.00)'
5);

Inserción remota con dblink_exec

Verificar desde el mismo Postgre2:

1SELECT *
2FROM dblink(
3    'host=192.168.122.159 dbname=bd1 user=josemanuel1 password=josemanuel1',
4    'SELECT * FROM portatiles WHERE marca = ''MSI'''
5) AS nuevo_portatil(id INTEGER, marca VARCHAR, modelo VARCHAR, ram INTEGER, almacenamiento INTEGER, precio NUMERIC);

Verificación del registro insertado remotamente

Con esto queda un enlace bidireccional funcionando entre dos servidores PostgreSQL, con consultas combinadas y escritura remota.


Interconexión Oracle ↔ PostgreSQL

Datos de partida:

  • Oracle1 → IP 192.168.122.221, usuario C##josemanuel1, tabla liga_espanola
  • Postgre1 → IP 192.168.122.159, usuario josemanuel1, base bd1, tabla portatiles

Parte 1: Oracle → PostgreSQL

Oracle no tiene soporte nativo para PostgreSQL, así que se usa ODBC + Heterogeneous Services (HS): un estándar que actúa de “traductor universal” entre gestores distintos.

1Cliente → Oracle → ODBC → PostgreSQL

Instalar paquetes ODBC en Oracle1:

1sudo apt update && sudo apt install unixodbc odbc-postgresql

Instalación de los paquetes ODBC en Oracle1

  • unixODBC → implementación abierta de la API ODBC
  • odbc-postgresql → driver específico para PostgreSQL

Configurar el driver de PostgreSQL (comprobar antes dónde se instaló, con ls -l /usr/lib/x86_64-linux-gnu/odbc/; se usa psqlodbcw.so por ser el más moderno):

1sudo nano /etc/odbcinst.ini
1[PostgreSQL]
2Description = PostgreSQL ODBC driver
3Driver      = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
4Setup       = /usr/lib/x86_64-linux-gnu/odbc/libodbcpsqlS.so
5FileUsage   = 1

**Crear el DSN Ruta de los drivers ODBC instalados

Configuración del driver en odbcinst.ini

Crear el DSN (Data Source Name) — define cómo conectarse a una base concreta:

1sudo nano /etc/odbc.ini
1[POSTGRE1]
2Description = PostgreSQL Database
3Driver      = PostgreSQL
4Servername  = 192.168.122.159
5Username    = josemanuel1
6Password    = josemanuel1
7Port        = 5432
8Database    = bd1

Configuración del DSN en odbc.ini

Probar la conexión ODBC antes de tocar Oracle:

1isql POSTGRE1
1SELECT * FROM portatiles;

Prueba de conexión ODBC con isql

Configurar Heterogeneous Services (HS es lo que permite a Oracle hablar con bases no-Oracle vía ODBC):

1sudo nano /opt/oracle/product/21c/dbhome_1/hs/admin/initPOSTGRE1.ora
1HS_FDS_CONNECT_INFO = POSTGRE1
2HS_FDS_TRACE_LEVEL = DEBUG
3HS_FDS_SHAREABLE_NAME = /usr/lib/x86_64-linux-gnu/odbc/psqlodbcw.so
4HS_LANGUAGE = AMERICAN_AMERICA.WE8ISO8859P1
5set ODBCINI=/etc/odbc.ini

Configuración de Heterogeneous Services (initPOSTGRE1.ora)

Configurar el listener de Oracle para que sepa invocar el gateway heterogéneo:

1sudo nano /opt/oracle/product/21c/dbhome_1/network/admin/listener.ora
 1#postgre
 2SID_LIST_LISTENER=
 3  (SID_LIST=
 4    (SID_DESC=
 5      (GLOBAL_DBNAME=POSTGRE1)
 6      (SID_NAME=POSTGRE1)
 7      (ORACLE_HOME=/opt/oracle/product/21c/dbhome_1)
 8      (PROGRAM=dg4odbc)
 9      (ENVS="LD_LIBRARY_PATH=/usr/lib/x86_64-linux-gnu/odbc:/opt/oracle/product/21c/dbhome_1/lib,ODBCINI=/etc/odbc.ini")
10    )
11  )
  • **GLOBAL_DBNAME listener.ora con la sección SID_LIST_LISTENER para el gateway

Detalle de la configuración del SID

  • GLOBAL_DBNAME / SID_NAME → nombre del servicio (debe coincidir con tnsnames.ora)
  • ORACLE_HOME → ruta de instalación de Oracle (crítico que sea correcta)
  • PROGRAMdg4odbc, el programa gateway para ODBC
  • ENVS → variables de entorno necesarias (rutas de librerías ODBC y del odbc.ini)

Configurar tnsnames.ora:

1sudo nano /opt/oracle/product/21c/dbhome_1/network/admin/tnsnames.ora
1#postgres
2POSTGRE1 =
3  (DESCRIPTION=
4    (ADDRESS=(PROTOCOL=tcp)(HOST=192.168.122.221)(PORT=1521))
5    (CONNECT_DATA=(SID=POSTGRE1))
6    (HS=OK)
7  )

tnsnames.ora con el alias POSTGRE1 y HS=OK

Detalle de la configuración de tnsnames

Aquí el HOST es la IP del listener de Oracle, no la de PostgreSQL. (HS=OK) marca que es un servicio heterogéneo.

Reiniciar el listener y comprobar el estado (el status UNKNOWN es normal para servicios heterogéneos, ya que no son instancias Oracle):

1sudo -u oracle bash -c 'export ORACLE_HOME=/opt/oracle/product/21c/dbhome_1 && export PATH=$ORACLE_HOME/bin:$PATH && lsnrctl stop'
2sudo -u oracle bash -c 'export ORACLE_HOME=/opt/oracle/product/21c/dbhome_1 && export PATH=$ORACLE_HOME/bin:$PATH && lsnrctl start'
3lsnrctl status

Crear el database link en Oracle: lsnrctl status mostrando el servicio POSTGRE1 registrado

Crear el database link en Oracle:

1sqlplus C##josemanuel1/josemanuel1
2
3CREATE DATABASE LINK link_postgres
4CONNECT TO "josemanuel1" IDENTIFIED BY "josemanuel1"
5USING 'POSTGRE1';

Creación del database link link_postgres

Confirmación del enlace creado

Probar el enlace (el nombre de tabla va entre comillas porque PostgreSQL distingue mayúsculas/minúsculas):

1SELECT * FROM "portatiles"@link_postgres;

Primera consulta remota a PostgreSQL desde Oracle

Consulta combinando ambos servidores (columnas de Postgre1 entre comillas porque Oracle las trata como case-sensitive a través del enlace):

1SELECT o.EQUIPO, o.CIUDAD, o.PUNTOS, p."marca", p."modelo", p."precio"
2FROM liga_espanola o, "portatiles"@link_postgres p
3WHERE o.ID = p."id"
4ORDER BY o.PUNTOS DESC;

Consulta JOIN combinando Oracle y PostgreSQL

Resultado de la consulta combinada

INSERT / UPDATE / DELETE remotos desde Oracle hacia Postgre1:

 1-- INSERT
 2INSERT INTO "portatiles"@link_postgres ("id", "marca", "modelo", "ram", "almacenamiento", "precio")
 3VALUES (7, 'Acer', 'Aspire 5', 8, 512, 650);
 4
 5-- UPDATE
 6UPDATE "portatiles"@link_postgres
 7SET "precio" = 1550
 8WHERE "marca" = 'Lenovo';
 9
10-- DELETE
11DELETE FROM "portatiles"@link_postgres
12WHERE "id" = 7;

INSERT y UPDATE remotos desde Oracle hacia PostgreSQL

Verificación de los cambios y DELETE remoto

Errores de Oracle a Postgres

Al consultar la tabla remota daba ORA-28500 / ORA-02063: line preciendo a LINK_POSTGRES. Causa: conflicto de rutas ORACLE_HOME — Oracle buscaba initPOSTGRE1.ora en /opt/oracle/homes/OraDBHome21cEE/hs/admin/ cuando en realidad estaba creado en /opt/oracle/product/21c/dbhome_1/hs/admin/.

Solución: usar una única ruta consistente para toda la configuración (/opt/oracle/product/21c/dbhome_1) Error ORA-28500 al consultar la tabla remota

Solución: usar una única ruta consistente para toda la configuración (/opt/oracle/product/21c/dbhome_1) y asegurarse de que el listener.ora tiene bien las variables LD_LIBRARY_PATH y ODBCINI. Si el listener no encuentra el ejecutable dg4odbc:

1ls -l /opt/oracle/product/21c/dbhome_1/bin/dg4odbc
2sudo find /opt/oracle -name "dg4odbc"

listener.ora corregido con la ruta ORACLE_HOME correcta

Comprobación del ejecutable dg4odbc

y se ajusta el ORACLE_HOME del listener.ora a la ruta correcta que devuelva ese comando.


Parte 2: PostgreSQL → Oracle

PostgreSQL tampoco tiene soporte nativo para Oracle, así que se usa oracle_fdw (Foreign Data Wrapper), que necesita el Oracle Instant Client instalado en la máquina PostgreSQL.

1Cliente → PostgreSQL → oracle_fdw → Oracle

Paquetes necesarios en Postgre1:

1sudo apt update && sudo apt install libaio1t64 postgresql-server-dev-all build-essential git unzip wget

Instalación de paquetes necesarios para oracle_fdw

Cambio al usuario postgres

  • libaio1t64 → E/S asíncrona que necesita Oracle
  • postgresql-server-dev-all → herramientas de desarrollo para extensiones de PostgreSQL
  • build-essential, git, unzip, wget → compilar y descargar

Descargar Oracle Instant Client (como usuario postgres):

 1sudo su - postgres
 2
 3wget https://download.oracle.com/otn_software/linux/instantclient/211000/instantclient-basic-linux.x64-21.1.0.0.0.zip
 4wget https://download.oracle.com/otn_software/linux/instantclient/211000/instantclient-sdk-linux.x64-21.1.0.0.0.zip
 5wget https://download.oracle.com/otn_software/linux/instantclient/211000/instantclient-sqlplus-linux.x64-21.1.0.0.0.zip
 6
 7unzip instantclient-basic-linux.x64-21.1.0.0.0.zip
 8unzip instantclient-sqlplus-linux.x64-21.1.0.0.0.zip
 9unzip instantclient-sdk-linux.x64-21.1.0.0.0.zip
10rm *.zip

Descarga de los paquetes de Oracle Instant Client

Comprobación de la descarga con ls -lh

Variables de entorno:

1sudo nano .bashrc
1export ORACLE_HOME=/var/lib/postgresql/instantclient_21_1
2export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$ORACLE_HOME
3export PATH=$PATH:$ORACLE_HOME

Descompresión de los paquetes Instant Client

Contenido de la carpeta instantclient_21_1

Configuración de variables de entorno en .bashrc

1source .bashrc
2which sqlplus

Verificación de que sqlplus está accesible

Creación del enlace simbólico de libaio

Verificación de conectividad con Oracle. En Debian 13, sqlplus busca libaio.so.1 (nombre del paquete en Debian 12), pero el sistema tiene libaio.so.1t64 — hace falta un enlace simbólico:

1sudo ln -s /lib/x86_64-linux-gnu/libaio.so.1t64 /lib/x86_64-linux-gnu/libaio.so.1
2sudo ldconfig
1sqlplus c##josemanuel1/josemanuel1@192.168.122.221/ORCLCDB
2select * from liga_espanola;
3exit;

Prueba de conexión a Oracle desde Postgre1 con sqlplus

Compilar e instalar oracle_fdw:

1cd /var/lib/postgresql
2git clone https://github.com/laurenz/oracle_fdw.git
3cd oracle_fdw
4sudo make ORACLE_HOME=/var/lib/postgresql/instantclient_21_1
5sudo make install

Compilación de oracle_fdw

Librerías compartidas:

1echo '/var/lib/postgresql/instantclient_21_1' | sudo tee /etc/ld.so.conf.d/oracle.conf
2sudo ldconfig

Creación del enlace en PostgreSQL:

1psql -U josemanuel1 -d bd1
2CREATE EXTENSION oracle_fdw;
3\dx

Esquema para las tablas remotas y definición del servidor:

1CREATE SCHEMA oracle_schema;
2
3CREATE SERVER servidor_oracle1
4FOREIGN DATA WRAPPER oracle_fdw
5OPTIONS (dbserver '//192.168.122.221/ORCLCDB');

Instalación de la extensión oracle_fdw en PostgreSQL

Creación del esquema y del servidor remoto

Mapeo de usuarios y permisos:

1CREATE USER MAPPING FOR josemanuel1
2SERVER servidor_oracle1
3OPTIONS (user 'c##josemanuel1', password 'josemanuel1');
4
5GRANT ALL PRIVILEGES ON SCHEMA oracle_schema TO josemanuel1;
6GRANT ALL PRIVILEGES ON FOREIGN SERVER servidor_oracle1 TO josemanuel1;

Importar las tablas remotas (el esquema de Oracle va en MAYÚSCULAS y entre comillas, porque Oracle almacena los nombres así por defecto):

1IMPORT FOREIGN SCHEMA "C##JOSEMANUEL1"
2FROM SERVER servidor_oracle1
3INTO oracle_schema;
4
5\det oracle_schema.*

Consultar datos combinados. El nombre de tabla mantiene el formato Oracle y va entre comillas ("liga_espanola"), pero los nombres de columna se convierten a minúsculas y no llevan comillas:

1SELECT p.id, p.marca, p.modelo, p.precio, o.equipo, o.ciudad, o.puntos
2FROM portatiles p
3LEFT JOIN oracle_schema."liga_espanola" o ON p.id = o.id
4ORDER BY p.id;

INSERT / UPDATE / DELETE remotos desde PostgreSQL hacia Oracle:

 1-- INSERT
 2INSERT INTO oracle_schema."liga_espanola" (id, equipo, ciudad, puntos, partidos_jugados)
 3VALUES (7, 'Real Sociedad', 'San Sebastián', 58, 30);
 4
 5-- UPDATE
 6UPDATE oracle_schema."liga_espanola"
 7SET puntos = 80, partidos_jugados = 31
 8WHERE equipo = 'Real Madrid';
 9
10-- DELETE
11DELETE FROM oracle_schema."liga_espanola"
12WHERE id = 7;

Conclusión

Con esto quedan cubiertos los tres escenarios de interconexión: Oracle↔Oracle (nativo, vía database links), PostgreSQL↔PostgreSQL (vía la extensión dblink) y la interconexión heterogénea Oracle↔PostgreSQL en ambos sentidos (ODBC + Heterogeneous Services para Oracle→Postgres, y oracle_fdw para Postgres→Oracle). En los tres casos se pueden hacer tanto consultas combinadas (JOIN entre servidores) como operaciones de escritura remota.