From e510f7a461bc97661f0911efc793faf6f3120c02 Mon Sep 17 00:00:00 2001 From: delthas Date: Sat, 11 Feb 2023 13:11:21 +0100 Subject: [PATCH] Add support for explicit PostgreSQL schema prefixes for tests PostgreSQL tests use pg_temp only. pg_temp is never searched for FTS objects, so creating then altering an FTS configuration will not work because PostgreSQL will not be able to find the FTS configuration it just created. Instead, we explicitly refer to the FTS objects with their full name including their prefix, which makes PostgreSQL able to find the object. This is only needed for tests. See: https://stackoverflow.com/a/31095452/2347617 See: https://www.postgresql.org/message-id/15191.1208975632@sss.pgh.pa.us --- database/postgres.go | 34 ++++++++++++++++++++++------------ database/postgres_test.go | 8 ++++---- 2 files changed, 26 insertions(+), 16 deletions(-) diff --git a/database/postgres.go b/database/postgres.go index a4597d3..ee4381c 100644 --- a/database/postgres.go +++ b/database/postgres.go @@ -122,11 +122,11 @@ CREATE TABLE "MessageTarget" ( UNIQUE(network, target) ); -CREATE TEXT SEARCH DICTIONARY "search_simple_dictionary" ( +CREATE TEXT SEARCH DICTIONARY search_simple_dictionary ( TEMPLATE = pg_catalog.simple ); -CREATE TEXT SEARCH CONFIGURATION "search_simple" ( COPY = pg_catalog.simple ); -ALTER TEXT SEARCH CONFIGURATION "search_simple" ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, hword, hword_part, word WITH "search_simple_dictionary"; +CREATE TEXT SEARCH CONFIGURATION @SCHEMA_PREFIX@search_simple ( COPY = pg_catalog.simple ); +ALTER TEXT SEARCH CONFIGURATION @SCHEMA_PREFIX@search_simple ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, hword, hword_part, word WITH @SCHEMA_PREFIX@search_simple_dictionary; CREATE TABLE "Message" ( id SERIAL PRIMARY KEY, target INTEGER NOT NULL REFERENCES "MessageTarget"(id) ON DELETE CASCADE, @@ -134,7 +134,7 @@ CREATE TABLE "Message" ( time TIMESTAMP WITH TIME ZONE NOT NULL, sender TEXT NOT NULL, text TEXT, - text_search tsvector GENERATED ALWAYS AS (to_tsvector('search_simple', text)) STORED + text_search tsvector GENERATED ALWAYS AS (to_tsvector('@SCHEMA_PREFIX@search_simple', text)) STORED ); CREATE INDEX "MessageIndex" ON "Message" (target, time); CREATE INDEX "MessageSearchIndex" ON "Message" USING GIN (text_search); @@ -206,11 +206,11 @@ var postgresMigrations = []string{ target TEXT NOT NULL, UNIQUE(network, target) ); - CREATE TEXT SEARCH DICTIONARY "search_simple_dictionary" ( + CREATE TEXT SEARCH DICTIONARY search_simple_dictionary ( TEMPLATE = pg_catalog.simple ); - CREATE TEXT SEARCH CONFIGURATION "search_simple" ( COPY = pg_catalog.simple ); - ALTER TEXT SEARCH CONFIGURATION "search_simple" ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, hword, hword_part, word WITH "search_simple_dictionary"; + CREATE TEXT SEARCH CONFIGURATION @SCHEMA_PREFIX@search_simple ( COPY = pg_catalog.simple ); + ALTER TEXT SEARCH CONFIGURATION @SCHEMA_PREFIX@search_simple ALTER MAPPING FOR asciiword, asciihword, hword_asciipart, hword, hword_part, word WITH @SCHEMA_PREFIX@search_simple_dictionary; CREATE TABLE "Message" ( id SERIAL PRIMARY KEY, target INTEGER NOT NULL REFERENCES "MessageTarget"(id) ON DELETE CASCADE, @@ -218,7 +218,7 @@ var postgresMigrations = []string{ time TIMESTAMP WITH TIME ZONE NOT NULL, sender TEXT NOT NULL, text TEXT, - text_search tsvector GENERATED ALWAYS AS (to_tsvector('search_simple', text)) STORED + text_search tsvector GENERATED ALWAYS AS (to_tsvector('@SCHEMA_PREFIX@search_simple', text)) STORED ); CREATE INDEX "MessageIndex" ON "Message" (target, time); CREATE INDEX "MessageSearchIndex" ON "Message" USING GIN (text_search); @@ -226,7 +226,8 @@ var postgresMigrations = []string{ } type PostgresDB struct { - db *sql.DB + db *sql.DB + temp bool } func OpenPostgresDB(source string) (Database, error) { @@ -270,7 +271,7 @@ func OpenTempPostgresDB(source string) (Database, error) { return nil, err } - db := &PostgresDB{db: sqlPostgresDB} + db := &PostgresDB{db: sqlPostgresDB, temp: true} if err := db.upgrade(); err != nil { sqlPostgresDB.Close() return nil, err @@ -279,6 +280,15 @@ func OpenTempPostgresDB(source string) (Database, error) { return db, nil } +func (db *PostgresDB) template(t string) string { + // Hack to convince postgres to lookup text search configurations in + // pg_temp + if db.temp { + return strings.ReplaceAll(t, "@SCHEMA_PREFIX@", "pg_temp.") + } + return strings.ReplaceAll(t, "@SCHEMA_PREFIX@", "") +} + func (db *PostgresDB) upgrade() error { tx, err := db.db.Begin() if err != nil { @@ -304,12 +314,12 @@ func (db *PostgresDB) upgrade() error { } if version == 0 { - if _, err := tx.Exec(postgresSchema); err != nil { + if _, err := tx.Exec(db.template(postgresSchema)); err != nil { return fmt.Errorf("failed to initialize schema: %s", err) } } else { for i := version; i < len(postgresMigrations); i++ { - if _, err := tx.Exec(postgresMigrations[i]); err != nil { + if _, err := tx.Exec(db.template(postgresMigrations[i])); err != nil { return fmt.Errorf("failed to execute migration #%v: %v", i, err) } } diff --git a/database/postgres_test.go b/database/postgres_test.go index 4df736b..a08b286 100644 --- a/database/postgres_test.go +++ b/database/postgres_test.go @@ -78,13 +78,13 @@ func TestPostgresMigrations(t *testing.T) { t.Fatalf("openTempPostgresDB() failed: %v", err) } - if _, err := sqlDB.Exec(postgresV0Schema); err != nil { + db := &PostgresDB{db: sqlDB, temp: true} + defer db.Close() + + if _, err := db.db.Exec(postgresV0Schema); err != nil { t.Fatalf("DB.Exec() failed for v0 schema: %v", err) } - db := &PostgresDB{db: sqlDB} - defer db.Close() - if err := db.upgrade(); err != nil { t.Fatalf("PostgresDB.Upgrade() failed: %v", err) }