Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions drizzle-kit/src/dialects/cockroach/convertor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -715,6 +715,20 @@ const toggleRlsConvertor = convertor('alter_rls', (st) => {
return `ALTER TABLE ${tableNameWithSchema} ${isRlsEnabled ? 'ENABLE' : 'DISABLE'} ROW LEVEL SECURITY;`;
});

const replicaIdentityConvertor = convertor('alter_replica_identity', (st) => {
const { schema, name, replicaIdentity } = st;

const tableNameWithSchema = schema !== 'public' ? `"${schema}"."${name}"` : `"${name}"`;

const clause = !replicaIdentity
? 'DEFAULT'
: replicaIdentity.type === 'index'
? `USING INDEX "${replicaIdentity.index}"`
: replicaIdentity.type.toUpperCase();

return `ALTER TABLE ${tableNameWithSchema} REPLICA IDENTITY ${clause};`;
});

const convertors = [
createSchemaConvertor,
dropSchemaConvertor,
Expand Down Expand Up @@ -767,6 +781,7 @@ const convertors = [
alterPolicyConvertor,
recreatePolicy,
toggleRlsConvertor,
replicaIdentityConvertor,
alterPrimaryKeyConvertor,
alterColumnAddNotNullConvertor,
alterColumnDropNotNullConvertor,
Expand Down
7 changes: 6 additions & 1 deletion drizzle-kit/src/dialects/cockroach/ddl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@ import { defaults } from './grammar';
export const createDDL = () => {
return create({
schemas: {},
tables: { schema: 'required', isRlsEnabled: 'boolean' },
tables: {
schema: 'required',
isRlsEnabled: 'boolean',
replicaIdentity: { type: ['full', 'nothing', 'index'], index: 'string?' },
},
enums: {
schema: 'required',
values: 'string[]',
Expand Down Expand Up @@ -138,6 +142,7 @@ export type Table = {
checks: CheckConstraint[];
policies: Policy[];
isRlsEnabled: boolean;
replicaIdentity: CockroachEntities['tables']['replicaIdentity'];
};

export type InterimColumn = Omit<Column, 'primaryKey'> & {
Expand Down
26 changes: 26 additions & 0 deletions drizzle-kit/src/dialects/cockroach/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -764,6 +764,30 @@ export const ddlDiff = async (
}
});

// replica identity: set for newly created tables (after indexes are created, see ordering below)
// and altered for existing tables when it changes
const jsonAlterReplicaIdentityStatements = [
...createdTables
.filter((it) => it.replicaIdentity)
.map((it) =>
prepareStatement('alter_replica_identity', {
schema: it.schema,
name: it.name,
replicaIdentity: it.replicaIdentity,
})
),
...alters
.filter((it) => it.entityType === 'tables')
.filter((it) => it.replicaIdentity)
.map((it) =>
prepareStatement('alter_replica_identity', {
schema: it.schema,
name: it.name,
replicaIdentity: it.replicaIdentity?.to ?? null,
})
),
];

// explicit rls alters
const rlsAlters = alters.filter((it) => it.entityType === 'tables').filter((it) => it.isRlsEnabled);

Expand Down Expand Up @@ -1063,6 +1087,8 @@ export const ddlDiff = async (
jsonStatements.push(...jsonRecreateFKs);
jsonStatements.push(...jsonCreateIndexes);

jsonStatements.push(...jsonAlterReplicaIdentityStatements); // after indexes for `USING INDEX` to resolve

jsonStatements.push(...jsonDropColumnsStatemets);

jsonStatements.push(...jsonCreatedCheckConstraints);
Expand Down
10 changes: 10 additions & 0 deletions drizzle-kit/src/dialects/cockroach/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,15 @@ import {
typeFor,
} from './grammar';

export const replicaIdentityFrom = (
replicaIdentity: ReturnType<typeof getTableConfig>['replicaIdentity'],
): CockroachEntities['tables']['replicaIdentity'] => {
if (!replicaIdentity || replicaIdentity === 'default') return null;
if (replicaIdentity === 'full') return { type: 'full', index: null };
if (replicaIdentity === 'nothing') return { type: 'nothing', index: null };
return { type: 'index', index: replicaIdentity.usingIndex };
};

export const policyFrom = (policy: CockroachPolicy, dialect: CockroachDialect) => {
const mappedTo = !policy.to
? ['public']
Expand Down Expand Up @@ -280,6 +289,7 @@ export const fromDrizzleSchema = (
schema,
name: config.name,
isRlsEnabled,
replicaIdentity: replicaIdentityFrom(config.replicaIdentity),
} satisfies CockroachEntities['tables'];
});

Expand Down
18 changes: 18 additions & 0 deletions drizzle-kit/src/dialects/cockroach/introspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,9 @@ export const fromDatabase = async (
accessMethod: number;
options: string[] | null;
rlsEnabled: boolean;
/* d - default, n - nothing, f - full, i - index */
replicaIdentity: 'd' | 'n' | 'f' | 'i';
replicaIdentityIndex: string | null;
tablespaceid: number;
definition: string | null;
}>(
Expand All @@ -141,6 +144,14 @@ export const fromDatabase = async (
reloptions::text[] as "options",
reltablespace as "tablespaceid",
relrowsecurity AS "rlsEnabled",
relreplident::text AS "replicaIdentity",
(
SELECT ci.relname
FROM pg_catalog.pg_index i
JOIN pg_catalog.pg_class ci ON ci.oid OPERATOR(pg_catalog.=) i.indexrelid
WHERE i.indrelid OPERATOR(pg_catalog.=) pg_class.oid AND i.indisreplident
LIMIT 1
) AS "replicaIdentityIndex",
CASE
WHEN relkind OPERATOR(pg_catalog.=) 'v' OR relkind OPERATOR(pg_catalog.=) 'm'
THEN pg_catalog.pg_get_viewdef(pg_class.oid, true)
Expand Down Expand Up @@ -191,6 +202,13 @@ export const fromDatabase = async (
schema: table.schema,
name: table.name,
isRlsEnabled: table.rlsEnabled,
replicaIdentity: table.replicaIdentity === 'f'
? { type: 'full', index: null }
: table.replicaIdentity === 'n'
? { type: 'nothing', index: null }
: table.replicaIdentity === 'i'
? { type: 'index', index: table.replicaIdentityIndex }
: null,
});
}

Expand Down
9 changes: 9 additions & 0 deletions drizzle-kit/src/dialects/cockroach/statements.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import type { Simplify } from '../../utils';
import type {
CheckConstraint,
CockroachEntities,
Column,
DiffEntities,
Enum,
Expand Down Expand Up @@ -182,6 +183,13 @@ export interface JsonAlterRLS {
isRlsEnabled: boolean;
}

export interface JsonAlterReplicaIdentity {
type: 'alter_replica_identity';
schema: string;
name: string;
replicaIdentity: CockroachEntities['tables']['replicaIdentity'];
}

export interface JsonAlterPolicy {
type: 'alter_policy';
diff: DiffEntities['policies'];
Expand Down Expand Up @@ -423,6 +431,7 @@ export type JsonStatement =
| JsonRecreatePolicy
| JsonRenamePolicy
| JsonAlterRLS
| JsonAlterReplicaIdentity
| JsonRenameRole
| JsonCreateRole
| JsonDropRole
Expand Down
8 changes: 7 additions & 1 deletion drizzle-kit/src/dialects/cockroach/typescript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,13 @@ export const ddlToTypeScript = (ddl: CockroachDDL, columnsForViews: ViewColumn[]
statement += createTableChecks(table.checks, casing);
statement += ']';
}
statement += ');';
statement += ')';
if (table.replicaIdentity) {
statement += table.replicaIdentity.type === 'index'
? `.replicaIdentity({ usingIndex: "${table.replicaIdentity.index}" })`
: `.replicaIdentity("${table.replicaIdentity.type}")`;
}
statement += ';';
return statement;
});

Expand Down
18 changes: 18 additions & 0 deletions drizzle-kit/src/dialects/postgres/aws-introspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,6 +166,9 @@ export const fromDatabase = async (
accessMethod: string;
options: string[] | null;
rlsEnabled: boolean;
/* d - default, n - nothing, f - full, i - index */
replicaIdentity: 'd' | 'n' | 'f' | 'i';
replicaIdentityIndex: string | null;
tablespaceid: string;
definition: string | null;
};
Expand All @@ -181,6 +184,14 @@ export const fromDatabase = async (
reloptions::text[] as "options",
reltablespace as "tablespaceid",
relrowsecurity AS "rlsEnabled",
relreplident::text AS "replicaIdentity",
(
SELECT ci.relname
FROM pg_catalog.pg_index i
JOIN pg_catalog.pg_class ci ON ci.oid OPERATOR(pg_catalog.=) i.indexrelid
WHERE i.indrelid OPERATOR(pg_catalog.=) pg_class.oid AND i.indisreplident
LIMIT 1
) AS "replicaIdentityIndex",
CASE
WHEN relkind OPERATOR(pg_catalog.=) 'v' OR relkind OPERATOR(pg_catalog.=) 'm'
THEN pg_catalog.pg_get_viewdef(pg_class.oid, true)
Expand Down Expand Up @@ -225,6 +236,13 @@ export const fromDatabase = async (
schema: trimChar(table.schema, "'"),
name: table.name,
isRlsEnabled: table.rlsEnabled,
replicaIdentity: table.replicaIdentity === 'f'
? { type: 'full', index: null }
: table.replicaIdentity === 'n'
? { type: 'nothing', index: null }
: table.replicaIdentity === 'i'
? { type: 'index', index: table.replicaIdentityIndex }
: null,
});
}

Expand Down
10 changes: 10 additions & 0 deletions drizzle-kit/src/dialects/postgres/commutativity.ts
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,7 @@ class PostgresCommutativity extends AbstractCommutativity<
'alter_policy',
'recreate_policy',
'alter_rls',
'alter_replica_identity',
'grant_privilege',
'revoke_privilege',
'regrant_privilege',
Expand Down Expand Up @@ -599,6 +600,15 @@ class PostgresCommutativity extends AbstractCommutativity<
}),
},

// Replica identity operations
alter_replica_identity: {
conflicts: ['alter_replica_identity'],
buildInfo: (statement) => ({
primary: makeTableTarget((statement as any).schema, (statement as any).name),
ancestors: [],
}),
},

// Role operations
create_role: {
conflicts: ['create_role', 'drop_role', 'rename_role', 'alter_role'],
Expand Down
17 changes: 17 additions & 0 deletions drizzle-kit/src/dialects/postgres/convertor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,22 @@ const toggleRlsConvertor = convertor('alter_rls', (st) => {
return `ALTER TABLE ${tableNameWithSchema} ${isRlsEnabled ? 'ENABLE' : 'DISABLE'} ROW LEVEL SECURITY;`;
});

const replicaIdentityConvertor = convertor('alter_replica_identity', (st) => {
const { schema, name, replicaIdentity } = st;

const tableNameWithSchema = schema !== 'public'
? `"${schema}"."${name}"`
: `"${name}"`;

const clause = !replicaIdentity
? 'DEFAULT'
: replicaIdentity.type === 'index'
? `USING INDEX "${replicaIdentity.index}"`
: replicaIdentity.type.toUpperCase();

return `ALTER TABLE ${tableNameWithSchema} REPLICA IDENTITY ${clause};`;
});

const convertors = [
createSchemaConvertor,
dropSchemaConvertor,
Expand Down Expand Up @@ -1097,6 +1113,7 @@ const convertors = [
alterPolicyConvertor,
recreatePolicy,
toggleRlsConvertor,
replicaIdentityConvertor,
];

export function fromJson(
Expand Down
7 changes: 6 additions & 1 deletion drizzle-kit/src/dialects/postgres/ddl.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,11 @@ import { defaultNameForPK, defaultNameForUnique } from './grammar';
export const createDDL = () => {
return create({
schemas: {},
tables: { schema: 'required', isRlsEnabled: 'boolean' },
tables: {
schema: 'required',
isRlsEnabled: 'boolean',
replicaIdentity: { type: ['full', 'nothing', 'index'], index: 'string?' },
},
enums: {
schema: 'required',
values: 'string[]',
Expand Down Expand Up @@ -201,6 +205,7 @@ export type Table = {
checks: CheckConstraint[];
policies: Policy[];
isRlsEnabled: boolean;
replicaIdentity: PostgresEntities['tables']['replicaIdentity'];
};

export type InterimColumn = Omit<Column, 'primaryKey'> & {
Expand Down
26 changes: 26 additions & 0 deletions drizzle-kit/src/dialects/postgres/diff.ts
Original file line number Diff line number Diff line change
Expand Up @@ -937,6 +937,30 @@ export const ddlDiff = async (
},
);

// replica identity: set for newly created tables (after indexes are created, see ordering below)
// and altered for existing tables when it changes
const jsonAlterReplicaIdentityStatements = [
...createdTables
.filter((it) => it.replicaIdentity)
.map((it) =>
prepareStatement('alter_replica_identity', {
schema: it.schema,
name: it.name,
replicaIdentity: it.replicaIdentity,
})
),
...alters
.filter((it) => it.entityType === 'tables')
.filter((it) => it.replicaIdentity)
.map((it) =>
prepareStatement('alter_replica_identity', {
schema: it.schema,
name: it.name,
replicaIdentity: it.replicaIdentity?.to ?? null,
})
),
];

// explicit rls alters
const rlsAlters = alters.filter((it) => it.entityType === 'tables').filter((it) => it.isRlsEnabled);

Expand Down Expand Up @@ -1267,6 +1291,8 @@ export const ddlDiff = async (
jsonStatements.push(...jsonAlteredUniqueConstraints);
jsonStatements.push(...jsonCreateIndexes); // above fks for uniqueness constraint to come first

jsonStatements.push(...jsonAlterReplicaIdentityStatements); // after indexes for `USING INDEX` to resolve

jsonStatements.push(...jsonCreateFKs);
jsonStatements.push(...jsonRecreateFKs);

Expand Down
10 changes: 10 additions & 0 deletions drizzle-kit/src/dialects/postgres/drizzle.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,15 @@ import {
typeFor,
} from './grammar';

export const replicaIdentityFrom = (
replicaIdentity: ReturnType<typeof getTableConfig>['replicaIdentity'],
): PostgresEntities['tables']['replicaIdentity'] => {
if (!replicaIdentity || replicaIdentity === 'default') return null;
if (replicaIdentity === 'full') return { type: 'full', index: null };
if (replicaIdentity === 'nothing') return { type: 'nothing', index: null };
return { type: 'index', index: replicaIdentity.usingIndex };
};

export const policyFrom = (policy: PgPolicy, dialect: PgDialect) => {
const mappedTo = !policy.to
? ['public']
Expand Down Expand Up @@ -307,6 +316,7 @@ export const fromDrizzleSchema = (
schema,
name: config.name,
isRlsEnabled,
replicaIdentity: replicaIdentityFrom(config.replicaIdentity),
} satisfies PostgresEntities['tables'];
});

Expand Down
1 change: 1 addition & 0 deletions drizzle-kit/src/dialects/postgres/duckdb-introspect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ export const fromDatabase = async (
schema: trimChar(table.schema, "'"),
name: table.name,
isRlsEnabled: false,
replicaIdentity: null,
});
}

Expand Down
Loading