@socialgouv/matomo-postgres 2.4.2 → 2.4.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/migrate-latest.js
CHANGED
|
@@ -12,6 +12,7 @@ import { FileMigrationProvider, Migrator } from 'kysely';
|
|
|
12
12
|
import * as path from 'path';
|
|
13
13
|
import { MATOMO_TABLE_NAME } from './config.js';
|
|
14
14
|
import { db } from './db.js';
|
|
15
|
+
import { syncDestinationTableSchema } from './syncDestinationTableSchema.js';
|
|
15
16
|
function migrateToLatest() {
|
|
16
17
|
return __awaiter(this, void 0, void 0, function* () {
|
|
17
18
|
console.log(`Starting migrate to latest`);
|
|
@@ -44,6 +45,7 @@ function migrateToLatest() {
|
|
|
44
45
|
else if (!(results === null || results === void 0 ? void 0 : results.length)) {
|
|
45
46
|
console.log('No migration to run');
|
|
46
47
|
}
|
|
48
|
+
yield syncDestinationTableSchema(db);
|
|
47
49
|
}
|
|
48
50
|
catch (uncaughtError) {
|
|
49
51
|
console.error('UNCAUGHT ERROR during migration:');
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { sql } from 'kysely';
|
|
11
|
+
export function up(db) {
|
|
12
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
13
|
+
// The EXCEPTION handler was added to 20250715-01-weekly-partitioning.ts in
|
|
14
|
+
// commit 7d703de, but that file had already been applied on existing
|
|
15
|
+
// databases, so kysely never re-ran it. This migration re-applies the
|
|
16
|
+
// CREATE OR REPLACE on already-deployed databases so they stop crashing
|
|
17
|
+
// with `relation "matomo_partitioned_YYYYwWW" already exists` under the
|
|
18
|
+
// p-all concurrent import.
|
|
19
|
+
yield sql `
|
|
20
|
+
CREATE OR REPLACE FUNCTION create_weekly_partition_if_not_exists(table_name text, partition_date timestamptz)
|
|
21
|
+
RETURNS void AS $$
|
|
22
|
+
DECLARE
|
|
23
|
+
partition_name text;
|
|
24
|
+
start_date timestamptz;
|
|
25
|
+
end_date timestamptz;
|
|
26
|
+
year_week text;
|
|
27
|
+
BEGIN
|
|
28
|
+
start_date := date_trunc('week', partition_date);
|
|
29
|
+
end_date := start_date + interval '1 week';
|
|
30
|
+
|
|
31
|
+
year_week := to_char(start_date, 'IYYY') || 'w' || to_char(start_date, 'IW');
|
|
32
|
+
partition_name := table_name || '_' || year_week;
|
|
33
|
+
|
|
34
|
+
IF NOT EXISTS (
|
|
35
|
+
SELECT 1 FROM pg_class c
|
|
36
|
+
JOIN pg_namespace n ON n.oid = c.relnamespace
|
|
37
|
+
WHERE c.relname = partition_name
|
|
38
|
+
AND n.nspname = current_schema()
|
|
39
|
+
) THEN
|
|
40
|
+
EXECUTE format('CREATE TABLE %I PARTITION OF %I FOR VALUES FROM (%L) TO (%L)',
|
|
41
|
+
partition_name, table_name, start_date, end_date);
|
|
42
|
+
|
|
43
|
+
RAISE NOTICE 'Created partition % for range % to %', partition_name, start_date, end_date;
|
|
44
|
+
END IF;
|
|
45
|
+
EXCEPTION
|
|
46
|
+
WHEN duplicate_table THEN
|
|
47
|
+
RAISE NOTICE 'Partition % already exists (created concurrently)', partition_name;
|
|
48
|
+
END;
|
|
49
|
+
$$ LANGUAGE plpgsql;
|
|
50
|
+
`.execute(db);
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
export function down(_db) {
|
|
54
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
55
|
+
// No-op: rolling back would re-introduce the race condition crash.
|
|
56
|
+
});
|
|
57
|
+
}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
2
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
3
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
4
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
5
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
6
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
7
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
8
|
+
});
|
|
9
|
+
};
|
|
10
|
+
import { sql } from 'kysely';
|
|
11
|
+
import { DESTINATION_TABLE, MATOMO_TABLE_NAME, PARTITIONED_MATOMO_TABLE_NAME } from './config.js';
|
|
12
|
+
// Kysely migrations operate on MATOMO_TABLE_NAME (default `matomo`), but
|
|
13
|
+
// several deployments run multiple cronjobs against a single database with
|
|
14
|
+
// distinct DESTINATION_TABLEs (matomo_back, matomo_app, matomo_landing, ...).
|
|
15
|
+
// Schema changes applied to `matomo` never reach those custom destinations,
|
|
16
|
+
// and INSERTs fail with `column "..." does not exist`. After running the
|
|
17
|
+
// regular migrations, mirror any missing columns from MATOMO_TABLE_NAME onto
|
|
18
|
+
// DESTINATION_TABLE so writes stay schema-compatible.
|
|
19
|
+
export function syncDestinationTableSchema(db) {
|
|
20
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
21
|
+
if (DESTINATION_TABLE === MATOMO_TABLE_NAME ||
|
|
22
|
+
DESTINATION_TABLE === PARTITIONED_MATOMO_TABLE_NAME) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
const { rows } = yield sql `
|
|
26
|
+
SELECT a.attname, format_type(a.atttypid, a.atttypmod) AS coltype
|
|
27
|
+
FROM pg_attribute a
|
|
28
|
+
WHERE a.attrelid = ${MATOMO_TABLE_NAME}::regclass
|
|
29
|
+
AND a.attnum > 0
|
|
30
|
+
AND NOT a.attisdropped
|
|
31
|
+
AND NOT EXISTS (
|
|
32
|
+
SELECT 1 FROM pg_attribute b
|
|
33
|
+
WHERE b.attrelid = ${DESTINATION_TABLE}::regclass
|
|
34
|
+
AND b.attname = a.attname
|
|
35
|
+
AND b.attnum > 0
|
|
36
|
+
AND NOT b.attisdropped
|
|
37
|
+
)
|
|
38
|
+
`.execute(db);
|
|
39
|
+
for (const { attname, coltype } of rows) {
|
|
40
|
+
console.log(`Syncing schema: adding column "${attname}" ${coltype} to ${DESTINATION_TABLE}`);
|
|
41
|
+
yield sql `
|
|
42
|
+
ALTER TABLE ${sql.id(DESTINATION_TABLE)}
|
|
43
|
+
ADD COLUMN ${sql.id(attname)} ${sql.raw(coltype)}
|
|
44
|
+
`.execute(db);
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
package/package.json
CHANGED