Adaptador SQL sobre Apache Ossie
Saiku entrega un adaptador SQL de Calcite que lee YAML de Apache Ossie — el mismo formato que el exportador de Ossie produce a partir de su schema Mondrian — y lo expone como una superficie JDBC consultable. Apunte cualquier cliente SQL a él y podrá hacer SELECT desde sus datasets Ossie exactamente como si fueran tablas reales de base de datos. Por debajo, Calcite planifica la consulta y la empuja a su warehouse real como SQL nativo.
Qué obtiene
- SQL estándar sobre sus datasets Ossie — cualquier cosa que Calcite entienda, que es un gran superconjunto de ANSI SQL.
- Pushdown al warehouse. Calcite lee la consulta, la planifica contra su warehouse (Postgres, Snowflake, H2, lo que sea), y emite SQL nativo. Los agregados corren en el warehouse, no en la JVM.
- Nativo de JDBC. Funciona con cualquier cliente JDBC: dbt, DBeaver, Tableau, Power BI,
psql, JetBrains DataGrip, código personalizado. - Los renombrados de Ossie sobreviven. Si su exportador emitió nombres de dataset que no coinciden con las tablas subyacentes del warehouse (mediante el campo
sourcede Ossie), el adaptador mapea de vuelta de forma transparente —SELECT * FROM CUSTOMERSse convierte enSELECT * FROM public.dim_customer_v2en el warehouse.
Inicio rápido
1. Produzca el YAML Ossie
saiku ossie-export --in saiku-home/data/Pharma.xml --out pharma.ossie.yamlVea Exportar a Apache Ossie para la tabla de mapeo y el ejemplo trabajado.
2. Escriba un modelo de conexión Calcite
Las conexiones JDBC de Calcite toman un modelo de conexión JSON que le dice a Calcite qué factory instanciar:
{ "version": "1.0", "defaultSchema": "PHARMA", "schemas": [{ "name": "PHARMA", "type": "custom", "factory": "org.saiku.sql.adapter.OssieSchemaFactory", "operand": { "ossieYaml": "/absolute/path/to/pharma.ossie.yaml", "jdbcUrl": "jdbc:postgresql://localhost:5432/warehouse", "jdbcUser": "app", "jdbcPassword": "changeme" } }]}Claves del operand:
| Clave | Requerida | Descripción |
|---|---|---|
ossieYaml | sí | Ruta absoluta al archivo YAML Ossie. |
modelName | no | Nombre de la entrada semantic_model[] a exponer cuando el documento lleva varias. Por defecto la primera. |
jdbcUrl | muy recomendada | URL JDBC del warehouse. Sin ella, las tablas se registran pero las consultas devuelven cero filas. |
jdbcUser, jdbcPassword | según sea necesario | Credenciales del warehouse. |
3. Conecte
Properties p = new Properties();p.put("model", "/path/to/model.json");p.put("caseSensitive", "false");try (Connection c = DriverManager.getConnection("jdbc:calcite:", p)) { var rs = c.createStatement().executeQuery( "SELECT REGION, COUNT(*) FROM PHARMA.PRESCRIBER GROUP BY REGION"); while (rs.next()) System.out.println(rs.getString(1) + " → " + rs.getInt(2));}O desde cualquier cliente JDBC — URL de conexión jdbc:calcite:model=/path/to/model.json.
4. O ejecútelo como un endpoint de red
Para clientes remotos, use el subcomando CLI saiku sql-serve. Expone dos endpoints — un endpoint Apache Avatica para clientes conscientes de Avatica Y un endpoint nativo del protocolo de cable de Postgres para psql / pgAdmin / Tableau / DBeaver / dbt-postgres. Active uno o ambos:
saiku sql-serve \ --ossie pharma.ossie.yaml \ --schema PHARMA \ --jdbc-url jdbc:postgresql://warehouse:5432/prod \ --jdbc-user app --jdbc-password changeme \ --port 8765 \ --pg-port 5432El servidor registra ambas URLs al arranque. Los clientes entonces se conectan mediante:
Avatica:
jdbc:avatica:remote:url=http://localhost:8765# with serialization=protobufCable de Postgres (cualquier cliente PG nativo):
# psqlPGSSLMODE=disable psql -h localhost -p 5432 saiku
# JDBC — pgjdbc defaults (extended query mode) work; simple mode is also finejdbc:postgresql://localhost:5432/saiku?sslmode=disableLas consultas parametrizadas mediante PreparedStatement.setString(1, ...) etc. funcionan de fábrica.
Qué funciona hoy
| Característica SQL | Estado | Notas |
|---|---|---|
SELECT desde un dataset | ✅ | Empujado al warehouse. |
WHERE en columnas del dataset | ✅ | Empujado. |
GROUP BY + agregados | ✅ | Los agregados corren en el warehouse. |
JOIN … ON explícito entre datasets | ✅ | El predicado del join se empuja. |
ORDER BY, LIMIT | ✅ | Empujado donde el dialecto del warehouse lo soporta. |
information_schema / DatabaseMetaData.getTables() | ✅ | Datasets, métricas Y vistas de join descubribles por herramientas BI. |
Mapeo de renombrado (nombre de dataset Ossie → tabla del warehouse vía source) | ✅ | Transparente. |
| Fallback insensible a mayúsculas (H2 UPPER vs Postgres lower) | ✅ | El adaptador prueba todos los casos. |
SELECT de métrica escalar — SELECT * FROM PHARMA.TOTAL_QUANTITY | ✅ | Expande el ANSI_SQL de la métrica contra su dataset de origen. Tipo de retorno derivado del tipo de la columna subyacente (sin conjeturas gruesas de DOUBLE/BIGINT). |
Vistas de join de relación — SELECT ... FROM PHARMA.FACT_PHARMA_JOIN_PRESCRIBER ... | ✅ | Una vista por relationship de Ossie, nombrada <from>_JOIN_<to>. El predicado del JOIN vive en el YAML, no en la consulta. Calcite empuja todo como un único JOIN — sin sobrecarga en tiempo de ejecución. |
Joins auto-inyectados — SELECT c.x, SUM(o.y) FROM ORDERS o, CUSTOMERS c GROUP BY c.x (sin cláusula JOIN) | ✅ | Una regla de planificador de Calcite personalizada detecta el join cartesiano entre dos datasets Ossie e inyecta el predicado ON a partir de la relación. Mismo resultado que escribir el JOIN a mano. Vea “Joins auto-inyectados” abajo. |
| Métricas MDX-only (miembros calculados) | ✅ (invisible) | Correctamente no expuestas en la superficie SQL. Viven en Mondrian. |
Todavía no soportado
Rastreado en el epic Ossie/SQL:
- SSL/TLS + auth en ambos endpoints. Actualmente anónimo + texto plano. Requisito básico antes de cualquier despliegue de producción.
- Parámetros/resultados en formato binario en el endpoint de cable PG — Bind ahora decodifica INT2/INT4/INT8 big-endian correctamente (la mayoría de las llamadas
setInt/setLongde herramientas BI) pero la decodificación binaria completa dirigida por tipo (BOOL/DATE/NUMERIC/TIMESTAMP contra los OID de parámetro declarados de la sentencia) es un seguimiento. - Suspensión de portal — Execute siempre devuelve todas las filas independientemente de la petición
maxRowsdel cliente. Bien para consultas BI interactivas; importa para la paginación de cursores grandes. - Auto-joins de tres vías —
FROM A, B, Cdonde A↔B y B↔C ambos existen. Las reescrituras anidadas deberían encadenarse pero esto aún no se ha verificado en tests. - Joins entre schemas — unir un dataset Ossie con una tabla no-Ossie (de un sub-schema de Calcite diferente). La regla de auto-join se abstiene cuando los dos lados pertenecen a schemas diferentes.
- Esquema de conexión
jdbc:saiku://. Hoy los usuarios pasan porjdbc:calcite:+ un archivo model.json. Un driver JDBC de primera parte viene con el trabajo de cable de Postgres. - Protocolo de cable de Postgres. Hoy la superficie es solo JDBC.
jdbc:saiku:en el cable (para que los clientespsql/libpqconecten de forma nativa) es #1386.
Ejemplo trabajado — cubo Pharma
Dado el YAML Ossie de Pharma que produce el exportador:
version: 0.2.0.dev0semantic_model:- name: Pharma Rx datasets: - name: fact_pharma source: public.fact_pharma - name: Prescriber source: public.dim_prescriber primary_key: [prescriberkey] relationships: - name: fact_pharma_to_Prescriber from: fact_pharma to: Prescriber from_columns: [prescriberkey] to_columns: [prescriberkey]Puede consultarlo así:
-- Simple dataset scan.SELECT * FROM "Pharma Rx".fact_pharma LIMIT 10;
-- Aggregate — pushed down as SUM() to Postgres.SELECT SUM(quantity_units) AS total_units FROM "Pharma Rx".fact_pharma;
-- Scalar metric SELECT — same result as above, but the aggregate-- expression lives in the Ossie YAML instead of the query.SELECT * FROM "Pharma Rx"."Quantity";
-- Join via the relationship's foreign key.SELECT p.prescribername, SUM(f.quantity_units) AS units, COUNT(*) AS rx_countFROM "Pharma Rx".fact_pharma fJOIN "Pharma Rx"."Prescriber" p ON f.prescriberkey = p.prescriberkeyGROUP BY p.prescribernameORDER BY units DESCLIMIT 20;
-- Same query using the pre-materialised join view — the ON predicate-- lives in the Ossie YAML. Users don't need to remember which columns-- link fact_pharma to Prescriber.SELECT prescribername, SUM(quantity_units) AS units, COUNT(*) AS rx_countFROM "Pharma Rx".fact_pharma_JOIN_PrescriberGROUP BY prescribernameORDER BY units DESCLIMIT 20;Las cinco corren enteramente en Postgres mediante el pushdown JDBC de Calcite — la JVM nunca ve filas individuales para los agregados.
Joins auto-inyectados
Los usuarios no necesitan recordar qué columnas enlazan ORDERS con CUSTOMERS. Simplemente liste ambos datasets en FROM y Calcite inyectará el predicado ON por usted:
-- User writes:SELECT c.REGION, SUM(o.AMOUNT) AS TOTALFROM "Pharma Rx".fact_pharma o, "Pharma Rx"."Prescriber" cGROUP BY c.REGION;
-- The adapter's OssieAutoJoinRule detects the Cartesian join between two datasets-- in the same Ossie schema, looks up the relationship, and rewrites to:SELECT c.REGION, SUM(o.AMOUNT) AS TOTALFROM "Pharma Rx".fact_pharma o JOIN "Pharma Rx"."Prescriber" c ON o.prescriberkey = c.prescriberkeyGROUP BY c.REGION;La reescritura ocurre durante la fase de optimización de Calcite — todo sigue empujándose al warehouse como una única consulta JOIN.
Cuándo se dispara la regla
- El join debe ser cartesiano. Las cláusulas
JOIN … ON …explícitas nunca se sobrescriben — el usuario pidió un predicado específico y lo respetamos. - Todas las tablas deben ser datasets propiedad de Ossie en el mismo OssieSchema. Las tablas entre schemas y no-Ossie dejan la consulta sin cambios.
- Exactamente una
relationshipde Ossie debe enlazar cada par que se auto-une. Múltiples candidatos lanzanAmbiguousJoinExceptioncon la lista de nombres candidatos — el usuario debe añadir un ON explícito para elegir uno. Los resultados silenciosamente-incorrectos son el modo de fallo contra el que nos protegemos.
Soporte N-vías
FROM A, B, C, … (tres o más tablas en un único cartesiano) también se auto-une. La regla desciende a través de los Joins anidados para alcanzar cada TableScan cruda, luego construye una cadena de join left-deep fresca usando las relaciones que enlazan cada nueva tabla al conjunto ya unido. Las consultas a escala Pharma contra hechos + múltiples dims funcionan sin cláusulas JOIN.
Cuándo no se dispara
- Sin relación entre dos datasets que necesitarían enlazarse → el cartesiano permanece (probablemente no lo que el usuario quiere, pero honesto).
- Self-joins (
FROM A a, A b) → se dejan como cartesianos. - Joins entre schemas mezclando tablas Ossie con no-Ossie.
Vistas de join
Para cada relationship de Ossie, el adaptador registra una vista pre-materializada nombrada <from>_JOIN_<to>. El SQL de la vista es el JOIN de los dos datasets sobre los from_columns / to_columns de la relación, y su tipo de fila es la concatenación de las columnas de ambos datasets (con sufijos numéricos en las colisiones).
Dado este fragmento Ossie:
relationships:- name: fact_pharma_to_Prescriber from: fact_pharma to: Prescriber from_columns: [prescriberkey] to_columns: [prescriberkey]El adaptador expone PHARMA.fact_pharma_JOIN_Prescriber con la unión de las columnas de ambas tablas. Los usuarios escriben:
SELECT * FROM "Pharma Rx".fact_pharma_JOIN_Prescriber WHERE prescribername LIKE 'Dr%';y Calcite empuja SELECT * FROM fact_pharma JOIN dim_prescriber ON fact_pharma.prescriberkey = dim_prescriber.prescriberkey WHERE prescribername LIKE 'Dr%' — sin sobrecarga en tiempo de ejecución respecto a escribir el JOIN a mano.
Las relaciones multi-columna están soportadas: el predicado ON hace AND de cada par from_columns[i] = to_columns[i].
Relacionado
- Exportar a Apache Ossie — cómo producir el archivo YAML que este adaptador lee.
- Anotaciones semánticas de Saiku — las anotaciones que sobreviven al YAML Ossie y (en un corte futuro) impulsan la resolución de métrica/dimensión en tiempo de consulta.
- Framework de adaptadores de Apache Calcite — el planificador subyacente.
- Repositorio de Apache Ossie — especificación upstream.