Skip to content

Commit 4357230

Browse files
committed
fix
1 parent e7f4516 commit 4357230

File tree

4 files changed

+248
-103
lines changed

4 files changed

+248
-103
lines changed

apps/sim/app/api/table/[tableId]/rows/route.ts

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,17 @@ import { checkHybridAuth } from '@/lib/auth/hybrid'
88
import { generateRequestId } from '@/lib/core/utils/request'
99
import type { Filter, RowData, Sort, TableSchema } from '@/lib/table'
1010
import {
11+
checkUniqueConstraintsDb,
1112
getUniqueColumns,
1213
TABLE_LIMITS,
14+
USER_TABLE_ROWS_SQL_NAME,
1315
validateBatchRows,
1416
validateRowAgainstSchema,
1517
validateRowData,
1618
validateRowSize,
17-
validateUniqueConstraints,
1819
} from '@/lib/table'
1920
import { buildFilterClause, buildSortClause } from '@/lib/table/sql'
20-
import { accessError, checkAccess, verifyTableWorkspace } from '../../utils'
21+
import { accessError, checkAccess } from '../../utils'
2122

2223
const logger = createLogger('TableRowsAPI')
2324

@@ -295,8 +296,7 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams
295296

296297
const { table } = accessResult
297298

298-
const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId)
299-
if (!isValidWorkspace) {
299+
if (validated.workspaceId !== table.workspaceId) {
300300
logger.warn(
301301
`[${requestId}] Workspace ID mismatch for table ${tableId}. Provided: ${validated.workspaceId}, Actual: ${table.workspaceId}`
302302
)
@@ -309,7 +309,7 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams
309309
]
310310

311311
if (validated.filter) {
312-
const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows')
312+
const filterClause = buildFilterClause(validated.filter as Filter, USER_TABLE_ROWS_SQL_NAME)
313313
if (filterClause) {
314314
baseConditions.push(filterClause)
315315
}
@@ -327,7 +327,7 @@ export async function GET(request: NextRequest, { params }: TableRowsRouteParams
327327

328328
if (validated.sort) {
329329
const schema = table.schema as TableSchema
330-
const sortClause = buildSortClause(validated.sort, 'user_table_rows', schema.columns)
330+
const sortClause = buildSortClause(validated.sort, USER_TABLE_ROWS_SQL_NAME, schema.columns)
331331
if (sortClause) {
332332
query = query.orderBy(sortClause) as typeof query
333333
}
@@ -417,7 +417,7 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams
417417
eq(userTableRows.workspaceId, validated.workspaceId),
418418
]
419419

420-
const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows')
420+
const filterClause = buildFilterClause(validated.filter as Filter, USER_TABLE_ROWS_SQL_NAME)
421421
if (filterClause) {
422422
baseConditions.push(filterClause)
423423
}
@@ -469,23 +469,16 @@ export async function PUT(request: NextRequest, { params }: TableRowsRouteParams
469469
}
470470
}
471471

472+
// Check unique constraints using optimized database query
472473
const uniqueColumns = getUniqueColumns(table.schema as TableSchema)
473474
if (uniqueColumns.length > 0) {
474-
const allRows = await db
475-
.select({
476-
id: userTableRows.id,
477-
data: userTableRows.data,
478-
})
479-
.from(userTableRows)
480-
.where(eq(userTableRows.tableId, tableId))
481-
482475
for (const row of matchingRows) {
483476
const existingData = row.data as RowData
484477
const mergedData = { ...existingData, ...updateData }
485-
const uniqueValidation = validateUniqueConstraints(
478+
const uniqueValidation = await checkUniqueConstraintsDb(
479+
tableId,
486480
mergedData,
487481
table.schema as TableSchema,
488-
allRows.map((r) => ({ id: r.id, data: r.data as RowData })),
489482
row.id
490483
)
491484

@@ -573,8 +566,7 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar
573566

574567
const { table } = accessResult
575568

576-
const isValidWorkspace = await verifyTableWorkspace(tableId, validated.workspaceId)
577-
if (!isValidWorkspace) {
569+
if (validated.workspaceId !== table.workspaceId) {
578570
logger.warn(
579571
`[${requestId}] Workspace ID mismatch for table ${tableId}. Provided: ${validated.workspaceId}, Actual: ${table.workspaceId}`
580572
)
@@ -586,7 +578,7 @@ export async function DELETE(request: NextRequest, { params }: TableRowsRoutePar
586578
eq(userTableRows.workspaceId, validated.workspaceId),
587579
]
588580

589-
const filterClause = buildFilterClause(validated.filter as Filter, 'user_table_rows')
581+
const filterClause = buildFilterClause(validated.filter as Filter, USER_TABLE_ROWS_SQL_NAME)
590582
if (filterClause) {
591583
baseConditions.push(filterClause)
592584
}

apps/sim/lib/table/constants.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,6 @@ export const TABLE_LIMITS = {
2626
export const COLUMN_TYPES = ['string', 'number', 'boolean', 'date', 'json'] as const
2727

2828
export const NAME_PATTERN = /^[a-z_][a-z0-9_]*$/i
29+
30+
/** Database table name for user table rows (used in SQL query building) */
31+
export const USER_TABLE_ROWS_SQL_NAME = 'user_table_rows'

apps/sim/lib/table/service.ts

Lines changed: 19 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ import { db } from '@sim/db'
1111
import { userTableDefinitions, userTableRows } from '@sim/db/schema'
1212
import { createLogger } from '@sim/logger'
1313
import { and, count, eq, sql } from 'drizzle-orm'
14-
import { TABLE_LIMITS } from './constants'
14+
import { TABLE_LIMITS, USER_TABLE_ROWS_SQL_NAME } from './constants'
1515
import { buildFilterClause, buildSortClause } from './sql'
1616
import type {
1717
BatchInsertData,
@@ -29,12 +29,13 @@ import type {
2929
UpdateRowData,
3030
} from './types'
3131
import {
32+
checkBatchUniqueConstraintsDb,
33+
checkUniqueConstraintsDb,
3234
getUniqueColumns,
3335
validateRowAgainstSchema,
3436
validateRowSize,
3537
validateTableName,
3638
validateTableSchema,
37-
validateUniqueConstraints,
3839
} from './validation'
3940

4041
const logger = createLogger('TableService')
@@ -227,19 +228,10 @@ export async function insertRow(
227228
throw new Error(`Schema validation failed: ${schemaValidation.errors.join(', ')}`)
228229
}
229230

230-
// Check unique constraints
231+
// Check unique constraints using optimized database query
231232
const uniqueColumns = getUniqueColumns(table.schema)
232233
if (uniqueColumns.length > 0) {
233-
const existingRows = await db
234-
.select({ id: userTableRows.id, data: userTableRows.data })
235-
.from(userTableRows)
236-
.where(eq(userTableRows.tableId, data.tableId))
237-
238-
const uniqueValidation = validateUniqueConstraints(
239-
data.data,
240-
table.schema,
241-
existingRows.map((r) => ({ id: r.id, data: r.data as RowData }))
242-
)
234+
const uniqueValidation = await checkUniqueConstraintsDb(data.tableId, data.data, table.schema)
243235
if (!uniqueValidation.valid) {
244236
throw new Error(uniqueValidation.errors.join(', '))
245237
}
@@ -306,23 +298,16 @@ export async function batchInsertRows(
306298
}
307299
}
308300

309-
// Check unique constraints across all rows
301+
// Check unique constraints across all rows using optimized database query
310302
const uniqueColumns = getUniqueColumns(table.schema)
311303
if (uniqueColumns.length > 0) {
312-
const existingRows = await db
313-
.select({ id: userTableRows.id, data: userTableRows.data })
314-
.from(userTableRows)
315-
.where(eq(userTableRows.tableId, data.tableId))
316-
317-
const allRows = existingRows.map((r) => ({ id: r.id, data: r.data as RowData }))
318-
319-
for (let i = 0; i < data.rows.length; i++) {
320-
const uniqueValidation = validateUniqueConstraints(data.rows[i], table.schema, allRows)
321-
if (!uniqueValidation.valid) {
322-
throw new Error(`Row ${i + 1}: ${uniqueValidation.errors.join(', ')}`)
323-
}
324-
// Add to allRows for checking subsequent rows in batch
325-
allRows.push({ id: `pending_${i}`, data: data.rows[i] })
304+
const uniqueResult = await checkBatchUniqueConstraintsDb(data.tableId, data.rows, table.schema)
305+
if (!uniqueResult.valid) {
306+
// Format errors for batch insert
307+
const errorMessages = uniqueResult.errors
308+
.map((e) => `Row ${e.row + 1}: ${e.errors.join(', ')}`)
309+
.join('; ')
310+
throw new Error(errorMessages)
326311
}
327312
}
328313

@@ -365,7 +350,7 @@ export async function queryRows(
365350
): Promise<QueryResult> {
366351
const { filter, sort, limit = TABLE_LIMITS.DEFAULT_QUERY_LIMIT, offset = 0 } = options
367352

368-
const tableName = 'user_table_rows'
353+
const tableName = USER_TABLE_ROWS_SQL_NAME
369354

370355
// Build WHERE clause
371356
const baseConditions = and(
@@ -493,18 +478,13 @@ export async function updateRow(
493478
throw new Error(`Schema validation failed: ${schemaValidation.errors.join(', ')}`)
494479
}
495480

496-
// Check unique constraints
481+
// Check unique constraints using optimized database query
497482
const uniqueColumns = getUniqueColumns(table.schema)
498483
if (uniqueColumns.length > 0) {
499-
const existingRows = await db
500-
.select({ id: userTableRows.id, data: userTableRows.data })
501-
.from(userTableRows)
502-
.where(eq(userTableRows.tableId, data.tableId))
503-
504-
const uniqueValidation = validateUniqueConstraints(
484+
const uniqueValidation = await checkUniqueConstraintsDb(
485+
data.tableId,
505486
data.data,
506487
table.schema,
507-
existingRows.map((r) => ({ id: r.id, data: r.data as RowData })),
508488
data.rowId // Exclude current row
509489
)
510490
if (!uniqueValidation.valid) {
@@ -567,7 +547,7 @@ export async function updateRowsByFilter(
567547
table: TableDefinition,
568548
requestId: string
569549
): Promise<BulkOperationResult> {
570-
const tableName = 'user_table_rows'
550+
const tableName = USER_TABLE_ROWS_SQL_NAME
571551

572552
// Build filter clause
573553
const filterClause = buildFilterClause(data.filter, tableName)
@@ -651,7 +631,7 @@ export async function deleteRowsByFilter(
651631
data: BulkDeleteData,
652632
requestId: string
653633
): Promise<BulkOperationResult> {
654-
const tableName = 'user_table_rows'
634+
const tableName = USER_TABLE_ROWS_SQL_NAME
655635

656636
// Build filter clause
657637
const filterClause = buildFilterClause(data.filter, tableName)

0 commit comments

Comments
 (0)