Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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: 7 additions & 5 deletions docs/src/config-file.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,11 @@ web_ui: disable

# Database configuration. This can also be a list of PG configs.
postgres:
# Database connection string. You can use env vars too, for example:
# $DATABASE_URL
# ${DATABASE_URL:-postgresql://postgres@localhost/db}
# Database connection string.
#
# You can use environment variables too, for example:
# connection_string: $DATABASE_URL
# connection_string: ${DATABASE_URL:-postgresql://postgres@localhost/db}
connection_string: 'postgresql://postgres@localhost:5432/db'

# Same as PGSSLCERT for psql
Expand All @@ -71,11 +73,11 @@ postgres:
# It is sensible to set this limit if you have user generated/untrusted geodata, e.g. a lot of data points at [Null Island](https://en.wikipedia.org/wiki/Null_Island).
max_feature_count: null # either a positive integer, or null=unlimited (default)

# Control the automatic generation of bounds for spatial tables [default: quick]
# Specify how bounds should be computed for the spatial PG tables [default: quick]
# 'calc' - compute table geometry bounds on startup.
# 'quick' - same as 'calc', but the calculation will be aborted if it takes more than 5 seconds.
# 'skip' - do not compute table geometry bounds on startup.
auto_bounds: skip
auto_bounds: quick

# Enable automatic discovery of tables and functions.
# You may set this to `false` to disable.
Expand Down
8 changes: 7 additions & 1 deletion martin/src/args/pg.rs
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,13 @@ pub struct PgArgs {
pub default_srid: Option<i32>,
#[arg(help = format!("Maximum Postgres connections pool size [DEFAULT: {POOL_SIZE_DEFAULT}]"), short, long)]
pub pool_size: Option<usize>,
/// Limit the number of features in a tile from a PG table source.
/// Limit the number of geo features per tile.
///
/// If the source table has more features than set here, they will not be included in the tile and the result will look "cut off"/incomplete.
/// This feature allows to put a maximum latency bound on tiles with extreme amount of detail at the cost of not returning all data.
/// It is sensible to set this limit if you have user generated/untrusted geodata, e.g. a lot of data points at [Null Island](https://en.wikipedia.org/wiki/Null_Island).
///
/// Can be either a positive integer or unlimited if omitted.
#[arg(short, long)]
pub max_feature_count: Option<usize>,
}
Expand Down
12 changes: 12 additions & 0 deletions martin/src/pg/builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,22 @@ pub type SqlTableInfoMapMapMap = InfoMap<InfoMap<InfoMap<TableInfo>>>;
#[derive(Debug)]
pub struct PgBuilder {
pool: PgPool,
/// If a spatial table has SRID 0, then this SRID will be used as a fallback
default_srid: Option<i32>,
/// Specify how bounds should be computed for the spatial PG tables
auto_bounds: BoundsCalcType,
/// Limit the number of geo features per tile.
///
/// If the source table has more features than set here, they will not be included in the tile and the result will look "cut off"/incomplete.
/// This feature allows to put a maximum latency bound on tiles with extreme amount of detail at the cost of not returning all data.
/// It is sensible to set this limit if you have user generated/untrusted geodata, e.g. a lot of data points at [Null Island](https://en.wikipedia.org/wiki/Null_Island).
///
/// Can be either a positive integer or unlimited if omitted.
max_feature_count: Option<usize>,
auto_functions: Option<PgBuilderFuncs>,
auto_tables: Option<PgBuilderTables>,
id_resolver: IdResolver,
/// Associative arrays of table sources
tables: TableInfoSources,
functions: FuncInfoSources,
}
Expand Down Expand Up @@ -78,6 +88,7 @@ macro_rules! get_auto_schemas {
}

impl PgBuilder {
/// Creates a new Builder from the [`PgConfig`] and a way to deterministically convert duplicate to unique names
pub async fn new(config: &PgConfig, id_resolver: IdResolver) -> PgResult<Self> {
let pool = PgPool::new(config).await?;

Expand All @@ -100,6 +111,7 @@ impl PgBuilder {
self.auto_bounds
}

/// ID under which this [`PgBuilder`] is identified externally
pub fn get_id(&self) -> &str {
self.pool.get_id()
}
Expand Down
16 changes: 16 additions & 0 deletions martin/src/pg/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,16 +39,32 @@ pub struct PgSslCerts {
#[serde_with::skip_serializing_none]
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
pub struct PgConfig {
/// Database connection string
pub connection_string: Option<String>,
#[serde(flatten)]
pub ssl_certificates: PgSslCerts,
/// If a spatial table has SRID 0, then this SRID will be used as a fallback
pub default_srid: Option<i32>,
/// Specify how bounds should be computed for the spatial PG tables
pub auto_bounds: Option<BoundsCalcType>,
/// Limit the number of geo features per tile.
///
/// If the source table has more features than set here, they will not be included in the tile and the result will look "cut off"/incomplete.
/// This feature allows to put a maximum latency bound on tiles with extreme amount of detail at the cost of not returning all data.
/// It is sensible to set this limit if you have user generated/untrusted geodata, e.g. a lot of data points at [Null Island](https://en.wikipedia.org/wiki/Null_Island).
///
/// Can be either a positive integer or unlimited if omitted.
pub max_feature_count: Option<usize>,
/// Maximum Postgres connections pool size [DEFAULT: 20]
pub pool_size: Option<usize>,
/// Enable/disable/configure automatic discovery of tables and functions.
///
/// You may set this to `OptBoolObj::Bool(false)` to disable.
#[serde(default, skip_serializing_if = "OptBoolObj::is_none")]
pub auto_publish: OptBoolObj<PgCfgPublish>,
/// Associative arrays of table sources
pub tables: Option<TableInfoSources>,
/// Associative arrays of function sources
pub functions: Option<FuncInfoSources>,
}

Expand Down
2 changes: 2 additions & 0 deletions martin/src/pg/config_table.rs
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,8 @@ impl TableInfo {
}

/// Determine the SRID value to use for a table, or None if unknown, assuming self is a table info from the database
///
/// Tries to use `default_srid` if a spatial table has SRID 0.
#[must_use]
pub fn calc_srid(&self, new_id: &str, cfg_srid: i32, default_srid: Option<i32>) -> Option<i32> {
match (self.srid, cfg_srid, default_srid) {
Expand Down
40 changes: 20 additions & 20 deletions martin/src/pg/pool.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,12 @@ impl PgPool {
.max_size(config.pool_size.unwrap_or(POOL_SIZE_DEFAULT))
.build()
.map_err(|e| PostgresPoolBuildError(e, id.clone()))?;

let conn = get_conn(&pool, &id).await?;
let mut res = Self {
id: id.clone(),
pool,
supports_tile_margin: false,
};
let conn = res.get().await?;
let pg_ver = get_postgres_version(&conn).await?;
if pg_ver < MINIMUM_POSTGRES_VERSION {
return Err(PostgresqlTooOld(pg_ver, MINIMUM_POSTGRES_VERSION));
Expand All @@ -59,14 +63,13 @@ impl PgPool {

// In the warning cases below, we could technically run.
// This is not ideal for reasons explained in the warnings

if pg_ver < RECOMMENDED_POSTGRES_VERSION {
warn!(
"PostgreSQL {pg_ver} is older than the recommended minimum {RECOMMENDED_POSTGRES_VERSION}."
);
}
let supports_tile_margin = postgis_ver >= ST_TILE_ENVELOPE_POSTGIS_VERSION;
if !supports_tile_margin {
res.supports_tile_margin = postgis_ver >= ST_TILE_ENVELOPE_POSTGIS_VERSION;
if !res.supports_tile_margin {
warn!(
"PostGIS {postgis_ver} is older than {ST_TILE_ENVELOPE_POSTGIS_VERSION}. Margin parameter in ST_TileEnvelope is not supported, so tiles may be cut off at the edges."
);
Expand All @@ -76,14 +79,8 @@ impl PgPool {
"PostGIS {postgis_ver} is older than the recommended minimum {MISSING_GEOM_FIXED_POSTGIS_VERSION}. In the used version, some geometry may be hidden on some zoom levels. If You encounter this bug, please consider updating your postgis installation. For further details please refer to https://github.com/maplibre/martin/issues/1651#issuecomment-2628674788"
);
}

info!("Connected to PostgreSQL {pg_ver} / PostGIS {postgis_ver} for source {id}");

Ok(Self {
id,
pool,
supports_tile_margin,
})
Ok(res)
}

fn parse_config(config: &PgConfig) -> PgResult<(String, Manager)> {
Expand Down Expand Up @@ -122,13 +119,22 @@ impl PgPool {
Ok((id, mgr))
}

/// Retrieves an [`Object`] from this [`PgPool`] or waits for one to become available.
///
/// # Errors
///
/// See [`PostgresPoolConnError`] for details.
pub async fn get(&self) -> PgResult<Object> {
get_conn(&self.pool, self.id.as_str()).await
self.pool
.get()
.await
.map_err(|e| PostgresPoolConnError(e, self.id.clone()))
}

/// ID under which this [`PgPool`] is identified externally
#[must_use]
pub fn get_id(&self) -> &str {
self.id.as_str()
&self.id
}

/// Indicates if `ST_TileEnvelope` supports the margin parameter.
Expand All @@ -141,12 +147,6 @@ impl PgPool {
}
}

async fn get_conn(pool: &Pool, id: &str) -> PgResult<Object> {
pool.get()
.await
.map_err(|e| PostgresPoolConnError(e, id.to_string()))
}

/// Get [PostgreSQL version](https://www.postgresql.org/support/versioning/).
/// `PostgreSQL` only has a Major.Minor versioning, so we use 0 the patch version
async fn get_postgres_version(conn: &Object) -> PgResult<Version> {
Expand Down
5 changes: 5 additions & 0 deletions martin/src/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -103,10 +103,15 @@ impl TileSources {

#[async_trait]
pub trait Source: Send + Debug {
/// ID under which this [`Source`] is identified if accessed externally
fn get_id(&self) -> &str;

/// TileJSON of this [`Source`]
///
/// Will be communicated verbatim to the outside to give rendering engines information about the source's contents such as zoom levels, center points, ...
fn get_tilejson(&self) -> &TileJSON;

/// Information for serving the source such as which Mime-type to apply or how compression should work
fn get_tile_info(&self) -> TileInfo;
Comment thread
CommanderStorm marked this conversation as resolved.

fn clone_source(&self) -> TileInfoSource;
Expand Down
Loading