refactor: shared internal postgres package around slonik (#7887)

Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com>
Co-authored-by: n1ru4l <14338007+n1ru4l@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Laurin Quast <laurinquast@googlemail.com>
This commit is contained in:
Copilot 2026-03-27 10:20:05 +01:00 committed by GitHub
parent e83bc29e2a
commit aac23596ec
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
194 changed files with 3635 additions and 3333 deletions

View file

@ -20,6 +20,7 @@
"@graphql-hive/core": "workspace:*", "@graphql-hive/core": "workspace:*",
"@graphql-typed-document-node/core": "3.2.0", "@graphql-typed-document-node/core": "3.2.0",
"@hive/commerce": "workspace:*", "@hive/commerce": "workspace:*",
"@hive/postgres": "workspace:*",
"@hive/schema": "workspace:*", "@hive/schema": "workspace:*",
"@hive/server": "workspace:*", "@hive/server": "workspace:*",
"@hive/service-common": "workspace:*", "@hive/service-common": "workspace:*",
@ -42,7 +43,6 @@
"human-id": "4.1.1", "human-id": "4.1.1",
"ioredis": "5.8.2", "ioredis": "5.8.2",
"set-cookie-parser": "2.7.1", "set-cookie-parser": "2.7.1",
"slonik": "30.4.4",
"strip-ansi": "7.1.2", "strip-ansi": "7.1.2",
"tslib": "2.8.1", "tslib": "2.8.1",
"vitest": "4.0.9", "vitest": "4.0.9",

View file

@ -1,10 +1,10 @@
import { DatabasePool } from 'slonik';
import { import {
AccessTokenKeyContainer, AccessTokenKeyContainer,
hashPassword, hashPassword,
} from '@hive/api/modules/auth/lib/supertokens-at-home/crypto'; } from '@hive/api/modules/auth/lib/supertokens-at-home/crypto';
import { SuperTokensStore } from '@hive/api/modules/auth/providers/supertokens-store'; import { SuperTokensStore } from '@hive/api/modules/auth/providers/supertokens-store';
import { NoopLogger } from '@hive/api/modules/shared/providers/logger'; import { NoopLogger } from '@hive/api/modules/shared/providers/logger';
import { PostgresDatabasePool } from '@hive/postgres';
import type { InternalApi } from '@hive/server'; import type { InternalApi } from '@hive/server';
import { createNewSession } from '@hive/server/supertokens-at-home/shared'; import { createNewSession } from '@hive/server/supertokens-at-home/shared';
import { createTRPCProxyClient, httpLink } from '@trpc/client'; import { createTRPCProxyClient, httpLink } from '@trpc/client';
@ -76,7 +76,7 @@ const tokenResponsePromise: {
} = {}; } = {};
export async function authenticate( export async function authenticate(
pool: DatabasePool, pool: PostgresDatabasePool,
email: string, email: string,
oidcIntegrationId?: string, oidcIntegrationId?: string,
): Promise<{ access_token: string; refresh_token: string; supertokensUserId: string }> { ): Promise<{ access_token: string; refresh_token: string; supertokensUserId: string }> {

View file

@ -1,9 +1,9 @@
import type { AddressInfo } from 'node:net'; import type { AddressInfo } from 'node:net';
import humanId from 'human-id'; import humanId from 'human-id';
import setCookie from 'set-cookie-parser'; import setCookie from 'set-cookie-parser';
import { sql, type DatabasePool } from 'slonik';
import z from 'zod'; import z from 'zod';
import formDataPlugin from '@fastify/formbody'; import formDataPlugin from '@fastify/formbody';
import { psql, type PostgresDatabasePool } from '@hive/postgres';
import { createServer, type FastifyReply, type FastifyRequest } from '@hive/service-common'; import { createServer, type FastifyReply, type FastifyRequest } from '@hive/service-common';
import { graphql } from './gql'; import { graphql } from './gql';
import { execute } from './graphql'; import { execute } from './graphql';
@ -157,7 +157,7 @@ const VerifyEmailMutation = graphql(`
export async function createOIDCIntegration(args: { export async function createOIDCIntegration(args: {
organizationId: string; organizationId: string;
accessToken: string; accessToken: string;
getPool: () => Promise<DatabasePool>; getPool: () => Promise<PostgresDatabasePool>;
}) { }) {
const { accessToken: authToken, getPool } = args; const { accessToken: authToken, getPool } = args;
const result = await execute({ const result = await execute({
@ -192,7 +192,7 @@ export async function createOIDCIntegration(args: {
}) + '.local'; }) + '.local';
const pool = await getPool(); const pool = await getPool();
const query = sql` const query = psql`
INSERT INTO "oidc_integration_domains" ( INSERT INTO "oidc_integration_domains" (
"organization_id" "organization_id"
, "oidc_integration_id" , "oidc_integration_id"

View file

@ -1,8 +1,9 @@
import { formatISO, subHours } from 'date-fns'; import { formatISO, subHours } from 'date-fns';
import { humanId } from 'human-id'; import { humanId } from 'human-id';
import { createPool, sql } from 'slonik'; import z from 'zod';
import { NoopLogger } from '@hive/api/modules/shared/providers/logger'; import { NoopLogger } from '@hive/api/modules/shared/providers/logger';
import { createRedisClient } from '@hive/api/modules/shared/providers/redis'; import { createRedisClient } from '@hive/api/modules/shared/providers/redis';
import { createPostgresDatabasePool, psql } from '@hive/postgres';
import type { Report } from '../../packages/libraries/core/src/client/usage.js'; import type { Report } from '../../packages/libraries/core/src/client/usage.js';
import { authenticate, userEmail } from './auth'; import { authenticate, userEmail } from './auth';
import { import {
@ -82,9 +83,9 @@ function createConnectionPool() {
db: ensureEnv('POSTGRES_DB'), db: ensureEnv('POSTGRES_DB'),
}; };
return createPool( return createPostgresDatabasePool({
`postgres://${pg.user}:${pg.password}@${pg.host}:${pg.port}/${pg.db}?sslmode=disable`, connectionParameters: `postgres://${pg.user}:${pg.password}@${pg.host}:${pg.port}/${pg.db}?sslmode=disable`,
); });
} }
async function createDbConnection() { async function createDbConnection() {
@ -97,9 +98,9 @@ async function createDbConnection() {
}; };
} }
export function initSeed() { let sharedDBPoolPromise: ReturnType<typeof createDbConnection>;
let sharedDBPoolPromise: ReturnType<typeof createDbConnection>;
export function initSeed() {
function getPool() { function getPool() {
if (!sharedDBPoolPromise) { if (!sharedDBPoolPromise) {
sharedDBPoolPromise = createDbConnection(); sharedDBPoolPromise = createDbConnection();
@ -118,7 +119,7 @@ export function initSeed() {
if (opts?.verifyEmail ?? true) { if (opts?.verifyEmail ?? true) {
const pool = await getPool(); const pool = await getPool();
await pool.query(sql` await pool.query(psql`
INSERT INTO "email_verifications" ("user_identity_id", "email", "verified_at") INSERT INTO "email_verifications" ("user_identity_id", "email", "verified_at")
VALUES (${auth.supertokensUserId}, ${email}, NOW()) VALUES (${auth.supertokensUserId}, ${email}, NOW())
`); `);
@ -142,18 +143,22 @@ export function initSeed() {
pollForEmailVerificationLink, pollForEmailVerificationLink,
async purgeOIDCDomains() { async purgeOIDCDomains() {
const pool = await getPool(); const pool = await getPool();
await pool.query(sql` await pool.query(psql`
TRUNCATE "oidc_integration_domains" TRUNCATE "oidc_integration_domains"
`); `);
}, },
async forgeOIDCDNSChallenge(orgSlug: string) { async forgeOIDCDNSChallenge(orgSlug: string) {
const pool = await getPool(); const pool = await getPool();
const domainChallengeId = await pool.oneFirst<string>(sql` const domainChallengeId = await pool
.oneFirst(
psql`
SELECT "oidc_integration_domains"."id" SELECT "oidc_integration_domains"."id"
FROM "oidc_integration_domains" INNER JOIN "organizations" ON "oidc_integration_domains"."organization_id" = "organizations"."id" FROM "oidc_integration_domains" INNER JOIN "organizations" ON "oidc_integration_domains"."organization_id" = "organizations"."id"
WHERE "organizations"."clean_id" = ${orgSlug} WHERE "organizations"."clean_id" = ${orgSlug}
`); `,
)
.then(z.string().parse);
const key = `hive:oidcDomainChallenge:${domainChallengeId}`; const key = `hive:oidcDomainChallenge:${domainChallengeId}`;
const challenge = { const challenge = {
@ -208,7 +213,7 @@ export function initSeed() {
async overrideOrgPlan(plan: 'PRO' | 'ENTERPRISE' | 'HOBBY') { async overrideOrgPlan(plan: 'PRO' | 'ENTERPRISE' | 'HOBBY') {
const pool = await createConnectionPool(); const pool = await createConnectionPool();
await pool.query(sql` await pool.query(psql`
UPDATE organizations SET plan_name = ${plan} WHERE id = ${organization.id} UPDATE organizations SET plan_name = ${plan} WHERE id = ${organization.id}
`); `);
@ -260,8 +265,8 @@ export function initSeed() {
async setFeatureFlag(name: string, value: boolean | string[]) { async setFeatureFlag(name: string, value: boolean | string[]) {
const pool = await createConnectionPool(); const pool = await createConnectionPool();
await pool.query(sql` await pool.query(psql`
UPDATE organizations SET feature_flags = ${sql.jsonb({ UPDATE organizations SET feature_flags = ${psql.jsonb({
[name]: value, [name]: value,
})} })}
WHERE id = ${organization.id} WHERE id = ${organization.id}
@ -272,7 +277,7 @@ export function initSeed() {
async setDataRetention(days: number) { async setDataRetention(days: number) {
const pool = await createConnectionPool(); const pool = await createConnectionPool();
await pool.query(sql` await pool.query(psql`
UPDATE organizations SET limit_retention_days = ${days} WHERE id = ${organization.id} UPDATE organizations SET limit_retention_days = ${days} WHERE id = ${organization.id}
`); `);
@ -340,13 +345,15 @@ export function initSeed() {
/** Expires tokens */ /** Expires tokens */
async forceExpireTokens(tokenIds: string[]) { async forceExpireTokens(tokenIds: string[]) {
const pool = await createConnectionPool(); const pool = await createConnectionPool();
const result = await pool.query(sql` const result = await pool.any(psql`
UPDATE "organization_access_tokens" UPDATE "organization_access_tokens"
SET "expires_at"=NOW() SET "expires_at" = NOW()
WHERE id IN (${sql.join(tokenIds, sql`, `)}) AND organization_id=${organization.id} WHERE id IN (${psql.join(tokenIds, psql`, `)}) AND organization_id = ${organization.id}
RETURNING
"id"
`); `);
await pool.end(); await pool.end();
expect(result.rowCount).toBe(tokenIds.length); expect(result.length).toBe(tokenIds.length);
for (const id of tokenIds) { for (const id of tokenIds) {
await purgeOrganizationAccessTokenById(id); await purgeOrganizationAccessTokenById(id);
} }
@ -390,7 +397,7 @@ export function initSeed() {
async setNativeFederation(enabled: boolean) { async setNativeFederation(enabled: boolean) {
const pool = await createConnectionPool(); const pool = await createConnectionPool();
await pool.query(sql` await pool.query(psql`
UPDATE projects SET native_federation = ${enabled} WHERE id = ${project.id} UPDATE projects SET native_federation = ${enabled} WHERE id = ${project.id}
`); `);

View file

@ -1,11 +1,12 @@
import { buildASTSchema, parse } from 'graphql'; import { buildASTSchema, parse } from 'graphql';
import { createLogger } from 'graphql-yoga'; import { createLogger } from 'graphql-yoga';
import { sql } from 'slonik';
import { pollFor } from 'testkit/flow'; import { pollFor } from 'testkit/flow';
import { initSeed } from 'testkit/seed'; import { initSeed } from 'testkit/seed';
import { getServiceHost } from 'testkit/utils'; import { getServiceHost } from 'testkit/utils';
import z from 'zod';
import { createHive } from '@graphql-hive/core'; import { createHive } from '@graphql-hive/core';
import { clickHouseInsert, clickHouseQuery } from '../../testkit/clickhouse'; import { psql } from '@hive/postgres';
import { clickHouseInsert } from '../../testkit/clickhouse';
import { graphql } from '../../testkit/gql'; import { graphql } from '../../testkit/gql';
import { execute } from '../../testkit/graphql'; import { execute } from '../../testkit/graphql';
@ -2056,12 +2057,20 @@ test('activeAppDeployments works for > 1000 records with a date filter (neverUse
); );
// insert into postgres // insert into postgres
const result = await conn.pool.query(sql` const result = await conn.pool
.any(
psql`
INSERT INTO app_deployments ("target_id", "name", "version", "activated_at") INSERT INTO app_deployments ("target_id", "name", "version", "activated_at")
SELECT * FROM ${sql.unnest(appDeploymentRows, ['uuid', 'text', 'text', 'timestamptz'])} SELECT * FROM ${psql.unnest(appDeploymentRows, ['uuid', 'text', 'text', 'timestamptz'])}
RETURNING "id", "target_id", "name", "version" RETURNING "id", "target_id", "name", "version"
`); `,
expect(result.rowCount).toBe(1200); )
.then(
z.array(
z.object({ id: z.string(), target_id: z.string(), name: z.string(), version: z.string() }),
).parse,
);
expect(result.length).toBe(1200);
// insert into clickhouse and activate // insert into clickhouse and activate
const query = `INSERT INTO app_deployments ( const query = `INSERT INTO app_deployments (
@ -2071,7 +2080,7 @@ test('activeAppDeployments works for > 1000 records with a date filter (neverUse
,"app_version" ,"app_version"
,"is_active" ,"is_active"
) VALUES ) VALUES
${result.rows ${result
.map( .map(
r => `( r => `(
'${r['target_id']}' '${r['target_id']}'

View file

@ -1,21 +1,28 @@
import 'reflect-metadata'; import 'reflect-metadata';
import { sql, type CommonQueryMethods } from 'slonik';
/* eslint-disable no-process-env */ /* eslint-disable no-process-env */
import { ProjectType } from 'testkit/gql/graphql'; import { ProjectType } from 'testkit/gql/graphql';
import { test } from 'vitest'; import { test } from 'vitest';
import z from 'zod';
import { psql, type CommonQueryMethods } from '@hive/postgres';
import { initSeed } from '../../../testkit/seed'; import { initSeed } from '../../../testkit/seed';
async function fetchCoordinates(db: CommonQueryMethods, target: { id: string }) { async function fetchCoordinates(db: CommonQueryMethods, target: { id: string }) {
const result = await db.query<{ const result = await db
coordinate: string; .any(
created_in_version_id: string; psql`
deprecated_in_version_id: string | null;
}>(sql`
SELECT coordinate, created_in_version_id, deprecated_in_version_id SELECT coordinate, created_in_version_id, deprecated_in_version_id
FROM schema_coordinate_status WHERE target_id = ${target.id} FROM schema_coordinate_status WHERE target_id = ${target.id}
`); `,
)
.then(
z.object({
coordinate: z.string(),
created_in_version_id: z.string(),
deprecated_in_version_id: z.string().nullable(),
}).parse,
);
return result.rows; return result;
} }
describe.skip('schema cleanup tracker', () => { describe.skip('schema cleanup tracker', () => {

View file

@ -1,10 +1,11 @@
import 'reflect-metadata'; import 'reflect-metadata';
import { createPool, sql } from 'slonik';
import { graphql } from 'testkit/gql'; import { graphql } from 'testkit/gql';
/* eslint-disable no-process-env */ /* eslint-disable no-process-env */
import { ProjectType } from 'testkit/gql/graphql'; import { ProjectType } from 'testkit/gql/graphql';
import { execute } from 'testkit/graphql'; import { execute } from 'testkit/graphql';
import { assertNonNull, getServiceHost } from 'testkit/utils'; import { assertNonNull, getServiceHost } from 'testkit/utils';
import z from 'zod';
import { createPostgresDatabasePool, psql } from '@hive/postgres';
// eslint-disable-next-line import/no-extraneous-dependencies // eslint-disable-next-line import/no-extraneous-dependencies
import { createStorage } from '@hive/storage'; import { createStorage } from '@hive/storage';
import { createTarget, publishSchema, updateSchemaComposition } from '../../../testkit/flow'; import { createTarget, publishSchema, updateSchemaComposition } from '../../../testkit/flow';
@ -3820,7 +3821,7 @@ test.concurrent(
); );
const insertLegacyVersion = async ( const insertLegacyVersion = async (
pool: Awaited<ReturnType<typeof createPool>>, pool: Awaited<ReturnType<typeof createPostgresDatabasePool>>,
args: { args: {
sdl: string; sdl: string;
projectId: string; projectId: string;
@ -3828,7 +3829,9 @@ const insertLegacyVersion = async (
serviceUrl: string; serviceUrl: string;
}, },
) => { ) => {
const logId = await pool.oneFirst<string>(sql` const logId = await pool
.oneFirst(
psql`
INSERT INTO schema_log INSERT INTO schema_log
( (
author, author,
@ -3854,9 +3857,13 @@ const insertLegacyVersion = async (
'PUSH' 'PUSH'
) )
RETURNING id RETURNING id
`); `,
)
.then(z.string().parse);
const versionId = await pool.oneFirst<string>(sql` const versionId = await pool
.oneFirst(
psql`
INSERT INTO schema_versions INSERT INTO schema_versions
( (
is_composable, is_composable,
@ -3870,9 +3877,11 @@ const insertLegacyVersion = async (
${logId} ${logId}
) )
RETURNING "id" RETURNING "id"
`); `,
)
.then(z.string().parse);
await pool.query(sql` await pool.query(psql`
INSERT INTO INSERT INTO
schema_version_to_log schema_version_to_log
(version_id, action_id) (version_id, action_id)
@ -3886,7 +3895,7 @@ const insertLegacyVersion = async (
test.concurrent( test.concurrent(
'service url change from legacy to new version is displayed correctly', 'service url change from legacy to new version is displayed correctly',
async ({ expect }) => { async ({ expect }) => {
let pool: Awaited<ReturnType<typeof createPool>> | undefined; let pool: Awaited<ReturnType<typeof createPostgresDatabasePool>> | undefined;
try { try {
const { createOrg } = await initSeed().createOwner(); const { createOrg } = await initSeed().createOwner();
const { createProject } = await createOrg(); const { createProject } = await createOrg();
@ -3899,7 +3908,9 @@ test.concurrent(
// We need to seed a legacy entry in the database // We need to seed a legacy entry in the database
const conn = connectionString(); const conn = connectionString();
pool = await createPool(conn); pool = await createPostgresDatabasePool({
connectionParameters: conn,
});
const sdl = 'type Query { ping: String! }'; const sdl = 'type Query { ping: String! }';
@ -3950,7 +3961,7 @@ test.concurrent(
test.concurrent( test.concurrent(
'service url change from legacy to legacy version is displayed correctly', 'service url change from legacy to legacy version is displayed correctly',
async ({ expect }) => { async ({ expect }) => {
let pool: Awaited<ReturnType<typeof createPool>> | undefined; let pool: Awaited<ReturnType<typeof createPostgresDatabasePool>> | undefined;
try { try {
const { createOrg } = await initSeed().createOwner(); const { createOrg } = await initSeed().createOwner();
const { createProject } = await createOrg(); const { createProject } = await createOrg();
@ -3963,7 +3974,7 @@ test.concurrent(
// We need to seed a legacy entry in the database // We need to seed a legacy entry in the database
const conn = connectionString(); const conn = connectionString();
pool = await createPool(conn); pool = await createPostgresDatabasePool({ connectionParameters: conn });
const sdl = 'type Query { ping: String! }'; const sdl = 'type Query { ping: String! }';

View file

@ -2,12 +2,13 @@ import { existsSync, rmSync, writeFileSync } from 'node:fs';
import { createServer } from 'node:http'; import { createServer } from 'node:http';
import { tmpdir } from 'node:os'; import { tmpdir } from 'node:os';
import { join } from 'node:path'; import { join } from 'node:path';
import { MaybePromise } from 'slonik/dist/src/types';
import { ProjectType } from 'testkit/gql/graphql'; import { ProjectType } from 'testkit/gql/graphql';
import { initSeed } from 'testkit/seed'; import { initSeed } from 'testkit/seed';
import { getServiceHost } from 'testkit/utils'; import { getServiceHost } from 'testkit/utils';
import { execa } from '@esm2cjs/execa'; import { execa } from '@esm2cjs/execa';
type MaybePromise<T> = T | Promise<T>;
describe('Apollo Router Integration', () => { describe('Apollo Router Integration', () => {
const getAvailablePort = () => const getAvailablePort = () =>
new Promise<number>(resolve => { new Promise<number>(resolve => {

View file

@ -74,7 +74,6 @@ test('update-retention script skips gracefully when no env vars are set', async
delete process.env.CLICKHOUSE_TTL_HOURLY_MV_TABLES; delete process.env.CLICKHOUSE_TTL_HOURLY_MV_TABLES;
delete process.env.CLICKHOUSE_TTL_MINUTELY_MV_TABLES; delete process.env.CLICKHOUSE_TTL_MINUTELY_MV_TABLES;
vi.resetModules();
const { updateRetention } = await import( const { updateRetention } = await import(
'../../../packages/migrations/src/scripts/update-retention' '../../../packages/migrations/src/scripts/update-retention'
); );

View file

@ -0,0 +1,3 @@
# Internal Postgres Client
This is a lightweight abstraction on top of Slonik, that sets up some things for ease of usage.

View file

@ -0,0 +1,16 @@
{
"name": "@hive/postgres",
"type": "module",
"license": "MIT",
"private": true,
"exports": {
".": "./src/index.ts"
},
"dependencies": {
"slonik": "30.4.4",
"slonik-interceptor-query-logging": "46.4.0"
},
"devDependencies": {
"@hive/service-common": "workspace:*"
}
}

View file

@ -1,11 +1,14 @@
export function createConnectionString(config: { export type PostgresConnectionParamaters = {
host: string; host: string;
port: number; port: number;
password: string | undefined; password: string | undefined;
user: string; user: string;
db: string; db: string;
ssl: boolean; ssl: boolean;
}) { };
/** Create a Postgres Connection String */
export function createConnectionString(config: PostgresConnectionParamaters) {
// prettier-ignore // prettier-ignore
const encodedUser = encodeURIComponent(config.user); const encodedUser = encodeURIComponent(config.user);
const encodedPassword = const encodedPassword =

View file

@ -0,0 +1,18 @@
export {
PostgresDatabasePool,
createPostgresDatabasePool,
type CommonQueryMethods,
} from './postgres-database-pool';
export { type PostgresConnectionParamaters, createConnectionString } from './connection-string';
export { psql } from './psql';
export {
UniqueIntegrityConstraintViolationError,
ForeignKeyIntegrityConstraintViolationError,
type TaggedTemplateLiteralInvocation,
type PrimitiveValueExpression,
type SerializableValue,
type Interceptor,
type Query,
type QueryContext,
} from 'slonik';
export { toDate } from './utils';

View file

@ -0,0 +1,246 @@
import {
createPool,
type DatabasePool,
type Interceptor,
type PrimitiveValueExpression,
type QueryResultRow,
type QueryResultRowColumn,
type CommonQueryMethods as SlonikCommonQueryMethods,
type TaggedTemplateLiteralInvocation,
} from 'slonik';
import { createQueryLoggingInterceptor } from 'slonik-interceptor-query-logging';
import { context, SpanKind, SpanStatusCode, trace } from '@hive/service-common';
import { createConnectionString, type PostgresConnectionParamaters } from './connection-string';
const tracer = trace.getTracer('storage');
export interface CommonQueryMethods {
exists(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<boolean>;
any(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>>;
maybeOne(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown>;
query(sql: TaggedTemplateLiteralInvocation, values?: PrimitiveValueExpression[]): Promise<void>;
oneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown>;
one(sql: TaggedTemplateLiteralInvocation, values?: PrimitiveValueExpression[]): Promise<unknown>;
anyFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>>;
maybeOneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown>;
}
export class PostgresDatabasePool implements CommonQueryMethods {
constructor(private pool: DatabasePool) {}
/** Retrieve the raw PgPool instance. Refrain from using this API. It only exists for postgraphile workers */
getRawPgPool() {
return this.pool.pool;
}
/** Retrieve the raw Slonik instance. Refrain from using this API. */
getSlonikPool() {
return this.pool;
}
async exists(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<boolean> {
return this.pool.exists(sql, values);
}
async any(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>> {
return this.pool.any<unknown>(sql, values);
}
async maybeOne(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return this.pool.maybeOne(sql, values);
}
async query(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<void> {
await this.pool.query<unknown>(sql, values);
}
async oneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await this.pool.oneFirst(sql, values);
}
async maybeOneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await this.pool.maybeOneFirst(sql, values);
}
async one(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await this.pool.one(sql, values);
}
async anyFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>> {
return await this.pool.anyFirst(sql, values);
}
async transaction<T = void>(
name: string,
handler: (methods: CommonQueryMethods) => Promise<T>,
): Promise<T> {
const span = tracer.startSpan(`PG Transaction: ${name}`, {
kind: SpanKind.INTERNAL,
});
return context.with(trace.setSpan(context.active(), span), async () => {
return await this.pool.transaction(async methods => {
try {
return await handler({
async exists(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<boolean> {
return methods.exists(sql, values);
},
async any(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>> {
return methods.any<unknown>(sql, values);
},
async maybeOne(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return methods.maybeOne(sql, values);
},
async query(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<void> {
await methods.query<unknown>(sql, values);
},
async oneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await methods.oneFirst(sql, values);
},
async maybeOneFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await methods.maybeOneFirst(sql, values);
},
async anyFirst(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<ReadonlyArray<unknown>> {
return await methods.anyFirst(sql, values);
},
async one(
sql: TaggedTemplateLiteralInvocation,
values?: PrimitiveValueExpression[],
): Promise<unknown> {
return await methods.one(sql, values);
},
});
} catch (err) {
span.setAttribute('error', 'true');
if (err instanceof Error) {
span.setAttribute('error.type', err.name);
span.setAttribute('error.message', err.message);
span.setStatus({
code: SpanStatusCode.ERROR,
message: err.message,
});
}
throw err;
} finally {
span.end();
}
});
});
}
end(): Promise<void> {
return this.pool.end();
}
}
const dbInterceptors: Interceptor[] = [createQueryLoggingInterceptor()];
export async function createPostgresDatabasePool(args: {
connectionParameters: PostgresConnectionParamaters | string;
maximumPoolSize?: number;
additionalInterceptors?: Interceptor[];
statementTimeout?: number;
}) {
const connectionString =
typeof args.connectionParameters === 'string'
? args.connectionParameters
: createConnectionString(args.connectionParameters);
const pool = await createPool(connectionString, {
interceptors: dbInterceptors.concat(args.additionalInterceptors ?? []),
captureStackTrace: false,
maximumPoolSize: args.maximumPoolSize,
idleTimeout: 30000,
statementTimeout: args.statementTimeout,
});
function interceptError<K extends Exclude<keyof SlonikCommonQueryMethods, 'transaction'>>(
methodName: K,
) {
const original: SlonikCommonQueryMethods[K] = pool[methodName];
function interceptor<T extends QueryResultRow>(
this: any,
sql: TaggedTemplateLiteralInvocation<T>,
values?: QueryResultRowColumn[],
): any {
return (original as any).call(this, sql, values).catch((error: any) => {
error.sql = sql.sql;
error.values = sql.values || values;
return Promise.reject(error);
});
}
pool[methodName] = interceptor;
}
interceptError('one');
interceptError('many');
return new PostgresDatabasePool(pool);
}

View file

@ -0,0 +1,3 @@
import { createSqlTag } from 'slonik';
export const psql = createSqlTag();

View file

@ -0,0 +1,5 @@
import { psql } from './psql';
export function toDate(date: Date) {
return psql`to_timestamp(${date.getTime() / 1000})`;
}

View file

@ -13,10 +13,12 @@
"db:init": "pnpm db:create && pnpm migration:run", "db:init": "pnpm db:create && pnpm migration:run",
"db:migrator": "tsx src/index.ts", "db:migrator": "tsx src/index.ts",
"migration:run": "pnpm db:migrator up", "migration:run": "pnpm db:migrator up",
"test": "WATCH=0 tsup-node --config ../../configs/tsup/dev.config.node.ts ./test/root.ts" "test": "WATCH=0 tsup-node --config ../../configs/tsup/dev.config.node.ts ./test/root.ts",
"typecheck": "tsc --noEmit"
}, },
"devDependencies": { "devDependencies": {
"@graphql-hive/core": "workspace:*", "@graphql-hive/core": "workspace:*",
"@hive/postgres": "workspace:*",
"@hive/service-common": "workspace:*", "@hive/service-common": "workspace:*",
"@types/bcryptjs": "2.4.6", "@types/bcryptjs": "2.4.6",
"@types/node": "24.10.9", "@types/node": "24.10.9",
@ -30,7 +32,6 @@
"graphql": "16.9.0", "graphql": "16.9.0",
"p-limit": "6.2.0", "p-limit": "6.2.0",
"pg-promise": "11.10.2", "pg-promise": "11.10.2",
"slonik": "30.4.4",
"tslib": "2.8.1", "tslib": "2.8.1",
"tsx": "4.19.2", "tsx": "4.19.2",
"typescript": "5.7.3", "typescript": "5.7.3",

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-03-05T19-06-23.initial.sql', name: '2021-03-05T19-06-23.initial.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--initial (up) --initial (up)
-- Extensions -- Extensions
CREATE EXTENSION CREATE EXTENSION

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-03-08T11-02-26.urls.sql', name: '2021-03-08T11-02-26.urls.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--urls (up) --urls (up)
ALTER TABLE ALTER TABLE
projects projects

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-03-09T10-30-35.roles.sql', name: '2021-03-09T10-30-35.roles.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--roles (up) --roles (up)
CREATE TYPE CREATE TYPE
user_role AS ENUM('ADMIN', 'MEMBER'); user_role AS ENUM('ADMIN', 'MEMBER');

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-03-09T14-02-34.activities.sql', name: '2021-03-09T14-02-34.activities.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--activities (up) --activities (up)
CREATE TABLE CREATE TABLE
activities ( activities (

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-03-15T19-32-01.commit-project-id.sql', name: '2021-03-15T19-32-01.commit-project-id.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--commit-project-id (up) --commit-project-id (up)
ALTER TABLE ALTER TABLE
commits commits

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-04-20T11-30-30.tokens.sql', name: '2021-04-20T11-30-30.tokens.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--tokens (up) --tokens (up)
ALTER TABLE ALTER TABLE
tokens tokens

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-04-30T07-01-57.token-per-target.sql', name: '2021-04-30T07-01-57.token-per-target.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--token-per-target (up) --token-per-target (up)
ALTER TABLE ALTER TABLE
tokens tokens

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-04-30T11-47-26.validation.sql', name: '2021-04-30T11-47-26.validation.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--validation (up) --validation (up)
ALTER TABLE ALTER TABLE
targets targets

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-04-30T18-30-00.persisted-operations.sql', name: '2021-04-30T18-30-00.persisted-operations.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--persisted_operations (up) --persisted_operations (up)
CREATE TYPE CREATE TYPE
operation_kind AS ENUM('query', 'mutation', 'subscription'); operation_kind AS ENUM('query', 'mutation', 'subscription');

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-05-07T07-28-07.token-last-used-at.sql', name: '2021-05-07T07-28-07.token-last-used-at.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--token-last-used-at (up) --token-last-used-at (up)
ALTER TABLE ALTER TABLE
tokens tokens

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-06-11T10-46-24.slack-integration.sql', name: '2021-06-11T10-46-24.slack-integration.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--slack-integration (up) --slack-integration (up)
ALTER TABLE ALTER TABLE
organizations organizations

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-06-11T15-38-28.alerts.sql', name: '2021-06-11T15-38-28.alerts.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--alerts (up) --alerts (up)
CREATE TYPE CREATE TYPE
alert_channel_type AS ENUM('SLACK', 'WEBHOOK'); alert_channel_type AS ENUM('SLACK', 'WEBHOOK');

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-08-18T13-20-45.urls.sql', name: '2021-08-18T13-20-45.urls.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--urls (up) --urls (up)
ALTER TABLE ALTER TABLE
version_commit version_commit

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021-08-27T14-19-48.non-unique-emails.sql', name: '2021-08-27T14-19-48.non-unique-emails.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--non-unique-emails (up) --non-unique-emails (up)
DROP INDEX DROP INDEX
email_idx; email_idx;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021.09.17T14.45.36.token-deleted.sql', name: '2021.09.17T14.45.36.token-deleted.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
tokens tokens
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021.10.07T12.11.13.access-scopes.sql', name: '2021.10.07T12.11.13.access-scopes.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Adds scopes to tokens -- Adds scopes to tokens
ALTER TABLE ALTER TABLE
tokens tokens

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021.11.22T11.23.44.base-schema.sql', name: '2021.11.22T11.23.44.base-schema.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Adds a base schema column in target table and versions table -- Adds a base schema column in target table and versions table
ALTER TABLE ALTER TABLE
targets targets

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2021.12.20T14.05.30.commits-with-targets.sql', name: '2021.12.20T14.05.30.commits-with-targets.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--creates and fills a target_id column on commits --creates and fills a target_id column on commits
ALTER TABLE ALTER TABLE
commits commits

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.01.21T12.34.46.validation-targets.sql', name: '2022.01.21T12.34.46.validation-targets.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE CREATE TABLE
target_validation ( target_validation (
target_id UUID NOT NULL REFERENCES targets (id) ON DELETE CASCADE, target_id UUID NOT NULL REFERENCES targets (id) ON DELETE CASCADE,

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.03.28T10.31.26.github-integration.sql', name: '2022.03.28T10.31.26.github-integration.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
--slack-integration (up) --slack-integration (up)
ALTER TABLE ALTER TABLE
organizations organizations

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.04.15T14.24.17.hash-tokens.sql', name: '2022.04.15T14.24.17.hash-tokens.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
tokens tokens
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.05.03T15.58.13.org_rate_limits.sql', name: '2022.05.03T15.58.13.org_rate_limits.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
organizations organizations
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.05.04T11.01.22.billing_plans.sql', name: '2022.05.04T11.01.22.billing_plans.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE CREATE TABLE
organizations_billing ( organizations_billing (
organization_id UUID NOT NULL REFERENCES organizations (id) ON DELETE CASCADE, -- org id organization_id UUID NOT NULL REFERENCES organizations (id) ON DELETE CASCADE, -- org id

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.05.05T08.05.35.commits-metadata.sql', name: '2022.05.05T08.05.35.commits-metadata.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
commits commits
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.07.07T12.15.10.no-schema-pushes-limit.sql', name: '2022.07.07T12.15.10.no-schema-pushes-limit.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
organizations organizations
DROP COLUMN DROP COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.07.11T10.09.41.get-started-wizard.sql', name: '2022.07.11T10.09.41.get-started-wizard.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Tracks feature discovery progress -- Tracks feature discovery progress
ALTER TABLE ALTER TABLE
organizations organizations

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.07.11T20.09.37.migrate-pro-hobby-retention.sql', name: '2022.07.11T20.09.37.migrate-pro-hobby-retention.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Update Hobby with 3d to 7d -- Update Hobby with 3d to 7d
UPDATE UPDATE
organizations organizations

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.07.18T10.10.44.target-validation-client-exclusion.sql', name: '2022.07.18T10.10.44.target-validation-client-exclusion.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
targets targets
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.08.25T09.59.16.multiple-invitation-codes.sql', name: '2022.08.25T09.59.16.multiple-invitation-codes.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
organizations organizations
DROP COLUMN DROP COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.08.26T06.23.24.add-supertokens-id.sql', name: '2022.08.26T06.23.24.add-supertokens-id.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
users users
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.09.14T16.09.43.external-projects.sql', name: '2022.09.14T16.09.43.external-projects.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
projects projects
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.10.20T08.00.46.oidc-integrations.sql', name: '2022.10.20T08.00.46.oidc-integrations.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE IF NOT EXISTS CREATE TABLE IF NOT EXISTS
"oidc_integrations" ( "oidc_integrations" (
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (), "id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (),

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.11.07T09.30.47.user-table-varchar-to-text.sql', name: '2022.11.07T09.30.47.user-table-varchar-to-text.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
"users" "users"
ALTER COLUMN ALTER COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.12.03T09.12.28.organization-transfer.sql', name: '2022.12.03T09.12.28.organization-transfer.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
organizations organizations
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2022.12.20T09.20.36.oidc-columns.sql', name: '2022.12.20T09.20.36.oidc-columns.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
"oidc_integrations" "oidc_integrations"
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.01.04T17.00.23.hobby-7-by-default.sql', name: '2023.01.04T17.00.23.hobby-7-by-default.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Update Hobby with 3d to 7d - personal orgs were created with the default value of 3d -- Update Hobby with 3d to 7d - personal orgs were created with the default value of 3d
UPDATE UPDATE
organizations organizations

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.01.12T17.00.23.cdn-tokens.sql', name: '2023.01.12T17.00.23.cdn-tokens.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE CREATE TABLE
"cdn_access_tokens" ( "cdn_access_tokens" (
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (), "id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (),

View file

@ -39,7 +39,7 @@ type Cursor = {
lastCreatedAt: string; lastCreatedAt: string;
}; };
const run: MigrationExecutor['run'] = async ({ connection, sql }) => { const run: MigrationExecutor['run'] = async ({ connection, psql }) => {
// eslint-disable-next-line no-process-env // eslint-disable-next-line no-process-env
const eenv = shouldRunModel.parse(process.env); const eenv = shouldRunModel.parse(process.env);
const shouldRun = eenv.RUN_S3_LEGACY_CDN_KEY_IMPORT === '1'; const shouldRun = eenv.RUN_S3_LEGACY_CDN_KEY_IMPORT === '1';
@ -71,7 +71,7 @@ const run: MigrationExecutor['run'] = async ({ connection, sql }) => {
// Also all this code runs inside a database transaction. // Also all this code runs inside a database transaction.
// This will block any other writes to the table. // This will block any other writes to the table.
// As the table should not be heavily in use when this is being run, it does not really matter. // As the table should not be heavily in use when this is being run, it does not really matter.
const query = sql` const query = psql`
SELECT SELECT
"id" "id"
, to_json("created_at") as "created_at_cursor" , to_json("created_at") as "created_at_cursor"
@ -79,12 +79,12 @@ const run: MigrationExecutor['run'] = async ({ connection, sql }) => {
"targets" "targets"
${ ${
cursor cursor
? sql` ? psql`
WHERE WHERE
("created_at" = ${cursor.lastCreatedAt} AND "id" > ${cursor.lastId}) ("created_at" = ${cursor.lastCreatedAt} AND "id" > ${cursor.lastId})
OR "created_at" > ${cursor.lastCreatedAt} OR "created_at" > ${cursor.lastCreatedAt}
` `
: sql`` : psql``
} }
ORDER BY ORDER BY
"created_at" ASC "created_at" ASC
@ -93,8 +93,8 @@ const run: MigrationExecutor['run'] = async ({ connection, sql }) => {
200 200
`; `;
const items = await connection.query(query); const items = await connection.any(query);
return TargetsModel.parse(items.rows); return TargetsModel.parse(items);
} }
let lastCursor: null | Cursor = null; let lastCursor: null | Cursor = null;
@ -120,7 +120,7 @@ const run: MigrationExecutor['run'] = async ({ connection, sql }) => {
throw new Error(`Unexpected Status for storing key. (status=${response.status})`); throw new Error(`Unexpected Status for storing key. (status=${response.status})`);
} }
const query = sql` const query = psql`
INSERT INTO INSERT INTO
"cdn_access_tokens" "cdn_access_tokens"
( (

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.01.18T11.03.41.registry-v2.sql', name: '2023.01.18T11.03.41.registry-v2.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- --
CREATE INDEX CREATE INDEX
IF NOT EXISTS version_commit_cid_vid_idx ON version_commit (commit_id, version_id); IF NOT EXISTS version_commit_cid_vid_idx ON version_commit (commit_id, version_id);

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.02.22T09.27.02.delete-personal-org.sql', name: '2023.02.22T09.27.02.delete-personal-org.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
-- Find and delete all organizations of type PERSONAL that have no projects -- Find and delete all organizations of type PERSONAL that have no projects
DELETE FROM DELETE FROM
organizations AS o organizations AS o

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.03.14T12.14.23.schema-policy.sql', name: '2023.03.14T12.14.23.schema-policy.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TYPE CREATE TYPE
schema_policy_resource AS ENUM('ORGANIZATION', 'PROJECT'); schema_policy_resource AS ENUM('ORGANIZATION', 'PROJECT');

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.03.29T11.42.44.feature-flags.sql', name: '2023.03.29T11.42.44.feature-flags.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
organizations organizations
ADD COLUMN ADD COLUMN

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.04.03T12.51.36.schema-versions-meta.sql', name: '2023.04.03T12.51.36.schema-versions-meta.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE CREATE TABLE
"schema_version_changes" ( "schema_version_changes" (
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (), "id" UUID PRIMARY KEY DEFAULT uuid_generate_v4 (),

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.05.08T12.23.45.clean-invalid-schema-version-changes.sql', name: '2023.05.08T12.23.45.clean-invalid-schema-version-changes.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
DELETE DELETE
FROM FROM
"schema_version_changes" "svc" "schema_version_changes" "svc"

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.05.12T08.29.06.store-supergraph-on-schema-versions.sql', name: '2023.05.12T08.29.06.store-supergraph-on-schema-versions.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_versions" ALTER TABLE "schema_versions"
ADD COLUMN "supergraph_sdl" text ADD COLUMN "supergraph_sdl" text
; ;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.06.01T09.07.53.create_collections.sql', name: '2023.06.01T09.07.53.create_collections.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE "document_collections" ( CREATE TABLE "document_collections" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(), "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"title" text NOT NULL, "title" text NOT NULL,

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.06.06T11.26.04.schema-checks.sql', name: '2023.06.06T11.26.04.schema-checks.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE "schema_checks" ( CREATE TABLE "schema_checks" (
"id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4() "id" uuid PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4()
, "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW() , "created_at" TIMESTAMP WITH TIME ZONE NOT NULL DEFAULT NOW()

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.07.10T11.26.04.schema-checks-manual-approval.sql', name: '2023.07.10T11.26.04.schema-checks-manual-approval.sql',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_checks" ALTER TABLE "schema_checks"
ADD COLUMN "github_check_run_id" bigint ADD COLUMN "github_check_run_id" bigint
, ADD COLUMN "is_manually_approved" boolean , ADD COLUMN "is_manually_approved" boolean

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.07.27T11.44.36.graphql-endpoint.ts', name: '2023.07.27T11.44.36.graphql-endpoint.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "targets" ALTER TABLE "targets"
ADD COLUMN "graphql_endpoint_url" text ADD COLUMN "graphql_endpoint_url" text
; ;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.08.01T11.44.36.schema-checks-expires-at.ts', name: '2023.08.01T11.44.36.schema-checks-expires-at.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_checks" ALTER TABLE "schema_checks"
ADD COLUMN "expires_at" TIMESTAMP WITH TIME ZONE ADD COLUMN "expires_at" TIMESTAMP WITH TIME ZONE
; ;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.09.01T09.54.00.zendesk-support.ts', name: '2023.09.01T09.54.00.zendesk-support.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "users" ADD COLUMN "zendesk_user_id" TEXT UNIQUE DEFAULT NULL; ALTER TABLE "users" ADD COLUMN "zendesk_user_id" TEXT UNIQUE DEFAULT NULL;
CREATE INDEX "users_by_zendesk_user_id" ON "users" ("zendesk_user_id" ASC); CREATE INDEX "users_by_zendesk_user_id" ON "users" ("zendesk_user_id" ASC);

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.09.25T15.23.00.github-check-with-project-name.ts', name: '2023.09.25T15.23.00.github-check-with-project-name.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "projects" ADD COLUMN "github_check_with_project_name" BOOLEAN; ALTER TABLE "projects" ADD COLUMN "github_check_with_project_name" BOOLEAN;
UPDATE "projects" SET "github_check_with_project_name" = FALSE WHERE "github_check_with_project_name" IS NULL; UPDATE "projects" SET "github_check_with_project_name" = FALSE WHERE "github_check_with_project_name" IS NULL;
ALTER TABLE "projects" ALTER TABLE "projects"

View file

@ -2,5 +2,6 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.09.28T14.14.14.native-fed-v2.ts', name: '2023.09.28T14.14.14.native-fed-v2.ts',
run: ({ sql }) => sql`ALTER TABLE "projects" ADD COLUMN native_federation BOOLEAN DEFAULT FALSE;`, run: ({ psql }) =>
psql`ALTER TABLE "projects" ADD COLUMN native_federation BOOLEAN DEFAULT FALSE;`,
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.08.03T11.44.36.schema-checks-github-repository.ts', name: '2023.08.03T11.44.36.schema-checks-github-repository.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_checks" ALTER TABLE "schema_checks"
ADD COLUMN "github_repository" text ADD COLUMN "github_repository" text
, ADD COLUMN "github_sha" text , ADD COLUMN "github_sha" text

View file

@ -3,10 +3,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.10.26T12.44.36.schema-checks-filters-index.ts', name: '2023.10.26T12.44.36.schema-checks-filters-index.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'schema_checks_connection_pagination_with_changes', name: 'schema_checks_connection_pagination_with_changes',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_changes" ON "schema_checks" ( CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_changes" ON "schema_checks" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC
@ -20,7 +20,7 @@ export default {
}, },
{ {
name: 'schema_checks_connection_pagination_with_no_success', name: 'schema_checks_connection_pagination_with_no_success',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_no_success" ON "schema_checks" ( CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_no_success" ON "schema_checks" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC
@ -33,7 +33,7 @@ export default {
}, },
{ {
name: 'schema_checks_connection_pagination_with_no_success_and_changes', name: 'schema_checks_connection_pagination_with_no_success_and_changes',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_no_success_and_changes" ON "schema_checks" ( CREATE INDEX CONCURRENTLY "schema_checks_connection_pagination_with_no_success_and_changes" ON "schema_checks" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.10.30T00-00-00.drop-persisted-operations.ts', name: '2023.10.30T00-00-00.drop-persisted-operations.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
DROP TABLE IF EXISTS "persisted_operations"; DROP TABLE IF EXISTS "persisted_operations";
DROP TYPE IF EXISTS "operation_kind"; DROP TYPE IF EXISTS "operation_kind";
`, `,

View file

@ -3,10 +3,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.10.25T14.41.41.schema-checks-dedup.ts', name: '2023.10.25T14.41.41.schema-checks-dedup.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'create sdl_store and alter schema_checks', name: 'create sdl_store and alter schema_checks',
query: sql` query: psql`
CREATE TABLE "sdl_store" ( CREATE TABLE "sdl_store" (
"id" text PRIMARY KEY NOT NULL, "id" text PRIMARY KEY NOT NULL,
"sdl" text NOT NULL "sdl" text NOT NULL
@ -25,25 +25,25 @@ export default {
}, },
{ {
name: 'Create sdl_store_unique_id index', name: 'Create sdl_store_unique_id index',
query: sql` query: psql`
CREATE UNIQUE INDEX sdl_store_unique_id ON "sdl_store" ("id"); CREATE UNIQUE INDEX sdl_store_unique_id ON "sdl_store" ("id");
`, `,
}, },
{ {
name: 'Create schema_check_by_schema_sdl_store_id index', name: 'Create schema_check_by_schema_sdl_store_id index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_check_by_schema_sdl_store_id" ON "schema_checks" ("schema_sdl_store_id" ASC) CREATE INDEX CONCURRENTLY "schema_check_by_schema_sdl_store_id" ON "schema_checks" ("schema_sdl_store_id" ASC)
`, `,
}, },
{ {
name: 'Create schema_check_by_supergraph_sdl_store_id index', name: 'Create schema_check_by_supergraph_sdl_store_id index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_check_by_supergraph_sdl_store_id" ON "schema_checks" ("supergraph_sdl_store_id" ASC) CREATE INDEX CONCURRENTLY "schema_check_by_supergraph_sdl_store_id" ON "schema_checks" ("supergraph_sdl_store_id" ASC)
`, `,
}, },
{ {
name: 'Create schema_check_by_composite_schema_sdl_store_id index', name: 'Create schema_check_by_composite_schema_sdl_store_id index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "schema_check_by_composite_schema_sdl_store_id" ON "schema_checks" ("composite_schema_sdl_store_id" ASC); CREATE INDEX CONCURRENTLY "schema_check_by_composite_schema_sdl_store_id" ON "schema_checks" ("composite_schema_sdl_store_id" ASC);
`, `,
}, },

View file

@ -2,7 +2,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.11.09T00.00.00.schema-check-approval.ts', name: '2023.11.09T00.00.00.schema-check-approval.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE "schema_change_approvals" ( CREATE TABLE "schema_change_approvals" (
"target_id" UUID NOT NULL REFERENCES "targets" ("id") ON DELETE CASCADE, "target_id" UUID NOT NULL REFERENCES "targets" ("id") ON DELETE CASCADE,
"context_id" text NOT NULL, "context_id" text NOT NULL,

View file

@ -3,10 +3,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2023.11.20T10-00-00.organization-member-roles.ts', name: '2023.11.20T10-00-00.organization-member-roles.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'Create organization_roles and alter organization_member table', name: 'Create organization_roles and alter organization_member table',
query: sql` query: psql`
CREATE TABLE organization_member_roles ( CREATE TABLE organization_member_roles (
"id" uuid NOT NULL UNIQUE DEFAULT uuid_generate_v4(), "id" uuid NOT NULL UNIQUE DEFAULT uuid_generate_v4(),
"organization_id" uuid NOT NULL REFERENCES "organizations" ("id") ON DELETE CASCADE, "organization_id" uuid NOT NULL REFERENCES "organizations" ("id") ON DELETE CASCADE,
@ -26,7 +26,7 @@ export default {
}, },
{ {
name: 'Create Admin role', name: 'Create Admin role',
query: sql` query: psql`
INSERT INTO organization_member_roles INSERT INTO organization_member_roles
( (
organization_id, organization_id,
@ -68,7 +68,7 @@ export default {
}, },
{ {
name: 'Create Contributor role', name: 'Create Contributor role',
query: sql` query: psql`
INSERT INTO organization_member_roles INSERT INTO organization_member_roles
( (
organization_id, organization_id,
@ -104,7 +104,7 @@ export default {
}, },
{ {
name: 'Create Viewer role', name: 'Create Viewer role',
query: sql` query: psql`
INSERT INTO organization_member_roles INSERT INTO organization_member_roles
( (
organization_id, organization_id,
@ -133,7 +133,7 @@ export default {
}, },
{ {
name: 'Assign roles to users with matching scopes', name: 'Assign roles to users with matching scopes',
query: sql` query: psql`
UPDATE organization_member UPDATE organization_member
SET role_id = ( SET role_id = (
SELECT id SELECT id
@ -150,7 +150,7 @@ export default {
}, },
{ {
name: 'Migrate organization_invitations table to use Viewer role', name: 'Migrate organization_invitations table to use Viewer role',
query: sql` query: psql`
ALTER TABLE organization_invitations ADD COLUMN "role_id" uuid REFERENCES "organization_member_roles" ("id"); ALTER TABLE organization_invitations ADD COLUMN "role_id" uuid REFERENCES "organization_member_roles" ("id");
UPDATE organization_invitations UPDATE organization_invitations

View file

@ -3,10 +3,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.01.08T10-00-00.schema-version-diff-schema-version-id', name: '2024.01.08T10-00-00.schema-version-diff-schema-version-id',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'add diff_schema_version_id column', name: 'add diff_schema_version_id column',
query: sql` query: psql`
ALTER TABLE "schema_versions" ALTER TABLE "schema_versions"
ADD COLUMN IF NOT EXISTS "diff_schema_version_id" uuid REFERENCES "schema_versions" ("id") ADD COLUMN IF NOT EXISTS "diff_schema_version_id" uuid REFERENCES "schema_versions" ("id")
, ADD COLUMN IF NOT EXISTS "record_version" text , ADD COLUMN IF NOT EXISTS "record_version" text
@ -15,7 +15,7 @@ export default {
}, },
{ {
name: 'create schema_versions_cursor_pagination index', name: 'create schema_versions_cursor_pagination index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_cursor_pagination" ON "schema_versions" ( CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_cursor_pagination" ON "schema_versions" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC
@ -25,7 +25,7 @@ export default {
}, },
{ {
name: 'create schema_versions_cursor_pagination index', name: 'create schema_versions_cursor_pagination index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_cursor_pagination_composable" ON "schema_versions" ( CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_cursor_pagination_composable" ON "schema_versions" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC

View file

@ -2,7 +2,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.01.26T00.00.00.contracts.ts', name: '2024.01.26T00.00.00.contracts.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_versions" ALTER TABLE "schema_versions"
ADD COLUMN "tags" text[] ADD COLUMN "tags" text[]
, ADD COLUMN "has_contract_composition_errors" boolean , ADD COLUMN "has_contract_composition_errors" boolean

View file

@ -3,10 +3,10 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.01.26T00.00.01.schema-check-pagination-index-update', name: '2024.01.26T00.00.01.schema-check-pagination-index-update',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'create index schema_checks_connection_pagination_with_changes_new', name: 'create index schema_checks_connection_pagination_with_changes_new',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_connection_pagination_with_changes_new" ON "schema_checks" ( CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_connection_pagination_with_changes_new" ON "schema_checks" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC
@ -21,7 +21,7 @@ export default {
}, },
{ {
name: 'create index schema_checks_connection_pagination_with_no_success_and_changes_new', name: 'create index schema_checks_connection_pagination_with_no_success_and_changes_new',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_connection_pagination_with_no_success_and_changes_new" ON "schema_checks" ( CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_connection_pagination_with_no_success_and_changes_new" ON "schema_checks" (
"target_id" ASC "target_id" ASC
, "created_at" DESC , "created_at" DESC
@ -39,19 +39,19 @@ export default {
}, },
{ {
name: 'drop index schema_checks_connection_pagination_with_changes', name: 'drop index schema_checks_connection_pagination_with_changes',
query: sql` query: psql`
DROP INDEX CONCURRENTLY IF EXISTS "schema_checks_connection_pagination_with_changes"; DROP INDEX CONCURRENTLY IF EXISTS "schema_checks_connection_pagination_with_changes";
`, `,
}, },
{ {
name: 'drop index schema_checks_connection_pagination_with_no_success_and_changes', name: 'drop index schema_checks_connection_pagination_with_no_success_and_changes',
query: sql` query: psql`
DROP INDEX CONCURRENTLY IF EXISTS "schema_checks_connection_pagination_with_no_success_and_changes"; DROP INDEX CONCURRENTLY IF EXISTS "schema_checks_connection_pagination_with_no_success_and_changes";
`, `,
}, },
{ {
name: 'create index contract_checks_supergraph_sdl_store_id index', name: 'create index contract_checks_supergraph_sdl_store_id index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "contract_checks_supergraph_sdl_store_id" ON "contract_checks" ( CREATE INDEX CONCURRENTLY "contract_checks_supergraph_sdl_store_id" ON "contract_checks" (
"supergraph_sdl_store_id" ASC "supergraph_sdl_store_id" ASC
); );
@ -59,7 +59,7 @@ export default {
}, },
{ {
name: 'create index contract_checks_composite_schema_sdl_store_id', name: 'create index contract_checks_composite_schema_sdl_store_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY "contract_checks_composite_schema_sdl_store_id" ON "contract_checks" ( CREATE INDEX CONCURRENTLY "contract_checks_composite_schema_sdl_store_id" ON "contract_checks" (
"composite_schema_sdl_store_id" ASC "composite_schema_sdl_store_id" ASC
); );

View file

@ -3,7 +3,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.02.19T00.00.01.schema-check-store-breaking-change-metadata.ts', name: '2024.02.19T00.00.01.schema-check-store-breaking-change-metadata.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_checks" ALTER TABLE "schema_checks"
ADD COLUMN IF NOT EXISTS "conditional_breaking_change_metadata" JSONB ADD COLUMN IF NOT EXISTS "conditional_breaking_change_metadata" JSONB
; ;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.04.09T10.10.00.check-approval-comment.ts', name: '2024.04.09T10.10.00.check-approval-comment.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_checks" ADD COLUMN IF NOT EXISTS "manual_approval_comment" text; ALTER TABLE "schema_checks" ADD COLUMN IF NOT EXISTS "manual_approval_comment" text;
`, `,
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.06.11T10-10-00.ms-teams-webhook.ts', name: '2024.06.11T10-10-00.ms-teams-webhook.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TYPE alert_channel_type ADD VALUE 'MSTEAMS_WEBHOOK'; ALTER TYPE alert_channel_type ADD VALUE 'MSTEAMS_WEBHOOK';
`, `,
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.07.16T13-44-00.oidc-only-access.ts', name: '2024.07.16T13-44-00.oidc-only-access.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "oidc_integrations" ALTER TABLE "oidc_integrations"
ADD COLUMN "oidc_user_access_only" BOOLEAN NOT NULL DEFAULT TRUE; ADD COLUMN "oidc_user_access_only" BOOLEAN NOT NULL DEFAULT TRUE;
`, `,

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.07.17T00-00-00.app-deployments.ts', name: '2024.07.17T00-00-00.app-deployments.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE IF NOT EXISTS "app_deployments" ( CREATE TABLE IF NOT EXISTS "app_deployments" (
"id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(), "id" UUID PRIMARY KEY DEFAULT uuid_generate_v4(),
"target_id" UUID NOT NULL REFERENCES "targets" ("id") ON DELETE CASCADE, "target_id" UUID NOT NULL REFERENCES "targets" ("id") ON DELETE CASCADE,

View file

@ -10,14 +10,15 @@ import {
isScalarType, isScalarType,
isUnionType, isUnionType,
} from 'graphql'; } from 'graphql';
import { sql, type CommonQueryMethods } from 'slonik'; import z from 'zod';
import { psql, type CommonQueryMethods } from '@hive/postgres';
import { env } from '../environment'; import { env } from '../environment';
import type { MigrationExecutor } from '../pg-migrator'; import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.07.23T09.36.00.schema-cleanup-tracker.ts', name: '2024.07.23T09.36.00.schema-cleanup-tracker.ts',
async run({ connection }) { async run({ connection }) {
await connection.query(sql` await connection.query(psql`
CREATE TABLE IF NOT EXISTS "schema_coordinate_status" ( CREATE TABLE IF NOT EXISTS "schema_coordinate_status" (
coordinate text NOT NULL, coordinate text NOT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now(), created_at TIMESTAMPTZ NOT NULL DEFAULT now(),
@ -48,9 +49,13 @@ export default {
return; return;
} }
const schemaVersionsTotal = await connection.oneFirst<number>(sql` const schemaVersionsTotal = await connection
.oneFirst(
psql`
SELECT count(*) as total FROM schema_versions SELECT count(*) as total FROM schema_versions
`); `,
)
.then(z.number().parse);
console.log(`Found ${schemaVersionsTotal} schema versions`); console.log(`Found ${schemaVersionsTotal} schema versions`);
if (schemaVersionsTotal > 1000) { if (schemaVersionsTotal > 1000) {
@ -93,24 +98,24 @@ function diffSchemaCoordinates(
export async function schemaCoordinateStatusMigration(connection: CommonQueryMethods) { export async function schemaCoordinateStatusMigration(connection: CommonQueryMethods) {
// Fetch targets // Fetch targets
const targetResult = await connection.query<{ id: string }>(sql` const targetResult = await connection
.any(
psql`
SELECT id FROM targets WHERE ID NOT IN (SELECT target_id FROM schema_coordinate_status) SELECT id FROM targets WHERE ID NOT IN (SELECT target_id FROM schema_coordinate_status)
`); `,
)
.then(z.array(z.object({ id: z.string() })).parse);
console.log(`Found ${targetResult.rowCount} targets`); console.log(`Found ${targetResult.length} targets`);
let i = 0; let i = 0;
for await (const target of targetResult.rows) { for await (const target of targetResult) {
try { try {
console.log(`Processing target (${i++}/${targetResult.rowCount}) - ${target.id}`); console.log(`Processing target (${i++}/${targetResult.length}) - ${target.id}`);
const latestSchema = await connection.maybeOne<{ const latestSchema = await connection
id: string; .maybeOne(
created_at: number; psql`
is_composable: boolean;
sdl?: string;
previous_schema_version_id?: string;
}>(sql`
SELECT SELECT
id, id,
created_at, created_at,
@ -121,7 +126,19 @@ export async function schemaCoordinateStatusMigration(connection: CommonQueryMet
WHERE target_id = ${target.id} AND is_composable = true WHERE target_id = ${target.id} AND is_composable = true
ORDER BY created_at DESC ORDER BY created_at DESC
LIMIT 1 LIMIT 1
`); `,
)
.then(
z
.object({
id: z.string(),
created_at: z.number(),
is_composable: z.boolean(),
sdl: z.string().nullable(),
previous_schema_version_id: z.string().nullable(),
})
.nullable().parse,
);
if (!latestSchema) { if (!latestSchema) {
console.log('[SKIPPING] No latest composable schema found for target %s', target.id); console.log('[SKIPPING] No latest composable schema found for target %s', target.id);
@ -270,10 +287,10 @@ async function insertRemainingCoordinates(
console.log( console.log(
`Adding remaining ${targetCoordinates.coordinates.size} coordinates for target ${targetId}`, `Adding remaining ${targetCoordinates.coordinates.size} coordinates for target ${targetId}`,
); );
await connection.query(sql` await connection.query(psql`
INSERT INTO schema_coordinate_status INSERT INTO schema_coordinate_status
( target_id, coordinate, created_at, created_in_version_id ) ( target_id, coordinate, created_at, created_in_version_id )
SELECT * FROM ${sql.unnest( SELECT * FROM ${psql.unnest(
Array.from(targetCoordinates.coordinates).map(coordinate => [ Array.from(targetCoordinates.coordinates).map(coordinate => [
targetId, targetId,
coordinate, coordinate,
@ -290,10 +307,10 @@ async function insertRemainingCoordinates(
console.log( console.log(
`Deprecating remaining ${remainingDeprecated.size} coordinates for target ${targetId}`, `Deprecating remaining ${remainingDeprecated.size} coordinates for target ${targetId}`,
); );
await connection.query(sql` await connection.query(psql`
INSERT INTO schema_coordinate_status INSERT INTO schema_coordinate_status
( target_id, coordinate, created_at, created_in_version_id, deprecated_at, deprecated_in_version_id ) ( target_id, coordinate, created_at, created_in_version_id, deprecated_at, deprecated_in_version_id )
SELECT * FROM ${sql.unnest( SELECT * FROM ${psql.unnest(
Array.from(remainingDeprecated).map(coordinate => [ Array.from(remainingDeprecated).map(coordinate => [
targetId, targetId,
coordinate, coordinate,
@ -343,13 +360,9 @@ async function processVersion(
return; return;
} }
const versionBefore = await connection.maybeOne<{ const versionBefore = await connection
id: string; .maybeOne(
sdl?: string; psql`
previous_schema_version_id?: string;
created_at: number;
is_composable: boolean;
}>(sql`
SELECT SELECT
id, id,
composite_schema_sdl as sdl, composite_schema_sdl as sdl,
@ -358,7 +371,19 @@ async function processVersion(
is_composable is_composable
FROM schema_versions FROM schema_versions
WHERE id = ${previousVersionId} AND target_id = ${targetId} WHERE id = ${previousVersionId} AND target_id = ${targetId}
`); `,
)
.then(
z
.object({
id: z.string(),
created_at: z.number(),
is_composable: z.boolean(),
sdl: z.string().nullable(),
previous_schema_version_id: z.string().nullable(),
})
.nullable().parse,
);
if (!versionBefore) { if (!versionBefore) {
console.error( console.error(
@ -440,10 +465,10 @@ async function processVersion(
if (added.length) { if (added.length) {
console.log(`Adding ${added.length} coordinates for target ${targetId}`); console.log(`Adding ${added.length} coordinates for target ${targetId}`);
await connection.query(sql` await connection.query(psql`
INSERT INTO schema_coordinate_status INSERT INTO schema_coordinate_status
( target_id, coordinate, created_at, created_in_version_id ) ( target_id, coordinate, created_at, created_in_version_id )
SELECT * FROM ${sql.unnest( SELECT * FROM ${psql.unnest(
added.map(coordinate => [targetId, coordinate, datePG, after.versionId]), added.map(coordinate => [targetId, coordinate, datePG, after.versionId]),
['uuid', 'text', 'date', 'uuid'], ['uuid', 'text', 'date', 'uuid'],
)} )}
@ -456,10 +481,10 @@ async function processVersion(
if (deprecated.length) { if (deprecated.length) {
console.log(`deprecating ${deprecated.length} coordinates for target ${targetId}`); console.log(`deprecating ${deprecated.length} coordinates for target ${targetId}`);
await connection.query(sql` await connection.query(psql`
INSERT INTO schema_coordinate_status INSERT INTO schema_coordinate_status
( target_id, coordinate, created_at, created_in_version_id, deprecated_at, deprecated_in_version_id ) ( target_id, coordinate, created_at, created_in_version_id, deprecated_at, deprecated_in_version_id )
SELECT * FROM ${sql.unnest( SELECT * FROM ${psql.unnest(
deprecated.map(coordinate => [ deprecated.map(coordinate => [
targetId, targetId,
coordinate, coordinate,

View file

@ -4,14 +4,14 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.12.23T00-00-00.improve-version-index.ts', name: '2024.12.23T00-00-00.improve-version-index.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: `create "schema_log"."action" with "created_at" sort index`, name: `create "schema_log"."action" with "created_at" sort index`,
query: sql`CREATE INDEX CONCURRENTLY idx_schema_log_action_created ON schema_log(action, created_at DESC);`, query: psql`CREATE INDEX CONCURRENTLY idx_schema_log_action_created ON schema_log(action, created_at DESC);`,
}, },
{ {
name: `create "schema_log"."action" + "service_name" index`, name: `create "schema_log"."action" + "service_name" index`,
query: sql`CREATE INDEX CONCURRENTLY idx_schema_log_action_service ON schema_log(action, lower(service_name));`, query: psql`CREATE INDEX CONCURRENTLY idx_schema_log_action_service ON schema_log(action, lower(service_name));`,
}, },
], ],
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -3,10 +3,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.12.24T00-00-00.improve-version-index-2.ts', name: '2024.12.24T00-00-00.improve-version-index-2.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: `create "schema_version_changes"."schema_version_id" lookup index`, name: `create "schema_version_changes"."schema_version_id" lookup index`,
query: sql`CREATE INDEX CONCURRENTLY idx_schema_version_changes_id ON schema_version_changes(schema_version_id);`, query: psql`CREATE INDEX CONCURRENTLY idx_schema_version_changes_id ON schema_version_changes(schema_version_id);`,
}, },
], ],
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2024.12.27T00.00.00.create-preflight-scripts.ts', name: '2024.12.27T00.00.00.create-preflight-scripts.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE IF NOT EXISTS "document_preflight_scripts" ( CREATE TABLE IF NOT EXISTS "document_preflight_scripts" (
"id" uuid NOT NULL DEFAULT uuid_generate_v4(), "id" uuid NOT NULL DEFAULT uuid_generate_v4(),
"source_code" text NOT NULL, "source_code" text NOT NULL,

View file

@ -7,10 +7,10 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.01.02T00-00-00.cascade-deletion-indices.ts', name: '2025.01.02T00-00-00.cascade-deletion-indices.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'index schema_checks_manual_approval_user_id', name: 'index schema_checks_manual_approval_user_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_manual_approval_user_id" CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_manual_approval_user_id"
ON "schema_checks"("manual_approval_user_id") ON "schema_checks"("manual_approval_user_id")
WHERE "manual_approval_user_id" is not null WHERE "manual_approval_user_id" is not null
@ -18,106 +18,106 @@ export default {
}, },
{ {
name: 'index organization_member_user_id', name: 'index organization_member_user_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_user_id" CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_user_id"
ON "organization_member"("user_id") ON "organization_member"("user_id")
`, `,
}, },
{ {
name: 'index organization_member_organization_id', name: 'index organization_member_organization_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_organization_id" CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_organization_id"
ON "organization_member"("organization_id") ON "organization_member"("organization_id")
`, `,
}, },
{ {
name: 'index organization_member_roles_organization_id', name: 'index organization_member_roles_organization_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_roles_organization_id" CREATE INDEX CONCURRENTLY IF NOT EXISTS "organization_member_roles_organization_id"
ON "organization_member_roles"("organization_id") ON "organization_member_roles"("organization_id")
`, `,
}, },
{ {
name: 'index projects_org_id', name: 'index projects_org_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "projects_org_id" ON "projects"("org_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "projects_org_id" ON "projects"("org_id")
`, `,
}, },
{ {
name: 'index targets_project_id', name: 'index targets_project_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "targets_project_id" ON "targets"("project_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "targets_project_id" ON "targets"("project_id")
`, `,
}, },
{ {
name: 'index schema_versions_target_id', name: 'index schema_versions_target_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_target_id" ON "schema_versions"("target_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_target_id" ON "schema_versions"("target_id")
`, `,
}, },
{ {
name: 'index schema_checks_target_id', name: 'index schema_checks_target_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_target_id" ON "schema_checks"("target_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_target_id" ON "schema_checks"("target_id")
`, `,
}, },
{ {
name: 'index schema_log_target_id', name: 'index schema_log_target_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_log_target_id" ON "schema_log"("target_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_log_target_id" ON "schema_log"("target_id")
`, `,
}, },
{ {
name: 'index schema_log_project_id', name: 'index schema_log_project_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_log_project_id" ON "schema_log"("project_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_log_project_id" ON "schema_log"("project_id")
`, `,
}, },
{ {
name: 'index contract_versions_schema_version_id', name: 'index contract_versions_schema_version_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "contract_versions_schema_version_id" ON "contract_versions"("schema_version_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "contract_versions_schema_version_id" ON "contract_versions"("schema_version_id")
`, `,
}, },
{ {
name: 'index schema_version_to_log_action_id', name: 'index schema_version_to_log_action_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_version_to_log_action_id" ON "schema_version_to_log"("action_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_version_to_log_action_id" ON "schema_version_to_log"("action_id")
`, `,
}, },
{ {
name: 'index schema_version_to_log_version_id', name: 'index schema_version_to_log_version_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_version_to_log_version_id" ON "schema_version_to_log"("version_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_version_to_log_version_id" ON "schema_version_to_log"("version_id")
`, `,
}, },
{ {
name: 'index contract_schema_change_approvals_schema_change_id', name: 'index contract_schema_change_approvals_schema_change_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "contract_schema_change_approvals_schema_change_id" ON "contract_schema_change_approvals"("schema_change_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "contract_schema_change_approvals_schema_change_id" ON "contract_schema_change_approvals"("schema_change_id")
`, `,
}, },
{ {
name: 'index schema_checks_schema_version_id', name: 'index schema_checks_schema_version_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_schema_version_id" ON "schema_checks"("schema_version_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_checks_schema_version_id" ON "schema_checks"("schema_version_id")
`, `,
}, },
{ {
name: 'index schema_versions_diff_schema_version_id', name: 'index schema_versions_diff_schema_version_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_diff_schema_version_id" ON "schema_versions"("diff_schema_version_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "schema_versions_diff_schema_version_id" ON "schema_versions"("diff_schema_version_id")
`, `,
}, },
{ {
name: 'index organizations_ownership_transfer_user_id', name: 'index organizations_ownership_transfer_user_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "organizations_ownership_transfer_user_id" ON "organizations"("ownership_transfer_user_id") CREATE INDEX CONCURRENTLY IF NOT EXISTS "organizations_ownership_transfer_user_id" ON "organizations"("ownership_transfer_user_id")
`, `,
}, },
{ {
name: 'index users_supertoken_user_id', name: 'index users_supertoken_user_id',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "users_supertoken_user_id_missing" CREATE INDEX CONCURRENTLY IF NOT EXISTS "users_supertoken_user_id_missing"
ON "users"("supertoken_user_id") ON "users"("supertoken_user_id")
WHERE 'supertoken_user_id' IS NULL WHERE 'supertoken_user_id' IS NULL

View file

@ -7,10 +7,10 @@ export default {
// we do not run this in a transaction as each user processing delete takes around 300ms. // we do not run this in a transaction as each user processing delete takes around 300ms.
// and we do not want to mess with live traffic. // and we do not want to mess with live traffic.
noTransaction: true, noTransaction: true,
async run({ sql, connection }) { async run({ psql, connection }) {
const userIds = await connection const userIds = await connection
.anyFirst( .anyFirst(
sql` psql`
SELECT SELECT
"id" "id"
FROM FROM
@ -32,7 +32,7 @@ export default {
`processing userId="${userId}" (${counter.toPrecision().padStart(padAmount, '0')}/${total})`, `processing userId="${userId}" (${counter.toPrecision().padStart(padAmount, '0')}/${total})`,
); );
// ON DELETE SET null constraint is missing, so we need to first update it manually // ON DELETE SET null constraint is missing, so we need to first update it manually
await connection.query(sql` await connection.query(psql`
UPDATE UPDATE
"organizations" "organizations"
SET SET
@ -41,14 +41,14 @@ export default {
"ownership_transfer_user_id" = ${userId} "ownership_transfer_user_id" = ${userId}
`); `);
// Delete the organizations of these users // Delete the organizations of these users
await connection.query(sql` await connection.query(psql`
DELETE DELETE
FROM FROM
"organizations" "organizations"
WHERE WHERE
"user_id" = ${userId} "user_id" = ${userId}
`); `);
await connection.query(sql` await connection.query(psql`
DELETE DELETE
FROM FROM
"users" "users"

View file

@ -24,8 +24,8 @@ const QUERY_RESULT = z.array(
export default { export default {
name: '2025.01.09T00-00-00.legacy-member-scopes.ts', name: '2025.01.09T00-00-00.legacy-member-scopes.ts',
noTransaction: true, noTransaction: true,
async run({ sql, connection }) { async run({ psql, connection }) {
const queryResult = await connection.query(sql` const queryResult = await connection.any(psql`
SELECT SELECT
organization_id as "organizationId", organization_id as "organizationId",
sorted_scopes as "sortedScopes", sorted_scopes as "sortedScopes",
@ -49,7 +49,7 @@ export default {
ORDER BY organization_id; ORDER BY organization_id;
`); `);
if (queryResult.rowCount === 0) { if (queryResult.length === 0) {
console.log('No members without role_id found.'); console.log('No members without role_id found.');
return; return;
} }
@ -57,7 +57,7 @@ export default {
// rows are sorted by organization_id // rows are sorted by organization_id
// and grouped by scopes // and grouped by scopes
// so we can process them in order // so we can process them in order
const rows = QUERY_RESULT.parse(queryResult.rows); const rows = QUERY_RESULT.parse(queryResult);
let counter = 1; let counter = 1;
let previousOrganizationId: string | null = null; let previousOrganizationId: string | null = null;
@ -70,12 +70,12 @@ export default {
} }
console.log( console.log(
`processing organization_id="${row.organizationId}" (${counter}) with ${row.userIds.length} users | ${index + 1}/${queryResult.rowCount}`, `processing organization_id="${row.organizationId}" (${counter}) with ${row.userIds.length} users | ${index + 1}/${queryResult.length}`,
); );
const startedAt = Date.now(); const startedAt = Date.now();
await connection.query(sql` await connection.query(psql`
WITH new_role AS ( WITH new_role AS (
INSERT INTO organization_member_roles ( INSERT INTO organization_member_roles (
organization_id, name, description, scopes organization_id, name, description, scopes
@ -84,13 +84,13 @@ export default {
${row.organizationId}, ${row.organizationId},
'Auto Role ' || substring(uuid_generate_v4()::text FROM 1 FOR 8), 'Auto Role ' || substring(uuid_generate_v4()::text FROM 1 FOR 8),
'Auto generated role to assign to members without a role', 'Auto generated role to assign to members without a role',
${sql.array(row.sortedScopes, 'text')} ${psql.array(row.sortedScopes, 'text')}
) )
RETURNING id RETURNING id
) )
UPDATE organization_member UPDATE organization_member
SET role_id = (SELECT id FROM new_role) SET role_id = (SELECT id FROM new_role)
WHERE organization_id = ${row.organizationId} AND user_id = ANY(${sql.array(row.userIds, 'uuid')}) WHERE organization_id = ${row.organizationId} AND user_id = ANY(${psql.array(row.userIds, 'uuid')})
`); `);
console.log(`finished after ${Date.now() - startedAt}ms`); console.log(`finished after ${Date.now() - startedAt}ms`);

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.01.10T00.00.00.breaking-changes-request-count.ts', name: '2025.01.10T00.00.00.breaking-changes-request-count.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TYPE CREATE TYPE
breaking_change_formula AS ENUM('PERCENTAGE', 'REQUEST_COUNT'); breaking_change_formula AS ENUM('PERCENTAGE', 'REQUEST_COUNT');

View file

@ -4,10 +4,10 @@ export default {
name: '2025.01.13T10-08-00.default-role.ts', name: '2025.01.13T10-08-00.default-role.ts',
noTransaction: true, noTransaction: true,
// Adds a default role to OIDC integration and set index on "oidc_integrations"."default_role_id" // Adds a default role to OIDC integration and set index on "oidc_integrations"."default_role_id"
run: ({ sql }) => [ run: ({ psql }) => [
{ {
name: 'Add a column', name: 'Add a column',
query: sql` query: psql`
ALTER TABLE "oidc_integrations" ALTER TABLE "oidc_integrations"
ADD COLUMN IF NOT EXISTS "default_role_id" UUID REFERENCES organization_member_roles(id) ADD COLUMN IF NOT EXISTS "default_role_id" UUID REFERENCES organization_member_roles(id)
ON DELETE SET NULL; ON DELETE SET NULL;
@ -15,7 +15,7 @@ export default {
}, },
{ {
name: 'Create an index', name: 'Create an index',
query: sql` query: psql`
CREATE INDEX CONCURRENTLY IF NOT EXISTS "oidc_integrations_default_role_id_idx" CREATE INDEX CONCURRENTLY IF NOT EXISTS "oidc_integrations_default_role_id_idx"
ON "oidc_integrations"("default_role_id") ON "oidc_integrations"("default_role_id")
WHERE "default_role_id" is not null; WHERE "default_role_id" is not null;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.01.17T10-08-00.drop-activities.ts', name: '2025.01.17T10-08-00.drop-activities.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
DROP TABLE IF EXISTS "activities"; DROP TABLE IF EXISTS "activities";
`, `,
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -6,7 +6,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.01.20T00-00-00.legacy-registry-model-removal.ts', name: '2025.01.20T00-00-00.legacy-registry-model-removal.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE projects DROP COLUMN IF EXISTS legacy_registry_model; ALTER TABLE projects DROP COLUMN IF EXISTS legacy_registry_model;
`, `,
} satisfies MigrationExecutor; } satisfies MigrationExecutor;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025-01-30T00-00-00.granular-member-role-permissions.ts', name: '2025-01-30T00-00-00.granular-member-role-permissions.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "organization_member_roles" ALTER TABLE "organization_member_roles"
ALTER "scopes" DROP NOT NULL ALTER "scopes" DROP NOT NULL
, ADD COLUMN "permissions" text[] , ADD COLUMN "permissions" text[]

View file

@ -2,7 +2,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.02.14T00.00.00.schema-versions-metadata.ts', name: '2025.02.14T00.00.00.schema-versions-metadata.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_versions" ALTER TABLE "schema_versions"
ADD COLUMN "schema_metadata" JSONB DEFAULT NULL ADD COLUMN "schema_metadata" JSONB DEFAULT NULL
; ;

View file

@ -2,7 +2,7 @@ import { type MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.02.20T00-00-00.organization-access-tokens.ts', name: '2025.02.20T00-00-00.organization-access-tokens.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
CREATE TABLE IF NOT EXISTS "organization_access_tokens" ( CREATE TABLE IF NOT EXISTS "organization_access_tokens" (
"id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4() "id" UUID PRIMARY KEY NOT NULL DEFAULT uuid_generate_v4()
, "organization_id" UUID NOT NULL REFERENCES "organizations" ("id") ON DELETE CASCADE , "organization_id" UUID NOT NULL REFERENCES "organizations" ("id") ON DELETE CASCADE

View file

@ -2,7 +2,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.02.21T00.00.00.schema-versions-metadata-attributes.ts', name: '2025.02.21T00.00.00.schema-versions-metadata-attributes.ts',
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE "schema_versions" ALTER TABLE "schema_versions"
ADD COLUMN "metadata_attributes" JSONB DEFAULT NULL ADD COLUMN "metadata_attributes" JSONB DEFAULT NULL
; ;

View file

@ -3,7 +3,7 @@ import type { MigrationExecutor } from '../pg-migrator';
export default { export default {
name: '2025.03.20T00-00-00.dangerous_breaking.ts', name: '2025.03.20T00-00-00.dangerous_breaking.ts',
noTransaction: true, noTransaction: true,
run: ({ sql }) => sql` run: ({ psql }) => psql`
ALTER TABLE ALTER TABLE
targets targets
ADD COLUMN ADD COLUMN

Some files were not shown because too many files have changed in this diff Show more