Docs / Reference

Diferencias entre adapters de source

Por qué existe esta página

Looky soporta tres adapters de source: BigQuery, Postgres y MySQL. La mayor parte de la plataforma se comporta idéntica en los tres. Un puñado de áreas se comportan distinto — esas son las que tropiezan a autores de model y dashboard. Esta página lista cada una en términos observables (lo que ves, no cómo está implementado).

Declaración de source

BigQuery

  • Requeridos: type: bigquery, project_id (el proyecto de GCP que paga las queries), credentials_file (filename plano del JSON de service account dentro de secrets/ — sin path).
  • Opcionales: location para datasets multi-region, datasets (requeridos en tiempo de introspección de schema).

Postgres

  • Requeridos: type: postgres, dsn (URI libpq sin user:password@; lleva host, port, database, y cualquier flag de query-string libpq como sslmode=require, application_name, connect_timeout, …), credentials_file (filename de un JSON en secrets/ con {"user", "password"}).
  • Opcionales: name (label cosmético), schemas (limita el scope de introspección; default es todos los schemas no-sistema).

MySQL

  • Requeridos: type: mysql, dsn (URI mysql://host:port/database sin user:password@ y sin parámetros ?query — el port default a 3306), credentials_file (filename de un JSON en secrets/ con {"user", "password"}).
  • Opcionales: name (label cosmético), schemas (databases a introspeccionar; default es la database nombrada en el dsn).
  • Nota: las conexiones MySQL no van encriptadas — todavía no hay opción TLS, así que usa MySQL sobre una red de confianza.

Mira Sources para ejemplos funcionando por adapter.

Introspección de schema

  • BigQuery — requiere una lista explícita de datasets. Sin eso, la introspección no puede encontrar tablas.
  • Postgres — descubre tablas entre los schemas en tu lista schemas (o el smart default de cada schema no-sistema). No hace falta declaración de table-list.
  • MySQL — descubre tablas en la database nombrada en el dsn (o las databases en tu lista schemas). Las databases de sistema (mysql, performance_schema, sys, information_schema) quedan siempre excluidas. No hace falta declaración de table-list.

Los mensajes de error en fallas de introspección se ven completamente distintos por adapter — cuando estés en duda, corre primero una pequeña query de test para confirmar que Looky puede llegar al source.

Parameter binding

Parámetros string, numéricos y array

Se comportan idéntico en los tres adapters. No hace falta manejo especial.

Parámetros date y timestamp

  • BigQuery — bindean nativos, sin trabajo extra.
  • Postgres y MySQL — el binding nativo falla con un error explícito en algunos shapes de query. Agrega un placeholder @param en el SQL subyacente de ese source así Looky sustituye el valor dentro del string SQL. Mira Soporte de Malloy para un ejemplo trabajado.

Tipos de filtro que producen parámetros date / timestamp: cutoff_date, date_range, date_range_preset. Planea para el patrón @param cuando esos filtros manejen un model con backing en Postgres o MySQL.

Valores NULL de parámetro

Cada adapter escribe un null tipado distinto: BigQuery y MySQL requieren un cast explícito, Postgres usa un null tipado pelado. Looky maneja esto por ti — no hay nada que configurar — pero es la razón por la que un parámetro nullable malformado puede fallar en un adapter y silenciosamente funcionar en otro.

Valores boolean (solo MySQL)

MySQL no tiene un tipo boolean real — las columnas que parecen boolean vuelven como números (0/1). Para filtrar sobre una condición true/false, agrega un cast(… as boolean) explícito en el model. BigQuery y Postgres tienen booleans nativos y no necesitan ese cast.

Ejecución de queries

  • BigQuery — expone una estimación de costo pre-flight gratis (en bytes escaneados) antes del run real. Útil para planear contra queries scan-heavy.
  • Postgres y MySQL — pre-flight valida el SQL plan (vía EXPLAIN) pero no estima costo.

Filtros y cross-filtering

El resolver de filtros y el mecanismo de pill de cross-filter son agnósticos del adapter. Las diferencias solo aparecen en el paso de parameter-binding descrito arriba.

  • Un filtro select se comporta idéntico en los tres.
  • Los filtros de fecha aterrizan en la caveat de parámetro temporal para Postgres y MySQL — arregla del lado del model con @param.
  • Los valores de pill de Cross-filtering son típicamente strings o identifiers cortos, así que no se afectan.

Paginación (grid & report_matrix)

La paginación server-side usa el mismo protocolo en los tres adapters. Las características de costo difieren:

  • BigQuery — cada página sin cache es un scan facturado. Page size más grande + caching es más barato en total. Evita grids sin bound en queries sin cache.
  • Postgres y MySQL — la latencia roundtrip de conexión domina; el costo depende mayormente del performance del indexed-scan de las tablas subyacentes. Asegúrate que las columnas referenciadas en order by tengan índices apropiados.

Recomendaciones de cache TTL

La lógica del cache en sí es la misma en los tres adapters. La elección de TTL es editorial:

  • BigQuery — favorece TTLs más largos para queries que escanean tablas grandes. Un TTL de 30 minutos en un dataset de batch diario es overkill; un TTL de 24 horas usualmente está bien.
  • Postgres y MySQL — el TTL es mayormente sobre freshness en vez de costo. El caching ayuda menos si la tabla subyacente es chica y bien indexada; considera si el costo ahorrado supera el staleness introducido.

Agregaciones y features de dialecto

Looky no se ramifica en adapter para funciones agregadas o windowing. Cualquier cosa específica de dialecto (funciones ARRAY solo de BigQuery, date_trunc de Postgres en ciertos tipos, funciones de fecha solo de MySQL, etc.) aparece como un error de compilación de Malloy. El fix es del lado de Malloy — ajusta el model para usar un feature que todos los adapters de destino soportan, o protege el model contra el adapter equivocado.

Patrones de migración trabajados

Un filtro de fecha que funciona en BigQuery pero rompe en Postgres o MySQL

El model con binding nativo:

# works on BigQuery, fragile on Postgres / MySQL in some shapes
##! experimental.parameters

source: ec_orders(
  p_date_from::date is @2024-01-01,
  p_date_to::date   is @2024-12-31
) is ecommerce.table('bigquery-public-data.thelook_ecommerce.order_items') extend {

  view: revenue is {
    where:
      created_at::date >= p_date_from
      and created_at::date <= p_date_to
    aggregate:
      revenue is sum(sale_price)
  }
}

Cambia el source a sql() con placeholders para compatibilidad con Postgres / MySQL. El body SQL corre en el dialecto propio del source, así que el snippet de abajo es solo-Postgres — sus casts ::date son un error de sintaxis en MySQL y su SQL en idioma lowercase no está pensado para BigQuery. (El alias warehouse es el alias de conexión propio del workspace, no el nombre del adapter.)

# works on Postgres
##! experimental.parameters

source: orders(
  p_date_from::date is null,
  p_date_to::date   is null
) is warehouse.sql("""
  select *
  from orders
  where (@date_from::date is null or created_at::date >= @date_from::date)
    and (@date_to::date   is null or created_at::date <= @date_to::date)
""") extend {
  view: revenue is {
    aggregate:
      revenue is sum(sale_price)
  }
}

En MySQL aplica el mismo patrón, con dos reglas de dialecto: los casts se escriben CAST(… AS DATE) — el operador :: es solo de Postgres y en MySQL es un error de sintaxis — y lo que el dialecto extrae (una hora, una fecha truncada) conviene calcularlo como columna en el propio SQL:

# works on MySQL
##! experimental.parameters

source: orders(
  p_date_from::date is null,
  p_date_to::date   is null
) is shop.sql("""
  SELECT
    o.sale_price           AS sale_price,
    o.created_at           AS created_at,
    HOUR(o.created_at)     AS created_hour
  FROM orders o
  WHERE (CAST(@date_from AS DATE) IS NULL OR DATE(o.created_at) >= CAST(@date_from AS DATE))
    AND (CAST(@date_to   AS DATE) IS NULL OR DATE(o.created_at) <= CAST(@date_to   AS DATE))
""") extend {
  dimension: created_hour_of_day is created_hour

  view: revenue is {
    aggregate:
      revenue is sum(sale_price)
  }
}

La columna extra created_hour muestra la regla de "calcula las extracciones en el SQL" en acción: HOUR() corre en MySQL, y la capa Malloy consume la columna ya lista en vez de pedirle al dialecto una extracción que quizás no soporta.

Cada placeholder @param tiene que tener una declaración matcheante en la signature del source; Looky sustituye el valor (o NULL tipado cuando está unset) antes del compile. Los paréntesis del source son obligatorios en todo modelo — () cuando no hay parámetros — y los valores date_from / date_to de un filtro date_range matchean automáticamente con los p_date_from / p_date_to declarados.

Un grid que pagina bien en Postgres / MySQL pero hace explotar el scan budget de BigQuery

Sube pagination.page_size, agrega un cache sidecar con TTL largo, y pre-agrega cuando sea posible. En BigQuery, paginar a través de millones de filas sin cache es caro — pre-resume.

Quick reference

  • Auth de source — BigQuery: archivo de service account. Postgres / MySQL: connection string + un secret {"user","password"}.
  • Transporte — BigQuery / Postgres: TLS disponible. MySQL: todavía sin encriptar.
  • Introspección — BigQuery: datasets explícitos requeridos. Postgres: schemas smart-default. MySQL: la database del DSN (o una lista schemas).
  • Parámetros numéricos / string — idéntico.
  • Parámetros date / timestamp — BigQuery: nativo. Postgres / MySQL: necesitan placeholder @param en SQL.
  • Booleans — nativos en BigQuery / Postgres. MySQL: numéricos — castea explícitamente.
  • Parámetros NULL — manejados automáticamente; sin configuración.
  • Pre-flight — BigQuery: estimación de costo gratis. Postgres / MySQL: chequeo de SQL plan.
  • Filtros & routing de cross-filter — idéntico.
  • Protocolo de paginación — idéntico; las características de costo difieren.
  • Agregaciones / features de dialecto — delegado enteramente a Malloy; sin ramificaciones del lado de Looky.