Published

P·1 · El inventario de verbos — preguntado al motor

Connect any source, model it as an ontology, transform it, and operationalize it, analytics, automation and machine learning, under one governed, self-hostable roof. --- Most teams stitch the...

P·1 · El inventario de verbos — preguntado al motor

Fecha de la medición: 2026-08-12. Sonda: scripts/bridge/p1-inventario-verbos.py,
corrida dentro del pod del puente contra spark 4.1.2-stackable26.7.0.

Fase P·1 de parser-al-motor-spec.md.

Nada se ejecutó. Todo va envuelto en EXPLAIN EXTENDED y con nombres de tabla
inexistentes a propósito: se lee el Parsed Logical Plan, anterior a la resolución.


1 · El titular

28 formas de sentencia  ·  planifica 28  ·  no planifica 0

El motor sabe planificarlas TODAS, incluidas las 23 que el editor rechaza hoy. La lista de cinco verbos de sql-parse.ts:383 no es una limitación del lenguaje ni del motor: es nuestra.


2 · Lo medido, entero

formanodo de comandoobjetos → privilegio declarado por el motor
selectProjectcat.sch.t → READ
select-joinProjecta → READ · b → READ
select-time-travelProjectcat.sch.t → READ
insert-valuesInsertIntoStatementtINSERT
insert-selectInsertIntoStatementdstINSERT · src → READ
insert-overwriteInsertIntoStatementdstINSERT,DELETE · src → READ
updateUpdateTabletUPDATE
deleteDeleteFromTabletDELETE
mergeMergeIntoTabledstUPDATE,INSERT · src → READ
truncateTruncateTable⚠️ el motor no se pronuncia
create-tableCreateTable⚠️ no se pronuncia
ctasCreateTableAsSelect⚠️ no se pronuncia · src → READ
create-or-replaceReplaceTable⚠️ no se pronuncia
drop-tableDropTable⚠️ no se pronuncia
alter-add-columnAddColumns⚠️ no se pronuncia
alter-drop-columnDropColumns⚠️ no se pronuncia
alter-rename-columnRenameColumn⚠️ no se pronuncia
alter-alter-columnAlterColumns⚠️ no se pronuncia
alter-rename-tableRenameTable⚠️ no se pronuncia
alter-set-propsSetTableProperties⚠️ no se pronuncia
alter-unset-propsUnsetTableProperties⚠️ no se pronuncia
create-viewCreateView⚠️ no se pronuncia · t → READ
drop-viewDropView⚠️ no se pronuncia
create-schemaCreateNamespace⚠️ no se pronuncia
drop-schemaDropNamespace⚠️ no se pronuncia
describeDescribeRelation⚠️ no se pronuncia
show-tablesShowTables⚠️ no se pronuncia
call-procedureCall⚠️ no se pronuncia

⚠️ Y una trampa que la sonda tuvo primero

La primera versión pintaba de READ los tres casos —declara lectura, anotación vacía y sin anotación— y el resultado parecía decir que Spark declara que un DROP TABLE es una lectura. No lo declara. Hay tres estados y hay que distinguirlos:

__required_write_privileges__=X   el motor DECLARA que exige X
anotación presente y vacía        declara que sólo lee
SIN anotación                     el motor NO DICE NADA   ← el DDL entero

Un instrumento que funde «no hay» con «no supe mirar» no mide: opina.


3 · ⭐⭐ Los dos ejes — y es lo que estructura todo P·5

Eje A · escrituras de DATOS — el privilegio lo regala el motor

Seis formas, y Spark declara el privilegio por relación, compuesto cuando toca y distinguiendo destino de fuente:

INSERT           INSERT
INSERT OVERWRITE INSERT,DELETE     ← compuesto
UPDATE           UPDATE
DELETE           DELETE
MERGE            UPDATE,INSERT     ← compuesto, y la fuente sólo READ

Eso es exactamente lo que locateDmlCommand intenta adivinar con una regex — y el motor lo dice sin que haya que adivinar nada. Aquí P·1 se reduce a traducir su vocabulario (INSERT/UPDATE/DELETE/READ) al nuestro (TABLE_WRITE_DATA, TABLE_READ_DATA…).

Eje B · DDL y metadata — el privilegio lo ponemos nosotros

22 formas donde el motor no se pronuncia. Pero da algo casi tan bueno: el nodo de comando, que es inequívoco (AddColumns, DropColumns, RenameTable, SetTableProperties, CreateTable, ReplaceTable, DropTable, CreateView, DropView, CreateNamespace, DropNamespace, TruncateTable, Call…).

⇒ El trabajo del eje B es una tabla nodo → privilegio, y buena parte ya existe: CATALOG_OPS cubre createTable, dropTable, createView, dropView, createNamespace, dropNamespace, updateTable.


4 · ⭐⭐⭐ El hallazgo que cambia el orden del trabajo

CATALOG_OPS no está indexado por verbo SQL: está indexado por operación del catálogo Iceberg (loadTable, updateTable, createTable…). Y un verbo SQL se convierte, al ejecutarse, en una o varias de esas operaciones contra la cara.

ALTER TABLE … ADD COLUMN YA ESTÁ GOBERNADO. Al ejecutarse acaba en un loadTable + updateTable contra /api/iceberg/v1, y la cara lo autoriza con TABLE_WRITE_DATA como cualquier otra escritura. Lo único que impide hacerlo es el gate de Node que lo rechaza antes de que llegue — la interceptación, otra vez.

No hay que construir gobernanza para los verbos nuevos. Hay que dejar de
interceptarlos antes de que lleguen a la que ya existe.

⚠️ Pero con un agujero que hay que anotar

La cara no distingue un commit que cambia FILAS de uno que cambia el ESQUEMA: los dos son updateTableTABLE_WRITE_DATA. Unity Catalog aquí pediría MODIFY o propiedad para un cambio de esquema, que es más severo que escribir datos.

⇒ Conceder TABLE_WRITE_DATA hoy implica poder añadir y quitar columnas. Es una decisión que ahora mismo está tomada por omisión, y conviene tomarla a propósito.


5 · La cuarta columna, que es la que muerde: reconciliar Index

Un verbo sin esto no es un verbo nuevo: es una mentira nueva. Medido: datasets.schema sólo se escribe al nacer la tabla (table-materialise.ts:211) o desde los caminos de ingesta. Ninguno es el editor.

VerboQué queda rancio en Index si no se reconcilia
ADD/DROP/RENAME/ALTER COLUMNdatasets.schema · column_count · dataset_schema_versions
INSERT · UPDATE · DELETE · MERGE · TRUNCATErow_count · last_committed_at
ALTER … RENAME TOdatasets.name — y con él toda resolución por nombre
DROP TABLEla fila del dataset (¿soft-delete de 7 d?) y sus versiones de esquema
CREATE TABLE · CTAS✅ ya cubierto por table-materialise
SET/UNSET TBLPROPERTIESdatasets.metadata — y tier, que se implementa así
CREATE/DROP VIEWlas columnas view_* del dataset
CREATE/DROP NAMESPACEla tabla schemas

El más peligroso no es el esquema: es RENAME TO. Si Index no se entera, el nombre lógico deja de casar con el físico y se rompe la resolución de esa tabla para todo el producto, no sólo para el editor.


5·bis · ⭐ El SEGUNDO verbo — y otro guardián con la condición caducada

Medido el 2026-08-12, en frío, tras cerrar N·5 y el primer verbo:

INSERT INTO dst SELECT * FROM src
  → WITH `src` AS (SELECT * FROM prod.w_abc.`src`) INSERT INTO dst SELECT * FROM src
INSERT OVERWRITE dst SELECT * FROM src
  → WITH `src` AS (SELECT * FROM prod.w_abc.`src`) INSERT OVERWRITE dst SELECT * FROM src

La fuente SE RESUELVE: le sale su CTE apuntando al namespace físico. Y el motivo que junction.ts da para dejar INSERT fuera es exactamente:

«Su fuente (SELECT … FROM …) no se resuelve: el camino de escritura cualifica el
destino pero no los nombres que lee, así que el motor recibiría un nombre lógico.»

Caducado. Era cierto antes del mecanismo de CTE; hoy el destino lo cualifica qualifyWriteTarget —que desde P·5 también reconoce ALTER TABLE— y la fuente la resuelve el prólogo. Es el mismo patrón que desbloqueó el ALTER: un guardián con su condición de retirada escrita en su propio mensaje, y el mundo movido debajo (§12·43 del sustrato-06).

⚠️ Sin verificar en vivo. Esto es una medición en frío del plan; falta el canary con control negativo antes de abrirlo. Pero dice que el segundo verbo es barato, y que el coste real de cada verbo nuevo no está en el motor: está en las tres listas que todavía hay que tocar (§6).


6 · Lo que P·1 deja decidido

  1. La riqueza de verbos no hay que construirla: hay que dejar de bloquearla. 28 de 28 planifican.
  2. El eje A sale gratis — el motor declara el privilegio, compuesto y por rol.
  3. El eje B es una tabla nodo → privilegio, y CATALOG_OPS ya cubre la mitad.
  4. El coste real por verbo es la reconciliación de Index, no la autorización.
  5. Hay una decisión de gobernanza pendiente: si un cambio de esquema debe exigir más que TABLE_WRITE_DATA. Hoy no lo exige, por omisión.

7 · Lo que P·1 NO cubre, y hay que medir aparte

  • Call (procedimientos de mantenimiento de Iceberg: rewrite_data_files, expire_snapshots) planifica — ⇒ B7 es alcanzable por la misma puerta. No medido: si el catálogo los admite tal cual.
  • El time travel (VERSION AS OF) planifica. Está en P2 del sustrato-06 como «producto que aún no existe»; el motor ya lo soporta.
  • La profundidad de coordenada: el corpus usa 3 segmentos. Con namespaces de 4 niveles medidos en ns-profundidad.ts, falta comprobar que el plan los segmenta igual.
  • describe / show-tables no nombran privilegio ni son escrituras — pero hoy el editor los deja pasar por otra vía (el SQL curado del catálogo). Hay que decidir si se unifican.