<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[DBA en la SOMBRA]]></title><description><![CDATA[Tutoriales, scripts y casos prácticos que hacemos los DBA de desarrollos.
Únase a mí en mi viaje para optimizar y aprender.]]></description><link>https://dbaenlasombra.com</link><generator>RSS for Node</generator><lastBuildDate>Sat, 11 Apr 2026 14:24:56 GMT</lastBuildDate><atom:link href="https://dbaenlasombra.com/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[[26ai] Hybrid Read-Only Mode PDB]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy, vamos a ver el nuevo open-mode introducido en 26ai denominado Hybrid Read-Only .
Cuando usamos este modo, e]]></description><link>https://dbaenlasombra.com/26ai-hybrid-read-only-mode-pdb</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-hybrid-read-only-mode-pdb</guid><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[pdb]]></category><category><![CDATA[commonuser]]></category><category><![CDATA[localuser]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Thu, 09 Apr 2026 23:18:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/de208bd2-5e24-4fd3-acf7-8e5394d95283.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-hybrid-read-only-mode-pdb"><em><strong>English version</strong></em></a><em>.</em></p>
<p>En el artículo de hoy, vamos a ver el nuevo <em><strong>open-mode</strong></em> introducido en 26ai denominado <strong>Hybrid Read-Only</strong> .</p>
<p>Cuando usamos este modo, el <strong>common user</strong> seguirá teniendo acceso <em><strong>read-write</strong></em>, mientras que el <strong>local user</strong> solo tendrá acceso <em><strong>read-only</strong></em>.</p>
<p>Sin más, vamos a ello.</p>
<p>El primer paso es definir una nueva PDB.</p>
<pre><code class="language-sql">SQL&gt; CREATE PLUGGABLE DATABASE PDB_HYBRID ADMIN USER admin IDENTIFIED BY admin keystore identified by external store;

Pluggable database created.

SQL&gt; ALTER PLUGGABLE DATABASE  PDB_HYBRID OPEN;
Pluggable database altered.

SQL&gt; ALTER PLUGGABLE DATABASE  PDB_HYBRID SAVE STATE;
Pluggable database altered.
</code></pre>
<p>Definimos los diferentes usuarios:</p>
<pre><code class="language-sql">SQL&gt; CREATE USER c##hybrid IDENTIFIED BY "XXXXXXX";
User created.
SQL&gt; GRANT DBA TO c##hybrid CONTAINER=ALL;
Grant succeeded.

SQL&gt; alter session set container=pdb_hybrid;
SQL&gt; create smallfile tablespace tb1 
      datafile size 1m autoextend on maxsize 10m;

Tablespace created.

SQL&gt; CREATE USER hybrid IDENTIFIED BY "XXXXXXX"
  DEFAULT TABLESPACE tb1
  TEMPORARY TABLESPACE temp
  QUOTA UNLIMITED ON tb1;  

User created.

SQL&gt; GRANT DBA TO hybrid;

Grant succeeded.
</code></pre>
<p>Perfecto, ya tenemos definidos tanto el <strong>common user</strong> (<em><strong>c##hybrid)</strong></em> como el <strong>local user</strong> (<em><strong>hybrid</strong></em>).</p>
<p>Antes de abrir la PDB con el nuevo estado, vamos a conectarnos con el usuario <em><strong>hybrid</strong></em> para definir un tabla con algo de información:</p>
<pre><code class="language-sql">SQL&gt; conn hybrid/"XXXXX"@X.X.X.X:1521/pdb_hybrid.XXXX.XXXX.XXXX.com
Connected.
SQL&gt;      
SQL&gt; 
 Create table T_TEST AS 
  SELECT OBJECT_NAME FROM ALL_OBJECTS;

Table created.
</code></pre>
<p>Nos conectamos a <strong>CDB$Root</strong> para poner en modo <strong>hybrid read-only</strong> la PDB.</p>
<pre><code class="language-sql">SQL&gt; alter pluggable database PDB_HYBRID close;

Pluggable database altered.

SQL&gt; alter pluggable database PDB_HYBRID open hybrid read only;

Pluggable database altered.

SQL&gt; r
    select con_name,
           open_mode,
           is_hybrid_read_only
    from   v$container_topology

CON_NAME             OPEN_MODE  IS_HYBRID_READ_ONLY
-------------------- ---------- --------------------
CDB$ROOT             READ WRITE NO
PDB$SEED             READ ONLY  NO
PDB_HYBRID           READ WRITE YES
</code></pre>
<p>Nos conectamos con el <strong>local user</strong> a la PDB e intentamos realizar operaciones DML (<em>delete</em>) y DDL (<em>truncate</em>).</p>
<pre><code class="language-sql">SQL&gt;  conn hybrid/"XXXXX"@X.X.X.X:1521/pdb_hybrid.XXXX.XXXX.XXXX.com
Connected.
SQL&gt; set linesize 1000
SQL&gt; col name format a20
SQL&gt; select name, open_mode from v$pdbs;

NAME                 OPEN_MODE
-------------------- ----------
PDB_HYBRID           READ ONLY

SQL&gt; delete T_TEST;
delete T_TEST
       *
ERROR at line 1:
ORA-16000: Attempting to modify database or pluggable database that is open for read-only access.
Help: https://docs.oracle.com/error-help/db/ora-16000/


SQL&gt; truncate table T_TEST;
truncate table T_TEST
*
ERROR at line 1:
ORA-16000: Attempting to modify database or pluggable database that is open for read-only access.
Help: https://docs.oracle.com/error-help/db/ora-16000/
</code></pre>
<p>Han fallado ambas operaciones con el siguiente mensaje "<em>ORA-16000: Attempting to modify database or pluggable database that is open for read-only access"</em>.</p>
<p>Ahora, nos conectamos con el <strong>common user</strong> y repetimos ambas operaciones.</p>
<pre><code class="language-sql">SQL&gt;  conn c##hybrid/"FJA_2026_Oracle19c" 
Connected.

SQL&gt;  alter session set container=PDB_HYBRID;
Session altered.

SQL&gt;  select name, open_mode from v$pdbs;

NAME                 OPEN_MODE
-------------------- ----------
PDB_HYBRID           READ WRITE

SQL&gt; delete hybrid.T_TEST;

52165 rows deleted.

SQL&gt; truncate table hybrid.T_TEST;

Table truncated.
</code></pre>
<p>Hemos podido hacer ambas operaciones.</p>
<p>Otro punto que hay que mirar es la <strong>v$pdbs</strong>, cuando estamos conectados con el <strong>local user</strong> el OPEN_MODE viene READ ONLY, en cambio, para el <strong>common user</strong> el valor es READ WRITE.</p>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] ALERT_LOG_MAX_SIZE]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
El artículo veremos un nuevo parámetro introducido en 26ai para poder gestionar de manera fácil y eficiente el tamaño del alert lo]]></description><link>https://dbaenlasombra.com/26ai-alert-log-max-size</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-alert-log-max-size</guid><category><![CDATA[Oracle]]></category><category><![CDATA[Oracle 26ai]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Fri, 03 Apr 2026 14:55:11 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/cbb365d0-4428-4a41-930d-516ce59e3355.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-alert-log-max-size"><em><strong>English version</strong></em></a><em>.</em></p>
<p>El artículo veremos un nuevo parámetro introducido en 26ai para poder gestionar de manera fácil y eficiente el tamaño del <strong>alert log</strong>, es <strong>alert_log_max_size</strong>.</p>
<p>El valor por defecto del nuevo parámetro es 1000Mb:</p>
<pre><code class="language-sql">SQL&gt;
SQL&gt; show parameter alert_log_max_size

NAME               TYPE        VALUE
------------------ ----------- -------
alert_log_max_size big integer 1000M

SQL&gt;
</code></pre>
<p>Oracle internamente gestiona el <strong>alert log</strong> en bloques de 50Mb, es decir, nunca vamos a ver un <strong>alert log</strong> con un tamaño de 1000M, sino que veremos <strong>20</strong> ficheros de 50Mb cada uno.</p>
<p>Este parámetro solo se puede modificar a nivel de <strong>CDB$Root</strong>,no a nivel de <strong>PDB</strong>:</p>
<pre><code class="language-sql">SQL&gt; ALTER SYSTEM SET ALERT_LOG_MAX_SIZE = 150M; 

ALTER SYSTEM SET ALERT_LOG_MAX_SIZE = 150M 
* 
ERROR at line 1: ORA-65040: Operation is not allowed from within a pluggable database.
</code></pre>
<p>Vamos añadir información en el <strong>alert log</strong> para ver cómo se comporta Oracle:</p>
<pre><code class="language-sql">[oracle@~]cat save_inf.sql
begin 
declare 
 i number := 0; 
 begin 
  loop i := i + 1; 
  sys.dbms_system.ksdwrt(2, 'string(''L'',?)= ' || dbms_random.string('L',trunc(dbms_random.value(10,21)))); 
 exit when i &gt;= 10000000; 
end loop; 
end; 
end; 
/
[oracle@~] 
 watch -n 10 "sqlplus -s / as sysdba @save_inf.sql"
[oracle@l23ai alert]$ ls -lash *xml 
51M -rw-r----- 1 oracle asmdba 51M Apr 2 18:11 log_1.xml 
51M -rw-r----- 1 oracle asmdba 51M Apr 2 18:11 log_2.xml 
46M -rw-r----- 1 oracle asmdba 46M Apr 2 18:11 log.xml
</code></pre>
<p>Como podemos observar el <strong>alert log</strong> se ha dividido en ficheros de 50Mb!.</p>
<p>Una pequeña puntualizacion del nuevo parámetro, si está seteado con valor 0, el <strong>alert log</strong> tendrá un crecimiento ilimitado del <strong>alert log</strong>.</p>
<p>Pero ¿Podemos cambiar el valor 50Mb a otro valor? La respuesta es sí. Con el parámetro oculto "<strong>_alert_segment_size</strong>" podremos controlar el tamaño.</p>
<pre><code class="language-sql">SQL&gt; ALTER SYSTEM SET "_alert_segment_size" = 30;
System altered.

SQL&gt; show parameter "_alert_segment_size"

NAME               TYPE        VALUE
------------------ ----------- -------
_alert_segment_size integer     30

[oracle@l23ai ~]$ 
 watch -n 10 "sqlplus -s / as sysdba @save_inf.sql"
[oracle@l23ai alert]$ ls -lash *xml 
51M -rw-r----- 1 oracle asmdba 51M Apr 2 18:11 log_1.xml 
51M -rw-r----- 1 oracle asmdba 51M Apr 2 18:11 log_2.xml 
46M -rw-r----- 1 oracle asmdba 46M Apr 2 22:10 log_3.xml 
31M -rw-r----- 1 oracle asmdba 31M Apr 3 09:11 log_4.xml 
31M -rw-r----- 1 oracle asmdba 31M Apr 3 09:11 log_5.xml 
4.9M -rw-r----- 1 oracle asmdba 4.9M Apr 3 09:11 log.xml
</code></pre>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] ConnStr Tool]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy, vamos a ver la nueva herramienta que se ha introducido en la 26ai ConnStr .
Es una herramienta command-line]]></description><link>https://dbaenlasombra.com/26ai-connstr-tool</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-connstr-tool</guid><category><![CDATA[Oracle]]></category><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[listener]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Wed, 25 Mar 2026 18:41:01 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/5005f06e-946c-49ba-bfe8-7fe18d1075d9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-connstr-tool"><em><strong>English version</strong></em></a><em>.</em></p>
<p>En el artículo de hoy, vamos a ver la nueva herramienta que se ha introducido en la 26ai <strong>ConnStr</strong> .</p>
<p>Es una herramienta command-line que sirve para mostrar las conexiones por cada servicio definido en nuestra base de datos en formatos <strong>Easy Connect</strong>, <strong>JDBC</strong> y <strong>Python</strong>, dándonos la posibilidad de incluir esa información dentro del <strong>tnsnames</strong>.</p>
<p>Otro punto que hay que tener en cuenta es que el listener debe estar levantado, sino nos dará error.</p>
<p>Sin más, vamos al lío.</p>
<p>Revisamos cómo se encuentra el <strong>listener</strong>:</p>
<pre><code class="language-sql">[oracle@~]$ lsnrctl status
LSNRCTL for Linux: Version 23.26.0.0.0 - for Oracle Cloud and Engineered Systems on 25-MAR-2026 14:28:21

Alias LISTENER 
Version TNSLSNR for Linux: Version 23.26.1.0.0 - Production 
Start Date 09-MAR-2026 17:13:58 
Uptime 15 days 21 hr. 14 min. 23 sec 
Trace Level off 
Security ON: Local OS Authentication 
SNMP OFF 
Listener Parameter File /u01/app/23.0.0.0/grid/network/admin/listener.ora
Listener Log File /u01/app/grid/diag/tnslsnr/l23ai/listener/alert/log.xml
Listening Endpoints Summary... 
(DESCRIPTION=(ADDRESS=(PROTOCOL=ipc)(KEY=LISTENER))) 
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=X.X.X.X)(PORT=1521)))

Services Summary... 
 Service "+APX" has 1 instance(s). 
Instance "+APX1", status READY, has 1 handler(s) for this service...
 Service "+ASM" has 1 instance(s). 
Instance "+ASM1", status READY, has 1 handler(s) for this service...
 Service "+ASM_DATA" has 1 instance(s). 
Instance "+ASM1", status READY, has 1 handler(s) for this service...
 Service "+ASM_RECO" has 1 instance(s). 
Instance "+ASM1", status READY, has 1 handler(s) for this service...
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s).
Instance "DB23ai", status READY, has 2 handler(s) for this service...
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service...  
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 1 handler(s) for this service...  
 Service "XXXXX.paas.oracle.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.paas.oracle.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.paas.oracle.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
 Service "XXXXX.XXXXX.XXXXX.oraclevcn.com" has 1 instance(s). 
Instance "DB23ai", status READY, has 2 handler(s) for this service... 
The command completed successfully
</code></pre>
<p>Perfecto, el <strong>listener</strong> esta arriba.</p>
<p>Vamos a ejecutar <strong>ConnStr</strong> y veamos que información nos da:</p>
<pre><code class="language-sql">[oracle@ ~]$ connstr 
Using Listener: LISTENER with Oracle Home: /u01/app/oracle/product/23.0.0.0/dbhome_1

Service Name: XXXXXXXXX.paas.oracle.com 
Connection String: X.X.X.X:1521/XXXXXXXXX.paas.oracle.com

Service Name: XXXXXXX.XXXXXX.vcn23ai.oraclevcn.com 
Connection String: X.X.X.X:1521/XXXXXX.XXXXXXXX.vcn23ai.oraclevcn.com

...
...

Connection strings can be used to connect to the specified service name.

For SQL*Plus you can use: 
SQL&gt; connect username@X.X.X.X:1521/XXXXX.paas.oracle.com

For Python you can use: 
connection = oracledb.connect(user="username", password="password", dsn="X.X.X.X:1521/XXXXXX.paas.oracle.com")

For JDBC Thin you can use: OracleDataSource ods = new OracleDataSource();
ods.setURL("jdbc:oracle:thin:@X.X.X.X:1521/XXXXXX.paas.oracle.com");
ods.setUser("username"); ods.setPassword("password");
Connection conn = ods.getConnection();

Write connect strings to tnsnames.ora (Y/N)? (Default: N):
</code></pre>
<p>Que maravilla de output no? Oracle nos facilita de manera muy sencilla las cadenas de conexión para <strong>Easy Connect</strong>, <strong>JDBC</strong> y <strong>Python.</strong></p>
<p>Y como nota final, nos da la opción de incluir las cadenas en el <strong>tnsnames.ora</strong>!</p>
<p>En este caso, hemos ejecutado directamente el comando porque tenemos un único <strong>listener</strong>. Si tuviéramos más de un listener, podríamos especificar la opción <strong>-L</strong> junto con el nombre del listener.</p>
<p>Otras utilidades del comando son las siguientes:</p>
<ul>
<li>Listar los <strong>listener</strong> disponibles.</li>
</ul>
<pre><code class="language-sql">[oracle@~]$ connstr -e 
PROTOCOL PORT HOST 
tcp      1521 X.X.X.X
</code></pre>
<ul>
<li>Listar los servicios disponibles:</li>
</ul>
<pre><code class="language-sql">[oracle@~]$ connstr -s 
XXXXXXXX.paas.oracle.com 
XXXXXXXX.paas.oracle.com 
XXXXXXXX.XXXXXXXX.vcn23ai.oraclevcn.com 
pdb_david.XXXXXXXX.vcn23ai.oraclevcn.com 
+APX 
+ASM 
+ASM_DATA 
+ASM_RECO 
XXXXXXXX.XXXXXXXX.vcn23ai.oraclevcn.com 
XXXXXXXX.paas.oracle.com 
XXXXXXXX.XXXXXXXX.vcn23ai.oraclevcn.com 
XXXXXXXX.XXXXXXXX.vcn23ai.oraclevcn.com
</code></pre>
<ul>
<li>Obtener la conexión <strong>JDBC</strong> de un servicio</li>
</ul>
<pre><code class="language-sql">[oracle@ ~]$ connstr -j pdb_david jdbc:oracle:thin:@X.X.X.X:1521/XXXXXXXX.paas.oracle.com
</code></pre>
<ul>
<li>Obtener la cadena de conexión de un servicio</li>
</ul>
<pre><code class="language-sql">[oracle@~]$ connstr -d pdb_david
(DESCRIPTION=(ADDRESS=(PROTOCOL=tcp)(HOST=X.X.X.X)(PORT=1521))(CONNECT_DATA=(SERVICE_NAME=XXXXXXXX.paas.oracle.com)))
</code></pre>
<ul>
<li>Obtener la cadena de conexión de un servicio en formato <strong>Easy Connect</strong>.</li>
</ul>
<pre><code class="language-sql">[oracle@~]$ connstr -z pdb_david 
 X.X.X.X:1521/XXXXXXXX.paas.oracle.com
</code></pre>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] Instalación de Oracle26ai en Linux 9]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy vamos a ver como podemos instalar Oracle 26ai en Linux 9.
[root@~]# grep PRETTY /etc/os-release
PRETTY_NAME=]]></description><link>https://dbaenlasombra.com/26ai-instalaci-n-de-oracle26ai-en-linux-9</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-instalaci-n-de-oracle26ai-en-linux-9</guid><category><![CDATA[Linux]]></category><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[setup]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sat, 14 Mar 2026 16:57:44 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/d9a6ba10-eb2d-4fee-8a0c-f4957cbdc676.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-install-oracle-ai-database-26ai-on-linux-9"><em><strong>English version</strong></em></a><em>.</em></p>
<p>En el artículo de hoy vamos a ver como podemos instalar Oracle 26ai en Linux 9.</p>
<pre><code class="language-shell">[root@~]# grep PRETTY /etc/os-release
PRETTY_NAME="Oracle Linux Server 9.7"
</code></pre>
<p>El primer paso es descarga Oracle 26ai pre-installation desde <a href="https://yum.oracle.com/repo/OracleLinux/OL9/appstream/x86_64/getPackage/oracle-ai-database-preinstall-26ai-1.0-1.el9.x86_64.rpm">aquí</a>.</p>
<p>Procedemos a instalar el rpm una vez que lo tengamos descargado.</p>
<pre><code class="language-shell">root@~]# ls -lac oracle-ai-database-preinstall-26ai-1.0-1.el9.x86_64.rpm
-rw-r--r--. 1 root root 35302 mar  8 11:39 oracle-ai-database-preinstall-26ai-1.0-1.el9.x86_64.rpm
[root@~]# dnf -y install oracle-ai-database-preinstall-26ai-1.0-1.el9.x86_64.rpm
   
...
...
¡Listo!
</code></pre>
<p>Listo, revisamos que se ha creado el usuario <strong>oracle</strong> para proceder a crear los directorios de <em>ORACLE_HOME</em>.</p>
<pre><code class="language-shell">[root@~]# id oracle
uid=54321(oracle) gid=54321(oinstall) grupos=54321(oinstall),54322(dba),54323(oper),54324(backupdba),54325(dgdba),54326(kmdba),54330(racdba)
[root@~]# 
[root@~]# mkdir -p /u01/app/oracle/product/23.26.1/dbhome_1
[root@~]# chown -R oracle:oinstall /u01 &amp;&amp; chown -R oracle:oinstall /u01
[root@~]# su - oracle
[oracle@~]$ cd /u01/app/oracle/product/23.26.1/dbhome_1/
</code></pre>
<p>Adaptamos los ficheros .bashrc y bash_profile, para definir las siguientes variables:</p>
<pre><code class="language-shell">[oracle@~]$ tail -8 .bashrc
# User specific aliases and functions
ORACLE_BASE=/u01/app/oracle/
ORACLE_HOME=/u01/app/oracle/product/23.26.1/dbhome_1; export ORACLE_HOME
PATH=\(PATH:\){ORACLE_HOME}/bin; export PATH
LD_LIBRARY_PATH={ORACLE_HOME}/lib; export LD_LIBRARY_PATH
</code></pre>
<p>Descargamos <strong>Oracle AI Database 26ai (23.26.1) for Linux x86-64</strong> desde <a href="https://www.oracle.com/database/technologies/oracle26ai-linux-downloads.html">aquí</a>.</p>
<p>Una vez descargado lo copiamos en /u01/app/oracle/product/23.26.1/dbhome_1.</p>
<pre><code class="language-shell">[oracle@]$ pwd &amp;&amp; ls -lash
/u01/app/oracle/product/23.26.1/dbhome_1
total 2,3G
   0 drwxr-xr-x. 2 oracle oinstall   43 mar 14 12:12 .
   0 drwxr-xr-x. 3 oracle oinstall   22 mar  9 22:54 ..
2,3G -rwxr-x---. 1 oracle oinstall 2,3G mar 14 12:18 LINUX.X64_2326100_db_home.zip
[oracle@]$ unzip LINUX.X64_2326100_db_home.zip
</code></pre>
<p>Antes de realizar la instalación, vamos a adaptar el fichero response a nuestra instalación ya que usaremos el modo <strong>silent</strong> como método de instalación.</p>
<pre><code class="language-shell">[oracle@]$ vi install/response/db_install.rsp
[oracle@]\( grep -v "#" install/response/db_install.rsp | grep -v '^\)'
oracle.install.responseFileVersion=/oracle/install/rspfmt_dbinstall_response_schema_v23.0.0
installOption=INSTALL_DB_SWONLY
UNIX_GROUP_NAME=oinstall
INVENTORY_LOCATION=${ORA_INVENTORY}
ORACLE_HOME=/u01/app/oracle/product/23.26.1/dbhome_1
ORACLE_BASE=/u01/app/oracle/
installEdition=EE
OSDBA=dba
OSBACKUPDBA=dba
OSDGDBA=dba
OSKMDBA=dba
OSRACDBA=dba
[oracle@]$ ./runInstaller -silent -responsefile install/response/db_install.rsp
</code></pre>
<p>Con usuario root, ejecutamos los dos últimos scripts</p>
<pre><code class="language-shell">[root@~]# .  /u01/app/oraInventory/orainstRoot.sh
...
[root@~]# .  /u01/app/oracle/product/23.26.1/dbhome_1/root.sh
...
</code></pre>
<p>Verificamos la instalación:</p>
<pre><code class="language-shell">[oracle@~]$ sqlplus /nolog

SQL*Plus: Release 23.26.1.0.0 - Production on Sat Mar 14 17:49:10 2026
Version 23.26.1.0.0

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

SQL&gt; 
</code></pre>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[OCI] Montaje de un Bucket como  un FS]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy vamos a ver como montar un Bucket como un filesystem en un DB System de OCI, ya que puede ser algo muy útil ]]></description><link>https://dbaenlasombra.com/oci-montaje-de-un-bucket-como-un-fs</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-montaje-de-un-bucket-como-un-fs</guid><category><![CDATA[OCI]]></category><category><![CDATA[bucket]]></category><category><![CDATA[Oracle Linux]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Mon, 09 Mar 2026 18:55:24 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/05595570-0fbd-46ce-9db4-d2f0e9f09003.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/oci-montaje-de-un-bucket-como-un-fs"><em><strong>English version</strong></em></a><em>.</em></p>
<p>En el artículo de hoy vamos a ver como montar un <em>Bucket</em> como un filesystem en un DB System de OCI, ya que puede ser algo muy útil poder administrar nuestro Bucket con si fuera un directorio más en nuestras máquinas.</p>
<p>El DB System de OCI tiene Oracle Linux Server 8.</p>
<pre><code class="language-shell">[root@~]$ grep ORACLE_* /etc/os-release
ORACLE_BUGZILLA_PRODUCT="Oracle Linux 8"
ORACLE_BUGZILLA_PRODUCT_VERSION=8.8
ORACLE_SUPPORT_PRODUCT="Oracle Linux"
ORACLE_SUPPORT_PRODUCT_VERSION=8.8
</code></pre>
<p>Para Oracle Linux 8 o superior, debemos utilizar <strong>OCIFS</strong>.</p>
<p>Antes de meternos en faena, tenemos que tener los siguientes puntos realizados:</p>
<ul>
<li><p>Tener un <em>Bucket</em> en nuestra <strong>tenancy</strong>.</p>
</li>
<li><p>Tener suficiente espacio en nuestra máquina, ya que <strong>OCIFS</strong> usa espacio en disco para cachear los objectos de nuestro <em>Bucket</em> .</p>
</li>
<li><p>OCI Cli configurado, ya que por defecto <strong>OCIFS</strong> usa <strong>API key-based authentication</strong> para autenticarse. Dejo por aquí, donde hablamos para configurarlo de manera sencilla.</p>
</li>
</ul>
<p><a href="https://dbaenlasombra.com/oci-oci-cli-y-su-configuracion"><strong>[OCI] OCI-cli y su configuración</strong></a></p>
<p>Así que que vamos a instalar <strong>OCIFS</strong> como usuario <strong>root</strong> en nuestra máquina:</p>
<pre><code class="language-shell">[root@~]$ dnf install ocifs -y
...
Last metadata expiration check: 2:19:05 ago on Sun 08 Mar 2026 12:38:39 PM UTC.
Dependencies resolved.
...
Installed:
  fuse-2.9.7-19.0.1.el8.x86_64                               
  fuse-common-3.3.0-19.0.1.el8.x86_64                               
  ocifs-1.2.0-6.el8.x86_64                              
...
...
Complete!
</code></pre>
<p>Perfecto, ahora vamos a mirar donde podríamos crear el directorio que actúe como cache para OCIFS.</p>
<pre><code class="language-shell">[root@~]$ 
df -h --output=source,size,used,avail,pcent,target | (read -r h; echo "$h"; sort -k2,2hr) | head -4
Filesystem                      Size  Used Avail Use% Mounted on
/dev/sdg                        196G   26G  161G  14% /u01
/dev/mapper/vg00-opt             33G  6.0G   26G  19% /opt
devtmpfs                         16G     0   16G   0% /dev
</code></pre>
<p>Donde más sitio tenemos es en /u01, así que crearemos ahí el directorio:</p>
<pre><code class="language-shell">[root@~]$ mkdir /u01/tmp
</code></pre>
<p>Consultamos el nombre del <em>Bucket</em> que queremos que sea un FS en nuestra máquina</p>
<img src="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/bbd7ba6a-1a40-4a50-b206-f6d5363712b2.png" alt="" style="display:block;margin:0 auto" />

<p>Una vez copiado el nombre del <em>Bucket</em>, lo montamos con el comando <strong>ocifs</strong>:</p>
<pre><code class="language-shell">[root@~]$ mkdir /mybucket 
[root@~]$ ocifs --cache=/u01/tmp &lt;name_bucket&gt; /mybucket
[root@~]$ df -h /mybucket
Filesystem            Size  Used Avail Use% Mounted on
&lt;name_bucket&gt;          0     0     0    - /mybucket
[root@l23ai ~]# grep mybucket /proc/mounts
&lt;name_bucket&gt; /mybucket fuse.ocifs rw,nosuid,nodev,relatime,user_id=0,group_id=0,default_permissions 0 0
</code></pre>
<p>Perfecto, hemos montado nuestro Bucket y además vemos que se ha creado correctamente la entrada en <strong>/proc/mounts</strong>.</p>
<p>Vamos a crear un fichero con algo de información de manera local:</p>
<pre><code class="language-shell">[root@~]$ 
 touch /mybucket/passwd.bck &amp;&amp; cat /etc/passwd &gt; /mybucket/passwd.bck
</code></pre>
<p>Abajo, podéis ver que el fichero está correctamente en nuestro <em>Bucket</em> :</p>
<img src="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/6e98eae6-a65b-4f8b-bd2e-d9cd6649a313.png" alt="" style="display:block;margin:0 auto" />

<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] DBMS_CLOUD — Capitulo Dos]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
Continuando con el artículo de la semana pasada, donde vimos la configuración y unos primeros usos de DBMS_CLOUD en el día a día, ]]></description><link>https://dbaenlasombra.com/26ai-dbms-cloud-capitulo-dos</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-dbms-cloud-capitulo-dos</guid><category><![CDATA[OCI]]></category><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[bucket]]></category><category><![CDATA[datapump]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Tue, 24 Feb 2026 16:28:46 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/65605419d28f19cc44df7ef1/a21fde07-5a03-4169-9247-2425a1ffe40a.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-dbms-cloud-chapter-two"><em><strong>English version</strong></em></a><em>.</em></p>
<p>Continuando con el artículo de la semana pasada, donde vimos la configuración y unos primeros usos de <em>DBMS_CLOUD</em> en el día a día, hoy continuaremos con la parte de <em>Data Pump</em>.</p>
<p>Dejo por aquí el enlace del primer capitulo:</p>
<p><a href="https://dbaenlasombra.com/26ai-dbms-cloud-capitulo-uno">[26ai] DBMS_CLOUD — Capitulo Uno</a></p>
<p>La idea del laboratorio es la siguiente:</p>
<ul>
<li><p>Export una tabla a nuestro <em>Bucket.</em></p>
</li>
<li><p>Importar la tabla que hemos exportado en el punto anterior</p>
</li>
</ul>
<p>Así que sin más, vamos ha realizar cada punto:</p>
<h3><strong>Export a un <em>Bucket</em></strong></h3>
<p>Vamos a definir un tabla que utilizaremos para exportar e importar:</p>
<pre><code class="language-sql">SQL&gt; 
  Create Table My_Grants As  
   Select TABLE_SCHEMA,TABLE_NAME,PRIVILEGE  
     From All_Tab_Privs;

 Table created.

SQL&gt; set linesize 1000 pages 1000
SQL&gt; col TABLE_SCHEMA format a20
SQL&gt; col table_name format a20
SQL&gt; col privilege format a20
SQL&gt; r
  1* Select * From My_Grants FETCH FIRST 10  ROWS ONLY

TABLE_SCHEMA         TABLE_NAME                     PRIVILEGE
-------------------- ------------------------------ --------------------
SYS                  DUAL                           SELECT
SYS                  SYSTEM_PRIVILEGE_MAP           READ
SYS                  TABLE_PRIVILEGE_MAP            READ
SYS                  USER_PRIVILEGE_MAP             READ
SYS                  STMT_AUDIT_OPTION_MAP          READ
SYS                  FINALHIST$                     INSERT
SYS                  DM$EXPIMP_ID_SEQ               SELECT
SYS                  MODELGTTRAW$                   SELECT
SYS                  MODELGTTRAW$                   INSERT
SYS                  PMAPGTT$                       SELECT
</code></pre>
<p>Utilizaremos la propiedad <strong>DEFAULT_CREDENTIAL</strong> para setear nuestra credencial por defecto y así no tener que indicarla dentro del <em>parfile</em> del <strong>export</strong>.</p>
<pre><code class="language-sql">SQL&gt; 
ALTER DATABASE PROPERTY SET DEFAULT_CREDENTIAL = 'TEST.DBA_IN_THE_SHADOW';

Database altered.

SQL&gt; 
Select PROPERTY_NAME,PROPERTY_VALUE 
  From database_properties  
 Where PROPERTY_NAME= 'DEFAULT_CREDENTIAL';

PROPERTY_NAME        PROPERTY_VALUE                           
-------------------- ---------------------------------------- 
DEFAULT_CREDENTIAL   TEST.DBA_IN_THE_SHADOW                    
</code></pre>
<p>Definimos el parfile:</p>
<pre><code class="language-sql">SQL&gt; ! vi export_to_bucket.par
SQL&gt; ! cat export_to_bucket.par
DUMPFILE=&lt;location_uri&gt;/&lt;TABLE&gt;.dmp
LOGFILE=&lt;TABLE&gt;.log
TABLES=&lt;SCHEMA&gt;.&lt;TABLE&gt;
CONTENT=DATA_ONLY
LOGTIME=ALL
METRICS=YES
FLASHBACK_TIME=SYSTIMESTAMP
encryption_password=hari
</code></pre>
<p>Esto es lo que hemos definido:</p>
<ul>
<li><p>DUMPILE: La ruta donde se va guardar el dmp, en nuestro caso es el Bucket más un nombre descriptivo.</p>
</li>
<li><p>LOGFILE: Nombre del log.</p>
</li>
<li><p>TABLES: Ahí indicamos la tabla que hemos creado previamente.</p>
</li>
<li><p>CONTENT=DATA_ONLY: Solo exportamos los datos</p>
</li>
<li><p>LOGTIME/METRICS: Métricas de tiempo de nuestro export.</p>
</li>
<li><p>FLASHBACK_TIME: Que sea un export consistente.</p>
</li>
<li><p>ENCRYPTION_PASSWORD: Encrypt de nuestro dmp.</p>
</li>
</ul>
<p>Ejecutamos el <em>export</em>:</p>
<img src="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/65605419d28f19cc44df7ef1/abcbe616-5049-4a34-b90e-db8f57914a8c.png" alt="" style="display:block;margin:0 auto" />

<p>Consultamos que el <strong>dmp</strong> se ha generado correctamente en el <em>Bucket</em>:</p>
<pre><code class="language-sql">SQL&gt; 
Select object_name, trunc(bytes / (1024*1024),2) MB
  From 
   dbms_cloud.list_objects('DBA_IN_THE_SHADOW', '&lt;location_uri&gt;'); 

OBJECT_NAME                                      MB
---------------------------------------- ----------
ORA7445pf_20260205085710_COM_1.zip            57.35
Wallet_OF2BAP.zip                               .02
Wallet_OF2BCE.zip                               .02
grant.dmp                                         0
grant.dmp_segments/aaaaaa                         2
grant.dmp_segments/aaaaab                       .86

6 rows selected.
</code></pre>
<p>Fijémonos una cosa sobre el <strong>dmp</strong> que tenemos en nuestro <em>Bucket</em>. Tenemos tres archivos, cuando hemos indicado uno único archivo sin comodines, es decir, sin <strong>%L</strong>, <strong>%l</strong>, <strong>%U</strong> o <strong>%u</strong>. El tamaño de nuestro <strong>dmp</strong> es 0Mb y los fragmentos son los que tiene el peso, esto es porque <em>Data Pump</em> divide cada parte del archivo en fragmentos más pequeños para una carga más rápida.</p>
<h3><strong>Import desde <em>Bucket</em></strong></h3>
<p>Ejecutamos el <em>import</em> creando una tabla previa para almacenar ahí la información:</p>
<pre><code class="language-sql">SQL&gt; CREATE TABLE MY_GRANTS_BCK AS SELECT * FROM MY_GRANTS WHERE 2 = 1;

Table created.
SQL&gt; ! vi import_to_bucket.par
SQL&gt; ! cat import_to_bucket.par
DUMPFILE=&lt;location_uri&gt;/&lt;TABLE&gt;.dmp
LOGFILE=MY_GRANTS.log
LOGTIME=ALL
METRICS=YES
REMAP_TABLE = TEST.MY_GRANTS:MY_GRANTS_BCK
encryption_password=yes
</code></pre>
<p>Esto es lo que hemos definido:</p>
<ul>
<li><p>DUMPILE: La ruta donde se encuentra el dmp.</p>
</li>
<li><p>LOGFILE: Nombre del log.</p>
</li>
<li><p>LOGTIME/METRICS: Métricas de tiempo de nuestro export.</p>
</li>
<li><p>REMAP_TABLE: Queremos que la información del dmp se albergue en otra tabla</p>
</li>
<li><p>ENCRYPTION_PASSWORD= Encrypt de nuestro dmp.</p>
</li>
</ul>
<p>Ejecutamos el <em>import</em>:</p>
<img src="https://cdn.hashnode.com/uploads/covers/65605419d28f19cc44df7ef1/1f70eaba-7bce-42eb-befe-5fcb06d93874.png" alt="" style="display:block;margin:0 auto" />

<p>Perfecto, hemos podido exportar e importar un dmp desde el <em>Bucket</em>.</p>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] DBMS_CLOUD — Capitulo Uno]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy vamos a ver algunos beneficios que podemos tener en el día a día al utilizar DBMS_CLOUD en 26ai para nuestra]]></description><link>https://dbaenlasombra.com/26ai-dbms-cloud-capitulo-uno</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-dbms-cloud-capitulo-uno</guid><category><![CDATA[OCI]]></category><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[dbms_cloud]]></category><category><![CDATA[bucket]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Fri, 20 Feb 2026 00:40:29 GMT</pubDate><enclosure url="https://cloudmate-test.s3.us-east-1.amazonaws.com/uploads/covers/65605419d28f19cc44df7ef1/971edde0-b81e-4a61-9ed1-bafbe4fc17bc.jpg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a href="https://dbaintheshadow.com/26ai-dbms-cloud-chapter-one"><em><strong>English version</strong></em></a><em>.</em></p>
<p>En el artículo de hoy vamos a ver algunos beneficios que podemos tener en el día a día al utilizar <em>DBMS_CLOUD</em> en 26ai para nuestras tareas diarias, como puede ser tareas de respaldo de un datapump o guardar informes, ya que se conecta a un <em>Object Storage</em> de OCI.</p>
<p>Por defecto, no viene configurado en nuestra 26ai, con lo que el primer paso que debemos hacer es configurarlo para un usuario válido de nuestra <strong>tenancy</strong>.</p>
<p>Antes de meternos en faena, debemos asegurarnos que nuestra base de datos tiene <em>DBMS_CLOUD</em> configurado y listo para usarse. Dejo por aquí la nota de Oracle para la instalación en caso que no tengamos configurado ese punto:</p>
<p><a href="https://support.oracle.com/support/?anchorId=&amp;kmContentId=2748362&amp;page=sptemplate&amp;sptemplate=km-article"><em><strong>How To Setup And Use DBMS_CLOUD Package (Doc ID 2748362.1)</strong></em></a></p>
<p>A nivel de OCI, debemos configurar para nuestro usuario un <strong>token</strong> o <strong>API Key</strong> ya que necesitamos definir una <strong>credencial</strong> porque nuestro <em>Bucket</em> es privado. No sería necesario definir la credencial si nuestro <em>Bucket</em> es público*.*</p>
<h3><strong>Token</strong></h3>
<p>El primer paso que vamos a realizar es generar el token de nuestro usuario en OCI. Para ello, tendremos que ir a la parte de configuración de nuestro usuario, justo en la parte superior derecha:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771410019467/915ef3cc-14f9-41d5-9e8f-feb4566a6036.png" alt="" style="display:block;margin:0 auto" />

<p>Una vez dentro, vamos a la pestaña <strong>Tokens and keys</strong>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771329298121/73011e3f-f972-434c-9386-e50f55798bf7.png" alt="" style="display:block;margin:0 auto" />

<p>Ahí vamos a ver tres secciones: <em>API Keys</em>, <em>Auth Tokens</em> y <em>Customer Secrets Keys</em>.</p>
<p>Bien, la que nos interesa es la de <em>Auth Tokens</em>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771329519186/02251fb9-d8c1-4ecb-b635-417e614f8d0a.png" alt="" style="display:block;margin:0 auto" />

<p>Ahí pulsamos en <em>Generate token</em>. Nos pedirá un nombre y pulsamos en <em>Generate token</em>:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771329590592/50419b9d-06d2-476b-b216-4b8fc0a8f0c0.png" alt="" style="display:block;margin:0 auto" />

<p>La siguiente ventana <strong>no</strong> la debemos cerrar sin antes copiar el token.</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771329749038/4ebb4308-6ad9-4092-a766-1e20ff09a5b9.png" alt="" style="display:block;margin:0 auto" />

<p>Una vez que hayamos copiado el token, definimos la <strong>credential</strong> en nuestra base de datos usando el token que hemos generado en el punto anterior.</p>
<h3><strong>Configuración de la Credencial</strong></h3>
<p>Para generarlo, debemos usar <strong>DBMS_CLOUD.CREATE_CREDENTIAL</strong>.</p>
<p>Este procedimiento recibe los siguientes parámetros:</p>
<ul>
<li><p>CREDENTIAL_NAME: Nombre de la credencial que vamos a crear.</p>
</li>
<li><p>USERNAME: Nombre del nuestro usuario, siempre debe seguir la estructura de “<em>/</em>”.</p>
</li>
<li><p>Password: Valor del  que hemos generado antes</p>
</li>
</ul>
<pre><code class="language-sql">SQL&gt;EXEC  dbms_cloud.create_credential(
    credential_name =&gt; 'DBA_IN_THE_SHADOW',
    username        =&gt; '&lt;IdentityDomain&gt;/&lt;usuario_tenancy&gt;',
    password        =&gt; '&lt;token&gt;');

SQL&gt; R
  1  select credential_name,
  2         username,
  3         enabled
  4  from   DBA_credentials
  5* order by credential_name

CREDENTIAL_NAME           USERNAME                                           ENABLED
------------------------- -------------------------------------------------- --------------------
DBA_IN_THE_SHADOW         &lt;IdentityDomain&gt;/&lt;usuario_tenancy&gt;       TRUE
</code></pre>
<p>Una manera rápida de verificar que funciona correctamente es listando el contenido que tenemos en nuestra <em>Bucket</em>. Para ello nos apoyaremos en la función <strong>DBMS_CLOUD.LIST_OBJECTS</strong>.</p>
<p>Esta función recibe dos parámetros, el primero sería el nombre de la credencial que hemos generado en el punto anterior, y el segundo sería la URL para acceder a nuestro <em>Bucket</em>.</p>
<p>La manera más fácil de obtener la url, es ir a un objecto de nuestro <em>Bucket</em> e ir a los detalles del mismo:</p>
<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771411079006/0d136145-6a8c-487d-b28a-7edff7d3abda.png" alt="" style="display:block;margin:0 auto" />

<img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1771411298002/5eee5747-6775-4097-9424-7e33298cdd84.png" alt="" style="display:block;margin:0 auto" />

<p>Ahí copiamos el valor de “<em>URL path (URI)</em>”.</p>
<p>Una vez que lo tenemos copiado, listamos vía SQL el contenido del <em>Bucket</em>:</p>
<pre><code class="language-sql">SQL&gt; 
Select object_name
  From Dbms_cloud.list_objects('DBA_IN_THE_SHADOW','&lt;location_uri&gt;'); 

OBJECT_NAME
--------------------------------------------------------------------------------
XXXXXXXX.zip
XXXXXXXX.zip
XXXXXXXX.zip
</code></pre>
<p>Perfecto, tenemos acceso.</p>
<h3><strong>Subida de ficheros</strong></h3>
<p>Generamos un fichero <strong>csv</strong> y lo subimos a nuestro <em>Bucket</em>.</p>
<p>Utilizaremos <strong>DBMS_CLOUD.PUT_OBJECTS</strong> para subir el fichero generado. Para utilizarlo, definiremos el directory apuntando al path donde se encuentra nuestro fichero.</p>
<pre><code class="language-sql">SQL&gt; ! cat generate.sql
set heading off
set feedback off
set echo off
set verify off
set termout off
set markup csv on
spool myfile.csv
Select trunc(dbms_random.value(1, 1000)), dbms_random.string('x', 10)     
 From dual  
connect by level &lt;= 10000;
spool off

SQL&gt; @generate.sql
SQL&gt; ! ls -lac *csv
-rw-r--r-- 1 oracle oinstall 168924 Feb 18 12:42 myfile.csv

SQL&gt; CREATE DIRECTORY OCI_HOME as '/home/oracle';
SQL&gt; GRANT READ, WRITE ON DIRECTORY OCI_HOME TO PUBLIC;
SQL&gt; begin
  dbms_cloud.put_object (
    credential_name =&gt; 'DBA_IN_THE_SHADOW',
    object_uri      =&gt; '&lt;location_uri&gt;/myfile.csv',
    directory_name  =&gt; 'OCI_HOME',
    file_name       =&gt; 'myfile.csv');
end;
/ 
SQL&gt;
SQL&gt; Select object_name
  From 
   dbms_cloud.list_objects('DBA_IN_THE_SHADOW','&lt;location_uri&gt;');  

OBJECT_NAME
--------------------------------------------------------------------------------
XXXXXXXX.zip
XXXXXXXX.zip
XXXXXXXX.zip
myfile.csv
</code></pre>
<p>Listo, ya tenemos el fichero en nuestro <em>Bucket</em>.</p>
<p>Una vez subido, podemos generar una tabla externa que lee ese fichero. Una manera rápida de definir este punto, es usando <strong>DBMS_CLOUD.CREATE_EXTERNAL_TABLE</strong>, que genera una tabla en nuestro schema que lee el fichero que le pasemos por parámetro.</p>
<pre><code class="language-sql">SQL&gt; 
BEGIN
  DBMS_CLOUD.CREATE_EXTERNAL_TABLE(
    table_name      =&gt; 'EXT_MYFILE_CSV',
    credential_name =&gt; 'DBA_IN_THE_SHADOW',
    file_uri_list   =&gt; '&lt;location_uri&gt;/myfile.csv',
    format          =&gt; JSON_OBJECT(
                         'type' VALUE 'csv',
                         'delimiter' VALUE ',',
                         'quote' VALUE '"',
                         'skipheaders' VALUE '0'
                       ),
    column_list     =&gt; '
        id   NUMBER,
        code VARCHAR2(50)
    '
  );
END;
/

SQL&gt; SELECT COUNT(*) FROM EXT_MYFILE_CSV;
1000
SQL&gt;
 SELECT * FROM EXT_MYFILE_CSV FETCH FIRST 25 ROWS ONLY
        ID CODE
---------- --------------------------------------------------
       224 3LU82RWW27
       654 LASYRZWUUE
        17 UW34WB3O4Y
       764 5Z2I01ITAX
       132 V4XCFDS9GA
       131 6PRK6G0VTC
       650 NST09S6RY9
       133 R8IIPLJZ95
       477 WK8Y1XC5XK
       930 O574M5RJK2
       371 P8G6PF91A9
       214 WRUYMY4BWY
       273 YD5NG441MV
       706 DQYEBYNF5H
       699 J3T3RHNHU0
       744 VVXATGVW0N
       945 U55X9EATLH
       371 RHW44MZ1DQ
       595 B1RU6VN33B
       864 P02I2A9Z8S
       218 R02XSI9MLY
       980 M0CSWHE4G7
       396 UZYFEL7UKI
       619 DYKCFI912X
       881 2TZ0GEQ6MM

25 rows selected.
</code></pre>
<p>También podemos exportar la información de una consulta válida a nuestro <em>Bucket</em> usando <strong>DBMS_CLOUD.EXPORT_DATA</strong>.</p>
<p>Este procedimiento recibe los siguientes parámetros:</p>
<ul>
<li><p>CREDENTIAL_NAME: Nombre de la credencial que vamos a crear.</p>
</li>
<li><p>URL: “<em>URL path (URI)</em>” más el nombre del fichero, no es necesario indicar la extensión, ya que eso lo indicamos en el parámetro Format.</p>
</li>
<li><p>Format: Formato en que queremos guardar el spool de nuestra consulta, en nuestra caso es <strong>json</strong>.</p>
</li>
<li><p>Query: SQL válida que queremos exportar.</p>
</li>
</ul>
<pre><code class="language-sql">SQL&gt; 
BEGIN
      DBMS_CLOUD.EXPORT_DATA(  
        credential_name =&gt;'DBA_IN_THE_SHADOW',
        file_uri_list =&gt;'&lt;location_uri&gt;/config_system_parameter',
        format =&gt; json_object(  'type' VALUE 'json' ),
        query =&gt; 'SELECT NAME, VALUE FROM V$SYSTEM_PARAMETER'
      );
   END;
  / 

SQL&gt; Select object_name
  From 
   dbms_cloud.list_objects('DBA_IN_THE_SHADOW','&lt;location_uri&gt;');  

OBJECT_NAME
--------------------------------------------------------------------------------
XXXXXXXX.zip
XXXXXXXX.zip
XXXXXXXX.zip
config_system_parameter_1_1_1.json
myfile.csv
</code></pre>
<p>Y si necesitamos descargar ese fichero en una tabla, podríamos usar el procedimiento <strong>DBMS_CLOUD.COPY_DATA</strong>.</p>
<pre><code class="language-sql">SQL&gt; 
  Create table BACK_SYSTEM_PARAMETER ( 
    INFO_PARAMETER CLOB  CONSTRAINT INFO_PARAMETER_JSON CHECK ( INFO_PARAMETER IS JSON )
      );

Table created.

SQL&gt;
begin
  DBMS_CLOUD.COPY_DATA (
    table_name      =&gt; 'BACK_SYSTEM_PARAMETER',
    credential_name =&gt; 'DBA_IN_THE_SHADOW',
    file_uri_list   =&gt; '&lt;location_uri&gt;/config_system_parameter_1_1_1.json'
     );
end;
/
PL/SQL procedure successfully completed.
</code></pre>
<p>Ahí he definido una tabla <em>BACK_SYSTEM_PARAMETER</em> con una única columna con el objecto de inyectar ahí el contenido del JSON. Una vez definida, ejecuto el procedimiento para descargar la información.</p>
<p>Si consultamos la tabla, vemos que se ha cargado correctamente la información:</p>
<pre><code class="language-sql">SQL&gt;
 select 
    JSON_VALUE(INFO_PARAMETER, '$.NAME') AS NAME, 
    JSON_VALUE(INFO_PARAMETER, '$.VALUE') AS VALUE 
   from BACK_SYSTEM_PARAMETER
 Where INSTR(UPPER(JSON_VALUE(INFO_PARAMETER, '$.NAME')),'OPTIMIZER') != 0
  FETCH FIRST 5 ROWS ONLY;

NAME                                     VALUE
---------------------------------------- ----------------------------------------
optimizer_ignore_hints                   FALSE
optimizer_secure_view_merging            TRUE
optimizer_use_pending_statistics         FALSE
optimizer_capture_sql_plan_baselines     FALSE
optimizer_use_sql_plan_baselines         TRUE

10 rows selected.
</code></pre>
<p>En el próximo artículo abordaremos el uso del <em>Data Pump</em> junto con el <em>DBMS_CLOUD</em></p>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] Blockchain & Immutable table]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
  
En este pequeño artículo vamos a ver las Blockchain table y Immutable table.
Este tipo de tablas fueron introducidas en Oracle, con el objetivo de protegernos contra la modifica...]]></description><link>https://dbaenlasombra.com/26ai-blockchain-and-immutable-table</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-blockchain-and-immutable-table</guid><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[tables]]></category><category><![CDATA[cdbroot]]></category><category><![CDATA[pdb]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Thu, 12 Feb 2026 14:25:08 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1770761522011/ed274c25-d834-4f91-bd27-c12217f72732.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/26ai-blockchain-and-immutable-table"><strong><em>English version</em></strong></a><em>.</em></p>
<p><a target="_blank" href="https://dbaintheshadow.com/26ai-mandatory-profile">  
</a>En este pequeño artículo vamos a ver las <strong>Blockchain table</strong> y <strong>Immutable table</strong>.</p>
<p>Este tipo de tablas fueron introducidas en Oracle, con el objetivo de protegernos contra la <strong>modificación</strong> de datos no autorizados por personal externo o accidentalmente, únicamente se pueden realizar <strong>inserciones</strong>. Además, las <strong>Blockchain table</strong> a diferencia de las <strong>Immutable table</strong>, incluyen una capa extra de seguridad incluyendo <strong>cryptographic hash</strong> a nivel de fila enlazado con la fila anterior, formando una cadena que permite detectar cualquier intento de manipulación.</p>
<p>Aunque no es una novedad en <strong>26ai</strong>, sino que fueron incluidas en la <strong>21c</strong>, si que han incluido importantes mejoras muy interesantes para potenciar este tipo de tablas:</p>
<ul>
<li><p>Poder añadir y eliminar columnas tanto en <strong>Blockchain</strong> como en <strong>Immutable</strong> table, especificando <strong>VERSION v2</strong>. Veamos un ejemplo, porque creo que es un matiz importante:</p>
<pre><code class="lang-sql">  SQL&gt; 
  <span class="hljs-keyword">CREATE</span> IMMUTABLE <span class="hljs-keyword">TABLE</span> T_TEST_VRS_1 (
    TC_ID <span class="hljs-built_in">NUMBER</span> <span class="hljs-keyword">GENERATED</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> PRIMARY <span class="hljs-keyword">KEY</span>
  )
  <span class="hljs-keyword">no</span> <span class="hljs-keyword">drop</span> <span class="hljs-keyword">until</span> <span class="hljs-number">2</span> <span class="hljs-keyword">days</span> idle
  <span class="hljs-keyword">no</span> <span class="hljs-keyword">delete</span> <span class="hljs-keyword">until</span> <span class="hljs-number">16</span> <span class="hljs-keyword">days</span> <span class="hljs-keyword">after</span> <span class="hljs-keyword">insert</span>
  <span class="hljs-keyword">VERSION</span> <span class="hljs-string">"v1"</span>;

  Table created.
  SQL&gt;  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_TEST_VRS_1 <span class="hljs-keyword">ADD</span> (FECHA <span class="hljs-built_in">DATE</span>);
  ERROR at line 1:
  ORA-05715: operation not allowed on the blockchain or immutable table
  <span class="hljs-keyword">Help</span>: https://docs.oracle.com/<span class="hljs-keyword">error</span>-<span class="hljs-keyword">help</span>/db/ora<span class="hljs-number">-05715</span>/

  <span class="hljs-keyword">SQL</span>&gt; 
  <span class="hljs-keyword">CREATE</span> IMMUTABLE <span class="hljs-keyword">TABLE</span> T_TEST_VRS_2 (
    TC_ID <span class="hljs-built_in">NUMBER</span> <span class="hljs-keyword">GENERATED</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> PRIMARY <span class="hljs-keyword">KEY</span>
  )
  <span class="hljs-keyword">no</span> <span class="hljs-keyword">drop</span> <span class="hljs-keyword">until</span> <span class="hljs-number">2</span> <span class="hljs-keyword">days</span> idle
  <span class="hljs-keyword">no</span> <span class="hljs-keyword">delete</span> <span class="hljs-keyword">until</span> <span class="hljs-number">16</span> <span class="hljs-keyword">days</span> <span class="hljs-keyword">after</span> <span class="hljs-keyword">insert</span>
  <span class="hljs-keyword">VERSION</span> <span class="hljs-string">"v2"</span>;

  SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_TEST_VRS_2 <span class="hljs-keyword">ADD</span> (FECHA <span class="hljs-built_in">DATE</span>);
  Table altered.
</code></pre>
</li>
<li><p><strong>BLOCKCHAIN_TABLE_MAX_NO_DROP</strong>, nuevo parámetro para especificar el tiempo máximo de inactividad cuando definimos este tipo de tablas. Cuando el parámetro esta seteado a un valor y el usuario crea una tabla con la cláusula <strong>NO DROP UNTIL</strong> n <strong>DAYS IDLE</strong>, el valor de n debe ser igual a ese valor o inferior. Si el parámetro esta nulo, es decir, no seteado, podemos definir cualquier valor en n <strong>DAYS IDLE</strong>.</p>
</li>
</ul>
<p>Tanto las filas nuevas como las antiguas, o el borrado de la tabla, están sujetas a un periodo de retención que el usuario especifica al crear la tabla, y cuando ese periodo de retención expira, pueden eliminarse las filas o directamente la tabla.</p>
<h3 id="heading-periodo-de-retencion"><strong>Periodo de Retención</strong></h3>
<p>En el modelo que vemos a continuación, podemos ver como podemos especificar el periodo de retención a nivel de tabla y fila:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770821644086/8a1db77f-9b85-4203-bb83-fb9cea008282.png" alt class="image--center mx-auto" /></p>
<p>A nivel de tabla:</p>
<ul>
<li><p><strong>NO DROP</strong>: La tabla no puede ser eliminada, a no ser que este vacía.</p>
</li>
<li><p><strong>NO DROP UNTIL</strong> n <strong>DAYS IDLE</strong>: Si aún quedan filas con un periodo de antigüedad menor, la tabla no puede ser eliminada. Por defecto, el valor es 0, aunque para garantizar la seguridad se recomienda un valor mínimo de 16.</p>
</li>
</ul>
<p>A nivel de fila:</p>
<ul>
<li><p><strong>NO DELETE [<em>locked</em>]</strong>: Las filas no pueden ser eliminadas. La clausula <strong>[<em>locked</em>]</strong> especifica que no se puede modificar la retención.</p>
</li>
<li><p><strong>NO DELETE UNTIL</strong> n <strong>DAYS AFTER INSERT [<em>locked</em>]</strong>: Cada fila no puede ser eliminada, es decir, esta protegida de la eliminación tantos días como especifiquemos en la cláusula n <strong>DAYS</strong>. Valor mínimo es 16 días. La clausula <strong>[<em>locked</em>]</strong> especifica que no se puede modificar la retención.</p>
</li>
</ul>
<h3 id="heading-restricciones"><strong>Restricciones</strong></h3>
<p>Las restricciones a nivel de tabla son las siguientes:</p>
<ul>
<li><p>Los siguientes tipo de datos no pueden ser soportados: <em>ROWID</em>, <em>UROWID, LONG, OBJECT TYPE, REF, VARRAY, NESTED TABLE, TIMESTAMP WITH TIME ZONE, TIMESTAMP WITH LOCAL TIME ZONE, BFILE</em> y <em>XMLType.</em></p>
</li>
<li><p>A nivel de tabla, no pueden ser <em>INDEX-ORGANIZED, ORGANIZATION CUBE, ORGANIZATION EXTERNAL</em> o <em>HIBRYD PARTITIONED</em>.</p>
</li>
</ul>
<p>Sin más vamos a definir un ejemplo de cada.</p>
<h3 id="heading-inmutable-table"><strong>Inmutable Table</strong></h3>
<pre><code class="lang-sql">SQL&gt; 
<span class="hljs-keyword">CREATE</span> IMMUTABLE <span class="hljs-keyword">TABLE</span> T_CLIENTES (
  TC_ID <span class="hljs-built_in">NUMBER</span> <span class="hljs-keyword">GENERATED</span> <span class="hljs-keyword">BY</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">AS</span> <span class="hljs-keyword">IDENTITY</span> PRIMARY <span class="hljs-keyword">KEY</span>,
  TC_name   <span class="hljs-built_in">VARCHAR2</span>(<span class="hljs-number">100</span>) <span class="hljs-keyword">NOT</span> <span class="hljs-literal">NULL</span>,
  TC_email       <span class="hljs-built_in">VARCHAR2</span>(<span class="hljs-number">200</span>),
  TC_created_at  <span class="hljs-built_in">DATE</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">SYSDATE</span>
)
<span class="hljs-keyword">no</span> <span class="hljs-keyword">drop</span> <span class="hljs-keyword">until</span> <span class="hljs-number">2</span> <span class="hljs-keyword">days</span> idle
<span class="hljs-keyword">no</span> <span class="hljs-keyword">delete</span> <span class="hljs-keyword">until</span> <span class="hljs-number">16</span> <span class="hljs-keyword">days</span> <span class="hljs-keyword">after</span> <span class="hljs-keyword">insert</span>
<span class="hljs-keyword">VERSION</span> <span class="hljs-string">"v2"</span>;

Table created.
</code></pre>
<p>Si necesitamos modificar los periodos de retención:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_CLIENTES  <span class="hljs-keyword">NO</span> <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">5</span> <span class="hljs-keyword">DAYS</span> IDLE;
Table altered.
SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_CLIENTES  <span class="hljs-keyword">NO</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">32</span> <span class="hljs-keyword">DAYS</span> <span class="hljs-keyword">AFTER</span> <span class="hljs-keyword">INSERT</span>;
Table altered.
</code></pre>
<p>Como hemos incluido <strong>VERSION v2</strong>, podemos añadir columnas:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_CLIENTES <span class="hljs-keyword">ADD</span> (TC_PROVINCIA <span class="hljs-built_in">VARCHAR2</span>(<span class="hljs-number">100</span>));
</code></pre>
<p>Insertamos información e intentamos modificar algún dato:</p>
<pre><code class="lang-sql">SQL&gt; r
  1* <span class="hljs-keyword">Select</span> * <span class="hljs-keyword">From</span> t_Clientes

     TC_ID TC_NAME              TC_EMAIL             TC_CREATE TC_PROVINCIA
<span class="hljs-comment">---------- -------------------- -------------------- --------- ----------------------------------------------------------------------------------------------------</span>
         <span class="hljs-number">1</span> Iker San             San@demo.local       <span class="hljs-number">11</span>-FEB<span class="hljs-number">-26</span>
         <span class="hljs-number">2</span> Juan Perro           Perro@demo.local     <span class="hljs-number">11</span>-FEB<span class="hljs-number">-26</span>
         <span class="hljs-number">3</span> Carla Marq           Marq@demo.local      <span class="hljs-number">11</span>-FEB<span class="hljs-number">-26</span>

<span class="hljs-keyword">SQL</span>&gt; <span class="hljs-keyword">Update</span> t_Clientes <span class="hljs-keyword">Set</span> Tc_Provincia=<span class="hljs-string">'VALLADOLID'</span> <span class="hljs-keyword">where</span> tc_id = <span class="hljs-number">3</span>;
<span class="hljs-keyword">Update</span> t_Clientes <span class="hljs-keyword">Set</span> Tc_Provincia=<span class="hljs-string">'VALLADOLID'</span> <span class="hljs-keyword">where</span> tc_id = <span class="hljs-number">3</span>
       *
<span class="hljs-keyword">ERROR</span> <span class="hljs-keyword">at</span> line <span class="hljs-number">1</span>:
ORA<span class="hljs-number">-05715</span>: operation <span class="hljs-keyword">not</span> allowed <span class="hljs-keyword">on</span> the blockchain <span class="hljs-keyword">or</span> immutable <span class="hljs-keyword">table</span>
<span class="hljs-keyword">Help</span>: https://docs.oracle.com/<span class="hljs-keyword">error</span>-<span class="hljs-keyword">help</span>/db/ora<span class="hljs-number">-05715</span>/
</code></pre>
<p>Como podemos observar, no podemos modificar información. Únicamente insertar nuevos registros.</p>
<h3 id="heading-blockchain-table"><strong>Blockchain Table</strong></h3>
<pre><code class="lang-sql">SQL&gt; 
<span class="hljs-keyword">CREATE</span> BLOCKCHAIN <span class="hljs-keyword">TABLE</span> T_PRODUCTOSTIENDA (
  TP_id       <span class="hljs-built_in">NUMBER</span>,
  TP_NOMBRE   <span class="hljs-built_in">VARCHAR2</span>(<span class="hljs-number">100</span>),
  TP_PRICE    <span class="hljs-built_in">FLOAT</span>
)
<span class="hljs-keyword">NO</span> <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">14</span> <span class="hljs-keyword">DAYS</span> IDLE
<span class="hljs-keyword">NO</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">30</span> <span class="hljs-keyword">DAYS</span> <span class="hljs-keyword">AFTER</span> <span class="hljs-keyword">INSERT</span>
HASHING <span class="hljs-keyword">USING</span> <span class="hljs-string">"SHA2_512"</span>
<span class="hljs-keyword">WITH</span> <span class="hljs-keyword">ROW</span> <span class="hljs-keyword">VERSION</span> <span class="hljs-keyword">AND</span> <span class="hljs-keyword">USER</span> <span class="hljs-keyword">CHAIN</span> T_PRODUCTOS_VALIDATION (TP_id)
<span class="hljs-keyword">VERSION</span> <span class="hljs-string">"v2"</span>;  

Table created.
</code></pre>
<p>Si necesitamos modificar los periodos de retención:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_PRODUCTOSTIENDA  <span class="hljs-keyword">NO</span> <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">16</span> <span class="hljs-keyword">DAYS</span> IDLE;
Table altered.
SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_PRODUCTOSTIENDA  <span class="hljs-keyword">NO</span> <span class="hljs-keyword">DELETE</span> <span class="hljs-keyword">UNTIL</span> <span class="hljs-number">32</span> <span class="hljs-keyword">DAYS</span> <span class="hljs-keyword">AFTER</span> <span class="hljs-keyword">INSERT</span>;
Table altered.
</code></pre>
<p>Como hemos incluido <strong>VERSION v2</strong>, podemos añadir columnas:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLE</span> T_PRODUCTOSTIENDA <span class="hljs-keyword">ADD</span> (TC_CANTIDAD <span class="hljs-built_in">INTEGER</span>);
</code></pre>
<p>Insertamos información e intentamos modificar algún dato:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">Select</span> * <span class="hljs-keyword">From</span> T_PRODUCTOSTIENDA;

     TP_ID TP_NOMBRE              TP_PRICE TC_CANTIDAD
<span class="hljs-comment">---------- -------------------- ---------- -----------</span>
         1 PERAS
         1 PERAS JULIANAS
         2 NARANJAS
         3 MANGOS

SQL&gt; <span class="hljs-keyword">UPDATE</span> T_PRODUCTOSTIENDA <span class="hljs-keyword">SET</span> TP_PRICE = <span class="hljs-number">9</span>;
<span class="hljs-keyword">UPDATE</span> T_PRODUCTOSTIENDA <span class="hljs-keyword">SET</span> TP_PRICE = <span class="hljs-number">9</span>
       *
<span class="hljs-keyword">ERROR</span> <span class="hljs-keyword">at</span> line <span class="hljs-number">1</span>:
ORA<span class="hljs-number">-05715</span>: operation <span class="hljs-keyword">not</span> allowed <span class="hljs-keyword">on</span> the blockchain <span class="hljs-keyword">or</span> immutable <span class="hljs-keyword">table</span>
<span class="hljs-keyword">Help</span>: https://docs.oracle.com/<span class="hljs-keyword">error</span>-<span class="hljs-keyword">help</span>/db/ora<span class="hljs-number">-05715</span>/
</code></pre>
<p>Como podemos observar, tampoco podemos modificar registros. Si nos fijamos en la información introducida, hay dos productos con el mismo identificador, pero…¿Cuál fue el último insertado? En las tablas <strong>BLOCKCHAIN</strong>, Oracle crea automáticamente una vista <strong>&lt;TABLE_NAME&gt;_LAST$</strong> para mostrar la última versión de cada fila.</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">Select</span> * <span class="hljs-keyword">From</span> T_PRODUCTOSTIENDA_LAST$;

     TP_ID TP_NOMBRE              TP_PRICE
<span class="hljs-comment">---------- -------------------- ----------</span>
         1 PERAS JULIANAS
         2 NARANJAS
         3 MANGOS
</code></pre>
<p>En próximos artículos, abordaremos el tema del borrado sobre estos ejemplos que hemos utilizado.</p>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] Mandatory Profile]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En este pequeño artículo vamos a ver los Mandatory Profile.
Aunque no es una novedad en 26ai, sino que fue en la 21c, vamos a proceder a configurarlo en la última release de Oracle...]]></description><link>https://dbaenlasombra.com/26ai-mandatory-profile</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-mandatory-profile</guid><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[cdbroot]]></category><category><![CDATA[pdb]]></category><category><![CDATA[profile]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Thu, 29 Jan 2026 14:32:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1769696516840/41345bc4-94f0-4d5e-8288-15ca1f814db9.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/26ai-mandatory-profile"><strong><em>English version</em></strong></a><em>.</em></p>
<p>En este pequeño artículo vamos a ver los <strong>Mandatory Profile</strong>.</p>
<p>Aunque no es una novedad en <strong>26ai</strong>, sino que fue en la <strong>21c</strong>, vamos a proceder a configurarlo en la última release de Oracle.</p>
<p>Este tipo de <strong>Profile</strong> pueden ser creados dentro de <em>CDB$Root</em> para luego asignarse a las distintas <em>PDBs</em>. Otro punto a tener en cuenta, es que si necesitamos modificarlos, debemos realizarlo a nivel de CDB$ROOT, <strong>no</strong> de PDB, usando <strong>common users</strong>, no <strong>local users</strong>.</p>
<p>Además, las políticas definidas en el <strong>Profile</strong> se aplicarán a todos los usuarios de la PDB.</p>
<p>¿Qué parámetros puedo usar? A diferencia de un <strong>Profile</strong>, donde tenemos multitud de parámetros para configurar, aquí solo podemos definir dos, son:</p>
<ul>
<li><p><strong>Password_verify_function</strong> para forzar la complejidad de la contraseña por medio de la función que tenga asignada. Valor por defecto es <em>null</em>.</p>
</li>
<li><p><strong>Password_grace_time</strong> permite especificar un periodo de gracia para aquellas cuentas que violen la complejidad de las contraseñas. Valor por defecto es <em>0</em>.</p>
</li>
</ul>
<p>Lo primero que vamos hacer es definir la función que asignaremos al parámetro <strong>password_verify_function.</strong></p>
<pre><code class="lang-sql">SQL&gt; 
<span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">OR</span> <span class="hljs-keyword">REPLACE</span> <span class="hljs-keyword">FUNCTION</span> VALIDATION_BUSINESS(username     <span class="hljs-built_in">varchar2</span>,
                                               <span class="hljs-keyword">password</span>     <span class="hljs-built_in">varchar2</span>,
                                               old_password <span class="hljs-built_in">varchar2</span>)
  <span class="hljs-keyword">return</span> <span class="hljs-built_in">boolean</span> <span class="hljs-keyword">IS</span>
<span class="hljs-keyword">BEGIN</span>
  <span class="hljs-keyword">if</span> <span class="hljs-keyword">not</span> ora_complexity_check(<span class="hljs-keyword">password</span>, chars =&gt; <span class="hljs-number">8</span>, digit =&gt; <span class="hljs-number">4</span>) <span class="hljs-keyword">then</span>
    <span class="hljs-keyword">return</span>(<span class="hljs-literal">false</span>);
  <span class="hljs-keyword">end</span> <span class="hljs-keyword">if</span>;
  return(true);
<span class="hljs-keyword">END</span>;
/
Function created.
</code></pre>
<p>Cómo podemos ver, estoy definiendo la función <strong>VALIDATION_BUSINESS</strong> dentro de CDB$Root con el objetivo de que todas las contraseñas de los usuarios tengan al menos una longitud de 8 caracteres “<em>chars =&gt; 8</em>” y 4 dígitos “<em>digit=&gt;4</em>“.</p>
<p>Definimos el <strong>Mandatory Profile</strong> usando los siguientes parámetros:</p>
<ul>
<li><p><em>PASSWORD_VERIFY_FUNCTION</em> para asignarle la función que hemos creado en el punto anterior.</p>
</li>
<li><p><em>PASSWORD_GRACE_TIME</em> para dar al usuario un periodo de gracia de 5 días para cambiar la contraseña.</p>
</li>
</ul>
<pre><code class="lang-sql">SQL&gt; 
  <span class="hljs-keyword">CREATE</span> MANDATORY PROFILE C<span class="hljs-comment">##VALIDATION_BUSINESS </span>
   <span class="hljs-keyword">LIMIT</span> 
     <span class="hljs-keyword">PASSWORD_VERIFY_FUNCTION</span> VALIDATION_BUSINESS 
     <span class="hljs-keyword">PASSWORD_GRACE_TIME</span> <span class="hljs-number">5</span> 
   <span class="hljs-keyword">CONTAINER</span> = <span class="hljs-keyword">ALL</span>; 

Profile created.
</code></pre>
<p>Como último paso sería modificar el parámetro <strong>MANDATORY_USER_PROFILE</strong>.</p>
<p>Aquí podemos hacer dos cosas, si queremos que el cambio afecte a todas las PDB, debemos ajustar ese parámetro a nivel de CDB$ROOT; pero si queremos que solo se aplique a una PDB específica, entonces debemos modificarlo directamente dentro de esa PDB.</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">show</span> parameter MANDATORY_USER_PROFILE
<span class="hljs-keyword">NAME</span>                                 <span class="hljs-keyword">TYPE</span>        <span class="hljs-keyword">VALUE</span>
<span class="hljs-comment">------------------------------------ ----------- ------------------------------</span>
mandatory_user_profile               <span class="hljs-keyword">string</span>

<span class="hljs-keyword">SQL</span>&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">SYSTEM</span> <span class="hljs-keyword">SET</span> MANDATORY_USER_PROFILE=C<span class="hljs-comment">##VALIDATION_BUSINESS;</span>
<span class="hljs-keyword">System</span> altered.

<span class="hljs-keyword">SQL</span>&gt; <span class="hljs-keyword">show</span> parameter MANDATORY_USER_PROFILE

<span class="hljs-keyword">NAME</span>                                 <span class="hljs-keyword">TYPE</span>        <span class="hljs-keyword">VALUE</span>
<span class="hljs-comment">------------------------------------ ----------- ------------------------------</span>
mandatory_user_profile               <span class="hljs-keyword">string</span>      C<span class="hljs-comment">##VALIDATION_BUSINESS</span>
</code></pre>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[26ai] Oracle Temporary Tablespace Groups]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En este pequeño artículo vamos a ver la creación de Temporary Tablespace Groups, pero antes de meternos en faena, vamos a dar una pequeña pincelada de que se trata esta opción.
Aun...]]></description><link>https://dbaenlasombra.com/26ai-oracle-temporary-tablespace-groups</link><guid isPermaLink="true">https://dbaenlasombra.com/26ai-oracle-temporary-tablespace-groups</guid><category><![CDATA[Oracle 26ai]]></category><category><![CDATA[pdb]]></category><category><![CDATA[temporal]]></category><category><![CDATA[group]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Wed, 21 Jan 2026 07:12:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768949041843/3956d7d9-b5aa-447b-a8b4-8e2104fb9fe4.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/26ai-oracle-temporary-tablespace-groups"><strong><em>English version</em></strong></a><em>.</em></p>
<p>En este pequeño artículo vamos a ver la creación de <strong>Temporary Tablespace Groups</strong>, pero antes de meternos en faena, vamos a dar una pequeña pincelada de que se trata esta opción.</p>
<p>Aunque no es una novedad en <strong>26ai</strong>, vamos a proceder a configurarla en nuestra base de datos..</p>
<p>Este tipo de tablespaces, permite al usuario que cuando realizar operaciones que requieren mucha área temporal, como por ejemplo ordenaciones, el fuljo de trabajo se reparta entre múltiples tablespaces temporales. De este modo evitamos que tengamos saturación en el tablespace en caso que tengamos un único tablespace al realizar este tipo de operaciones.</p>
<p>Para poder llevar a cabo este laboratorio, vamos a definir una máquina virtual con la última versión de Oracle en OCI.</p>
<p>Las máquina que hemos definido es la siguientes:</p>
<ul>
<li><strong>Source</strong>: 26ai VM DB System Oracle Cloud (<em>test</em>).</li>
</ul>
<p><strong>1º</strong>: Definiremos una nueva PDB llamada <strong>TEST</strong> en source y comprobaremos cuál es el <strong>tablespace temporal</strong> que tiene asignado por defecto:</p>
<pre><code class="lang-sql">SQL&gt; ! hostname
source
SQL&gt; <span class="hljs-keyword">SHOW</span> PDBS

    CON_ID CON_NAME                       <span class="hljs-keyword">OPEN</span> <span class="hljs-keyword">MODE</span>  <span class="hljs-keyword">RESTRICTED</span>
<span class="hljs-comment">---------- ------------------------------ ---------- ----------</span>
         <span class="hljs-number">3</span> <span class="hljs-keyword">TEST</span>                           <span class="hljs-keyword">READ</span> WRITE <span class="hljs-keyword">NO</span>

<span class="hljs-keyword">SQL</span>&gt; <span class="hljs-keyword">Select</span> Property_Name, Property_Value 
     <span class="hljs-keyword">From</span> database_properties 
     <span class="hljs-keyword">Where</span> property_name = <span class="hljs-string">'DEFAULT_TEMP_TABLESPACE'</span>;

PROPERTY_NAME                                      PROPERTY_VALUE
<span class="hljs-comment">-------------------------------------------------- ----------------------------------------------------------------------------------------------------</span>
DEFAULT_TEMP_TABLESPACE                            TEMP
</code></pre>
<p><strong>2º</strong>: Vamos a definir un nuevo grupo tablespace temporal que incluirá dos tablespaces asociados. Como Oracle no ofrece una sintaxis especifica para crear directamente un grupo de tablespaces, debemos indicarlo durante el proceso de creación del propio tablespace.</p>
<pre><code class="lang-sql"> SQL&gt; 
 <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TEMPORARY</span> <span class="hljs-keyword">TABLESPACE</span> GRP_TMP_TAB_1 TEMPFILE  <span class="hljs-keyword">SIZE</span> <span class="hljs-number">50</span>M <span class="hljs-keyword">TABLESPACE</span> <span class="hljs-keyword">GROUP</span> GRP_TMP;
  Tablespace created.
SQL&gt; 
 <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TEMPORARY</span> <span class="hljs-keyword">TABLESPACE</span> GRP_TMP_TAB_2 TEMPFILE  <span class="hljs-keyword">SIZE</span> <span class="hljs-number">50</span>M <span class="hljs-keyword">TABLESPACE</span> <span class="hljs-keyword">GROUP</span> GRP_TMP;
  Tablespace created.
</code></pre>
<p>Para consultar el nuevo grupo tablespace temporal podemos usar la vista <strong>DBA_TABLESPACE_GROUPS</strong>:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">Select</span> * <span class="hljs-keyword">From</span> DBA_TABLESPACE_GROUPS ;

GROUP_NAME                     TABLESPACE_NAME
<span class="hljs-comment">------------------------------ ------------------------------</span>
GRP_TMP                        GRP_TMP_TAB_1
GRP_TMP                        GRP_TMP_TAB_2
</code></pre>
<p>Si ya tenemos algún <em>tablespace temporal</em> creado o hemos generado uno nuevo pero aún no lo hemos asignado el grupo, podemos mover el tablespace al grupo haciendo lo siguiente:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">TEMPORARY</span> <span class="hljs-keyword">TABLESPACE</span> GRP_TMP_TAB_3 TEMPFILE  <span class="hljs-keyword">SIZE</span> <span class="hljs-number">50</span>M ;

Tablespace created.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLESPACE</span> GRP_TMP_TAB_3 <span class="hljs-keyword">TABLESPACE</span> <span class="hljs-keyword">GROUP</span> GRP_TMP;

Tablespace altered.

SQL&gt;  <span class="hljs-keyword">Select</span> * <span class="hljs-keyword">From</span> DBA_TABLESPACE_GROUPS ;

GROUP_NAME                     TABLESPACE_NAME
<span class="hljs-comment">------------------------------ ------------------------------</span>
GRP_TMP                        GRP_TMP_TAB_1
GRP_TMP                        GRP_TMP_TAB_2
GRP_TMP                        GRP_TMP_TAB_3
</code></pre>
<p><strong>3º</strong>: Cómo último paso, configuramos el grupo de tablespaces temporales <strong>GRP_TMP</strong> como el <em>default temporary tablespace</em> de la base de datos.</p>
<pre><code class="lang-sql">SQL&gt;  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">DEFAULT</span> <span class="hljs-keyword">TEMPORARY</span> <span class="hljs-keyword">TABLESPACE</span> GRP_TMP;

Database altered.

SQL&gt; 
   <span class="hljs-keyword">Select</span> Property_Name, Property_Value 
   <span class="hljs-keyword">From</span> database_properties <span class="hljs-keyword">where</span> property_name = <span class="hljs-string">'DEFAULT_TEMP_TABLESPACE'</span>;

PROPERTY_NAME                                      PROPERTY_VALUE
<span class="hljs-comment">-------------------------------------------------- ----------------------------------------------------------------------------------------------------</span>
DEFAULT_TEMP_TABLESPACE                            GRP_TMP
</code></pre>
<p>Cuando marcamos un grupo como default tablespace temporal, ninguno de los tablespaces que lo integran puede eliminarse. Para poder borrarlo, antes debemos asignar <strong>otro</strong> <em>tablespace</em> temporal como predeterminado.</p>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[OCI] Proxy PDB]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy, vamos a ver las Proxy PDB.
Pero, ¿Qué es un proxy PDB? Es hacer referencia a otra PDB pero de manera remota o en mismo CDB. Podríamos decir que es algo simil...]]></description><link>https://dbaenlasombra.com/oci-proxy-pdb</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-proxy-pdb</guid><category><![CDATA[OCI]]></category><category><![CDATA[pdb]]></category><category><![CDATA[Oracle 23ai]]></category><category><![CDATA[#proxypdb]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Tue, 13 Jan 2026 19:52:27 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1768329866558/7b4b48b7-68df-47e7-a7fa-714481e293be.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/oci-26ai-refreshable-clone-pdbs?showSharer=true"><strong><em>English version</em></strong></a><em>.</em></p>
<p>En el artículo de hoy, vamos a ver las <strong>Proxy PDB.</strong></p>
<p>Pero, ¿Qué es un <strong>proxy PDB</strong>? Es hacer referencia a otra PDB pero de manera remota o en mismo CDB. Podríamos decir que es algo similar a los clásicos <strong>DB Links</strong> cuando hacemos referencia a una tabla, pero en este caso no se trata de una tabla, sino de una PDB.</p>
<p>Algunas notas que debemos tener en cuenta:</p>
<ul>
<li><p>El usuario que utilicemos debe tener el permiso de <strong>CREATE PLUGGABLE DATABASE</strong>.</p>
</li>
<li><p>La PDB debe estar en <strong>local undo mode</strong>.</p>
</li>
<li><p>La PDB debe estar en modo <strong>ARCHIVELOG</strong>.</p>
</li>
<li><p>La PDB que hacemos referencia debe estar <strong>OPEN READ WRITE</strong> cuando el proxy PDB es creado. Una vez creada, puede ser cambiada.</p>
</li>
<li><p>Debemos crear un <strong>DB Link</strong> que hace referencia tanto a la CDB como a la PDB.</p>
</li>
</ul>
<p>Para poder llevar a cabo este laboratorio, vamos a definir dos máquinas virtuales, ambas con la última versión de Oracle en OCI.</p>
<p>Las máquinas que hemos definido son las siguientes:</p>
<ul>
<li><p><strong>Source</strong>: 26ai VM DB System Oracle Cloud (<em>test_ORI</em>).</p>
</li>
<li><p><strong>Target</strong>: 26ai VM DB System Oracle Cloud (<em>test_TARGET</em>).</p>
</li>
</ul>
<p>¡Vamos a ello!</p>
<p>1º: Definir la PDB PDB_DAVID en TEST_ORI:</p>
<pre><code class="lang-plaintext">SQL&gt; 
CREATE PLUGGABLE DATABASE PDB_DAVID ADMIN USER ADMIN 
  IDENTIFIED BY ADMIN keystore identified by external store;
Pluggable database created.
SQL&gt; ALTER PLUGGABLE DATABASE PDB_DAVID OPEN;
Pluggable database altered.
SQL&gt; ALTER PLUGGABLE DATABASE PDB_DAVID SAVE STATE;
Pluggable database altered.
SQL&gt; show pdbs
    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         4 PDB_DAVID                      READ WRITE NO
SQL&gt; ! hostname
source
</code></pre>
<p>2º: Definir un usuario común. El rol DBA ya incluye tanto <em>CREATE SESSION</em> como <em>CREATE PLUGGABLE DATABASE</em>. Haremos lo mismo en target.</p>
<pre><code class="lang-plaintext">SQL&gt; CREATE USER C##MANAGER IDENTIFIED BY "XXXXXXX";
User created.
SQL&gt; GRANT RESOURCE, DBA, UNLIMITED TABLESPACE TO C##MANAGER CONTAINER=ALL;
Grant succeeded.
</code></pre>
<p>3º: Revisamos que la PDB en origen se encuentra <strong>local undo mode</strong>. En caso que no se encuentre, debemos configurarlo.</p>
<pre><code class="lang-plaintext">SQL&gt; r
  1* Select property_name, property_value 
       From database_properties 
      Where property_name='LOCAL_UNDO_ENABLED'

PROPERTY_NAME        PROPERTY_VALUE
-------------------- ----------------------------------------
LOCAL_UNDO_ENABLED   TRUE
</code></pre>
<p>4º: Definimos el <strong>DB Links</strong> en destino. Para hacerlo diferente, vamos a utilizar <strong>easy connect</strong> en vez de <strong>local naming</strong>:</p>
<pre><code class="lang-plaintext">SQL&gt; 
CREATE DATABASE LINK SOURCE_PROXY CONNECT TO C##MANAGER 
  IDENTIFIED BY "XXXXX" 
    USING 'source:1521/pdb_david.XXXXXXXXXXXXXXXX';

Database link created.

SQL&gt; 
SQL&gt;  SELECT * FROM DUAL@SOURCE_PROXY;

D
-
X
</code></pre>
<p>5º: Definimos nuestra PDB incluyendo <strong>AS PROXY</strong> para indicar que vamos a crear una proxy PDB y <strong>FROM</strong> para especificar la PDB a la que vamos hacer referencia.</p>
<pre><code class="lang-plaintext">SQL&gt; 
CREATE PLUGGABLE DATABASE PDB_DAVID AS PROXY 
  FROM PDB_DAVID@SOURCE_PROXY keystore identified by external store;
Pluggable database created.
SQL&gt; alter pluggable database pdb_david open;
Pluggable database altered.
SQL&gt; show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         3 PDB_DAVID                      READ WRITE NO

SQL&gt; r
  1  SELECT pdb_name, is_proxy_pdb
  2* FROM   cdb_pdbs

PDB_NAME                       IS_PROXY_PDB
------------------------------ ----------------------------------------
PDB$SEED                       NO
PDB_DAVID                      YES
</code></pre>
<p>Cosas a tener en cuenta:</p>
<ul>
<li><strong>V$PROXY_PDB_TARGETS:</strong> Muestra información sobre los detalles de conexión sobre la PDB a la que hace referencia nuestra Proxy PDB.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; r
  1* select * from v$proxy_pdb_targets

    CON_ID TARGET_PORT TARGET_HOST          TARGET_SERVICE                                                                   TARGET_USER
---------- ----------- -------------------- -------------------------------------------------------------------------------- --------------------
         3        1521 source               4843e8f3860c707be063cfdf680abf32.XXXXXXX.XXXXXXX.XXXXXXX.com
</code></pre>
<ul>
<li><p>Los tablespace de <strong>SYSTEM</strong> y <strong>SYSAUX</strong> serán copiados y sincronizador con el origen.</p>
</li>
<li><p>Solo admite <strong>password authentication</strong>, no permite <strong>OS authentication</strong>. Si nos fijamos en el ejemplo de abajo, vemos como funcionan ambos métodos de autenticación y que ocurre al consultar una tabla en nuestra <strong>Proxy PDB</strong>.</p>
</li>
</ul>
<pre><code class="lang-plaintext">[oracle@target ~]$ sqlplus / as sysdba
Connected to:
Oracle AI Database 26ai EE High Perf Release 23.26.0.0.0 - for Oracle Cloud and Engineered Systems
Version 23.26.0.0.0
SQL&gt; alter session set container= PDB_DAVID;
Session altered.
SQL&gt;  Select * From C##MANAGER.CODIGO_POSTAL;
 Select * From C##MANAGER.CODIGO_POSTAL
                          *
ERROR at line 1:
ORA-00942: table or view "C##MANAGER"."CODIGO_POSTAL" does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/

SQL&gt; conn C##MANAGER/"XXXXXXXX"@'target:1521/pdb_david.XXXXXXXX.XXXXXXXX.XXXXXXXX.com'
Connected.
SQL&gt;  Select * From C##MANAGER.CODIGO_POSTAL;

ID    POBLACION                                                                                            PAIS
----- ---------------------------------------------------------------------------------------------------- ----------------------------------------------------------------------------------------------------
46000 Valencia                                                                                             Espa??a
08000 Barcelona                                                                                            Espa??a
28000 Madrid                                                                                               Espa??a
55500 Wisconsin                                                                                            U.S.A.
</code></pre>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[OCI-26ai] Refreshable Clone PDBs]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy, vamos a ver como podemos realizar una copia de una PDB y mantenerla actualizada con el origen.
Este tipo de migración, la podemos realizar de manera local (d...]]></description><link>https://dbaenlasombra.com/oci-26ai-refreshable-clone-pdbs</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-26ai-refreshable-clone-pdbs</guid><category><![CDATA[OCI]]></category><category><![CDATA[pdb]]></category><category><![CDATA[#refresh]]></category><category><![CDATA[Oracle 26ai]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Tue, 23 Dec 2025 15:27:06 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1766420197468/40e152f1-5bbb-49cc-b98b-878fb407105d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/oci-26ai-refreshable-clone-pdbs?showSharer=true"><strong><em>English version</em></strong></a><em>.</em></p>
<p>En el artículo de hoy, vamos a ver como podemos realizar una copia de una PDB y mantenerla actualizada con el origen.</p>
<p>Este tipo de migración, la podemos realizar de manera local (dentro de la misma CDB) o de manera remota (otra CDB). En este artículo nos vamos a centrar en como hacerlo remotamente.</p>
<p>Otro punto que tenemos que tener en cuenta es que debemos tener configurado <strong>SEPS</strong> ya que vamos a definir las distintas PDB usando <strong>external store</strong>. En próximos artículos, os explicare como configurar este punto fácilmente.</p>
<p>Para poder llevar a cabo este laboratorio, vamos a definir dos máquinas virtuales, ambas con la última versión de Oracle en OCI.</p>
<p>Las máquinas que hemos definido son las siguientes:</p>
<ul>
<li><p><strong>Source</strong>: 26ai VM DB System Oracle Cloud (<em>test_ORI</em>).</p>
</li>
<li><p><strong>Target</strong>: 26ai VM DB System Oracle Cloud (<em>test_TARGET</em>).</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766234418615/7af4855c-a9fd-42fd-8227-5b195d359ea8.png" alt class="image--center mx-auto" /></p>
<p>Y el modelo que vamos a seguir es el siguiente:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1766234319392/ee0c4aa8-31b2-48bd-8f0b-05c4e91b44c3.png" alt class="image--center mx-auto" /></p>
<ul>
<li>1º: Definir la PDB REFRESH_PDB en TEST_ORI:</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; CREATE PLUGGABLE DATABASE REFRESH_PDB ADMIN USER admin IDENTIFIED BY admin;
Pluggable database created.
SQL&gt; ALTER PLUGGABLE DATABASE REFRESH_PDB OPEN;
Pluggable database altered.
SQL&gt; ALTER PLUGGABLE DATABASE REFRESH_PDB SAVE STATE;
Pluggable database altered.
SQL&gt; SHOW PDBS

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
         4 REFRESH_PDB                    READ WRITE NO
SQL&gt; ! hostname
source
</code></pre>
<ul>
<li>2º: Comprobamos que no hay otra PDB, solo está la PDB$SEED.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB$SEED                       READ ONLY  NO
SQL&gt; ! hostname
target
</code></pre>
<ul>
<li>3º: Definir un <strong>common user</strong>, no <strong>local user</strong>. El usuario debe llamarse igual y debe tener la misma contraseña en origen como destino.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; CREATE USER C##MANAGER IDENTIFIED BY "XXXXXX";
User created.
SQL&gt; GRANT CREATE SESSION, RESOURCE, CREATE ANY TABLE, UNLIMITED TABLESPACE TO C##MANAGER CONTAINER=ALL;
Grant succeeded.
SQL&gt; GRANT CREATE PLUGGABLE DATABASE TO C##MANAGER CONTAINER=ALL;
Grant succeeded.
SQL&gt; GRANT SYSOPER TO C##MANAGER CONTAINER=ALL;
Grant succeeded.
</code></pre>
<ul>
<li>4º: Definir los <strong>DB Links</strong>. Antes de definirlo, debemos modificar el <em>tnsnames</em> para incluir ambas cadenas de conexión.</li>
</ul>
<p>Origen:</p>
<pre><code class="lang-plaintext">SQL&gt; ! hostname
source

SQL&gt; ! tail -8  $ORACLE_HOME/network/admin/tnsnames.ora
TARGET =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = XXXXXXXXXXXXXXXXXXXX)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XXXXXXXXXXXXXXXXXXXX)
    )
  )
</code></pre>
<p>Destino:</p>
<pre><code class="lang-plaintext">SQL&gt; ! hostname
target
SQL&gt; ! tail -8  $ORACLE_HOME/network/admin/tnsnames.ora
SOURCE =
  (DESCRIPTION =
    (ADDRESS = (PROTOCOL = TCP)(HOST = XXXXXXXXXXXXXXXXXXXX)(PORT = 1521))
    (CONNECT_DATA =
      (SERVER = DEDICATED)
      (SERVICE_NAME = XXXXXXXXXXXXXXXXXXXX)
    )
  )
</code></pre>
<p>Una vez que tenemos modificado el <strong>tnsnames</strong>, podemos definir el DB Link en <strong>target</strong> y en <strong>source</strong>.</p>
<pre><code class="lang-plaintext">SQL&gt; CREATE DATABASE LINK DB_Source CONNECT TO C##MANAGER IDENTIFIED BY "XXXXXXXXX" USING 'SOURCE';
Database link created.
SQL&gt; Select * From Dual@DB_Source;
D
-
X
</code></pre>
<p>Una vez que hemos comprobado que el DB funciona correctamente, ya podemos definir nuestra PDB.</p>
<p>La PDB en origen no contiene nada de información ahora mismo, así que vamos a definir algo en ella antes de clonarla.</p>
<pre><code class="lang-plaintext">SQL&gt; alter session set container=REFRESH_PDB;
Session altered.
SQL&gt; CREATE TABLE C##MANAGER.COPY_NUMERICOS AS 
     SELECT ROWNUM NUM 
       FROM DUAL 
    CONNECT BY ROWNUM &lt;= 1000000;
Table created.
</code></pre>
<ul>
<li>5º: Procedemos a clonar nuestra PDB usando external store:</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; CREATE PLUGGABLE DATABASE REFRESH_PDB FROM REFRESH_PDB@DB_Source REFRESH MODE MANUAL keystore identified by external store;

Pluggable database created.
</code></pre>
<p>Si nos fijamos en la sentencia que hemos ejecutado, hemos indicado <strong>REFRESH MODE MANUAL</strong>.</p>
<p>Cuando definimos la PDB como <strong>REFRESH MODE MANUAL</strong>, la PDB no se actualiza de manera automática, en cambio si la definimos <strong>REFRESH MODE EVERY &lt;min&gt;</strong>, la PDB sí se actualiza de manera automática.</p>
<p>El estado de la PDB que nos marca <em>DBA_PDBs</em> es <strong>REFRESHING</strong>.</p>
<pre><code class="lang-plaintext">SQL&gt; select con_id, pdb_name, status, refresh_mode, refresh_interval from dba_pdbs where pdb_name='REFRESH_PDB';

    CON_ID PDB_NAME             STATUS               REFRESH_MODE         REFRESH_INTERVAL
---------- -------------------- -------------------- -------------------- ----------------
         3 REFRESH_PDB          REFRESHING           MANUAL
</code></pre>
<p>Abrimos las PDB en modo lectura y consultamos si la tabla se encuentra en la PDB</p>
<pre><code class="lang-plaintext">SQL&gt; ALTER SESSION SET CONTAINER=REFRESH_PDB;
Session altered.
SQL&gt; ALTER PLUGGABLE DATABASE OPEN READ ONLY;
Pluggable database altered.
SQL&gt; SELECT COUNT(*) FROM C##MANAGER.COPY_NUMERICOS;

  COUNT(*)
----------
   1000000
</code></pre>
<p>Volvemos a insertar registros en la tabla y refrescamos de nuevo la PDB.</p>
<pre><code class="lang-plaintext"> SQL&gt; ALTER PLUGGABLE DATABASE CLOSE IMMEDIATE;

Pluggable database altered.

SQL&gt; ALTER PLUGGABLE DATABASE REFRESH;

Pluggable database altered.

SQL&gt; ALTER PLUGGABLE DATABASE OPEN READ ONLY;

Pluggable database altered.

SQL&gt;  SELECT COUNT(*) FROM C##MANAGER.COPY_NUMERICOS;

  COUNT(*)
----------
   2000000
</code></pre>
<p>Perfecto, tenemos ambas PDBs sincronizadas.</p>
<p>Como último paso, podemos hacer dos cosas:</p>
<ul>
<li><strong>Finalizar</strong>, es decir, que pase de <strong>refreshable clone</strong> a <strong>regular PDB</strong>:</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; ALTER PLUGGABLE DATABASE CLOSE IMMEDIATE;
Pluggable database altered.
SQL&gt;  alter pluggable database refresh mode none;
Pluggable database altered.
SQL&gt; ALTER PLUGGABLE DATABASE OPEN;
Pluggable database altered.

SQL&gt; select con_id, pdb_name, status, refresh_mode, refresh_interval 
     from dba_pdbs where pdb_name='REFRESH_PDB';

    CON_ID PDB_NAME             STATUS               REFRESH_MODE         REFRESH_INTERVAL
---------- -------------------- -------------------- -------------------- ----------------
         3 REFRESH_PDB          NORMAL               NONE
</code></pre>
<ul>
<li><p><strong>Switch the roles:</strong> Nuestra PDB de <em>source</em> pasará de <strong>NORMAL</strong> a <strong>REFRESHING</strong> y la PDB de <em>target</em> de <strong>REFRESHING</strong> a <strong>NORMAL</strong>:</p>
<p>  Origen:</p>
<pre><code class="lang-plaintext">  SQL&gt; col pdb_name format a15
  SQL&gt;  set linesize 1000
  SQL&gt; select con_id, pdb_name, status, refresh_mode, refresh_interval from dba_pdbs where pdb_name='REFRESH_PDB';

      CON_ID PDB_NAME        STATUS     REFRES REFRESH_INTERVAL
  ---------- --------------- ---------- ------ ----------------
           4 REFRESH_PDB     NORMAL     NONE

  SQL&gt; ALTER SESSION SET CONTAINER = REFRESH_PDB;                                                                 

  Session altered.

  SQL&gt;  ALTER PLUGGABLE DATABASE REFRESH MODE MANUAL FROM REFRESH_PDB@DB_Target SWITCHOVER;

  Pluggable database altered.

  SQL&gt; alter pluggable database open read only;

  Pluggable database altered.

  SQL&gt; select con_id, pdb_name, status, refresh_mode, refresh_interval from dba_pdbs where pdb_name='REFRESH_PDB';

      CON_ID PDB_NAME        STATUS     REFRES REFRESH_INTERVAL
  ---------- --------------- ---------- ------ ----------------
           4 REFRESH_PDB     REFRESHING MANUAL
</code></pre>
<p>  Destino:</p>
<pre><code class="lang-plaintext">  SQL&gt; r
    1* select con_id, pdb_name, status, refresh_mode, refresh_interval from dba_pdbs where pdb_name='REFRESH_PDB'

      CON_ID PDB_NAME        STATUS     REFRES REFRESH_INTERVAL
  ---------- --------------- ---------- ------ ----------------
           3 REFRESH_PDB     REFRESHING MANUAL

  SQL&gt; alter pluggable database refresh_pdb open read only;

  Pluggable database altered.

  SQL&gt; select con_id, pdb_name, status, refresh_mode, refresh_interval from dba_pdbs where pdb_name='REFRESH_PDB';

      CON_ID PDB_NAME        STATUS     REFRES REFRESH_INTERVAL
  ---------- --------------- ---------- ------ ----------------
           3 REFRESH_PDB     NORMAL     NONE
</code></pre>
<p>  Así de sencillo podemos cambiar los roles.</p>
</li>
</ul>
<p>Espero que os guste. ¡Nos vemos en el próximo artículo!</p>
]]></content:encoded></item><item><title><![CDATA[[OCI] Resource Scheduler]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
Uno de los puntos que hay que tener en cuenta a la hora de crear nuevos entornos en OCI es la gestión de apagado/encendido de los entornos para reducir los costes dentro de la tena...]]></description><link>https://dbaenlasombra.com/oci-resource-scheduler</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-resource-scheduler</guid><category><![CDATA[OCI]]></category><category><![CDATA[scheduler]]></category><category><![CDATA[switchon]]></category><category><![CDATA[switchoff]]></category><category><![CDATA[instance]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sat, 25 Oct 2025 12:16:13 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1761114496929/e3dac661-21d9-4fa7-9d8c-06da78c36baa.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/oci-resource-scheduler"><strong><em>English version</em></strong></a><em>.</em></p>
<p>Uno de los puntos que hay que tener en cuenta a la hora de crear nuevos entornos en OCI es la gestión de apagado/encendido de los entornos para reducir los costes dentro de la <strong>tenancy</strong> de nuestros clientes.</p>
<p>Desde <em>Mayo del 2024</em>, OCI lanzo el servicio de “<strong>OCI resource scheduler</strong>“ para facilitarnos esta tarea. Antes de esa fecha, podríamos controlarlo con alguno de estos métodos:</p>
<ul>
<li><p><strong>Crontab</strong>. Normalmente, en el entorno de producción se configuraba dentro del usuario <strong>opc</strong> un shell script con el listado de nodos que se deberían apagar/arrancar.</p>
<p>  Ese <em>sh</em> se ejecutaba dentro de una tarea planificada:</p>
</li>
</ul>
<pre><code class="lang-bash">[opc ~]$ crontab -l
 30 7 * * 1,2,3,4,5 /home/opc/dbsystem/start_dbsystem.sh &gt;&gt; /home/opc/dbsystem/start_dbsystem.log
[opc ~]$ cat /home/opc/dbsystem/start_dbsystem.sh
<span class="hljs-comment">#!/bin/bash</span>
<span class="hljs-built_in">cd</span> /home/opc/dbsystem 

NODE_SWITCH=&lt;OCID&gt;

. ./oci-curl.sh

oci-curl &lt;END_POINT&gt; POST ./empty.json /20160918/dbNodes/<span class="hljs-variable">${NODE_SWITCH}</span>?action=start
</code></pre>
<ul>
<li><p><strong>Auto Scaling</strong> para OCI. el gran <strong>Richard Garsthagen</strong> tiene publicado en su <em>github</em> un repositorio denominado <strong>OCI-AutoScale</strong> (<a target="_blank" href="https://github.com/AnykeyNL/OCI-AutoScale">link repositorio</a>) para gestionar este tipo de operaciones por medio de <strong>tags.</strong></p>
<p>  Cuando realizas la instalación de <strong>OCI-AutoScale</strong> en una instancia, uno de los pasos es definir en la etiqueta <strong>Schedule:</strong></p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761066421080/8d6ff26f-6b78-4e54-b088-13fb3fa38783.png" alt class="image--center mx-auto" /></p>
<p>  Asociaríamos esas etiquetas a las instancias que queremos aplicarlo:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761067383472/96de0ce7-76eb-4820-82d4-11b573cd26d2.png" alt class="image--center mx-auto" /></p>
<p>  En este ejemplo, tendríamos dos etiquetas aplicadas, son:</p>
<ul>
<li><p><strong>Weekend</strong>: Apagado el fin de semana.</p>
</li>
<li><p><strong>WeekDay</strong>: Encendido ciertas horas. La traducción de “<em>0,0,0,0,0,0,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0</em>” sería la tabla de la izquierda.</p>
</li>
</ul>
</li>
</ul>
<h3 id="heading-oci-resource-scheduler"><strong>OCI resource scheduler</strong></h3>
<p>Estos son los pasos que vamos a seguir para utilizar <strong>Resource Scheduler</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761387868063/3a4ba0ab-e96e-43b6-9a47-8a173f5fb812.png" alt class="image--center mx-auto" /></p>
<p>Antes de ver como trabaja este servicio en OCI, debemos de asegurarnos que:</p>
<ul>
<li><p>El usuario que utilicemos tiene permisos para crear <strong>schedules</strong>.</p>
<pre><code class="lang-bash">  Allow any-user to manage resource-schedule-family <span class="hljs-keyword">in</span> tenancy
</code></pre>
</li>
<li><p>Debemos asegurarnos que a nivel <strong>schedule</strong> se le han dado permisos para administrar recursos en nuestro tenancy.</p>
<pre><code class="lang-bash">  Allow any-user to manage all-resources <span class="hljs-keyword">in</span> compartment <span class="hljs-string">'&lt;compartment&gt;'</span> <span class="hljs-built_in">where</span> all{request.principal.type=<span class="hljs-string">'resourceschedule'</span>}
</code></pre>
</li>
<li><p>Definimos una instancia para testear el nuevo servicio:</p>
</li>
<li><p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761309258652/d97f8028-ace1-46c6-9092-6f82c533a42d.png" alt class="image--center mx-auto" /></p>
</li>
</ul>
<p>Después de asegurarnos que tenemos todos esos puntos, ya podríamos configurar nuestro servicio, ¡vamos a ello!</p>
<p>Vamos a la sección “<strong>Governance &amp; Administration</strong>” y “<strong>Schedules</strong>”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761068526472/9929d499-5f66-4a50-acda-833868cecc06.png" alt class="image--center mx-auto" /></p>
<p>Una vez dentro, veremos la siguiente ventana. Aquí solo debemos pulsar en “<strong><em>Create a schedule</em></strong>”:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761309028722/e9034b35-103f-4096-8104-788d10cd0319.png" alt class="image--center mx-auto" /></p>
<p>En la primera parte tendremos que cumplimentar la información básica, como es un nombre y la acción a realizar, en nuestro caso es <em>start.</em></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761121076288/22ac80c3-37ba-49c2-b89a-a253255b91c8.png" alt class="image--center mx-auto" /></p>
<p>En la siguiente ventana, debemos seleccionar el recurso de nuestra <em>tenancy</em> que queremos aplicar este servicio:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761121425419/0d50bdf6-6c94-4f46-a2d8-d84085a17919.png" alt class="image--center mx-auto" /></p>
<p>Si nos fijamos bien, tenemos un filtro con dos valores: <strong>Static</strong> o <strong>Dynamic</strong>. La diferencia entre ambos, es que <strong>Static</strong>, sería aplicarlo de manera individual, es decir, a un único recurso (este sería nuestro caso) o <strong>Dynamic</strong>, que sería aplicarlo a un conjunto de recursos que cumplan el siguiente criterio:</p>
<ul>
<li><p><em>Tag:</em> Aplica a todos los recursos que lleven ese tag<em>.</em></p>
</li>
<li><p><em>Resource Type</em>: Los tipos de recurso a los que se puede aplicar son: <em>Instance</em>, <em>InstancePool</em>, <em>AutonomousDatabase</em>, <em>Function o DBSystem</em>.</p>
</li>
<li><p><em>Compartment:</em> Podemos seleccionar un compartment especifico o que aplique a todos<em>.</em></p>
</li>
<li><p><em>Status</em>: Los estados son Available, Running, Stopped o que aplique a todos</p>
</li>
</ul>
<p>Seleccionamos <strong>Static</strong> y seleccionamos la instancia en el que queremos aplicarlo:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761229546582/04090d6d-5cd1-492f-841c-535a9d6cfec7.png" alt class="image--center mx-auto" /></p>
<p>En la siguiente ventana sería para agregar parámetros a nuestro recurso que hemos seleccionado en el paso anterior. En nuestro caso no aplica.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761229642165/3d8f9104-96b9-4a2d-940e-08fb1a3982d7.png" alt class="image--center mx-auto" /></p>
<p>La siguiente ventana es la más importante, porque vamos a definir la hora de arranque.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761168926242/91b09e8b-7e19-46b8-9176-0532bc4c6a56.png" alt class="image--center mx-auto" /></p>
<p>La primera opción es, como quieres definir la información referente al <strong>schedule</strong>, si bien desde la propia interface “<strong>Form Interface</strong>“ o bien desde “<strong>Cron expression</strong>”. Como vamos ha definir dos, uno de arranque y otro de parada, probaremos los dos.</p>
<p>En nuestro caso, para el arranque vamos a seleccionar “<strong>Cron expression</strong>”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761390922785/506e6db2-1023-4086-a47b-6bcafcc1dfd1.png" alt class="image--center mx-auto" /></p>
<p>Las opciones que vemos son:</p>
<ul>
<li><p><strong>Recurrence details</strong>: Una pequeña descripción, es opcional. Cuidado con este punto, porque en caso de que seleccionemos vía cron, debemos informar ahí la <strong>Cron expression</strong>.</p>
<p>  De no hacerlo, no salta un error interno a la hora de crearlo:</p>
<p>  <img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761171906611/e9bee26a-4174-4bcb-92ae-4b48db164b46.png" alt /></p>
<p>  Nuestra <strong>Cron expression</strong> es: “<em>30 12 \</em> * *<em>“<em>*.</em></em></p>
</li>
<li><p><strong>Time</strong>: En formato de 24h-UTC, sería la hora en la que se ejecuta.</p>
</li>
<li><p><strong>Start date</strong>: Que día empieza a estar vigente. Podemos introducir la fecha a mano o por medio del calendario.</p>
</li>
<li><p><strong>End date</strong>: Si queremos marcar una fecha fin, es opcional.</p>
</li>
</ul>
<p>La última ventana sería como un resumen del <em>schedule</em> que vamos a definir .</p>
<p>Para el apagado será exactamente igual salvo la información referente al <strong>schedule,</strong> donde la vamos a definir como <strong>From interface</strong>:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761392305970/8afad909-ad45-4f2d-88cb-867c27c4d3b2.png" alt class="image--center mx-auto" /></p>
<p>Con esto ya tenemos definidos el arranque/parada de nuestra instancia.</p>
<p>Si volvemos a la ventana del principio, podemos ver que nuestros <strong>schedules</strong> se están ejecutando correctamente:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1761392915448/08e0f6c3-0336-4909-aa71-4fe3b1d07bf9.png" alt class="image--center mx-auto" /></p>
<p>Listo, de esta manera tan sencilla podemos controlar el arranque/apagado de nuestros recursos.</p>
<p>Espero que os sirva :)</p>
]]></content:encoded></item><item><title><![CDATA[[23ai] Shadow Tablespaces]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En el artículo de hoy vamos hablar sobre un tipo de tablespace que se “encuentran” nunca mejor dicho, en la sombra de la página oficial de Oracle, son los Shadow Tablespaces.
Este ...]]></description><link>https://dbaenlasombra.com/23ai-shadow-tablespaces</link><guid isPermaLink="true">https://dbaenlasombra.com/23ai-shadow-tablespaces</guid><category><![CDATA[Oracle 23ai]]></category><category><![CDATA[pdb]]></category><category><![CDATA[cdbroot]]></category><category><![CDATA[tablespace]]></category><category><![CDATA[#shadow]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sat, 04 Oct 2025 17:22:33 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759570916299/ad8b1f40-97ef-47b1-9c42-ff26deef2c3d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/23ai-shadow-tablespaces"><strong><em>English version</em></strong></a><em>.</em></p>
<p>En el artículo de hoy vamos hablar sobre un tipo de <strong><em>tablespace</em></strong> que se “encuentran” nunca mejor dicho, en la sombra de la página oficial de Oracle, son los <strong>Shadow Tablespaces</strong>.</p>
<p>Este tipo de <strong><em>tablespace</em></strong> no es un <strong>feature</strong> de la <strong>23ai</strong>, sino que existen desde la <strong>18c</strong> en adelante.</p>
<p>Antes de entrar en detalle, no tiene ningún tipo de relación con el parámetro <em>DB_LOST_WRITE_PROTECT</em>, podríamos decir que tiene un propósito parecido pero van por caminos diferentes.</p>
<h2 id="heading-proposito"><strong>Propósito</strong></h2>
<p><strong>Shadow Tablespace</strong> es un <strong><em>tablespace</em></strong>, en 23ai de tipo <strong>bigfile</strong>, destinado a protegernos rápidamente cuando se producen pérdidas de escritura en <strong><em>tablespaces</em></strong> o <strong><em>datafiles</em></strong>, minimizando el tiempo requerido para reparar una base de datos.</p>
<h2 id="heading-como-trabaja"><strong>¿Cómo trabaja?</strong></h2>
<p>Para poder usar esta funcionalidad, necesitamos un <strong>shadow tablespace</strong> y un <strong>no-shadow tablespace</strong>.</p>
<p>Vamos a ver un pequeño modelo de como funciona:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1759578208948/10488d8d-3acd-46d3-b3ee-43fec08a70d1.png" alt class="image--center mx-auto" /></p>
<p>Aquí podemos ver un <strong>shadow tablespace “*</strong>STTab001<strong>*”</strong> rastreando dos <strong>tablespaces</strong> muy distintos entre si, uno es de tipo <strong>bigfile “*</strong>Tab002<strong>*”</strong>, y el otro es de tipo <strong>smallfile “*</strong>SmallTab001<strong>*”.</strong></p>
<p>Esta pequeña diferencia es que quiero que veamos como podemos ligarlos a un <strong><em>tablespace</em></strong> o un <strong><em>datafile</em></strong>, y la manera de tener en 23ai varios <strong><em>datafiles</em></strong> en un <strong>tablespace</strong>, es definiéndolo de tipo <strong>smallfile</strong> (<strong>bigfile</strong> solo puede tener un <strong><em>datafile</em></strong>).</p>
<p>Vamos a configurarlo en nuestra PDB.</p>
<p>Lo primero que debemos crear es definir el tablespace “<strong>shadow tablespace“</strong> antes de activarlo, porque si lo hacemos al revés, primero activar y después crear, recibiremos el siguiente error:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>
*
<span class="hljs-keyword">ERROR</span> <span class="hljs-keyword">at</span> line <span class="hljs-number">1</span>:
ORA<span class="hljs-number">-65491</span>: A lost write <span class="hljs-keyword">bigfile</span> <span class="hljs-keyword">tablespace</span> must exist <span class="hljs-keyword">before</span> attempting <span class="hljs-keyword">to</span> <span class="hljs-keyword">enable</span> lost write.
<span class="hljs-keyword">Help</span>: https://docs.oracle.com/<span class="hljs-keyword">error</span>-<span class="hljs-keyword">help</span>/db/ora<span class="hljs-number">-65491</span>/
</code></pre>
<p>Vamos a crear primero <em>STTab001</em>:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">BIGFILE</span> <span class="hljs-keyword">TABLESPACE</span> STTab001 <span class="hljs-keyword">DATAFILE</span> <span class="hljs-keyword">SIZE</span> <span class="hljs-number">10</span>M LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Tablespace created.
</code></pre>
<p>Una vez creado, procedemos activarlo en nuestra PDB:</p>
<pre><code class="lang-sql">SQL&gt; r
  1* <span class="hljs-keyword">select</span> PROPERTY_NAME, PROPERTY_VALUE <span class="hljs-keyword">from</span> database_properties <span class="hljs-keyword">where</span> PROPERTY_NAME=<span class="hljs-string">'NEW_LOST_WRITE'</span>

<span class="hljs-keyword">no</span> <span class="hljs-keyword">rows</span> selected

<span class="hljs-keyword">SQL</span>&gt;  <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Pluggable database altered.

SQL&gt; r
  1* <span class="hljs-keyword">select</span> PROPERTY_NAME, PROPERTY_VALUE <span class="hljs-keyword">from</span> database_properties <span class="hljs-keyword">where</span> PROPERTY_NAME=<span class="hljs-string">'NEW_LOST_WRITE'</span>

PROPERTY_NAME        PROPERTY_VALUE
<span class="hljs-comment">-------------------- --------------------</span>
NEW_LOST_WRITE       <span class="hljs-literal">TRUE</span>
</code></pre>
<p>Si nosotros lo hubiéramos intentado habilitar desde <strong>CDB$Root</strong> para que aplique a todas las PDB, hubiéramos recibido el siguiente error:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">alter</span> <span class="hljs-keyword">session</span> <span class="hljs-keyword">set</span> <span class="hljs-keyword">container</span>=cdb$root;

Session altered.

SQL&gt; <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">BIGFILE</span> <span class="hljs-keyword">TABLESPACE</span> STTab001 <span class="hljs-keyword">DATAFILE</span> <span class="hljs-keyword">SIZE</span> <span class="hljs-number">10</span>M LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Tablespace created.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;
<span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>
*
<span class="hljs-keyword">ERROR</span> <span class="hljs-keyword">at</span> line <span class="hljs-number">1</span>:
ORA<span class="hljs-number">-65046</span>: operation <span class="hljs-keyword">not</span> allowed <span class="hljs-keyword">from</span> outside a <span class="hljs-keyword">pluggable</span> <span class="hljs-keyword">database</span>
<span class="hljs-keyword">Help</span>: https://docs.oracle.com/<span class="hljs-keyword">error</span>-<span class="hljs-keyword">help</span>/db/ora<span class="hljs-number">-65046</span>/
</code></pre>
<p>Definimos ambos tablespaces:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">BIGFILE</span>   <span class="hljs-keyword">TABLESPACE</span> Tab002 <span class="hljs-keyword">DATAFILE</span> <span class="hljs-keyword">SIZE</span> <span class="hljs-number">10</span>M;
Tablespace created.

SQL&gt; <span class="hljs-keyword">CREATE</span> <span class="hljs-keyword">SMALLFILE</span> <span class="hljs-keyword">TABLESPACE</span> SmallTab001 <span class="hljs-keyword">DATAFILE</span> <span class="hljs-keyword">SIZE</span> <span class="hljs-number">1</span>M ;
Tablespace created.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLESPACE</span> SmallTab001 <span class="hljs-keyword">ADD</span> <span class="hljs-keyword">DATAFILE</span> <span class="hljs-keyword">SIZE</span> <span class="hljs-number">1</span>M: 
<span class="hljs-keyword">Tablespace</span> altered.
</code></pre>
<p>Verificamos el paso previo:</p>
<pre><code class="lang-sql">SQL&gt; r
  <span class="hljs-keyword">Select</span> tablespace_name, <span class="hljs-keyword">contents</span>, lost_write_protect <span class="hljs-keyword">from</span> dba_tablespaces

TABLESPACE_NAME                <span class="hljs-keyword">CONTENTS</span>              LOST_WR
<span class="hljs-comment">------------------------------ --------------------- -------</span>
<span class="hljs-keyword">SYSTEM</span>                         <span class="hljs-keyword">PERMANENT</span>             <span class="hljs-keyword">OFF</span>
<span class="hljs-keyword">SYSAUX</span>                         <span class="hljs-keyword">PERMANENT</span>             <span class="hljs-keyword">OFF</span>
UNDOTBS1                       <span class="hljs-keyword">UNDO</span>                  <span class="hljs-keyword">OFF</span>
TEMP                           <span class="hljs-keyword">TEMPORARY</span>             <span class="hljs-keyword">OFF</span>
<span class="hljs-keyword">USERS</span>                          <span class="hljs-keyword">PERMANENT</span>             <span class="hljs-keyword">OFF</span>
STTAB001                       LOST WRITE <span class="hljs-keyword">PROTECTION</span> <span class="hljs-keyword">OFF</span>
TAB002                         <span class="hljs-keyword">PERMANENT</span>             <span class="hljs-keyword">OFF</span>
SMALLTAB001                    <span class="hljs-keyword">PERMANENT</span>             <span class="hljs-keyword">OFF</span>

<span class="hljs-keyword">SQL</span>&gt; r
  <span class="hljs-number">1</span>   <span class="hljs-keyword">Select</span> Tablespace_Name,LOST_WRITE_PROTECT ,File_Name, File_Id
  <span class="hljs-number">2</span>     <span class="hljs-keyword">From</span> Dba_Data_Files
  <span class="hljs-number">3</span>    <span class="hljs-keyword">Where</span> Tablespace_Name <span class="hljs-keyword">in</span> (<span class="hljs-string">'STTAB001'</span>,<span class="hljs-string">'TAB002'</span>,<span class="hljs-string">'SMALLTAB001'</span>)
  <span class="hljs-number">4</span>*  <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> file_id

TABLESPACE_NAME                LOST_WR FILE_NAME                                                                                               FILE_ID
<span class="hljs-comment">------------------------------ ------- ---------------------------------------------------------------------------------------------------- ----------</span>
STTAB001                       <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/sttab001<span class="hljs-number">.280</span><span class="hljs-number">.1213616675</span>                       <span class="hljs-number">13</span>
TAB002                         <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/tab002<span class="hljs-number">.281</span><span class="hljs-number">.1213625663</span>                         <span class="hljs-number">14</span>
SMALLTAB001                    <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/smalltab001<span class="hljs-number">.282</span><span class="hljs-number">.1213625677</span>                    <span class="hljs-number">15</span>
SMALLTAB001                    <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/smalltab001<span class="hljs-number">.283</span><span class="hljs-number">.1213625699</span>                    <span class="hljs-number">16</span>
</code></pre>
<p>Habilitamos la protección de datos a dos niveles:</p>
<ul>
<li><p>A nivel de tablespace <em>TAB002</em></p>
</li>
<li><p>A nivel d<em>e</em> datafiles de <em>SMALLTAB001</em>.</p>
</li>
</ul>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLESPACE</span> TAB002 <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Tablespace altered.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">DATAFILE</span> <span class="hljs-string">'+DATA/DB23AI_FWX_MAD/4004456EB4523134E0634C00000AA230/DATAFILE/smalltab001.283.1213625699'</span> <span class="hljs-keyword">ENABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Database altered.
</code></pre>
<p>Verificamos de nuevo que la protección la tenemos activa en ambos tablespace:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">Select</span> tablespace_name, <span class="hljs-keyword">contents</span>, lost_write_protect <span class="hljs-keyword">from</span> dba_tablespaces;

TABLESPACE_NAME                CONTENTS              LOST_WR
<span class="hljs-comment">------------------------------ --------------------- -------</span>
SYSTEM                         PERMANENT             OFF
SYSAUX                         PERMANENT             OFF
UNDOTBS1                       UNDO                  OFF
TEMP                           TEMPORARY             OFF
USERS                          PERMANENT             OFF
STTAB001                       LOST WRITE PROTECTION OFF
TAB002                         PERMANENT             ENABLED
SMALLTAB001                    PERMANENT             OFF

8 rows selected.

SQL&gt; r
  1   <span class="hljs-keyword">Select</span> Tablespace_Name,LOST_WRITE_PROTECT ,File_Name, File_Id
  <span class="hljs-number">2</span>     <span class="hljs-keyword">From</span> Dba_Data_Files
  <span class="hljs-number">3</span>    <span class="hljs-keyword">Where</span> Tablespace_Name <span class="hljs-keyword">in</span> (<span class="hljs-string">'STTAB001'</span>,<span class="hljs-string">'TAB002'</span>,<span class="hljs-string">'SMALLTAB001'</span>)
  <span class="hljs-number">4</span>*  <span class="hljs-keyword">order</span> <span class="hljs-keyword">by</span> file_id

TABLESPACE_NAME                LOST_WR FILE_NAME                                                                                               FILE_ID
<span class="hljs-comment">------------------------------ ------- ---------------------------------------------------------------------------------------------------- ----------</span>
STTAB001                       <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/sttab001<span class="hljs-number">.280</span><span class="hljs-number">.1213616675</span>                       <span class="hljs-number">13</span>
TAB002                         ENABLED +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/tab002<span class="hljs-number">.281</span><span class="hljs-number">.1213625663</span>                         <span class="hljs-number">14</span>
SMALLTAB001                    <span class="hljs-keyword">OFF</span>     +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/smalltab001<span class="hljs-number">.282</span><span class="hljs-number">.1213625677</span>                    <span class="hljs-number">15</span>
SMALLTAB001                    ENABLED +<span class="hljs-keyword">DATA</span>/DB23AI_FWX_MAD/<span class="hljs-number">4004456</span>EB4523134E0634C00000AA230/<span class="hljs-keyword">DATAFILE</span>/smalltab001<span class="hljs-number">.283</span><span class="hljs-number">.1213625699</span>                    <span class="hljs-number">16</span>
</code></pre>
<p>Como veis es muy sencillo activarlo.</p>
<p>Para desactivarlo, sería de la siguiente manera:</p>
<pre><code class="lang-sql">SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">TABLESPACE</span> TAB002 REMOVE LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Tablespace altered.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">DATAFILE</span> <span class="hljs-string">'+DATA/DB23AI_FWX_MAD/4004456EB4523134E0634C00000AA230/DATAFILE/smalltab001.283.1213625699'</span> <span class="hljs-keyword">SUSPEND</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Pluggable database altered.

SQL&gt; <span class="hljs-keyword">ALTER</span> <span class="hljs-keyword">PLUGGABLE</span> <span class="hljs-keyword">DATABASE</span> <span class="hljs-keyword">DISABLE</span> LOST WRITE <span class="hljs-keyword">PROTECTION</span>;

Pluggable database altered.

SQL&gt; <span class="hljs-keyword">DROP</span> <span class="hljs-keyword">TABLESPACE</span> STTAB001 <span class="hljs-keyword">INCLUDING</span> <span class="hljs-keyword">CONTENTS</span> <span class="hljs-keyword">AND</span> <span class="hljs-keyword">DATAFILES</span>;

Tablespace dropped.
</code></pre>
<p>Espero que os sirva :)</p>
]]></content:encoded></item><item><title><![CDATA[[OGG] Lentitud de replicat]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
Hoy vamos a ver un caso sencillo de un proceso de replicat en OGG que corre demasiado lento.
La situación del OGG cuando me pidieron ayuda era la siguiente:
GGSCI 1> info all

Prog...]]></description><link>https://dbaenlasombra.com/ogg-lentitud-de-replicat</link><guid isPermaLink="true">https://dbaenlasombra.com/ogg-lentitud-de-replicat</guid><category><![CDATA[ogg]]></category><category><![CDATA[19c]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[replication]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sun, 28 Sep 2025 15:53:21 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1759058059908/0f349b3a-9301-4db4-b25d-5b253a8f56b5.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/ogg-replicat-is-running-too-slowly"><strong><em>English version</em></strong></a><em>.</em></p>
<p>Hoy vamos a ver un caso sencillo de un proceso de replicat en OGG que corre demasiado lento.</p>
<p>La situación del OGG cuando me pidieron ayuda era la siguiente:</p>
<pre><code class="lang-bash">GGSCI 1&gt; info all

Program     Status      Group       Lag at Chkpt  Time Since Chkpt

MANAGER     RUNNING
EXTRACT     RUNNING     EXXX        00:00:06      00:00:09
REPLICAT    RUNNING     Rxxx        53:14:49      00:00:03
</code></pre>
<p>El proceso de replicat <strong>RXXX</strong> llevaba una semana donde el <strong><em>lag</em></strong> siempre estaba en constante crecimiento, nunca disminuyendo.</p>
<p>Antes de entra en faena, hay que decir que la versión del ogg es una 19.1.0.0.4.</p>
<p>Lo primero que vamos a ver son las estadísticas del proceso con la tasa de procesamiento en minutos a ver si nos da alguna pista:</p>
<pre><code class="lang-bash">GGSCI 4&gt; stats replicat Rxxx, totalsonly *.*, reportrate min

Sending STATS request to REPLICAT Rxxx...

Start of Statistics at 2025-09-28 00:23:05.

DDL replication statistics:

*** Total statistics since replicat started     ***
        Operations                                       104.00
        Mapped operations                                  0.00
        Unmapped operations                              104.00
        Other operations                                   0.00
        Excluded operations                                0.00
        Errors                                             0.00
        Retried errors                                     0.00
        Discarded errors                                   0.00
        Ignored errors                                     0.00

Cumulative totals <span class="hljs-keyword">for</span> specified table(s):

*** Total statistics since 2025-09-27 23:19:02 ***
        Total inserts/minute                       0.00
        Total updates/minute                       0.00
        Total deletes/minute                       0.00
        Total upserts/minute                       0.00
        Total discards/minute                  13497.48
        Total operations/minute                    0.00

*** Daily statistics since 2025-09-28 00:00:00 ***

        No database operations have been performed.

*** Hourly statistics since 2025-09-28 00:00:00 ***

        No database operations have been performed.

*** Latest statistics since 2025-09-27 23:19:02 ***
        Total inserts/minute                       0.00
        Total updates/minute                       0.00
        Total deletes/minute                       0.00
        Total upserts/minute                       0.00
        Total discards/minute                 864456.00
        Total operations/minute                    0.00

End of Statistics.
</code></pre>
<p>Ahí ya podemos ver algún problema de performance ya que el nº de <strong><em>operations</em></strong> es muy bajo al igual que apenas hay estadísticas en las operaciones <strong>dml</strong>.</p>
<p>Miramos las definición del proceso:</p>
<pre><code class="lang-bash">REPLICAT Rxxx

useridalias ogg_xxxx
assumetargetdefs

DDL INCLUDE ALL
DDLERROR DEFAULT IGNORE

discardfile ./dirrpt/Rxxx.dsc, Append,megabytes 4000
REPERROR (0001,discard)
REPERROR (1403,discard)

TABLE SCHEMA.TABLE , TARGET PDB.SCHEMA.TABLE;
</code></pre>
<p>Ahí podemos ver que el origen es un <strong><em>single instance</em></strong> y el destino es <strong><em>multitenant</em></strong>, esto no tiene que tener percance en el performance, pero si es un dato que hay que tener en cuenta.</p>
<p>Mirando las distintas propiedades, hecho en falta el <strong>HANDLECOLLISIONS</strong>, este parámetro es muy útil para ayudar a resolver conflictos a la hora de replicar la información, estos conflictos son llamados <strong>colisiones</strong>.</p>
<p>Este parámetro ayuda a OGG a resolver esas <strong>colisiones</strong> más eficientemente como son:</p>
<ul>
<li><p><strong>Insert</strong>: Se intenta realizar un <em>insert</em> cuando la fila ya existe en el destino.</p>
</li>
<li><p><strong>Update</strong>: Se intenta realiza una operación de <em>update</em> en el destino cuándo la fila aún no existe</p>
</li>
<li><p><strong>Delete</strong>: Se intentan hacer el borrado de un registro cuando la fila no existe en el destino.</p>
</li>
</ul>
<p>Modificamos nuestro proceso para incluir el parámetro quedando de la siguiente manera:</p>
<pre><code class="lang-bash">REPLICAT Rxxx

useridalias ogg_xxxx
assumetargetdefs

DDL INCLUDE ALL
DDLERROR DEFAULT IGNORE
HANDLECOLLISIONS

discardfile ./dirrpt/Rxxx.dsc, Append,megabytes 4000
REPERROR (0001,discard)
REPERROR (1403,discard)

TABLE SCHEMA.TABLE , TARGET PDB.SCHEMA.TABLE;
</code></pre>
<p>Activamos y esperamos unas horas para ver si ya va mejor.</p>
<p>Efectivamente, el proceso de replicat va mucho mejor, cada vez el <strong><em>lag</em></strong> está más cerca del valor cero.</p>
<pre><code class="lang-bash">GGSCI 1&gt; info all

Program     Status      Group       Lag at Chkpt  Time Since Chkpt

MANAGER     RUNNING
EXTRACT     RUNNING     EXXX        00:00:02      00:00:07
REPLICAT    RUNNING     Rxxx        26:38:41      00:00:00
</code></pre>
<p>Vamos a ver de nuevo las estadísticas del proceso:</p>
<pre><code class="lang-bash">GGSCI 3&gt;   stats replicat Rxxx, totalsonly *.*, reportrate min

Sending STATS request to REPLICAT Rxxx...

Start of Statistics at 2025-09-28 12:19:43.

DDL replication statistics:

*** Total statistics since replicat started     ***
        Operations                                      4804.00
        Mapped operations                                  5.00
        Unmapped operations                             4799.00
        Other operations                                   0.00
        Excluded operations                                0.00
        Errors                                             7.00
        Retried errors                                     0.00
        Discarded errors                                   0.00
        Ignored errors                                     6.00

Cumulative totals <span class="hljs-keyword">for</span> specified table(s):

*** Total statistics since 2025-09-28 00:26:27 ***
        Total inserts/minute                   63670.08
        Total updates/minute                       0.00
        Total deletes/minute                   54358.40
        Total upserts/minute                       0.00
        Total discards/minute                      0.00
        Total operations/minute               118028.47
        Total insert collisions/minute         63670.08
        Total delete collisions/minute         31318.92

*** Daily statistics since 2025-09-28 00:26:27 ***
        Total inserts/minute                   63670.08
        Total updates/minute                       0.00
        Total deletes/minute                   54358.40
        Total upserts/minute                       0.00
        Total discards/minute                      0.00
        Total operations/minute               118028.47
        Total insert collisions/minute         63670.08
        Total delete collisions/minute         31318.92

*** Hourly statistics since 2025-09-28 12:00:00 ***
        Total inserts/minute                    7272.71
        Total updates/minute                       0.00
        Total deletes/minute                  421092.52
        Total upserts/minute                       0.00
        Total discards/minute                      0.00
        Total operations/minute               428365.24
        Total insert collisions/minute          7272.71
        Total delete collisions/minute        421092.52

*** Latest statistics since 2025-09-28 00:26:27 ***
        Total inserts/minute                45413289.00
        Total updates/minute                       0.00
        Total deletes/minute                38771646.00
        Total upserts/minute                       0.00
        Total discards/minute                      0.00
        Total operations/minute             84184935.00
        Total insert collisions/minute      45413289.00
        Total delete collisions/minute      22338520.00

End of Statistics.
</code></pre>
<p>El nº de <strong><em>operations</em></strong> ha pasado de 104, <strong>a tener más de 4000</strong> con un flujo de operaciones <strong>dml</strong> correcto.</p>
<p>El problema de performance se ha resuelto.</p>
<p><img src="https://media1.giphy.com/media/v1.Y2lkPWZjZGU1NDk1aGI3OWphNTlja215bmxxa3N1MTlvMzUzeDdmZjl5cDlpYWxmNmRoZyZlcD12MV9naWZzX3NlYXJjaCZjdD1n/UqvkuqTLBP8mfDgbEI/giphy.gif" alt="Caso Cerrado Telemundo GIF by Universo (imagen GIF)" /></p>
<p> Espero que os sirva :)</p>
]]></content:encoded></item><item><title><![CDATA[[23ai] Instalación de Oracle23ai en OraLinux9]]></title><description><![CDATA[El artículo también se encuentra publicado en Ingles English version.
En este pequeño artículo, vamos a ver a definir una base de datos 23ai con Oracle Database Configuration Assistant (DBCA).
Para poder hacer este laboratorio me he creado un IaaS en...]]></description><link>https://dbaenlasombra.com/23ai-instalacion-de-oracle23ai-en-oralinux9</link><guid isPermaLink="true">https://dbaenlasombra.com/23ai-instalacion-de-oracle23ai-en-oralinux9</guid><category><![CDATA[OCI]]></category><category><![CDATA[ACE]]></category><category><![CDATA[Oracle 23ai]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Fri, 05 Sep 2025 09:25:37 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1756902043196/a3251e0e-4419-4358-9cbc-a5fb6dab4624.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p><em>El artículo también se encuentra publicado en Ingles</em> <a target="_blank" href="https://dbaintheshadow.com/23ai-dbca"><em>English version</em></a><em>.</em></p>
<p>En este pequeño artículo, vamos a ver a definir una base de datos 23ai con <em>Oracle Database Configuration Assistant</em> (<strong>DBCA</strong>).</p>
<p>Para poder hacer este laboratorio me he creado un <strong>IaaS</strong> en OCI con <strong>Oracle Linux 9</strong>. Es un entorno nuevo con lo que el primer paso que vamos a realizar es la instalación de Oracle 23ai, así que vamos a ello.</p>
<pre><code class="lang-bash">[root@~]<span class="hljs-comment"># grep PRETTY_NAME /etc/os-release</span>
PRETTY_NAME=<span class="hljs-string">"Oracle Linux Server 9.6"</span>
</code></pre>
<p>Para poder realizar la instalación de esta versión, nos vamos apoyar en los siguientes rpm:</p>
<pre><code class="lang-bash">[root@test1 tmp]<span class="hljs-comment"># ls -lac *rpm</span>
-rw-r--r--. 1 root root 1412957372 Sep  2 09:47 oracle-database-free-23ai-23.9-1.el9.x86_64.rpm
-rw-r--r--. 1 root root      30688 Sep  2 09:30 oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm
</code></pre>
<p>Dejo a continuación donde podemos descargar cada uno de ellos:</p>
<ul>
<li><p>oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm, lo podemos descargar desde <a target="_blank" href="https://yum.oracle.com/repo/OracleLinux/OL8/developer/x86_64/">aquí</a>.</p>
</li>
<li><p>oracle-database-free-23ai-23.9-1.el9.x86_64.rpm. Este rpm lo podemos descargar desde <a target="_blank" href="https://www.oracle.com/database/free/get-started/">aquí</a>.</p>
</li>
</ul>
<h3 id="heading-instalacion-y-configuracion">Instalación y Configuración</h3>
<ul>
<li>Instalación del RPM oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm:</li>
</ul>
<pre><code class="lang-bash">[root@tmp]<span class="hljs-comment"># dnf install oracle-database-preinstall-23c-1.0-0.5.el8.x86_64.rpm -y</span>
Last metadata expiration check: 1:54:40 ago on Tue 02 Sep 2025 07:46:07 AM GMT.
Dependencies resolved.
....
....
Complete!
</code></pre>
<ul>
<li>Instalación del RPM oracle-database-free-23ai-23.9-1.el9.x86_64.rpm:</li>
</ul>
<pre><code class="lang-bash">[root@test1 tmp]<span class="hljs-comment"># wget https://objectstorage.....</span>
[root@test1 tmp]<span class="hljs-comment"># dnf install oracle-database-free-23ai-23.9-1.el9.x86_64.rpm -y</span>
Last metadata expiration check: 2:06:18 ago on Tue 02 Sep 2025 07:46:07 AM GMT.
Dependencies resolved.
....
....                                                                                                                                     3/3
Installed:
  oracle-database-free-23ai-23.9-1.x86_64                                                         oracle-database-preinstall-23ai-1.0-3.el9.x86_64
Complete!
</code></pre>
<p>Gracias a la instalación de ambos rpm, tendremos el siguiente script <strong>oracle-free-23ai</strong> en /etc/init.d para poder crear nuestra bbdd 23ai. Antes de ejecutar el script, debemos setear la variable DB_PASSWORD.</p>
<pre><code class="lang-bash">[root@test1 tmp]<span class="hljs-comment">#  export DB_PASSWORD=oracle</span>
[root@test1 tmp]<span class="hljs-comment"># (echo "${DB_PASSWORD}"; echo "${DB_PASSWORD}";) | /etc/init.d/oracle-free-23ai configure</span>
Specify a password to be used <span class="hljs-keyword">for</span> database accounts. Oracle recommends that the password entered should be at least 8 characters <span class="hljs-keyword">in</span> length, contain at least 1 uppercase character, 1 lower <span class="hljs-keyword">case</span> character and 1 digit [0-9]. Note that the same password will be used <span class="hljs-keyword">for</span> SYS, SYSTEM and PDBADMIN accounts:
Confirm the password:
Configuring Oracle Listener.
Listener configuration succeeded.
Configuring Oracle Database FREE.
Enter SYS user password:
*****
Enter SYSTEM user password:
********
Enter PDBADMIN User Password:
*********
Prepare <span class="hljs-keyword">for</span> db operation
7% complete
Copying database files
29% complete
Creating and starting Oracle instance
30% complete
33% complete
36% complete
39% complete
43% complete
Completing Database Creation
47% complete
49% complete
50% complete
Creating Pluggable Databases
54% complete
71% complete
Executing Post Configuration Actions
93% complete
Running Custom Scripts
100% complete
Database creation complete. For details check the logfiles at:
 /opt/oracle/cfgtoollogs/dbca/FREE.
Database Information:
Global Database Name:FREE
System Identifier(SID):FREE
Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/FREE/FREE.log"</span> <span class="hljs-keyword">for</span> further details.

Connect to Oracle Database using one of the connect strings:
     Pluggable database: test1/FREEPDB1
     Multitenant container database: test1
</code></pre>
<p>Listo, en este punto tendríamos una 23ai lista para poder trabajar. Vamos a configurar los ficheros <strong>.bashrc</strong> y <strong>.bash_profile</strong> para que sea más sencillo interactuar con los binarios de Oracle, como por ejemplo <strong>sqlplus</strong>, <strong>dbca</strong>, o <strong>tkproff</strong>.</p>
<p>Abajo podemos ver el <strong>.bashrc</strong> después de actualizarlo:</p>
<pre><code class="lang-bash"><span class="hljs-comment"># .bashrc</span>
<span class="hljs-comment"># Source global definitions</span>
<span class="hljs-keyword">if</span> [ -f /etc/bashrc ]; <span class="hljs-keyword">then</span>
        . /etc/bashrc
<span class="hljs-keyword">fi</span>
<span class="hljs-comment"># User specific environment</span>
<span class="hljs-keyword">if</span> ! [[ <span class="hljs-string">"<span class="hljs-variable">$PATH</span>"</span> =~ <span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.local/bin:<span class="hljs-variable">$HOME</span>/bin:"</span> ]]
<span class="hljs-keyword">then</span>
    PATH=<span class="hljs-string">"<span class="hljs-variable">$HOME</span>/.local/bin:<span class="hljs-variable">$HOME</span>/bin:<span class="hljs-variable">$PATH</span>"</span>
<span class="hljs-keyword">fi</span>
<span class="hljs-built_in">export</span> PATH
<span class="hljs-comment"># Uncomment the following line if you don't like systemctl's auto-paging feature:</span>
<span class="hljs-comment"># export SYSTEMD_PAGER=</span>
<span class="hljs-comment"># User specific aliases and functions</span>
<span class="hljs-keyword">if</span> [ -d ~/.bashrc.d ]; <span class="hljs-keyword">then</span>
        <span class="hljs-keyword">for</span> rc <span class="hljs-keyword">in</span> ~/.bashrc.d/*; <span class="hljs-keyword">do</span>
                <span class="hljs-keyword">if</span> [ -f <span class="hljs-string">"<span class="hljs-variable">$rc</span>"</span> ]; <span class="hljs-keyword">then</span>
                        . <span class="hljs-string">"<span class="hljs-variable">$rc</span>"</span>
                <span class="hljs-keyword">fi</span>
        <span class="hljs-keyword">done</span>
<span class="hljs-keyword">fi</span>
ORACLE_HOME=/opt/oracle/product/23ai/dbhomeFree; <span class="hljs-built_in">export</span> ORACLE_HOME
PATH=/bin:/usr/bin:/usr/<span class="hljs-built_in">local</span>/sbin:/usr/sbin:<span class="hljs-variable">$ORACLE_HOME</span>/bin:<span class="hljs-variable">$ORACLE_HOME</span>/lib:<span class="hljs-variable">$ORACLE_HOME</span>/OPatch/:<span class="hljs-variable">$ORACLE_HOME</span>/lib; <span class="hljs-built_in">export</span> PATH
LD_LIBRARY_PATH=<span class="hljs-variable">${ORACLE_HOME}</span>/lib; <span class="hljs-built_in">export</span> LD_LIBRARY_PATH

ORACLE_SID=FREE; <span class="hljs-built_in">export</span> ORACLE_SID
PDB_NAME=FREEPDB1; <span class="hljs-built_in">export</span> PDB_NAME
</code></pre>
<p>Nuestra idea inicial no es trabajar con esta versión, sino testear <strong>dbca</strong> para realizar lo siguiente:</p>
<ul>
<li><p>Generar el template de la actual 23ai</p>
</li>
<li><p>Eliminar esta base de datos que hemos creado previamente.</p>
</li>
<li><p>Crear una nueva basa de datos usando el fichero response junto con el template.</p>
</li>
</ul>
<p>Todo estos puntos se ejecutaran en modo -<strong>silent</strong>. Essta opción nos permitirá ejecutarr dbca en silent mode.</p>
<h3 id="heading-generar-el-template">Generar el template</h3>
<pre><code class="lang-bash">[oracle@test1 ~]$ dbca -silent \
-createTemplateFromDB \
-sourceDB FREE \
-templateName FREE.template \
-sysDBAUserName SYS \
-sysDBAPassword oracle
Prepare <span class="hljs-keyword">for</span> db operation
33% complete
Creating a template from the database
37% complete
40% complete
43% complete
47% complete
50% complete
53% complete
57% complete
60% complete
63% complete
67% complete
Creating template
100% complete
The template <span class="hljs-string">"/opt/oracle/product/23ai/dbhomeFree/assistants/dbca/templates/FREE.template.dbt"</span> creation completed.
Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/silent.log_2025-09-02_12-09-54PM_170638"</span> <span class="hljs-keyword">for</span> further details.
</code></pre>
<h3 id="heading-eliminar-la-bbdd">Eliminar la BBDD</h3>
<p>Eliminamos la BBDD previamente habiendo hecho un shutdown.</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ dbca -silent \
-deleteDatabase \
-sourceDB FREE \
-sysDBAUserName sys \
-sysDBAPassword oracle
[WARNING] [DBT-11503] The instance (FREE) is not running on the <span class="hljs-built_in">local</span> node. This may result <span class="hljs-keyword">in</span> partial delete of Oracle database.
   CAUSE: A locally running instance is required <span class="hljs-keyword">for</span> complete deletion of Oracle database instance and database files.
   ACTION: Specify a locally running database, or execute DBCA on a node <span class="hljs-built_in">where</span> the database instance is running.
[WARNING] [DBT-19202] The Database Configuration Assistant will delete the Oracle instances and datafiles <span class="hljs-keyword">for</span> your database. All information <span class="hljs-keyword">in</span> the database will be destroyed.
Prepare <span class="hljs-keyword">for</span> db operation
32% complete
Connecting to database
35% complete
39% complete
42% complete
45% complete
48% complete
52% complete
65% complete
Updating network configuration files
68% complete
Deleting instance and datafiles
84% complete
100% complete
Database deletion completed.
Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/FREE/FREE0.log"</span> <span class="hljs-keyword">for</span> further details.
</code></pre>
<h3 id="heading-creacion-de-la-bbdd">Creación de la BBDD</h3>
<p>Para la creación de la BBDD vamos a trabajar con los siguientes ficheros:</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ ls -lac config/
total 52
drwxr-xr-x. 2 oracle oinstall    49 Sep  3 06:35 .
drwx------. 3 oracle oinstall   127 Sep  2 15:47 ..
-rw-r-----. 1 oracle oinstall 20942 Sep  2 15:47 FREE.template.dbt
-rw-r-----. 1 oracle oinstall 25378 Sep  3 06:35 shadow.rsp
</code></pre>
<ul>
<li><strong>FREE.template.dbt</strong>, el template que hemos generado en el paso previo. Lo vamos a editar porque solo queremos que nos defina la seed. Para ello, procedemos a eliminar todas las referencias de FREEPDB1:</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1756818902278/4902b0c3-df86-4a22-b5ca-240319b3a2b1.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p><strong>shadow.rsp</strong>. Es el fichero response para automatizar el <strong>dbca</strong>. Es una copia del fichero $ORACLE_HOME/assistants/dbca/dbca.rsp modificado para nuestro caso.</p>
<p>  Los valores que contiene el response son los siguientes:</p>
<pre><code class="lang-bash">  [oracle@test1 ~]$ more config/shadow.rsp | grep -v ^$ | grep -v ^<span class="hljs-comment">#</span>
  responseFileVersion=/oracle/assistants/rspfmt_dbca_response_schema_v23.0.0
  gdbName=FREE
  sid=FREE
  createAsContainerDatabase=<span class="hljs-literal">true</span>
  numberOfPDBs=0
  templateName=/home/oracle/config/FREE.template.dbt
  sysPassword=*********
  systemPassword=********
</code></pre>
</li>
</ul>
<p>Una vez modificado ambos ficheros, lanzamos la creación de nuestra 23ai vía <strong>dbca</strong> en silent mode:</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ dbca -createDatabase -silent -responseFile /home/oracle/config/shadow.rsp
Prepare <span class="hljs-keyword">for</span> db operation
4% complete
Creating and starting Oracle instance
5% complete
6% complete
8% complete
Creating database files
12% complete
Creating data dictionary views
13% complete
16% complete
17% complete
18% complete
24% complete
Oracle JVM
30% complete
36% complete
42% complete
44% complete
Oracle Text
46% complete
48% complete
Oracle OLAP
52% complete
Oracle Spatial
53% complete
60% complete
Oracle Label Security
68% complete
Oracle Database Vault
76% complete
Creating cluster database views
77% complete
84% complete
Completing Database Creation
86% complete
88% complete
Executing Post Configuration Actions
100% complete
Database creation complete. For details check the logfiles at:
 /opt/oracle/cfgtoollogs/dbca/FREE.
Database Information:
Global Database Name:FREE
System Identifier(SID):FREE
Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/FREE/FREE10.log"</span> <span class="hljs-keyword">for</span> further details
</code></pre>
<p>Nos conectamos a la base de datos como SYS:</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ sqlplus /nolog

SQL*Plus: Release 23.0.0.0.0 - Production on Wed Sep 3 09:57:09 2025
Version 23.9.0.25.07

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

SQL&gt;
SQL&gt; conn / as sysdba
Connected.
SQL&gt; show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB<span class="hljs-variable">$SEED</span>                       READ ONLY  NO
</code></pre>
<p>La base de datos se encuentra levantada y solo nos ha creado la seed, tal y como habíamos configurado en nuestro template.</p>
<p>Vamos hacer una prueba más jugando con el parámetro <strong>numberOfPDBs</strong>. Este parámetro indica el número de PDBs que se deben crear. Si nos fijamos, el fichero response que hemos usado tenía especificado que el número de PDB es cero, acorde a nuestro template.</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ grep numberOfPDBs  config/shadow.rsp
<span class="hljs-comment"># Name          : numberOfPDBs</span>
numberOfPDBs=0
</code></pre>
<p>Pero, ¿Qué sucede si usamos el template original? ¿Hará caso al parámetro del response o hará caso al template?</p>
<p>Vamos a probarlo:</p>
<ul>
<li><p>Eliminamos la base de datos:</p>
<pre><code class="lang-bash">  [oracle@test1 ~]$ dbca -silent \
  -deleteDatabase \
  -sourceDB FREE \
  -sysDBAUserName sys \
  -sysDBAPassword ******
  [WARNING] [DBT-11503] The instance (FREE) is not running on the <span class="hljs-built_in">local</span> node. This may result <span class="hljs-keyword">in</span> partial delete of Oracle database.
     CAUSE: A locally running instance is required <span class="hljs-keyword">for</span> complete deletion of Oracle database instance and database files.
     ACTION: Specify a locally running database, or execute DBCA on a node <span class="hljs-built_in">where</span> the database instance is running.
  [WARNING] [DBT-19202] The Database Configuration Assistant will delete the Oracle instances and datafiles <span class="hljs-keyword">for</span> your database. All information <span class="hljs-keyword">in</span> the database will be destroyed.
  Prepare <span class="hljs-keyword">for</span> db operation
  32% complete
  Connecting to database
  35% complete
  39% complete
  42% complete
  45% complete
  48% complete
  52% complete
  65% complete
  Updating network configuration files
  68% complete
  Deleting instance and datafiles
  84% complete
  100% complete
  Database deletion completed.
  Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/FREE/FREE11.log"</span> <span class="hljs-keyword">for</span> further details.
</code></pre>
</li>
<li><p>Creamos la bbdd:</p>
<pre><code class="lang-bash">  [oracle@test1 ~]$ dbca -createDatabase -silent -responseFile /home/oracle/config/shadow.rsp
  Enter PDBADMIN User Password:

  Prepare <span class="hljs-keyword">for</span> db operation
  4% complete
  Creating and starting Oracle instance
  5% complete
  6% complete
  8% complete
  Creating database files
  12% complete
  Creating data dictionary views
  13% complete
  16% complete
  17% complete
  18% complete
  24% complete
  Oracle JVM
  30% complete
  36% complete
  42% complete
  44% complete
  Oracle Text
  46% complete
  48% complete
  Oracle OLAP
  52% complete
  Oracle Spatial
  53% complete
  60% complete
  Oracle Label Security
  68% complete
  Oracle Database Vault
  76% complete
  Creating cluster database views
  77% complete
  84% complete
  Completing Database Creation
  86% complete
  88% complete
  Executing Post Configuration Actions
  100% complete
  Database creation complete. For details check the logfiles at:
   /opt/oracle/cfgtoollogs/dbca/FREE.
  Database Information:
  Global Database Name:FREE
  System Identifier(SID):FREE
  Look at the <span class="hljs-built_in">log</span> file <span class="hljs-string">"/opt/oracle/cfgtoollogs/dbca/FREE/FREE13.log"</span> <span class="hljs-keyword">for</span> further details.
</code></pre>
</li>
</ul>
<p>Nos conectamos a la base de datos como SYS:</p>
<pre><code class="lang-bash">[oracle@test1 ~]$ sqlplus /nolog

SQL*Plus: Release 23.0.0.0.0 - Production on Wed Sep 3 12:02:06 2025
Version 23.9.0.25.07

Copyright (c) 1982, 2025, Oracle.  All rights reserved.

SQL&gt; conn / as sysdba
Connected.
SQL&gt; show pdbs

    CON_ID CON_NAME                       OPEN MODE  RESTRICTED
---------- ------------------------------ ---------- ----------
         2 PDB<span class="hljs-variable">$SEED</span>                       READ ONLY  NO
SQL&gt;! grep pdb_dbid /home/oracle/config/FREE.template.dbt
      &lt;PluggableDatabase pdb_name=<span class="hljs-string">"PDB<span class="hljs-variable">$SEED</span>"</span> con_id=<span class="hljs-string">"2"</span> pdb_dbid=<span class="hljs-string">"3172087764"</span>/&gt;
      &lt;PluggableDatabase pdb_name=<span class="hljs-string">"FREEPDB1"</span> con_id=<span class="hljs-string">"3"</span> pdb_dbid=<span class="hljs-string">"302716810"</span>/&gt;
</code></pre>
<p>No tenemos PDBs creadas, solo tenemos la seed.</p>
<p>Aunque nuestro template si que tiene definida la creación de una PDB "FREEPDB1", el fichero response indica a DBCA que el número de PDBs debe ser 0.</p>
<p>Podéis descargar tanto el template como el response que he usado desde <a target="_blank" href="https://github.com/dbaenlasombra/dbca">aquí</a>.</p>
<p>Espero que os sirva :)</p>
]]></content:encoded></item><item><title><![CDATA[[OCI] Recuperación del acceso de un IaaS]]></title><description><![CDATA[Vamos a ver como podríamos recuperar de forma fácil el acceso a un IaaS al que se ha perdido el authorized_keys del usuario opc.
Antes de entrar en faena, vamos hacer un pequeño resumen de lo que es cada punto:

authorized_keys: fichero que contiene ...]]></description><link>https://dbaenlasombra.com/oci-recuperacion-del-acceso-de-un-iaas</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-recuperacion-del-acceso-de-un-iaas</guid><category><![CDATA[OCI]]></category><category><![CDATA[oracleace]]></category><category><![CDATA[IaaS]]></category><category><![CDATA[RSA]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sun, 13 Jul 2025 16:42:03 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1752424474347/afc877a0-a390-4874-944b-6f2720c22825.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Vamos a ver como podríamos recuperar de forma fácil el acceso a un IaaS al que se ha perdido el <strong>authorized_keys</strong> del usuario <strong>opc</strong>.</p>
<p>Antes de entrar en faena, vamos hacer un pequeño resumen de lo que es cada punto:</p>
<ul>
<li><p><strong>authorized_keys</strong>: fichero que contiene las claves públicas para poder acceder a un servidor sin contraseña.</p>
</li>
<li><p><strong>opc</strong> “<em>Oracle Public Cloud</em>”: cuenta del administrador del sistema. Todos los recursos que tengamos en nuestra tenancy, a la hora de acceder lo haremos con este usuario.</p>
</li>
</ul>
<p>Accedemos a nuestra máquina y listamos el contenido de /home/opc/.ssh.</p>
<pre><code class="lang-bash">[opc@zdm-host .ssh]$ ls -lac
total 16
drwx------. 2 opc opc   75 Jul 13 14:24 .
drwx------. 5 opc opc 4096 Oct 18  2023 ..
-rw-------. 1 opc opc  400 Oct 18  2023 authorized_keys
-rw-rw-r--. 1 opc opc  514 Oct 18  2023 authorized_keys.zip
-rw-r--r--. 1 opc opc  203 Oct 30  2023 known_hosts
</code></pre>
<p>Ahí podemos ver diferentes archivos, ninguna clave pública ni privada, solo el <strong>authorized_keys</strong>, un backup del mismo y el known_hosts. Procedemos a eliminar el <strong>authorized_keys</strong> e intentamos loguearnos de nuevo:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752417713299/7641df74-7626-4b27-a26d-ba0c34f44ddc.png" alt class="image--center mx-auto" /></p>
<p>¡Upps! Efectivamente, hemos perdido el acceso a nuestra máquina.</p>
<p>Esto es algo que nos puede pasar en el día a día, alguien borro sin querer el <strong>authorized_keys</strong> o bien que se ha corrompido o simplemente que modificaron los permisos del fichero. En este caso hay un backup, pero en el caso que no lo tengamos, tendríamos que generar nosotros la clave.</p>
<p>Para ambos problemas, la manera de solucionarla es idéntica.</p>
<p>Para poder solucionarlo tendremos que hacer un detach de nuestro boot volumen para adjuntarlo en otro IaaS.</p>
<p>Así que… <strong>let's get down to business!</strong></p>
<p>Primer paso es <strong>detener</strong> la instancia afectada:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752418538643/d3217440-d72a-4073-9ca1-5ac8561cc606.png" alt class="image--center mx-auto" /></p>
<p>Una vez detenida, procedemos a realizar el <strong>detach</strong> del boot volumen. Para ello vamos a la sección de “Storage” y ahí veremos nuestro boot volumen.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752419540682/b55d6a1a-eabe-46bc-a6ea-d2562a899ef7.png" alt class="image--center mx-auto" /></p>
<p>Una vez que la operación ha terminado, el estado del boot volumen pasará a “Detached”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752419781405/d060c6c8-a294-40a4-91fe-01798b8ff7bf.png" alt class="image--center mx-auto" /></p>
<p>Antes de pasar al siguiente punto, recomiendo copiar el OCID:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752420427485/e9451ed7-1639-4580-984d-1707f3915f2f.png" alt class="image--center mx-auto" /></p>
<p>Vamos a la IaaS donde vamos a realizar la recuperación del mismo. El primer paso que tenemos que hacer es adjuntar ahí nuestro boot volume. Para hacerlo iríamos a la sección de “Storage” y de ahí a “Attach block Volume”</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752419961407/9846ad21-45b7-499c-ac65-a05ca4cd3c46.png" alt class="image--center mx-auto" /></p>
<p>En esta nueva ventana que aparece nos pedirá una serie de información acerca del boot volume. Copiamos ahí el OCID que habíamos copiado previamente e indicamos que queremos tener acceso read/write</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752420193450/bbb2e2fc-2259-44a5-8855-2fa57e8fe0a4.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752420277512/d3eda88c-01c3-4f66-991c-cdee8b2084f3.png" alt class="image--center mx-auto" /></p>
<p>Una vez finalizada podremos ver nuestro boot volume se ha adjuntado de manera correcta.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752420506339/b2ad4c94-45c3-4b8b-aafd-65722691ecfc.png" alt class="image--center mx-auto" /></p>
<p>Si accedemos a la máquina, podemos ver el volumen que hemos adjuntado (/dev/sdb3):</p>
<pre><code class="lang-bash">[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># fdisk -l /dev/sdb</span>
Disk /dev/sdb: 200 GiB, 214748364800 bytes, 419430400 sectors
Disk model: BlockVolume
Units: sectors of 1 * 512 = 512 bytes
Sector size (logical/physical): 512 bytes / 4096 bytes
I/O size (minimum/optimal): 4096 bytes / 1048576 bytes
Disklabel <span class="hljs-built_in">type</span>: gpt
Disk identifier: AEAC51C9-5D28-48CB-82FF-1B7312827FEB

Device        Start       End   Sectors   Size Type
/dev/sdb1      2048    411647    409600   200M EFI System
/dev/sdb2    411648  17188863  16777216     8G Linux swap
/dev/sdb3  17188864 419430365 402241502 191.8G Microsoft basic data
</code></pre>
<p>El siguiente paso es marcar como disponible para el S.O nuevo boot volumen por medio del comando <strong>mount</strong>:</p>
<pre><code class="lang-bash">[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># mount -o nouuid /dev/sdb3 /mnt</span>
[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># df -h</span>
Filesystem                  Size  Used Avail Use% Mounted on
devtmpfs                    4.0M     0  4.0M   0% /dev
tmpfs                       7.6G     0  7.6G   0% /dev/shm
tmpfs                       3.1G  8.7M  3.1G   1% /run
efivarfs                    256K   17K  235K   7% /sys/firmware/efi/efivars
/dev/mapper/ocivolume-root   30G  9.4G   21G  32% /
/dev/sda2                   2.0G  412M  1.6G  21% /boot
/dev/mapper/ocivolume-oled   15G  178M   15G   2% /var/oled
/dev/sda1                   100M  6.3M   94M   7% /boot/efi
tmpfs                       1.6G     0  1.6G   0% /run/user/989
tmpfs                       1.6G     0  1.6G   0% /run/user/1000
/dev/sdb3                   192G   13G  179G   7% /mnt
</code></pre>
<p>Accedemos a /mnt/home/opc/.ssh para recuperar nuestro <strong>authorized_keys</strong>.</p>
<pre><code class="lang-bash">[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># cd /mnt/home/opc/.ssh</span>
[root@<span class="hljs-built_in">test</span> .ssh]<span class="hljs-comment"># unzip authorized_keys.zip &amp;&amp; chown opc:opc authorized_keys</span>
Archive:  authorized_keys.zip
[root@<span class="hljs-built_in">test</span> .ssh]<span class="hljs-comment"># ls -lac</span>
total 16
drwx------. 2 opc opc   75 Jul 13 15:38 .
drwx------. 5 opc opc 4096 Oct 18  2023 ..
-rw-------. 1 opc opc  400 Jul 13 15:38 authorized_keys
-rw-rw-r--. 1 opc opc  514 Jul 13 14:24 authorized_keys.zip
-rw-r--r--. 1 opc opc  203 Oct 30  2023 known_hosts
</code></pre>
<p>Accedemos, realizamos unzip y cambiamos el propietario/grupo del fichero.</p>
<p>Desmontamos /mnt y comprobamos que ya no se encuentra accesible, para poder realizar detach de nuevo:</p>
<pre><code class="lang-bash">[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># umount /mnt</span>
[root@<span class="hljs-built_in">test</span> ~]<span class="hljs-comment"># df -h</span>
Filesystem                  Size  Used Avail Use% Mounted on
devtmpfs                    4.0M     0  4.0M   0% /dev
tmpfs                       7.6G     0  7.6G   0% /dev/shm
tmpfs                       3.1G  8.7M  3.1G   1% /run
efivarfs                    256K   17K  235K   7% /sys/firmware/efi/efivars
/dev/mapper/ocivolume-root   30G  9.4G   21G  32% /
/dev/sda2                   2.0G  412M  1.6G  21% /boot
/dev/mapper/ocivolume-oled   15G  178M   15G   2% /var/oled
/dev/sda1                   100M  6.3M   94M   7% /boot/efi
tmpfs                       1.6G     0  1.6G   0% /run/user/989
tmpfs                       1.6G     0  1.6G   0% /run/user/1000
</code></pre>
<p>Perfecto, vamos adjuntar de nuevo el boot volumen en la IaaS que habíamos perdido acceso:</p>
<ul>
<li>Realizamos detaching</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752421470015/9e500ac1-a423-459b-9d40-38778c3aadec.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Una vez que la operación ha terminado correctamente, realizamos attach de nuevo en el IaaS que habíamos perdido acceso</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752421946032/06412d6f-6001-4edd-a15b-bb469c30e4b6.png" alt class="image--center mx-auto" /></p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1752422064042/820552fa-f08b-4196-a190-b104f4b8c333.png" alt class="image--center mx-auto" /></p>
<p>Una vez terminada la operación, arrancamos de nuevo el entorno de nuevo y verificamos que hemos recuperado el acceso:</p>
<pre><code class="lang-bash">Using username <span class="hljs-string">"opc"</span>.
Authenticating with public key <span class="hljs-string">"imported-openssh-key"</span>
Last login: Sun Jul 13 14:06:47 2025 from 10.98.140.36
[opc@zdm-host ~]$
</code></pre>
<p>Perfecto, tenemos acceso de nuevo. ¡Espero que os sirva!</p>
]]></content:encoded></item><item><title><![CDATA[[OCI] Monitorización Dataguard]]></title><description><![CDATA[Os paso un script sencillo para supervisar nuestro Oracle Data Guard, cuya función es enviarnos una alerta si se detectan errores o advertencias.
En caso que nuestro script haya detectado problemas, nos enviará un correo con la siguiente información:...]]></description><link>https://dbaenlasombra.com/oci-monitorizacion-dataguard</link><guid isPermaLink="true">https://dbaenlasombra.com/oci-monitorizacion-dataguard</guid><category><![CDATA[oracleace]]></category><category><![CDATA[dataguard]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[19c]]></category><category><![CDATA[monitoring]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Thu, 03 Jul 2025 12:03:26 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1751267264284/5af656c5-219a-434b-9088-3a0c15c8db4b.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Os paso un script sencillo para supervisar nuestro Oracle Data Guard, cuya función es enviarnos una alerta si se detectan errores o advertencias.</p>
<p>En caso que nuestro script haya detectado problemas, nos enviará un correo con la siguiente información:</p>
<ul>
<li><p>Estado del Data Guard.</p>
</li>
<li><p>Posibles errores ORA a nivel de primary o standby.</p>
</li>
<li><p>Lag actual del dataguard.</p>
</li>
</ul>
<p>La alerta que nos llegaría sería la siguiente:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1751237843066/3644c962-3227-4294-82cb-1f3a291c342a.png" alt class="image--center mx-auto" /></p>
<p>Este sería el script:</p>
<pre><code class="lang-powershell"><span class="hljs-comment">#!/bin/bash</span>

. /home/oracle/.bashrc

getInfo_dgmgrl(){
  <span class="hljs-keyword">while</span> read <span class="hljs-literal">-a</span> line;
  <span class="hljs-keyword">do</span>
    <span class="hljs-keyword">if</span> [[ <span class="hljs-string">"<span class="hljs-variable">$</span>{line[2]}"</span> == *<span class="hljs-string">"Primary"</span>* ]];
    then
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$</span>{line[0]}"</span>
    elif [[ <span class="hljs-string">"<span class="hljs-variable">$</span>{line[2]}"</span> == *<span class="hljs-string">"Physical"</span>* ]];
    then
      <span class="hljs-built_in">echo</span> <span class="hljs-string">"<span class="hljs-variable">$</span>{line[0]}"</span>
    fi
  done &lt;&lt;&lt; <span class="hljs-variable">$1</span>
}

check_output=<span class="hljs-variable">$</span>(getInfo_dgmgrl <span class="hljs-string">"`dgmgrl -silent /  "</span>show configuration<span class="hljs-string">"`")

set <span class="hljs-variable">$</span>{check_output}
PRY=<span class="hljs-variable">$1</span>
STB=<span class="hljs-variable">$2</span>

LAG=`dgmgrl -silent /  "</span>show configuration lag<span class="hljs-string">" | grep -ie 'transport' -ie 'apply' | sed 's/^[[:space:]]*//'`
STATUS=`dgmgrl -silent /  "</span>show configuration<span class="hljs-string">" | grep -i status | awk 'END{print}'  | sed 's/^[[:space:]]*//' | awk '{printf <span class="hljs-variable">$1</span>}'`

SUMMARY_DATAGUARD=`
   dgmgrl -silent /  "</span>show database <span class="hljs-string">'${PRY}'</span> <span class="hljs-string">'StatusReport'</span><span class="hljs-string">" | grep ORA | awk -F"</span>ERROR|WARNING<span class="hljs-string">" '{ print <span class="hljs-variable">$2</span> }' | \
   awk -v primary="</span><span class="hljs-variable">$</span>{PRY}<span class="hljs-string">" '
   {
            printf "</span>Primary database: <span class="hljs-string">" primary "</span>\n<span class="hljs-string">"
            printf "</span>Errores encontrados:<span class="hljs-string">"
            if ( <span class="hljs-variable">$0</span> != "</span><span class="hljs-string">") {
             printf <span class="hljs-variable">$0</span>
            } else {
             printf "</span>sin errores<span class="hljs-string">"
            }
            printf "</span>\n\n<span class="hljs-string">"
   }
' &amp;&amp;
   dgmgrl -silent /  "</span>show database <span class="hljs-string">'${STB}'</span> <span class="hljs-string">'StatusReport'</span><span class="hljs-string">" | grep ORA | awk -F"</span>ERROR|WARNING<span class="hljs-string">" '{ print <span class="hljs-variable">$2</span> }' | \
   awk -v standby="</span><span class="hljs-variable">$</span>{STB}<span class="hljs-string">" '
   {
            printf "</span>Standby database: <span class="hljs-string">" standby "</span>\n<span class="hljs-string">"
            printf "</span>Errores encontrados:<span class="hljs-string">"
            if ( <span class="hljs-variable">$0</span> != "</span><span class="hljs-string">") {
             printf <span class="hljs-variable">$0</span>
            } else {
             printf "</span>sin errores<span class="hljs-string">"
            }

   }
'`

if [[ ! "</span><span class="hljs-variable">$</span>{STATUS}<span class="hljs-string">" =~ "</span>SUCCESS<span class="hljs-string">" ]]; then
printf "</span>Resumen de errores Dataguard\n\nFecha de Inicio=<span class="hljs-variable">$</span>(date <span class="hljs-string">'+%d-%m-%Y %H:%M:%S'</span>)\n\nStatus=<span class="hljs-variable">$</span>{STATUS}\n\n<span class="hljs-variable">$</span>{SUMMARY_DATAGUARD}\n\n<span class="hljs-variable">$</span>{LAG}<span class="hljs-string">" | mailx -s "</span>[<span class="hljs-variable">$</span>{<span class="hljs-type">ORACLE_UNQNAME</span>}] Summary Dataguard<span class="hljs-string">" <span class="hljs-variable">$</span>{EMAIL}
fi</span>
</code></pre>
<p>Descarga desde github:</p>
<p><a target="_blank" href="https://github.com/dbaenlasombra/MonitoreDataguard/">https://github.com/dbaenlasombra/MonitoreDataguard/</a></p>
<p>Ahora solo faltaría planificarlo en cron. En mi caso es diario y cada media hora.</p>
<pre><code class="lang-powershell">[<span class="hljs-type">root</span><span class="hljs-selector-tag">@</span> <span class="hljs-type">scripts</span>]<span class="hljs-comment"># crontab -l</span>
*/<span class="hljs-number">30</span> * * * * /root/scripts/check_backups.sh
</code></pre>
<p>Listo. ¡Espero que os sirva!</p>
]]></content:encoded></item><item><title><![CDATA[[23ai] SQL*Plus]]></title><description><![CDATA[Today, we’re going to speak about SQL*Plus and it’s new features in 23ai such as config, argument, ping and so on. Everyone really know SQLPlus command-line tool remains the go-to for many DBA in order to iron out every issue in their environments.
S...]]></description><link>https://dbaenlasombra.com/23ai-sqlplus</link><guid isPermaLink="true">https://dbaenlasombra.com/23ai-sqlplus</guid><category><![CDATA[23ai]]></category><category><![CDATA[Oracle 23ai]]></category><category><![CDATA[Oracle]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Sun, 11 May 2025 21:38:04 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1746606687180/b6cffcc2-500e-45cc-9764-b3c04d2a03e1.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today, we’re going to speak about SQL*Plus and it’s new features in 23ai such as config, argument, ping and so on. Everyone really know SQLPlus command-line tool remains the go-to for many DBA in order to iron out every issue in their environments.</p>
<p>So, let's get down to business!</p>
<h3 id="heading-argument">ARGUMENT</h3>
<p>Previous to 23ai, when we work with substitution variables in the command-line, these can be set explicitly in SQL*Plus, or passed as arguments our scripts. But from 23ai onwards, we can do it with the new command: <strong>argument</strong>.</p>
<p>The sintax is the following:</p>
<p><em>ARGUMENT</em> &lt;VALUE_NUMERIC&gt; [<em>PROMPT/DEFAULT</em>] [<em>HIDE</em>]</p>
<p>Let’s doing a wee bit example in order to see the differences of every option.</p>
<ul>
<li><strong>prompt</strong>.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; ! vi argument.sql
SQL&gt; ! cat argument.sql
set verify off 
argument 1 prompt "Enter the acount status in order to check:"
Select Username From Dba_Users Where Account_Status='&amp;1';
undefine 1
SQL&gt; @argument.sql
Enter the acount status in order to check:LOCKED

USERNAME
--------------------------------------------------------------------------------
XS$NULL
LBACSYS
OUTLN
DBSNMP
APPQOSSYS
VECSYS
DBSFWUSER
GGSYS
ANONYMOUS
FLOWS_FILES
</code></pre>
<p>As we can see, our script is straightforward enough. The first line is in order to not display old and new values and next line is the new command. Here it sees the value that we’ve typed in the command-line.</p>
<ul>
<li><strong>default</strong>.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; ! vi argument.sql
SQL&gt; ! cat argument.sql
set verify off 
argument 1 default "OPEN"
Select Username From Dba_Users Where Account_Status='&amp;1';
undefine 1

SQL&gt; @argument.sql

USERNAME
--------------------------------------------------------------------------------
SYS
SYSTEM
APEX_PUBLIC_USER
APEX_PUBLIC_ROUTER
AV
PDBADMIN
HR
SYSRAC
ORDS_PUBLIC_USER
ORDS_METADATA
</code></pre>
<p>Here it doesn’t display any prompt because we’ve assigned a default value for our variable “<em>argument 1 default "OPEN"</em>“</p>
<ul>
<li><strong>hide</strong>.</li>
</ul>
<pre><code class="lang-plaintext">SQL&gt; ! vi argument.sql
SQL&gt; ! cat argument.sql
set verify off 
argument 1 prompt "Enter the acount status in order to check:" hide
Select Username From Dba_Users Where Account_Status='&amp;1';
undefine 1

SQL&gt; @argument.sql
Enter the acount status in order to check:

USERNAME
--------------------------------------------------------------------------------
SYS
SYSTEM
APEX_PUBLIC_USER
APEX_PUBLIC_ROUTER
AV
PDBADMIN
HR
SYSRAC
ORDS_PUBLIC_USER
ORDS_METADATA
CO
</code></pre>
<p>As we can see, here it has had a prompt but no way in order to see the value typing. This would be the difference respect to prompt.</p>
<h3 id="heading-ping-or-p">PING <strong>or</strong> -P</h3>
<p>To check availability our database.</p>
<p>On one hand, from the command line, we can use <strong>-p</strong> in order to see quickly whether our database is up or down.</p>
<p>Let’s doing a wee bit example:</p>
<pre><code class="lang-plaintext">SQL&gt; ! sqlplus -p localhost/FREEPDB1
Attempting to contact: (DESCRIPTION=(CONNECT_DATA=(SERVICE_NAME=FREEPDB1))(ADDRESS=(PROTOCOL=tcp)(HOST=127.0.0.1)(PORT=1521)))
Ok (4704.178 msec)
</code></pre>
<p>On the other hand, we can use <strong>ping</strong> in order to check the current connection. Here it can use the comand with or without arguments.</p>
<p>Again, let’s doing a wee bit example:</p>
<pre><code class="lang-plaintext">SQL&gt; SQL&gt; conn hr/oracle
Connected.
SQL&gt; ping
Ok (0.267 msec)
SQL&gt; disconnect
Disconnected from Oracle Database 23ai Free Release 23.0.0.0.0 - Develop, Learn, and Run for Free
Version 23.6.0.24.10
SQL&gt; ping
SP2-0640: Not connected
</code></pre>
<p>Also, we can use <strong>ping</strong> with the alias in order to do ping the network listener:</p>
<pre><code class="lang-plaintext">
SQL&gt; ping FREEPDB1
Local Net Naming configuration file: /opt/oracle/product/23ai/dbhomeFree/network/admin/tnsnames.ora
Attempting to contact: (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = FREEPDB1)))
Ok (7.032 msec)
</code></pre>
<p>In a nutshell, we can say the functionality is like tnsping, don’t you?</p>
<h3 id="heading-errordetails"><strong>ERRORDETAILS</strong></h3>
<p>To displays us an url with information about the trouble. That page gives us information more detail about the mistake in order to try to iron out it.</p>
<p>The syntax is the following:</p>
<p><em>SET ERRORDETAILS</em> [ <em>ON / OFF / VERBOSE</em>]</p>
<p>The default value is <strong>ON</strong>.</p>
<p>Below you can see a wee bit example:</p>
<pre><code class="lang-plaintext">SQL&gt; Select * From pickle;
Select * From pickle
              *
ERROR at line 1:
ORA-00942: table or view "SYS"."PICKLE" does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/
SQL&gt; set errordetails off
SQL&gt; r
  1* Select * From pickle
Select * From pickle
              *
ERROR at line 1:
ORA-00942: table or view "SYS"."PICKLE" does not exist
SQL&gt; set errordetails verbose 
SQL&gt; r
  1* Select * From pickle
Select * From pickle
              *
ERROR at line 1:
ORA-00942: table or view "SYS"."PICKLE" does not exist
Help: https://docs.oracle.com/error-help/db/ora-00942/
Cause:       The specified table or view did not exist, or a synonym
       pointed to a table or view that did not exist.
       To find existing user tables and views, query the
       ALL_TABLES and ALL_VIEWS data dictionary views. Certain
       privileges may be required to access the table. If an
       application returned this message, then the table that the
       application tried to access did not exist in the database, or
       the application did not have access to it.
Action:    Check each of the following:
       - The spelling of the table or view name is correct.
       - The referenced table or view name does exist.
       - The synonym points to an existing table or view.

       If the table or view does exist, ensure that the correct access
       privileges are granted to the database user requiring access
       to the table. Otherwise, create the table.

       Also, if you are attempting to access a table or view in another
       schema, make sure that the correct schema is referenced and that
       access to the object is granted.
Params: 1) object_name: The table or view name specified as
            SCHEMA.OBJECT_NAME, if one is provided.
            Otherwise, it is blank.
</code></pre>
<p>We’re going to see the sequence that we’ve followed.</p>
<ul>
<li><p>First: As we know, the value of default is <strong><em>on</em></strong>. For this reason, we can see in the prompt the tag <em>help</em> with the url in order to get information about ORA error.</p>
</li>
<li><p>Second: we apply <strong><em>off</em></strong> for our parameter. In this case, we don’t see the tag <em>help</em>, we only see the ORA error.</p>
</li>
<li><p>Third: we apply <strong><em>verbose</em></strong> for our parameter. In this case, it displays us helpful message information about error.</p>
</li>
</ul>
<h3 id="heading-oerr"><strong>OERR</strong></h3>
<p>This command would display us helpful message information about error.</p>
<p>The syntax is the following:</p>
<p><em>OERR [ ERROR_CODE ]</em></p>
<p>Below you can see a wee bit example:</p>
<pre><code class="lang-plaintext">SQL&gt; OERR ORA-01422
Message: "exact fetch returned more than the requested number of rows %s"
Help: https://docs.oracle.com/error-help/db/ora-01422/
Cause: The cause is one of the following:
    1. A SELECT statement was executed in the exact fetch
       mode and returned more rows than requested.
    2. In PL/SQL, a SELECT INTO statement returned more than one row.
    3. In PL/SQL, a DML RETURNING INTO statement
       returned more than one row.
Action: Choose the action corresponding to the cause as numbered:
    1. Increase the number of rows requested to accommodate the
       number of rows returned;
       or omit the exact fetch mode on the fetch call.
    2. In PL/SQL, use a FOR loop to process the rows.
    3. In PL/SQL, use BULK COLLECT to return values into a table.
Params: 1) requested_rows: The number of requested rows.
</code></pre>
<h3 id="heading-show-connection"><strong>SHOW CONNECTION</strong></h3>
<p>This command would display us information about our tnsnames.ora.</p>
<p>The syntax is the following:</p>
<p><em>SHOW [ CONNECTION / CONN ] [ NETSERVICENAMES / NETS] SERVICE_NAME</em></p>
<p>Below you can see a wee bit example:</p>
<pre><code class="lang-plaintext">SQL&gt;
SQL&gt; show connection netservicenames FREEPDB1
Local Net Naming configuration file: /opt/oracle/product/23ai/dbhomeFree/network/admin/tnsnames.ora
FREEPDB1 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = FREEPDB1)))
SQL&gt; 
SQL&gt; show conn nets FREEPDB1
Local Net Naming configuration file: /opt/oracle/product/23ai/dbhomeFree/network/admin/tnsnames.ora
FREEPDB1 = (DESCRIPTION = (ADDRESS = (PROTOCOL = TCP)(HOST = localhost)(PORT = 1521)) (CONNECT_DATA = (SERVER = DEDICATED) (SERVICE_NAME = FREEPDB1)))
SQL&gt;
</code></pre>
<p>Looking forward to seeing you in the next article.</p>
]]></content:encoded></item><item><title><![CDATA[Monitore High-Frequency Statistics]]></title><description><![CDATA[Today, we’re going to see a little script in order to check in High-Frequency Statistics inside the database every day.
Before seeing the script, we’re going to talk about High-Frequency Statistics. This feature, who runs automatically in maintenance...]]></description><link>https://dbaenlasombra.com/monitore-high-frequency-statistics</link><guid isPermaLink="true">https://dbaenlasombra.com/monitore-high-frequency-statistics</guid><category><![CDATA[stadistics]]></category><category><![CDATA[Oracle]]></category><category><![CDATA[shell script]]></category><dc:creator><![CDATA[David Sanz]]></dc:creator><pubDate>Wed, 16 Apr 2025 07:20:51 GMT</pubDate><enclosure url="https://cdn.hashnode.com/res/hashnode/image/upload/v1744785554551/bc20b40f-3b6a-446e-bc95-4de5feee3b7d.jpeg" length="0" type="image/jpeg"/><content:encoded><![CDATA[<p>Today, we’re going to see a little script in order to check in High-Frequency Statistics inside the database every day.</p>
<p>Before seeing the script, we’re going to talk about High-Frequency Statistics. This feature, who runs automatically in maintenance windows, was born in 19c. By default, the high-frequency statistics collection occurs every 15 minutes.</p>
<p>Somehow this feature doesn’t replace the standard statistics collection job inside is a complement.</p>
<p>How can we set it?  We should set with the procedure <em>DBMS_STATS.SET_GLOBAL_PREFS</em>. in order to do such as switch on/switch off, change the execution interval or even maximum run time.</p>
<ul>
<li>Switch on/ Switch off (By default).</li>
</ul>
<pre><code class="lang-sql">EXEC DBMS_STATS.SET_GLOBAL_PREFS('AUTO_TASK_STATUS','ON');

EXEC DBMS_STATS.SET_GLOBAL_PREFS('AUTO_TASK_STATUS','OFF');
</code></pre>
<ul>
<li>Maximum run time (By default, it’s 3600)</li>
</ul>
<pre><code class="lang-sql">EXEC DBMS_STATS.SET_GLOBAL_PREFS('AUTO_TASK_MAX_RUN_TIME','3600');
</code></pre>
<ul>
<li>Change the execution interval (By default, it’s 900)</li>
</ul>
<pre><code class="lang-sql">EXEC DBMS_STATS.SET_GLOBAL_PREFS('AUTO_TASK_INTERVAL','900');
</code></pre>
<p>Below you can see the script together the email with the summary.</p>
<ul>
<li>Script:</li>
</ul>
<pre><code class="lang-sql"><span class="hljs-comment">#!/bin/bash</span>

. /home/oracle/.bashrc

getInfo() {
   sqlplus -s ${VUSER}/${VPASS}@${LOCAL_SID} &lt;&lt;EOF

   COL STATUS      FOR a11    HEAD 'STATUS'
   COL ORIGIN      FOR a20    HEAD 'ORIGIN'
   COL COMPLETED   FOR 99999  HEAD 'COMPLETED'
   COL FAILED      FOR 99999  HEAD 'FAILED'
   COL TIMEOUT     FOR 99999  HEAD 'TIMEOUT'
   COL INPROG      FOR 99999  HEAD 'INPROG'
   COL BEGIN_TIME  FOR a40    HEAD 'BEGIN_TIME'
   COL END_TIME    FOR a40    HEAD 'END_TIME'

   <span class="hljs-keyword">set</span> pagesize <span class="hljs-number">1000</span> linesize <span class="hljs-number">1000</span> <span class="hljs-keyword">heading</span> <span class="hljs-keyword">on</span> feedback <span class="hljs-keyword">off</span> echo <span class="hljs-keyword">off</span>
   <span class="hljs-keyword">set</span> <span class="hljs-keyword">space</span>  <span class="hljs-number">1</span> colsep <span class="hljs-string">' '</span> underline <span class="hljs-keyword">off</span>

   break <span class="hljs-keyword">on</span> ORIGIN
   <span class="hljs-keyword">compute</span> <span class="hljs-keyword">sum</span> <span class="hljs-keyword">of</span> COMPLETED <span class="hljs-keyword">on</span> ORIGIN
   <span class="hljs-keyword">compute</span> <span class="hljs-keyword">sum</span> <span class="hljs-keyword">of</span> <span class="hljs-keyword">FAILED</span> <span class="hljs-keyword">on</span> ORIGIN
   <span class="hljs-keyword">compute</span> <span class="hljs-keyword">sum</span> <span class="hljs-keyword">of</span> <span class="hljs-keyword">TIMEOUT</span> <span class="hljs-keyword">on</span> ORIGIN
   <span class="hljs-keyword">compute</span> <span class="hljs-keyword">sum</span> <span class="hljs-keyword">of</span> INPROG <span class="hljs-keyword">on</span> ORIGIN

   <span class="hljs-keyword">Select</span> Origin,
          <span class="hljs-keyword">Status</span>,
          Start_Time  <span class="hljs-keyword">As</span> Begin_Time,
          End_Time    <span class="hljs-keyword">As</span> End_Time,
          Completed,
          <span class="hljs-keyword">Failed</span>,
          Timed_Out   <span class="hljs-keyword">As</span> <span class="hljs-keyword">Timeout</span>,
          In_Progress <span class="hljs-keyword">As</span> Inprog
     <span class="hljs-keyword">From</span> Dba_Auto_Stat_Executions
    <span class="hljs-keyword">Where</span> Trunc(Start_Time) &gt;= (<span class="hljs-keyword">Sysdate</span> - <span class="hljs-number">7</span>)
    <span class="hljs-keyword">Order</span> <span class="hljs-keyword">By</span> Opid;
    spool off
    exit;
EOF
}

   (
   echo "To: ${EMAIL}"
   echo "Subject:<span class="hljs-keyword">Check</span> <span class="hljs-keyword">high</span>-frequency task<span class="hljs-string">"
   echo
   echo "</span>$(getInfo)<span class="hljs-string">"
   ) | mailx -t</span>
</code></pre>
<ul>
<li>Email</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1744787862163/1c25185a-3f63-4a29-bcb8-b324d3de447e.png" alt class="image--center mx-auto" /></p>
<p>Looking forward to seeing you in the next article.</p>
]]></content:encoded></item></channel></rss>