Index v2 runner (#14537)

## Introduction
https://github.com/twentyhq/twenty/pull/14363
In this PR we're creating builder and runner for index metadata
Removing its previous integration within object builder

## Next:
- implem index impact on field and object metadata api
- Add integration testing coverage add integration test on index
- Implement uniqueness toggle in field metadata api
- add integration test on uniqueness

related to https://github.com/twentyhq/core-team-issues/issues/1344
This commit is contained in:
Paul Rastoin
2025-09-17 15:02:43 +02:00
committed by GitHub
parent 6e47c1b5c6
commit 2d8669ae71
55 changed files with 1692 additions and 1064 deletions
@@ -15,48 +15,34 @@ export class WorkspaceSchemaIndexManagerService {
tableName: string;
index: WorkspaceSchemaIndexDefinition;
}): Promise<void> {
try {
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeTableName = removeSqlDDLInjection(tableName);
const safeIndexName = removeSqlDDLInjection(index.name);
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeTableName = removeSqlDDLInjection(tableName);
const safeIndexName = removeSqlDDLInjection(index.name);
const quotedColumns = index.columns.map(
(column) => `"${removeSqlDDLInjection(column)}"`,
);
const isUnique = index.isUnique ? 'UNIQUE' : '';
const indexType =
index.type && index.type !== 'BTREE' ? `USING ${index.type}` : '';
const whereClause = index.where ? `WHERE ${index.where}` : ''; // TODO: to sanitize
const includeClause = index.include?.length
? `INCLUDE (${index.include
.map((col) => `"${removeSqlDDLInjection(col)}"`)
.join(', ')})`
: '';
const quotedColumns = index.columns.map(
(column) => `"${removeSqlDDLInjection(column)}"`,
);
const isUnique = index.isUnique ? 'UNIQUE' : '';
const indexType =
index.type && index.type !== 'BTREE' ? `USING ${index.type}` : '';
const whereClause = index.where ? `WHERE ${index.where}` : ''; // TODO: to sanitize -> might search for a lib to sanitize sql queries
const sql = [
'CREATE',
isUnique && 'UNIQUE',
'INDEX IF NOT EXISTS',
`"${safeIndexName}"`,
'ON',
`"${safeSchemaName}"."${safeTableName}"`,
indexType,
`(${quotedColumns.join(', ')})`,
includeClause,
whereClause,
]
.filter(Boolean)
.join(' ')
.trim();
const sql = [
'CREATE',
isUnique && 'UNIQUE',
'INDEX IF NOT EXISTS',
`"${safeIndexName}"`,
'ON',
`"${safeSchemaName}"."${safeTableName}"`,
indexType,
`(${quotedColumns.join(', ')})`,
whereClause,
]
.filter(Boolean)
.join(' ')
.trim();
await queryRunner.query(sql);
} catch (error: unknown) {
// Ignore error if index already exists
if (error instanceof Error && 'code' in error && error.code === '42P07') {
return;
}
throw error;
}
await queryRunner.query(sql);
}
async dropIndex({
@@ -67,80 +53,10 @@ export class WorkspaceSchemaIndexManagerService {
queryRunner: QueryRunner;
schemaName: string;
indexName: string;
}): Promise<void> {
try {
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeIndexName = removeSqlDDLInjection(indexName);
const sql = `DROP INDEX IF EXISTS "${safeSchemaName}"."${safeIndexName}"`;
await queryRunner.query(sql);
} catch (error: unknown) {
// Ignore error if index does not exist
if (error instanceof Error && 'code' in error && error.code === '42704') {
return;
}
throw error;
}
}
async renameIndex({
queryRunner,
schemaName,
oldIndexName,
newIndexName,
}: {
queryRunner: QueryRunner;
schemaName: string;
oldIndexName: string;
newIndexName: string;
}): Promise<void> {
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeOldIndexName = removeSqlDDLInjection(oldIndexName);
const safeNewIndexName = removeSqlDDLInjection(newIndexName);
const sql = `ALTER INDEX "${safeSchemaName}"."${safeOldIndexName}" RENAME TO "${safeNewIndexName}"`;
await queryRunner.query(sql);
}
async createUniqueConstraint({
queryRunner,
schemaName,
tableName,
constraintName,
columnNames,
}: {
queryRunner: QueryRunner;
schemaName: string;
tableName: string;
constraintName: string;
columnNames: string[];
}): Promise<void> {
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeTableName = removeSqlDDLInjection(tableName);
const safeConstraintName = removeSqlDDLInjection(constraintName);
const quotedColumns = columnNames
.map((col) => `"${removeSqlDDLInjection(col)}"`)
.join(', ');
const sql = `ALTER TABLE "${safeSchemaName}"."${safeTableName}" ADD CONSTRAINT "${safeConstraintName}" UNIQUE (${quotedColumns})`;
await queryRunner.query(sql);
}
async dropUniqueConstraint({
queryRunner,
schemaName,
tableName,
constraintName,
}: {
queryRunner: QueryRunner;
schemaName: string;
tableName: string;
constraintName: string;
}): Promise<void> {
const safeSchemaName = removeSqlDDLInjection(schemaName);
const safeTableName = removeSqlDDLInjection(tableName);
const safeConstraintName = removeSqlDDLInjection(constraintName);
const sql = `ALTER TABLE "${safeSchemaName}"."${safeTableName}" DROP CONSTRAINT IF EXISTS "${safeConstraintName}"`;
const safeIndexName = removeSqlDDLInjection(indexName);
const sql = `DROP INDEX IF EXISTS "${safeSchemaName}"."${safeIndexName}"`;
await queryRunner.query(sql);
}
@@ -12,5 +12,4 @@ export type WorkspaceSchemaIndexDefinition = {
type?: WorkspaceSchemaIndexType;
isUnique?: boolean;
where?: string;
include?: string[];
};