Refactor search vector field (#21947)

# Introduction
Refactoring the search vector field validation

<!-- This is an auto-generated description by cubic. -->
<a
href="https://cubic.dev/pr/twentyhq/twenty/pull/21947?utm_source=github"
target="_blank" rel="noopener noreferrer"
data-no-image-dialog="true"><picture><source
media="(prefers-color-scheme: dark)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"><source
media="(prefers-color-scheme: light)"
srcset="https://www.cubic.dev/buttons/review-in-cubic-light.svg"><img
alt="Review in cubic"
src="https://www.cubic.dev/buttons/review-in-cubic-dark.svg"></picture></a>
<!-- End of auto-generated description by cubic. -->
This commit is contained in:
Paul Rastoin
2026-06-22 13:44:52 +02:00
committed by GitHub
parent a08f424cc5
commit 0b8368cd6c
6 changed files with 418 additions and 4 deletions
@@ -1,7 +1,10 @@
import { isDefined } from 'twenty-shared/utils';
import { type WorkspaceSchemaColumnDefinition } from 'src/engine/twenty-orm/workspace-schema-manager/types/workspace-schema-column-definition.type';
import { escapeIdentifier } from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
import {
assertSafeTsVectorExpression,
escapeIdentifier,
} from 'src/engine/workspace-manager/workspace-migration/utils/remove-sql-injection.util';
const ALLOWED_GENERATED_TYPES = new Set(['STORED', 'VIRTUAL']);
@@ -14,9 +17,12 @@ export const buildSqlColumnDefinition = (
// (safe enum-mapped), or a schema-qualified enum type pre-escaped by the caller.
parts.push(column.isArray ? `${column.type}[]` : column.type);
// asExpression is built internally by getTsVectorColumnExpressionFromFields
// (never user-provided). Field names within are escaped at the source.
// asExpression is normally built server-side by getTsVectorColumnExpressionFromFields, but it
// can technically reach here from user input (metadata API or app-sync manifest). The TS_VECTOR
// validator rejects corrupted expressions on every build path; this assert is the last-resort
// guard at the DDL sink so nothing can break out of the GENERATED ALWAYS AS (...) clause.
if (column.asExpression && column.type === 'tsvector') {
assertSafeTsVectorExpression(column.asExpression);
parts.push(`GENERATED ALWAYS AS (${column.asExpression})`);
if (
column.generatedType &&