MCPcopy Index your code
hub / github.com/simstudioai/sim / checkUniqueConstraintsDb

Function checkUniqueConstraintsDb

apps/sim/lib/table/validation.ts:421–504  ·  view source on GitHub ↗
(
  tableId: string,
  data: RowData,
  schema: TableSchema,
  excludeRowId?: string,
  executor: UniqueCheckExecutor = db
)

Source from the content-addressed store, hash-verified

419 * writes; otherwise the default `db` connection only observes committed state.
420 */
421export async function checkUniqueConstraintsDb(
422 tableId: string,
423 data: RowData,
424 schema: TableSchema,
425 excludeRowId?: string,
426 executor: UniqueCheckExecutor = db
427): Promise<ValidationResult> {
428 const errors: string[] = []
429 const uniqueColumns = getUniqueColumns(schema)
430
431 if (uniqueColumns.length === 0) {
432 return { valid: true, errors: [] }
433 }
434
435 // Build conditions for each unique column value
436 const conditions: Array<{ column: ColumnDefinition; value: unknown; sql: SQL }> = []
437
438 for (const column of uniqueColumns) {
439 const key = getColumnId(column)
440 if (!NAME_PATTERN.test(key)) {
441 throw new Error(`Invalid column id: ${key}`)
442 }
443
444 const value = data[key]
445 if (value === null || value === undefined) continue
446
447 if (typeof value === 'string') {
448 conditions.push({
449 column,
450 value,
451 sql: sql`lower(${userTableRows.data}->>${sql.raw(`'${key}'`)}) = ${value.toLowerCase()}`,
452 })
453 } else {
454 // For other types, use direct JSONB comparison
455 conditions.push({
456 column,
457 value,
458 sql: sql`(${userTableRows.data}->${sql.raw(`'${key}'`)})::jsonb = ${JSON.stringify(value)}::jsonb`,
459 })
460 }
461 }
462
463 if (conditions.length === 0) {
464 return { valid: true, errors: [] }
465 }
466
467 // Query for each unique column separately to provide specific error messages.
468 // Tenant-bounded: `lower(data->>'col') = ...` is unestimatable, so the planner
469 // otherwise seq-scans the whole shared relation per check — 3.5s on every
470 // insert/edit when the value is unique (no early exit). With an external
471 // transaction the flag is set on it directly — opening our own transaction
472 // inside the caller's would be the nested pool checkout the migration-
473 // hardening work eliminated (self-deadlock under pool exhaustion).
474 const checkConditions = async (ex: UniqueCheckExecutor) => {
475 for (const condition of conditions) {
476 const baseCondition = and(eq(userTableRows.tableId, tableId), condition.sql)
477
478 const whereClause = excludeRowId

Callers 6

insertRowFunction · 0.90
upsertRowFunction · 0.90
updateRowFunction · 0.90
updateRowsByFilterFunction · 0.90
batchUpdateRowsFunction · 0.90
validateRowDataFunction · 0.85

Calls 7

getColumnIdFunction · 0.90
withSeqscanOffFunction · 0.90
getUniqueColumnsFunction · 0.85
checkConditionsFunction · 0.85
testMethod · 0.80
executeMethod · 0.65
pushMethod · 0.45

Tested by

no test coverage detected