Skip to content

Commit d8b08c0

Browse files
saihajlutter
authored andcommitted
store/postgres: insist on a database with C locale
1 parent 2da697b commit d8b08c0

File tree

4 files changed

+84
-1
lines changed

4 files changed

+84
-1
lines changed

NEWS.md

+2-1
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- The two affected deployments are: `Qmccst5mbV5a6vT6VvJMLPKMAA1VRgT6NGbxkLL8eDRsE7` and `Qmd9nZKCH8UZU1pBzk7G8ECJr3jX3a2vAf3vowuTwFvrQg`;
99
- Here's an example [manifest](https://ipfs.io/ipfs/Qmd9nZKCH8UZU1pBzk7G8ECJr3jX3a2vAf3vowuTwFvrQg), taking a look at the data sources of name `ERC721` and `CryptoKitties`, both listen to the `Transfer(...)` event. Considering a block where there's only one occurence of this event, `graph-node` would duplicate it and call `handleTransfer` twice. Now this is fixed and it will be called only once per event/call that happened on chain.
1010
- In the case you're indexing one of those, you should first upgrade the `graph-node` version, then rewind the affected subgraphs to the smallest `startBlock` of their subgraph manifest. To achieve that the `graphman rewind` CLI command can be used.
11+
- We now check that the database uses the `C` locale and `UTF8` character encoding. For new installations, `graph-node` will panic on startup if the database uses any other locale. The easiest way to make sure this check passes is to create the database cluster with `initdb -E UTF8 --locale C`. We will provide instructions on migrating existing installations in the future.
1112

1213
## v0.28.2
1314

@@ -42,7 +43,7 @@ Yanked. Please migrate to `v0.28.2`.
4243
E.g. `$ graphman --node-id index_node_0 --config graph-node.toml config provider mainnet`
4344
- Experimental support for GraphQL API versioning has landed. [#3185](https://github.com/graphprotocol/graph-node/pull/3185)
4445
- Progress towards experimental support for off-chain data sources. [#3791](https://github.com/graphprotocol/graph-node/pull/3791)
45-
- Experimental integration for substreams. [#3777](https://github.com/graphprotocol/graph-node/pull/3777), [#3784](https://github.com/graphprotocol/graph-node/pull/3784), [#3897](https://github.com/graphprotocol/graph-node/pull/3897), [#3765](https://github.com/graphprotocol/graph-node/pull/3765), and others
46+
- Experimental integration for substreams. [#3777](https://github.com/graphprotocol/graph-node/pull/3777), [#3784](https://github.com/graphprotocol/graph-node/pull/3784), [#3897](https://github.com/graphprotocol/graph-node/pull/3897), [#3765](https://github.com/graphprotocol/graph-node/pull/3765), and others
4647

4748
### Bug fixes
4849

store/postgres/src/catalog.rs

+58
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,14 @@ table! {
4242
nspname -> Text,
4343
}
4444
}
45+
// Readonly; only mapping the columns we want
46+
table! {
47+
pg_database(datname) {
48+
datname -> Text,
49+
datcollate -> Text,
50+
datctype -> Text,
51+
}
52+
}
4553

4654
table! {
4755
subgraphs.table_stats {
@@ -72,6 +80,56 @@ const CREATE_EXCLUSION_CONSTRAINT: bool = true;
7280
#[cfg(not(debug_assertions))]
7381
const CREATE_EXCLUSION_CONSTRAINT: bool = false;
7482

83+
pub struct Locale {
84+
collate: String,
85+
ctype: String,
86+
encoding: String,
87+
}
88+
89+
impl Locale {
90+
/// Load locale information for current database
91+
pub fn load(conn: &PgConnection) -> Result<Locale, StoreError> {
92+
use diesel::dsl::sql;
93+
use pg_database as db;
94+
95+
let (collate, ctype, encoding) = db::table
96+
.filter(db::datname.eq(sql("current_database()")))
97+
.select((
98+
db::datcollate,
99+
db::datctype,
100+
sql::<Text>("pg_encoding_to_char(encoding)::text"),
101+
))
102+
.get_result::<(String, String, String)>(conn)?;
103+
Ok(Locale {
104+
collate,
105+
ctype,
106+
encoding,
107+
})
108+
}
109+
110+
pub fn suitable(&self) -> Result<(), String> {
111+
if self.collate != "C" {
112+
return Err(format!(
113+
"database collation is `{}` but must be `C`",
114+
self.collate
115+
));
116+
}
117+
if self.ctype != "C" {
118+
return Err(format!(
119+
"database ctype is `{}` but must be `C`",
120+
self.ctype
121+
));
122+
}
123+
if self.encoding != "UTF8" {
124+
return Err(format!(
125+
"database encoding is `{}` but must be `UTF8`",
126+
self.encoding
127+
));
128+
}
129+
Ok(())
130+
}
131+
}
132+
75133
/// Information about what tables and columns we have in the database
76134
#[derive(Debug, Clone)]
77135
pub struct Catalog {

store/postgres/src/connection_pool.rs

+13
Original file line numberDiff line numberDiff line change
@@ -980,6 +980,19 @@ impl PoolInner {
980980
let conn = self.get().map_err(|_| StoreError::DatabaseUnavailable)?;
981981

982982
let start = Instant::now();
983+
if let Err(msg) = catalog::Locale::load(&conn)?.suitable() {
984+
if &self.shard == &*PRIMARY_SHARD && primary::is_empty(&conn)? {
985+
die(
986+
&pool.logger,
987+
"Database does not use C locale. \
988+
Please check the graph-node documentation for how to set up the database locale",
989+
&msg,
990+
);
991+
} else {
992+
warn!(pool.logger, "{}.\nPlease check the graph-node documentation for how to set up the database locale", msg);
993+
}
994+
}
995+
983996
advisory_lock::lock_migration(&conn)
984997
.unwrap_or_else(|err| die(&pool.logger, "failed to get migration lock", &err));
985998
// This code can cause a race in database setup: if pool A has had

store/postgres/src/primary.rs

+11
Original file line numberDiff line numberDiff line change
@@ -1519,6 +1519,17 @@ impl<'a> Connection<'a> {
15191519
}
15201520
}
15211521

1522+
/// Return `true` if we deem this installation to be empty, defined as
1523+
/// having no deployments and no subgraph names in the database
1524+
pub fn is_empty(conn: &PgConnection) -> Result<bool, StoreError> {
1525+
use deployment_schemas as ds;
1526+
use subgraph as s;
1527+
1528+
let empty = ds::table.count().get_result::<i64>(conn)? == 0
1529+
&& s::table.count().get_result::<i64>(conn)? == 0;
1530+
Ok(empty)
1531+
}
1532+
15221533
/// A struct that reads from pools in order, trying each pool in turn until
15231534
/// a query returns either success or anything but a
15241535
/// `Err(StoreError::DatabaseUnavailable)`. This only works for tables that

0 commit comments

Comments
 (0)