Skip to content

Commit 35b0a11

Browse files
feat(materialize): add new backend for Materialize streaming database
This commit adds comprehensive support for Materialize, including: - Core backend implementation with cluster and connection management - Support for materialized views, indexes, sources, sinks, and secrets - Idiomatic Materialize SQL patterns (mz_now(), SUBSCRIBE, etc.) - Test coverage for Materialize-specific features - Integration with existing Ibis test suite
1 parent f1c888b commit 35b0a11

File tree

67 files changed

+10443
-49
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

67 files changed

+10443
-49
lines changed

.github/workflows/ibis-backends.yml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,13 @@ jobs:
166166
sys-deps:
167167
- libgeos-dev
168168
- libpq5
169+
- name: materialize
170+
title: Materialize
171+
serial: true
172+
services:
173+
- materialize
174+
extras:
175+
- --extra materialize
169176
- name: risingwave
170177
title: RisingWave
171178
serial: true
@@ -335,6 +342,15 @@ jobs:
335342
sys-deps:
336343
- libgeos-dev
337344
- libpq5
345+
- os: windows-latest
346+
backend:
347+
name: materialize
348+
title: Materialize
349+
serial: true
350+
services:
351+
- materialize
352+
extras:
353+
- --extra materialize
338354
- os: windows-latest
339355
backend:
340356
name: risingwave

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ This allows you to combine the flexibility of Python with the scale and performa
135135

136136
## Backends
137137

138-
Ibis supports nearly 20 backends:
138+
Ibis supports 20 backends:
139139

140140
- [Apache DataFusion](https://ibis-project.org/backends/datafusion/)
141141
- [Apache Druid](https://ibis-project.org/backends/druid/)
@@ -146,6 +146,7 @@ Ibis supports nearly 20 backends:
146146
- [ClickHouse](https://ibis-project.org/backends/clickhouse/)
147147
- [DuckDB](https://ibis-project.org/backends/duckdb/)
148148
- [Exasol](https://ibis-project.org/backends/exasol)
149+
- [Materialize](https://ibis-project.org/backends/materialize/)
149150
- [MySQL](https://ibis-project.org/backends/mysql/)
150151
- [Oracle](https://ibis-project.org/backends/oracle/)
151152
- [Polars](https://ibis-project.org/backends/polars/)

ci/schema/materialize.sql

Lines changed: 185 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,185 @@
1+
DROP TABLE IF EXISTS diamonds CASCADE;
2+
3+
CREATE TABLE diamonds (
4+
carat FLOAT,
5+
cut TEXT,
6+
color TEXT,
7+
clarity TEXT,
8+
depth FLOAT,
9+
"table" FLOAT,
10+
price BIGINT,
11+
x FLOAT,
12+
y FLOAT,
13+
z FLOAT
14+
);
15+
16+
-- Note: In real usage, data would be loaded via sources or INSERT statements
17+
-- For CI, we'll need to mount CSV files and use a different loading strategy
18+
19+
DROP TABLE IF EXISTS astronauts CASCADE;
20+
21+
CREATE TABLE astronauts (
22+
"id" BIGINT,
23+
"number" BIGINT,
24+
"nationwide_number" BIGINT,
25+
"name" VARCHAR,
26+
"original_name" VARCHAR,
27+
"sex" VARCHAR,
28+
"year_of_birth" BIGINT,
29+
"nationality" VARCHAR,
30+
"military_civilian" VARCHAR,
31+
"selection" VARCHAR,
32+
"year_of_selection" BIGINT,
33+
"mission_number" BIGINT,
34+
"total_number_of_missions" BIGINT,
35+
"occupation" VARCHAR,
36+
"year_of_mission" BIGINT,
37+
"mission_title" VARCHAR,
38+
"ascend_shuttle" VARCHAR,
39+
"in_orbit" VARCHAR,
40+
"descend_shuttle" VARCHAR,
41+
"hours_mission" DOUBLE PRECISION,
42+
"total_hrs_sum" DOUBLE PRECISION,
43+
"field21" BIGINT,
44+
"eva_hrs_mission" DOUBLE PRECISION,
45+
"total_eva_hrs" DOUBLE PRECISION
46+
);
47+
48+
DROP TABLE IF EXISTS batting CASCADE;
49+
50+
CREATE TABLE batting (
51+
"playerID" TEXT,
52+
"yearID" BIGINT,
53+
"stint" BIGINT,
54+
"teamID" TEXT,
55+
"lgID" TEXT,
56+
"G" BIGINT,
57+
"AB" BIGINT,
58+
"R" BIGINT,
59+
"H" BIGINT,
60+
"X2B" BIGINT,
61+
"X3B" BIGINT,
62+
"HR" BIGINT,
63+
"RBI" BIGINT,
64+
"SB" BIGINT,
65+
"CS" BIGINT,
66+
"BB" BIGINT,
67+
"SO" BIGINT,
68+
"IBB" BIGINT,
69+
"HBP" BIGINT,
70+
"SH" BIGINT,
71+
"SF" BIGINT,
72+
"GIDP" BIGINT
73+
);
74+
75+
DROP TABLE IF EXISTS awards_players CASCADE;
76+
77+
CREATE TABLE awards_players (
78+
"playerID" TEXT,
79+
"awardID" TEXT,
80+
"yearID" BIGINT,
81+
"lgID" TEXT,
82+
"tie" TEXT,
83+
"notes" TEXT
84+
);
85+
86+
DROP TABLE IF EXISTS functional_alltypes CASCADE;
87+
88+
CREATE TABLE functional_alltypes (
89+
"id" INTEGER,
90+
"bool_col" BOOLEAN,
91+
"tinyint_col" SMALLINT,
92+
"smallint_col" SMALLINT,
93+
"int_col" INTEGER,
94+
"bigint_col" BIGINT,
95+
"float_col" REAL,
96+
"double_col" DOUBLE PRECISION,
97+
"date_string_col" TEXT,
98+
"string_col" TEXT,
99+
"timestamp_col" TIMESTAMP WITHOUT TIME ZONE,
100+
"year" INTEGER,
101+
"month" INTEGER
102+
);
103+
104+
DROP TABLE IF EXISTS tzone CASCADE;
105+
106+
CREATE TABLE tzone (
107+
"ts" TIMESTAMP WITH TIME ZONE,
108+
"key" TEXT,
109+
"value" DOUBLE PRECISION
110+
);
111+
112+
INSERT INTO tzone
113+
SELECT
114+
CAST('2017-05-28 11:01:31.000400' AS TIMESTAMP WITH TIME ZONE) +
115+
(t * INTERVAL '1 day' + t * INTERVAL '1 second') AS "ts",
116+
CHR(97 + t) AS "key",
117+
t + t / 10.0 AS "value"
118+
FROM generate_series(0, 9) AS t;
119+
120+
DROP TABLE IF EXISTS array_types CASCADE;
121+
122+
CREATE TABLE IF NOT EXISTS array_types (
123+
"x" BIGINT[],
124+
"y" TEXT[],
125+
"z" DOUBLE PRECISION[],
126+
"grouper" TEXT,
127+
"scalar_column" DOUBLE PRECISION,
128+
"multi_dim" BIGINT[][]
129+
);
130+
131+
-- Note: Materialize does not currently support multi-dimensional arrays containing NULL sub-arrays, so they are not included.
132+
-- Fix pending: https://github.com/MaterializeInc/materialize/pull/33786
133+
134+
INSERT INTO array_types VALUES
135+
(ARRAY[1::BIGINT, 2::BIGINT, 3::BIGINT], ARRAY['a', 'b', 'c'], ARRAY[1.0::DOUBLE PRECISION, 2.0::DOUBLE PRECISION, 3.0::DOUBLE PRECISION], 'a', 1.0, NULL),
136+
(ARRAY[4::BIGINT, 5::BIGINT], ARRAY['d', 'e'], ARRAY[4.0::DOUBLE PRECISION, 5.0::DOUBLE PRECISION], 'a', 2.0, NULL),
137+
(ARRAY[6::BIGINT, NULL], ARRAY['f', NULL], ARRAY[6.0::DOUBLE PRECISION, NULL::DOUBLE PRECISION], 'a', 3.0, NULL),
138+
(ARRAY[NULL::BIGINT, 1::BIGINT, NULL::BIGINT], ARRAY[NULL, 'a', NULL], ARRAY[]::DOUBLE PRECISION[], 'b', 4.0, NULL),
139+
(ARRAY[2::BIGINT, NULL, 3::BIGINT], ARRAY['b', NULL, 'c'], NULL, 'b', 5.0, NULL),
140+
(ARRAY[4::BIGINT, NULL, NULL, 5::BIGINT], ARRAY['d', NULL, NULL, 'e'], ARRAY[4.0::DOUBLE PRECISION, NULL::DOUBLE PRECISION, NULL::DOUBLE PRECISION, 5.0::DOUBLE PRECISION], 'c', 6.0, NULL);
141+
142+
DROP TABLE IF EXISTS json_t CASCADE;
143+
144+
CREATE TABLE IF NOT EXISTS json_t (rowid BIGINT, "js" JSONB);
145+
146+
INSERT INTO json_t VALUES
147+
(1, '{"a": [1,2,3,4], "b": 1}'),
148+
(2, '{"a":null,"b":2}'),
149+
(3, '{"a":"foo", "c":null}'),
150+
(4, 'null'),
151+
(5, '[42,47,55]'),
152+
(6, '[]'),
153+
(7, '"a"'),
154+
(8, '""'),
155+
(9, '"b"'),
156+
(10, NULL),
157+
(11, 'true'),
158+
(12, 'false'),
159+
(13, '42'),
160+
(14, '37.37');
161+
162+
DROP TABLE IF EXISTS win CASCADE;
163+
164+
CREATE TABLE win ("g" TEXT, "x" BIGINT, "y" BIGINT);
165+
166+
INSERT INTO win VALUES
167+
('a', 0, 3),
168+
('a', 1, 2),
169+
('a', 2, 0),
170+
('a', 3, 1),
171+
('a', 4, 1);
172+
173+
DROP TABLE IF EXISTS topk CASCADE;
174+
175+
CREATE TABLE topk ("x" BIGINT);
176+
177+
INSERT INTO topk VALUES (1), (1), (NULL);
178+
179+
DROP TABLE IF EXISTS map CASCADE;
180+
181+
CREATE TABLE map (idx BIGINT, kv JSONB);
182+
183+
INSERT INTO map VALUES
184+
(1, '{"a": 1, "b": 2, "c": 3}'),
185+
(2, '{"d": 4, "e": 5, "f": 6}');

compose.yaml

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -556,6 +556,28 @@ services:
556556
networks:
557557
- impala
558558

559+
materialize:
560+
image: materialize/materialized:v0.160.0
561+
ports:
562+
- 6875:6875 # SQL port
563+
- 6876:6876 # HTTP port
564+
- 6874:6874 # Internal port
565+
environment:
566+
MZ_LOG: info
567+
MZ_TELEMETRY: "false"
568+
MZ_SYSTEM_PARAMETER_DEFAULT: "max_tables=1000" # Increase from default 200 for testing
569+
healthcheck:
570+
test:
571+
- CMD-SHELL
572+
- bash -c 'printf "GET / HTTP/1.1\n\n" > /dev/tcp/127.0.0.1/6875; exit $$?;'
573+
interval: 2s
574+
retries: 30
575+
timeout: 5s
576+
volumes:
577+
- materialize:/data
578+
networks:
579+
- materialize
580+
559581
risingwave:
560582
image: ghcr.io/risingwavelabs/risingwave:v2.1.3
561583
command: "standalone --meta-opts=\" \
@@ -623,6 +645,7 @@ networks:
623645
oracle:
624646
exasol:
625647
flink:
648+
materialize:
626649
risingwave:
627650
spark-connect:
628651

@@ -635,5 +658,6 @@ volumes:
635658
postgres:
636659
exasol:
637660
impala:
661+
materialize:
638662
risingwave:
639663
spark-connect:

docs/backends/materialize.qmd

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
# Materialize
2+
3+
[https://materialize.com](https://materialize.com)
4+
5+
![](https://img.shields.io/badge/memtables-fallback-yellow?style=flat-square) ![](https://img.shields.io/badge/inputs-Materialize tables | Streaming sources-blue?style=flat-square) ![](https://img.shields.io/badge/outputs-Materialize tables | CSV | pandas | Parquet | PyArrow-orange?style=flat-square) ![](https://img.shields.io/badge/streaming-SUBSCRIBE-purple?style=flat-square)
6+
7+
## Install
8+
9+
Install Ibis and dependencies for the Materialize backend:
10+
11+
```{.bash}
12+
pip install 'ibis-framework[materialize]'
13+
```
14+
15+
And connect:
16+
17+
```{.python}
18+
import ibis
19+
20+
con = ibis.materialize.connect() # <1>
21+
```
22+
23+
1. Adjust connection parameters as needed.
24+
25+
## Connect
26+
27+
### `ibis.materialize.connect`
28+
29+
```python
30+
con = ibis.materialize.connect(
31+
user="materialize",
32+
password="password",
33+
host="localhost",
34+
port=6875,
35+
database="materialize",
36+
cluster="quickstart", # Optional: specify default cluster
37+
)
38+
```
39+
40+
::: {.callout-note}
41+
`ibis.materialize.connect` is a thin wrapper around
42+
[`ibis.backends.materialize.Backend.do_connect`](#ibis.backends.materialize.Backend.do_connect).
43+
:::
44+
45+
### Connection Parameters
46+
47+
```{python}
48+
#| echo: false
49+
#| output: asis
50+
from _utils import render_do_connect
51+
52+
render_do_connect("materialize")
53+
```
54+
55+
### `ibis.connect` URL format
56+
57+
In addition to `ibis.materialize.connect`, you can also connect to Materialize by
58+
passing a properly-formatted connection URL to `ibis.connect`:
59+
60+
```python
61+
con = ibis.connect(f"materialize://{user}:{password}@{host}:{port}/{database}")
62+
```
63+
64+
```{python}
65+
#| echo: false
66+
BACKEND = "Materialize"
67+
```
68+
69+
{{< include ./_templates/api.qmd >}}

0 commit comments

Comments
 (0)