<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Mysqldump on inetshell</title><link>https://inet.sh/es/tags/mysqldump/</link><description>Recent content in Mysqldump on inetshell</description><generator>Hugo</generator><language>es</language><lastBuildDate>Sat, 26 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://inet.sh/es/tags/mysqldump/index.xml" rel="self" type="application/rss+xml"/><item><title>Importar dumps a Cloud SQL sin errores: DEFINER y SUPER (ERROR 1227) en MySQL, extensiones y owners en PostgreSQL</title><link>https://inet.sh/es/posts/gcp/cloud-sql-importar-dumps-definer-gtid-extensiones/</link><pubDate>Sat, 26 Sep 2026 00:00:00 +0000</pubDate><guid>https://inet.sh/es/posts/gcp/cloud-sql-importar-dumps-definer-gtid-extensiones/</guid><description>Un dump de mysqldump o pg_dump que funciona en cualquier servidor falla al importarlo en Cloud SQL: ERROR 1227 &amp;#39;you need the SUPER privilege&amp;#39; por los DEFINER y por SET @@GLOBAL.GTID_PURGED, &amp;#39;must be owner of extension plpgsql&amp;#39; y &amp;#39;role does not exist&amp;#39; en PostgreSQL. Los flags de exportación que Cloud SQL espera, qué hacer con los DEFINER (crear los usuarios, quitarlos o pasar a INVOKER), el sed que comenta los EXTENSION, el permiso del bucket y la importación con gcloud sql import sql.</description><content:encoded><![CDATA[<h2 id="en-corto">En corto</h2>
<p><strong>MySQL</strong>, en el servidor origen:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">mysqldump --databases app -h db.example -u dumper -p <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --hex-blob --single-transaction --set-gtid-purged<span class="o">=</span>OFF <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --default-character-set<span class="o">=</span>utf8mb4 --routines --triggers --events <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> sed -E <span class="s1">&#39;s/DEFINER=`[^`]+`@`[^`]+`//g&#39;</span> <span class="p">|</span> gzip &gt; app.sql.gz
</span></span></code></pre></td></tr></table>
</div>
</div><p><strong>PostgreSQL</strong>, en el servidor origen:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pg_dump app -h db.example -U dumper --format<span class="o">=</span>plain --no-owner --no-acl <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  <span class="p">|</span> sed -E <span class="s1">&#39;s/(DROP|CREATE|COMMENT ON) EXTENSION/-- \1 EXTENSION/g&#39;</span> <span class="p">|</span> gzip &gt; app.sql.gz
</span></span></code></pre></td></tr></table>
</div>
</div><p>Y en los dos casos:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span><span class="lnt">5
</span><span class="lnt">6
</span><span class="lnt">7
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl"><span class="nv">SA</span><span class="o">=</span><span class="k">$(</span>gcloud sql instances describe my-instance --format<span class="o">=</span><span class="s1">&#39;value(serviceAccountEmailAddress)&#39;</span><span class="k">)</span>
</span></span><span class="line"><span class="cl">gcloud storage buckets add-iam-policy-binding gs://my-dumps <span class="se">\
</span></span></span><span class="line"><span class="cl"><span class="se"></span>  --member<span class="o">=</span><span class="s2">&#34;serviceAccount:</span><span class="nv">$SA</span><span class="s2">&#34;</span> --role<span class="o">=</span>roles/storage.objectAdmin
</span></span><span class="line"><span class="cl">gcloud storage cp app.sql.gz gs://my-dumps/
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">gcloud sql import sql my-instance gs://my-dumps/app.sql.gz --database<span class="o">=</span>app                 <span class="c1"># MySQL</span>
</span></span><span class="line"><span class="cl">gcloud sql import sql my-instance gs://my-dumps/app.sql.gz --database<span class="o">=</span>app --user<span class="o">=</span>postgres  <span class="c1"># PostgreSQL</span>
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>gcloud sql import sql</code> acepta SQL plano o <code>.gz</code>: el formato <em>custom</em> de <code>pg_dump</code> va por <code>pg_restore</code> (abajo).</p>
<h2 id="los-errores-y-de-dónde-salen">Los errores y de dónde salen</h2>
<p>En Cloud SQL nadie tiene <code>SUPER</code> (MySQL) ni es superusuario (PostgreSQL); el administrador es <code>root@%</code> o <code>postgres</code>, miembro de <code>cloudsqlsuperuser</code>. Todo lo que un dump normal da por hecho de un servidor propio se rompe justo ahí:</p>
<table>
  <thead>
      <tr>
          <th>Error al importar</th>
          <th>Motor</th>
          <th>Causa</th>
          <th>Arreglo</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>ERROR 1227 (42000) at line N: Access denied; you need (at least one of) the SUPER privilege(s) for this operation</code> (línea de un <code>CREATE … VIEW</code>, <code>TRIGGER</code>, <code>PROCEDURE</code>, <code>FUNCTION</code> o <code>EVENT</code>)</td>
          <td>MySQL</td>
          <td><code>DEFINER=`user`@`host` </code> de un usuario que no existe en la instancia, o de <code>root@localhost</code>, que en Cloud SQL nunca existe. El dump no trae usuarios (y no debe traerlos)</td>
          <td>Crear esos usuarios antes de importar, o quitar los <code>DEFINER=</code> con el <code>sed</code></td>
      </tr>
      <tr>
          <td>El mismo <code>ERROR 1227</code> en las primeras líneas, en <code>SET @@GLOBAL.GTID_PURGED=…</code></td>
          <td>MySQL</td>
          <td>El origen tiene GTID y <code>mysqldump</code> escribe la posición, que solo <code>SUPER</code> puede fijar</td>
          <td><code>--set-gtid-purged=OFF</code></td>
      </tr>
      <tr>
          <td><code>ERROR 1449 (HY000): The user specified as a definer ('app'@'localhost') does not exist</code> al <strong>usar</strong> la vista, no al importar</td>
          <td>MySQL</td>
          <td>Dejaste un <code>DEFINER</code> de un usuario que no existe en la instancia</td>
          <td>Quitar el <code>DEFINER</code> o crear el usuario con ese host exacto</td>
      </tr>
      <tr>
          <td><code>ERROR: must be owner of extension plpgsql</code></td>
          <td>PostgreSQL</td>
          <td><code>COMMENT ON EXTENSION plpgsql</code> del dump: en Cloud SQL las extensiones pertenecen a <code>cloudsqladmin</code></td>
          <td>Comentar las líneas <code>EXTENSION</code> con el <code>sed</code></td>
      </tr>
      <tr>
          <td><code>ERROR: role &quot;app_owner&quot; does not exist</code></td>
          <td>PostgreSQL</td>
          <td><code>ALTER … OWNER TO</code> y <code>GRANT … TO</code> para roles del servidor viejo</td>
          <td><code>--no-owner --no-acl</code>, o crear los roles antes</td>
      </tr>
      <tr>
          <td><code>ERROR: permission denied for schema public</code> o <code>must be owner of …</code> a mitad del dump</td>
          <td>PostgreSQL</td>
          <td>Importaste con un <code>--user</code> que no es el dueño de lo que ya existe en la base</td>
          <td>Importa como <code>postgres</code> en una base vacía; reasigna después</td>
      </tr>
  </tbody>
</table>
<h2 id="mysql-qué-hacer-con-los-definer">MySQL: qué hacer con los DEFINER</h2>
<p>Primero mira cuántos y cuáles hay en el origen:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span><span class="lnt">4
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w"> </span><span class="k">DEFINER</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">VIEWS</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w"> </span><span class="k">DEFINER</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">TRIGGERS</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w"> </span><span class="k">DEFINER</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">ROUTINES</span><span class="p">;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w"></span><span class="k">SELECT</span><span class="w"> </span><span class="k">DISTINCT</span><span class="w"> </span><span class="k">DEFINER</span><span class="w"> </span><span class="k">FROM</span><span class="w"> </span><span class="n">INFORMATION_SCHEMA</span><span class="p">.</span><span class="n">EVENTS</span><span class="p">;</span><span class="w">
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Tres salidas, de más a menos fiel al original:</p>
<ol>
<li><strong>Crear los usuarios en la instancia antes de importar</strong>, con el mismo <code>user@host</code>: <code>gcloud sql users create app --host=% --instance=my-instance --password=…</code>. Es lo que recomienda la documentación, y mantiene las vistas y procedimientos exactamente como estaban. <code>root@localhost</code> es la excepción: no se puede crear en Cloud SQL, así que un <code>DEFINER</code> con ese valor hay que cambiarlo sí o sí.</li>
<li><strong>Quitar el <code>DEFINER</code></strong> con el <code>sed</code> de arriba: los objetos se crean con el usuario que importa como definer. Suficiente cuando los usuarios del origen no van a existir en Cloud SQL.</li>
<li><strong>Pasar a <code>SQL SECURITY INVOKER</code></strong> (<code>sed -E 's/SQL SECURITY DEFINER/SQL SECURITY INVOKER/g'</code> además del anterior): la vista o el procedimiento corre con los privilegios de quien lo llama y el definer deja de importar. Cambia el modelo de permisos; revisa que los usuarios de la aplicación puedan leer las tablas de debajo.</li>
</ol>
<p>El <code>sed</code> es deliberadamente amplio porque <code>DEFINER=</code> aparece también dentro de comentarios condicionales (<code>/*!50013 DEFINER=… SQL SECURITY DEFINER */</code>); borrar solo el <code>DEFINER=</code> deja el resto intacto y válido.</p>
<h2 id="mysql-los-flags-uno-por-uno">MySQL: los flags, uno por uno</h2>
<table>
  <thead>
      <tr>
          <th>Flag</th>
          <th>Por qué</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td><code>--hex-blob</code></td>
          <td>Columnas binarias en hexadecimal; sin él, un <code>BLOB</code> con bytes raros se corrompe al pasar por el juego de caracteres</td>
      </tr>
      <tr>
          <td><code>--single-transaction</code></td>
          <td>Snapshot consistente de InnoDB sin bloquear tablas; imprescindible en producción</td>
      </tr>
      <tr>
          <td><code>--set-gtid-purged=OFF</code></td>
          <td>No escribir <code>SET @@GLOBAL.GTID_PURGED</code>, que Cloud SQL no deja ejecutar</td>
      </tr>
      <tr>
          <td><code>--default-character-set=utf8mb4</code></td>
          <td>Que el dump y el <code>SET NAMES</code> que trae coincidan con lo que hay en las tablas; evita el clásico <code>Ã©</code></td>
      </tr>
      <tr>
          <td><code>--databases app</code></td>
          <td>Obligatorio según la documentación, y nunca con la base <code>mysql</code>. Añade <code>CREATE DATABASE IF NOT EXISTS</code> y <code>USE app</code>, así <code>--database</code> en la importación es opcional</td>
      </tr>
      <tr>
          <td><code>--routines --triggers --events</code></td>
          <td><code>mysqldump</code> solo incluye triggers por defecto; procedimientos, funciones y eventos hay que pedirlos. Todos llevan <code>DEFINER</code></td>
      </tr>
  </tbody>
</table>
<p>Si el usuario con el que haces el dump no tiene <code>PROCESS</code>, <code>mysqldump</code> 8.0.21+ falla al inicio con <em>&ldquo;Access denied; you need (at least one of) the PROCESS privilege(s)&rdquo;</em> al consultar tablespaces: añade <code>--no-tablespaces</code>.</p>
<h2 id="postgresql-plano-o-custom">PostgreSQL: plano o custom</h2>
<p><code>--no-owner --no-acl</code> hacen que todo lo importado pertenezca al <code>--user</code> y no haya <code>GRANT</code> a roles fantasma. Si la aplicación se conecta con otro rol, crea el rol antes y usa <code>--user=app</code>, o reasigna después:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-sql" data-lang="sql"><span class="line"><span class="cl"><span class="n">REASSIGN</span><span class="w"> </span><span class="n">OWNED</span><span class="w"> </span><span class="k">BY</span><span class="w"> </span><span class="n">postgres</span><span class="w"> </span><span class="k">TO</span><span class="w"> </span><span class="n">app</span><span class="p">;</span><span class="w">   </span><span class="c1">-- conectado como postgres, en la base importada
</span></span></span></code></pre></td></tr></table>
</div>
</div><p>Las extensiones que necesite la base (<code>pg_trgm</code>, <code>postgis</code>, <code>uuid-ossp</code>…) créalas <strong>antes</strong> de importar, como <code>postgres</code>, en la base destino: <code>CREATE EXTENSION IF NOT EXISTS pg_trgm;</code>. Solo funcionan las de la lista de extensiones soportadas de Cloud SQL; <code>plpgsql</code> ya está. Con eso, comentar los <code>CREATE EXTENSION</code> del dump no quita nada.</p>
<p>Si prefieres el formato <em>custom</em> (<code>-Fc</code>), el filtro de extensiones va en la lista de contenidos de <code>pg_restore</code>, tal como lo documenta Google, y restauras a través del Cloud SQL Auth Proxy:</p>
<div class="highlight"><div class="chroma">
<table class="lntable"><tr><td class="lntd">
<pre tabindex="0" class="chroma"><code><span class="lnt">1
</span><span class="lnt">2
</span><span class="lnt">3
</span></code></pre></td>
<td class="lntd">
<pre tabindex="0" class="chroma"><code class="language-shell" data-lang="shell"><span class="line"><span class="cl">pg_dump app -h db.example -U dumper --format<span class="o">=</span>custom --no-owner --no-acl &gt; app.dmp
</span></span><span class="line"><span class="cl">pg_restore --list app.dmp <span class="p">|</span> sed -E <span class="s1">&#39;s/(.*EXTENSION )/; \1/g&#39;</span> &gt; app.toc
</span></span><span class="line"><span class="cl">pg_restore --use-list<span class="o">=</span>app.toc --no-owner --no-acl -j <span class="m">4</span> -h 127.0.0.1 -U postgres -d app app.dmp
</span></span></code></pre></td></tr></table>
</div>
</div><p><code>-j 4</code> restaura en paralelo, cosa que la importación desde el bucket no hace.</p>
<h2 id="trampas">Trampas</h2>
<ul>
<li><strong>El rol del bucket va a la service account de la instancia</strong>, no a la tuya: la documentación pide <code>storage.objectAdmin</code> (para importar bastan <code>storage.objects.get</code> y <code>list</code>, es decir, <code>objectViewer</code>). Sin él, <em>&ldquo;The service account does not have the required permissions for the bucket&rdquo;</em> aunque tú puedas leer el objeto.</li>
<li><strong>Una operación a la vez por instancia</strong>: si hay un backup o export en marcha, la importación falla con <em>&ldquo;operation already in progress&rdquo;</em>. <code>gcloud sql operations list --instance=my-instance</code> lo muestra.</li>
<li><strong>Dumps grandes</strong>: la importación es en serie y tarda; lanza con <code>--async</code> y vigila con <code>gcloud sql operations wait OPERATION_ID --timeout=unlimited</code>, o el <code>gcloud</code> se te muere por el camino.</li>
<li><strong>Versión de <code>pg_dump</code></strong>: usa la misma versión mayor que el destino o inferior. <code>pg_dump</code> 17 escribe <code>SET transaction_timeout = 0;</code>, que un PostgreSQL 16 rechaza con <em>&ldquo;unrecognized configuration parameter&rdquo;</em> y aborta la importación.</li>
<li><strong><code>utf8mb4</code> en la instancia</strong>: <code>--default-character-set=utf8mb4</code> en el dump no arregla una instancia creada con <code>latin1</code>; comprueba <code>character_set_server</code> en los flags de la instancia antes de importar.</li>
<li><strong>El <code>sed</code> de DEFINER también toca datos</strong>: si alguna fila contiene literalmente <code>DEFINER=`x`@`y` </code> (raro, pero un CMS que guarda SQL puede), sáltala con un patrón anclado a <code>CREATE</code>, o revisa el diff del dump.</li>
</ul>
<h2 id="ver-también">Ver también</h2>
<ul>
<li><a href="/es/posts/databases/postgresql-cheat-sheet-administracion/">PostgreSQL: cheat sheet de administración</a> — roles, <code>pg_dump</code>, <code>REASSIGN OWNED</code> y compañía.</li>
<li><a href="https://docs.cloud.google.com/sql/docs/mysql/import-export/import-export-sql">Import and export using SQL dump files (MySQL)</a>, <a href="https://docs.cloud.google.com/sql/docs/mysql/import-export#definer-clause">DEFINER clause</a> y <a href="https://docs.cloud.google.com/sql/docs/postgres/import-export/import-export-dmp">pg_dump / pg_restore (PostgreSQL)</a> en la documentación de Cloud SQL.</li>
</ul>
]]></content:encoded></item></channel></rss>