Adapter SQL nad Apache Ossie
Saiku dostarcza adapter SQL Calcite, który czyta YAML Apache Ossie — ten sam format, który eksporter Ossie produkuje z twojej schemy Mondrian — i udostępnia go jako odpytywalną powierzchnię JDBC. Wskaż na nią dowolnego klienta SQL i możesz SELECT-ować ze swoich zbiorów danych Ossie dokładnie tak, jakby były prawdziwymi tabelami bazy. Pod maską Calcite planuje zapytanie i pcha je w dół do twojej faktycznej hurtowni jako natywny SQL.
Co dostajesz
- Standardowy SQL nad twoimi zbiorami danych Ossie — cokolwiek Calcite rozumie, co jest dużym nadzbiorem ANSI SQL.
- Pushdown do hurtowni. Calcite czyta zapytanie, planuje je względem twojej hurtowni (Postgres, Snowflake, H2, cokolwiek) i emituje natywny SQL. Agregaty działają w hurtowni, nie w JVM.
- Natywnie JDBC. Działa z dowolnym klientem JDBC: dbt, DBeaver, Tableau, Power BI,
psql, JetBrains DataGrip, własny kod. - Zmiany nazw Ossie przeżywają. Jeśli twój eksporter wyemitował nazwy zbiorów danych, które nie pasują do bazowych tabel hurtowni (przez pole
sourceOssie), adapter przezroczyście mapuje z powrotem —SELECT * FROM CUSTOMERSstaje sięSELECT * FROM public.dim_customer_v2w hurtowni.
Szybki start
1. Wyprodukuj YAML Ossie
saiku ossie-export --in saiku-home/data/Pharma.xml --out pharma.ossie.yamlZobacz Eksport do Apache Ossie po tabelę mapowania i przykład.
2. Napisz model połączenia Calcite
Połączenia JDBC Calcite przyjmują model połączenia JSON, który mówi Calcite, którą fabrykę zinstancjonować:
{ "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" } }]}Klucze operanda:
| Klucz | Wymagany | Opis |
|---|---|---|
ossieYaml | tak | Bezwzględna ścieżka do pliku YAML Ossie. |
modelName | nie | Nazwa wpisu semantic_model[] do udostępnienia, gdy dokument niesie kilka. Domyślnie pierwszy. |
jdbcUrl | mocno zalecany | URL JDBC hurtowni. Bez niego tabele rejestrują się, ale zapytania zwracają zero wierszy. |
jdbcUser, jdbcPassword | wedle potrzeb | Poświadczenia hurtowni. |
3. Połącz się
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));}Lub z dowolnego klienta JDBC — URL połączenia jdbc:calcite:model=/path/to/model.json.
4. Albo uruchom to jako endpoint sieciowy
Dla zdalnych klientów użyj podkomendy CLI saiku sql-serve. Udostępnia ona dwa endpointy — endpoint Apache Avatica dla klientów świadomych Avatiki ORAZ natywny endpoint na drucie Postgres dla psql / pgAdmin / Tableau / DBeaver / dbt-postgres. Włącz jeden albo oba:
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 5432Serwer loguje oba URL-e przy starcie. Klienci łączą się następnie przez:
Avatica:
jdbc:avatica:remote:url=http://localhost:8765# with serialization=protobufDrut Postgres (dowolny natywny klient PG):
# 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=disableZapytania parametryzowane przez PreparedStatement.setString(1, ...) itd. działają od ręki.
Co działa dziś
| Funkcja SQL | Status | Uwagi |
|---|---|---|
SELECT ze zbioru danych | ✅ | Pchane w dół do hurtowni. |
WHERE na kolumnach zbioru danych | ✅ | Pchane w dół. |
GROUP BY + agregaty | ✅ | Agregaty działają w hurtowni. |
Jawny JOIN … ON między zbiorami danych | ✅ | Predykat złączenia pcha się w dół. |
ORDER BY, LIMIT | ✅ | Pchane w dół tam, gdzie dialekt hurtowni to obsługuje. |
information_schema / DatabaseMetaData.getTables() | ✅ | Zbiory danych, metryki ORAZ widoki złączeń wykrywalne przez narzędzia BI. |
Mapowanie zmian nazw (nazwa zbioru danych Ossie → tabela hurtowni przez source) | ✅ | Przezroczyste. |
| Fallback bez rozróżniania wielkości liter (H2 UPPER vs Postgres lower) | ✅ | Adapter próbuje wszystkich wielkości. |
Skalarny SELECT metryki — SELECT * FROM PHARMA.TOTAL_QUANTITY | ✅ | Rozwija ANSI_SQL metryki względem jej macierzystego zbioru danych. Typ zwracany wyprowadzony z typu kolumny bazowej (brak zgrubnych zgadywań DOUBLE/BIGINT). |
Widoki złączeń relacji — SELECT ... FROM PHARMA.FACT_PHARMA_JOIN_PRESCRIBER ... | ✅ | Jeden widok na relationship Ossie, nazwany <from>_JOIN_<to>. Predykat JOIN żyje w YAML, nie w zapytaniu. Calcite pcha całość w dół jako pojedynczy JOIN — brak narzutu w runtime. |
Auto-wstrzykiwane złączenia — SELECT c.x, SUM(o.y) FROM ORDERS o, CUSTOMERS c GROUP BY c.x (bez klauzuli JOIN) | ✅ | Niestandardowa reguła planera Calcite wykrywa złączenie kartezjańskie między dwoma zbiorami danych Ossie i wstrzykuje predykat ON z relacji. Ten sam wynik co ręczne napisanie JOIN. Zobacz „Auto-wstrzykiwane złączenia” niżej. |
| Metryki tylko-MDX (członkowie obliczani) | ✅ (niewidoczne) | Poprawnie nieudostępniane na powierzchni SQL. Żyją w Mondrianie. |
Jeszcze nieobsługiwane
Śledzone na epicu Ossie/SQL:
- SSL/TLS + uwierzytelnianie na obu endpointach. Obecnie anonimowe + plaintext. Podstawa przed jakimkolwiek wdrożeniem produkcyjnym.
- Parametry/wyniki formatu binarnego na endpoincie na drucie PG — Bind teraz poprawnie dekoduje big-endian INT2/INT4/INT8 (większość wywołań
setInt/setLongnarzędzi BI), ale pełne dekodowanie binarne sterowane typem (BOOL/DATE/NUMERIC/TIMESTAMP względem zadeklarowanych OID-ów parametrów instrukcji) to kontynuacja. - Zawieszanie portala — Execute zawsze zwraca wszystkie wiersze niezależnie od żądania
maxRowsklienta. W porządku dla interaktywnych zapytań BI; ma znaczenie dla paginacji dużych kursorów. - Trójstronne auto-złączenia —
FROM A, B, C, gdzie A↔B i B↔C oba istnieją. Zagnieżdżone przepisania powinny kaskadować, ale nie zostało to jeszcze zweryfikowane w testach. - Złączenia międzyschemowe — łączenie zbioru danych Ossie z tabelą nie-Ossie (z innej podschemy Calcite). Reguła auto-złączenia rezygnuje, gdy obie strony należą do różnych schem.
- Schemat połączenia
jdbc:saiku://. Dziś użytkownicy przechodzą przezjdbc:calcite:+ plik model.json. Sterownik JDBC first-party przychodzi z pracą nad drutem Postgres. - Protokół na drucie Postgres. Dziś powierzchnia jest tylko-JDBC.
jdbc:saiku:na drucie (aby kliencipsql/libpqłączyli się natywnie) to #1386.
Przykład — kostka Pharma
Mając YAML Ossie Pharma, który eksporter produkuje:
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]Możesz odpytywać go tak:
-- 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;Wszystkie pięć działają całkowicie w Postgresie przez pushdown JDBC Calcite — JVM nigdy nie widzi pojedynczych wierszy dla agregatów.
Auto-wstrzykiwane złączenia
Użytkownicy nie muszą pamiętać, które kolumny łączą ORDERS z CUSTOMERS. Wystarczy wylistować oba zbiory danych w FROM, a Calcite wstrzyknie za ciebie predykat ON:
-- 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;Przepisanie zdarza się podczas fazy optymalizacji Calcite — całość nadal pcha się w dół do hurtowni jako pojedyncze zapytanie JOIN.
Kiedy reguła się uruchamia
- Złączenie musi być kartezjańskie. Jawne klauzule
JOIN … ON …nigdy nie są nadpisywane — użytkownik poprosił o konkretny predykat i to szanujemy. - Wszystkie tabele muszą być zbiorami danych będącymi własnością Ossie w tej samej OssieSchema. Tabele międzyschemowe i nie-Ossie pozostawiają zapytanie niezmienione.
- Dokładnie jedna
relationshipOssie musi łączyć każdą auto-łączoną parę. Wielu kandydatów podnosiAmbiguousJoinExceptionz listą nazw kandydatów — użytkownik musi dodać jawne ON, aby wybrać jednego. Cicho-błędne-wyniki to tryb awarii, przed którym się chronimy.
Wsparcie N-stronne
FROM A, B, C, … (trzy lub więcej tabel w jednym kartezjaniku) też się auto-łączy. Reguła schodzi w dół przez zagnieżdżone Joins, aby dosięgnąć każdego surowego TableScan, po czym buduje świeży lewostronny łańcuch złączeń używając relacji, które łączą każdą nową tabelę z już-złączonym zbiorem. Zapytania w skali Pharma względem faktu + wielu wymiarów działają bez klauzul JOIN.
Kiedy się nie uruchamia
- Brak relacji między dwoma zbiorami danych, które należałoby połączyć → kartezjanik pozostaje (prawdopodobnie nie to, czego chce użytkownik, ale uczciwie).
- Self-joins (
FROM A a, A b) → pozostawione jako kartezjanik. - Złączenia międzyschemowe mieszające tabele Ossie z nie-Ossie.
Widoki złączeń
Dla każdej relationship Ossie adapter rejestruje pre-zmaterializowany widok nazwany <from>_JOIN_<to>. SQL widoku to JOIN dwóch zbiorów danych na from_columns / to_columns relacji, a jego typ wiersza to konkatenacja kolumn obu zbiorów danych (z numerycznymi sufiksami przy kolizjach).
Mając ten fragment Ossie:
relationships:- name: fact_pharma_to_Prescriber from: fact_pharma to: Prescriber from_columns: [prescriberkey] to_columns: [prescriberkey]Adapter udostępnia PHARMA.fact_pharma_JOIN_Prescriber z unią kolumn obu tabel. Użytkownicy piszą:
SELECT * FROM "Pharma Rx".fact_pharma_JOIN_Prescriber WHERE prescribername LIKE 'Dr%';a Calcite pcha w dół SELECT * FROM fact_pharma JOIN dim_prescriber ON fact_pharma.prescriberkey = dim_prescriber.prescriberkey WHERE prescribername LIKE 'Dr%' — brak narzutu w runtime nad ręcznym napisaniem JOIN.
Relacje wielokolumnowe są obsługiwane: predykat ON łączy przez AND każdą parę from_columns[i] = to_columns[i].
Powiązane
- Eksport do Apache Ossie — jak wyprodukować plik YAML, który ten adapter czyta.
- Adnotacje semantyczne Saiku — adnotacje, które przeżywają do YAML-a Ossie i (w przyszłym wycinku) napędzają rozwiązywanie metryk/wymiarów w czasie zapytania.
- Framework adapterów Apache Calcite — bazowy planer.
- Repozytorium Apache Ossie — specyfikacja upstream.