diff --git a/docs/environment-variables.md b/docs/environment-variables.md index 3a87514bc77..583c3089625 100644 --- a/docs/environment-variables.md +++ b/docs/environment-variables.md @@ -192,3 +192,4 @@ those. identified as unused, `graph-node` will wait at least this long before actually deleting the data (value is in minutes, defaults to 360, i.e. 6 hours) +- `GRAPH_STORE_LOCAL`: default is `en_US.utf8`. This is used to mandate the locale for database. This is useful for comparing strings fields. diff --git a/graph/src/env/store.rs b/graph/src/env/store.rs index 077088b5b39..d31dfb3443b 100644 --- a/graph/src/env/store.rs +++ b/graph/src/env/store.rs @@ -91,6 +91,10 @@ pub struct EnvVarsStore { /// once the new behavior has run in the hosted service for a few days /// without issues. pub disable_error_for_toplevel_parents: bool, + + /// This is used to mandate what locale the Database run. + /// Set by the environment variable `GRAPH_STORE_LOCALE`. + pub store_locale: String, } // This does not print any values avoid accidentally leaking any sensitive env vars @@ -128,6 +132,7 @@ impl From for EnvVarsStore { connection_idle_timeout: Duration::from_secs(x.connection_idle_timeout_in_secs), write_queue_size: x.write_queue_size, disable_error_for_toplevel_parents: x.disable_error_for_toplevel_parents.0, + store_locale: x.store_locale, } } } @@ -173,4 +178,6 @@ pub struct InnerStore { write_queue_size: usize, #[envconfig(from = "GRAPH_DISABLE_ERROR_FOR_TOPLEVEL_PARENTS", default = "false")] disable_error_for_toplevel_parents: EnvVarBoolean, + #[envconfig(from = "GRAPH_STORE_LOCALE", default = "en_US.utf8")] + store_locale: String, } diff --git a/store/postgres/src/catalog.rs b/store/postgres/src/catalog.rs index 3a9de92bf66..1718cbe8708 100644 --- a/store/postgres/src/catalog.rs +++ b/store/postgres/src/catalog.rs @@ -37,6 +37,13 @@ table! { nspname -> Text, } } +// Readonly; only mapping the columns we want +table! { + pg_database(datname) { + datname -> Text, + datcollate -> Text, + } +} table! { subgraphs.table_stats { @@ -47,6 +54,17 @@ table! { } } +/// Get collate for current database +pub fn get_collation(conn: &PgConnection) -> Result { + use pg_database as db; + + return db::table + .filter(db::datname.eq(diesel::dsl::sql("current_database()"))) + .select(db::datcollate) + .get_result(conn) + .map_err(StoreError::from); +} + // In debug builds (for testing etc.) create exclusion constraints, in // release builds for production, skip them #[cfg(debug_assertions)] diff --git a/store/postgres/src/connection_pool.rs b/store/postgres/src/connection_pool.rs index d1f48a5bf48..4cb6dedc04f 100644 --- a/store/postgres/src/connection_pool.rs +++ b/store/postgres/src/connection_pool.rs @@ -977,6 +977,12 @@ impl PoolInner { let pool = self.clone(); let conn = self.get().map_err(|_| StoreError::DatabaseUnavailable)?; + let db_locale = catalog::get_collation(&conn)?; + + if !db_locale.eq(&ENV_VARS.store.store_locale) { + warn!(pool.logger, "Database locale is not '{}', but {}.\n If this is the locale your Database is running in then set 'GRAPH_STORE_LOCALE' environment variable to '{}'.", ENV_VARS.store.store_locale, db_locale, db_locale); + } + advisory_lock::lock_migration(&conn) .unwrap_or_else(|err| die(&pool.logger, "failed to get migration lock", &err)); let result = pool