Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
931b899
Add support for Postgres materialized views (relkind = 'm') and test …
kunalsinghdadhwal Oct 2, 2025
5c131da
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 2, 2025
5b8b21e
Merge branch 'main' into fix/materialized-views
kunalsinghdadhwal Oct 6, 2025
4dae341
Changed Option<bool> to Option<Char> in TableInfo Struct
kunalsinghdadhwal Oct 8, 2025
0bf3dce
Apply suggestion from @CommanderStorm
CommanderStorm Oct 18, 2025
05bb10e
Merge branch 'main' into fix/materialized-views
CommanderStorm Oct 18, 2025
487805c
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 18, 2025
4f731e0
change the collum to be named relkind
CommanderStorm Oct 18, 2025
729108e
fix more of the tests
CommanderStorm Oct 19, 2025
aec861f
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 19, 2025
3a0a9fb
Update martin/src/config/file/tiles/postgres/resolver/scripts/query_a…
CommanderStorm Oct 19, 2025
ce58dca
Added 'r' as per suggestion
kunalsinghdadhwal Oct 24, 2025
aaa135d
Merge branch 'main' into fix/materialized-views
kunalsinghdadhwal Oct 24, 2025
933a68c
Refactor materialized view creation for comments
nyurik Oct 24, 2025
f9f381d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Oct 24, 2025
82fbf10
Merge branch 'main' into fix/materialized-views
nyurik Jan 5, 2026
aed4f1c
cleanup tests
nyurik Jan 5, 2026
16ca5d8
chore(fmt): apply pre-commit formatting fixes
pre-commit-ci[bot] Jan 5, 2026
e7df470
clippy fixes
nyurik Jan 5, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 8 additions & 4 deletions martin/src/config/file/tiles/postgres/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -569,13 +569,17 @@ fn warn_on_rename(old_id: &String, new_id: &String, typ: &str) {
}

fn summary(info: &TableInfo) -> String {
let relkind = match info.is_view {
Some(true) => "view",
_ => "table",
let long_relkind = match info.relkind {
Some('v') => "view".to_string(),
Some('m') => "materialized view".to_string(),
Some('r') => "table".to_string(),
// if we get to any of below lines, this is likely a bug
Some(r) => format!("unknown relkind={r}"),
None => "unknown relkind".to_string(),
};
// TODO: add column_id to the summary if it is set
format!(
"{relkind} {}.{} with {} column ({}, SRID={})",
"{long_relkind} {}.{} with {} column ({}, SRID={})",
info.schema,
info.table,
info.geometry_column,
Expand Down
9 changes: 6 additions & 3 deletions martin/src/config/file/tiles/postgres/config_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,9 +32,12 @@ pub struct TableInfo {
#[serde(skip)]
pub geometry_index: Option<bool>,

/// Flag indicating if table is actually a view (`PostgreSQL relkind = 'v'`)
/// Flag indicating the `PostgreSQL relkind`:
/// - `"t"`: Table
/// - `"v"`: View
/// - `"m"`: Materialized View
#[serde(skip)]
pub is_view: Option<bool>,
pub relkind: Option<char>,

/// Feature id column name
pub id_column: Option<String>,
Expand Down Expand Up @@ -131,7 +134,7 @@ impl TableInfo {
geometry_column: self.geometry_column.clone(),
// These values are not serialized, so copy auto-detected values from the database
geometry_index: self.geometry_index,
is_view: self.is_view,
relkind: self.relkind,
tilejson: self.tilejson.clone(),
// Srid requires some logic
srid: self.calc_srid(new_id, cfg_inf.srid, default_srid)?,
Expand Down
10 changes: 6 additions & 4 deletions martin/src/config/file/tiles/postgres/resolver/query_tables.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,19 @@ pub async fn query_available_tables(
table,
geometry_column: row.get("geom"),
geometry_index: row.get("geom_idx"),
is_view: row.get("is_view"),
relkind: row
.get::<_, Option<i8>>("relkind")
.and_then(|r| u8::try_from(r).ok().map(char::from)),
srid: row.get("srid"), // casting i32 to u32?
geometry_type: row.get("type"),
properties: Some(serde_json::from_value(row.get("properties")).unwrap()),
tilejson,
..Default::default()
};

// Warn for missing geometry indices. Ignore views since those can't have indices
// and will generally refer to table columns.
if let (Some(false), Some(false)) = (info.geometry_index, info.is_view) {
// Warn for missing geometry indices.
// Ignore views since those can't have indices and will generally refer to table columns.
if info.geometry_index == Some(false) && info.relkind != Some('v') {
warn!(
"Table {}.{} has no spatial index on column {}",
info.schema, info.table, info.geometry_column
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ annotated_geometry_columns AS (
geometry_columns.srid,
geometry_columns.type,
-- 'geometry' AS column_type
coalesce(cls.relkind = 'v', false) AS is_view,
cls.relkind,
bool_or(sic.column_name IS NOT null) AS geom_idx
FROM geometry_columns
INNER JOIN pg_catalog.pg_namespace AS ns
Expand All @@ -79,7 +79,7 @@ annotated_geography_columns AS (
geography_columns.srid,
geography_columns.type,
-- 'geography' AS column_type
coalesce(cls.relkind = 'v', false) AS is_view,
cls.relkind,
bool_or(sic.column_name IS NOT null) AS geom_idx
FROM geography_columns
INNER JOIN pg_catalog.pg_namespace AS ns
Expand Down Expand Up @@ -113,7 +113,7 @@ descriptions AS (
FROM pg_class AS cls
INNER JOIN pg_namespace ON cls.relnamespace = pg_namespace.oid
LEFT JOIN pg_description ON cls.oid = pg_description.objoid AND pg_description.objsubid = 0
WHERE cls.relkind = 'r' OR cls.relkind = 'v'
WHERE cls.relkind IN ('r', 'v', 'm') -- table, view or materialised view
)

SELECT
Expand All @@ -122,7 +122,7 @@ SELECT
gc.geom,
gc.srid,
gc.type,
gc.is_view,
gc.relkind,
gc.geom_idx,
dc.description,
coalesce(
Expand All @@ -145,4 +145,4 @@ LEFT JOIN descriptions AS dc
gc.schema = dc.schema_name
AND gc.name = dc.table_name
GROUP BY -- noqa: AM06
gc.schema, gc.name, gc.geom, gc.srid, gc.type, gc.is_view, gc.geom_idx, dc.description;
gc.schema, gc.name, gc.geom, gc.srid, gc.type, gc.relkind, gc.geom_idx, dc.description;
4 changes: 4 additions & 0 deletions tests/expected/auto/catalog_auto.json
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,10 @@
"content_type": "application/json",
"name": "Dummy json data"
},
"mat_view": {
"content_type": "application/x-protobuf",
"description": "materialized view comment"
},
"png": {
"content_type": "image/png",
"name": "ne2sr"
Expand Down
22 changes: 22 additions & 0 deletions tests/expected/auto/mv_comment.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"bounds": [
-0.0010997122,
0.0003416247,
-0.0006641716,
0.0003683552
],
"description": "materialized view comment",
"name": "mat_view",
"tilejson": "3.0.0",
"tiles": [
"http://localhost:3111/mat_view/{z}/{x}/{y}"
],
"vector_layers": [
{
"fields": {
"id": "int4"
},
"id": "mat_view"
}
]
}
5 changes: 5 additions & 0 deletions tests/expected/auto/mv_comment.json.headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
content-encoding: br
content-type: application/json
etag: "unstable due to floating-point rounding"
transfer-encoding: chunked
vary: accept-encoding, Origin, Access-Control-Request-Method, Access-Control-Request-Headers
Binary file added tests/expected/auto/mv_comment_0_0_0.pbf
Binary file not shown.
34 changes: 34 additions & 0 deletions tests/expected/auto/mv_comment_0_0_0.pbf.geojson
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
{
"features": [
{
"geometry": {
"coordinates": [
655,
2513
],
"type": "Point"
},
"properties": {
"id": 1,
"source_mvt_layer": "mat_view"
},
"type": "Feature"
},
{
"geometry": {
"coordinates": [
1207,
2556
],
"type": "Point"
},
"properties": {
"id": 2,
"source_mvt_layer": "mat_view"
},
"type": "Feature"
}
],
"name": "merged",
"type": "FeatureCollection"
}
5 changes: 5 additions & 0 deletions tests/expected/auto/mv_comment_0_0_0.pbf.headers
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
content-encoding: gzip
content-length: 82
content-type: application/x-protobuf
etag: "hOO9engERDDV6rq6nxxfPQ"
vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers
13 changes: 13 additions & 0 deletions tests/expected/auto/save_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,19 @@ postgres:
properties:
big_feat_id: int8
id: int4
mat_view:
schema: public
table: mat_view
srid: 900913
geometry_column: geom
bounds:
- -0.0010997121 # truncated to 10 digits
- 0.0003416246 # truncated to 10 digits
- -0.0006641715 # truncated to 10 digits
- 0.0003683552 # truncated to 10 digits
geometry_type: GEOMETRY
properties:
id: int4
points1:
schema: public
table: points1
Expand Down
13 changes: 13 additions & 0 deletions tests/expected/martin-cp/composite_save_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ postgres:
properties:
big_feat_id: int8
id: int4
mat_view:
schema: public
table: mat_view
srid: 900913
geometry_column: geom
bounds:
- -0.0010997121 # truncated to 10 digits
- 0.0003416246 # truncated to 10 digits
- -0.0006641715 # truncated to 10 digits
- 0.0003683552 # truncated to 10 digits
geometry_type: GEOMETRY
properties:
id: int4
points1:
schema: public
table: points1
Expand Down
13 changes: 13 additions & 0 deletions tests/expected/martin-cp/flat-with-hash_save_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ postgres:
properties:
big_feat_id: int8
id: int4
mat_view:
schema: public
table: mat_view
srid: 900913
geometry_column: geom
bounds:
- -0.0010997121 # truncated to 10 digits
- 0.0003416246 # truncated to 10 digits
- -0.0006641715 # truncated to 10 digits
- 0.0003683552 # truncated to 10 digits
geometry_type: GEOMETRY
properties:
id: int4
points1:
schema: public
table: points1
Expand Down
13 changes: 13 additions & 0 deletions tests/expected/martin-cp/flat_save_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ postgres:
properties:
big_feat_id: int8
id: int4
mat_view:
schema: public
table: mat_view
srid: 900913
geometry_column: geom
bounds:
- -0.0010997121 # truncated to 10 digits
- 0.0003416246 # truncated to 10 digits
- -0.0006641715 # truncated to 10 digits
- 0.0003683552 # truncated to 10 digits
geometry_type: GEOMETRY
properties:
id: int4
points1:
schema: public
table: points1
Expand Down
13 changes: 13 additions & 0 deletions tests/expected/martin-cp/normalized_save_config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,19 @@ postgres:
properties:
big_feat_id: int8
id: int4
mat_view:
schema: public
table: mat_view
srid: 900913
geometry_column: geom
bounds:
- -0.0010997121 # truncated to 10 digits
- 0.0003416246 # truncated to 10 digits
- -0.0006641715 # truncated to 10 digits
- 0.0003683552 # truncated to 10 digits
geometry_type: GEOMETRY
properties:
id: int4
points1:
schema: public
table: points1
Expand Down
22 changes: 22 additions & 0 deletions tests/fixtures/tables/materialized_view.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
CREATE TABLE mat_view_src (
id serial PRIMARY KEY,
txt text
);

INSERT INTO mat_view_src (txt) VALUES
('POINT(-122.4194 37.7749)'),
('POINT(-73.935242 40.730610)');

CREATE MATERIALIZED VIEW mat_view AS
SELECT
id,
ST_GEOMFROMTEXT(txt, 4326) AS geom
FROM mat_view_src;

DO $do$ BEGIN
EXECUTE 'COMMENT ON MATERIALIZED VIEW mat_view IS $tj$' || $$
{
"description": "materialized view comment"
}
$$::json || '$tj$';
END $do$;
6 changes: 6 additions & 0 deletions tests/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -433,6 +433,10 @@ test_pbf antimeridian_4_0_5 antimeridian/4/0/5
test_jsn tbl_comment MixPoints
test_jsn fnc_comment function_Mixed_Name

>&2 echo "***** Test server response for materialized view *****"
test_jsn mv_comment mat_view
test_pbf mv_comment_0_0_0 mat_view/0/0/0

>&2 echo "***** Test server response for the same name in different schemas *****"
test_jsn same_name_different_schema_table1 table_name_existing_two_schemas
test_pbf same_name_different_schema_table1_0_0_0 table_name_existing_two_schemas/0/0/0
Expand All @@ -451,6 +455,7 @@ kill_process "$MARTIN_PROC_ID" Martin

test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.table_source has no spatial index on column geom'
test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.table_source_geog has no spatial index on column geog'
test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.mat_view has no spatial index on column geom'
test_log_has_str "$LOG_FILE" 'WARN martin_core::resources::fonts] Ignoring duplicate font Overpass Mono Regular from tests'
test_log_has_str "$LOG_FILE" 'was renamed to `stamen_toner__raster_CC-BY-ODbL_z3`'
test_log_has_str "$LOG_FILE" 'was renamed to `table_source_multiple_geom.1`'
Expand Down Expand Up @@ -565,6 +570,7 @@ test_metrics "metrics_1"
kill_process "$MARTIN_PROC_ID" Martin
test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.table_source has no spatial index on column geom'
test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.table_source_geog has no spatial index on column geog'
test_log_has_str "$LOG_FILE" 'WARN martin::config::file::tiles::postgres::resolver::query_tables] Table public.mat_view has no spatial index on column geom'
test_log_has_str "$LOG_FILE" 'WARN martin_core::resources::fonts] Ignoring duplicate font Overpass Mono Regular from tests'
test_log_has_str "$LOG_FILE" "WARN martin::config::file::main] Ignoring unrecognized configuration key 'warning'. Please check your configuration file for typos."
test_log_has_str "$LOG_FILE" "WARN martin::config::file::main] Ignoring unrecognized configuration key 'observability.warning'. Please check your configuration file for typos."
Expand Down
Loading