Skip to content

Commit 2bb6a6d

Browse files
icecrasher321claude
andcommitted
improvement(db): keep declarations, route reads through live-column maps
Restructures the contract prep per review: the no-op 0309 migration is gone and schema.ts keeps the deprecated declarations (with the contract-pending markers on the columns), so the follow-up PR ships the declaration removal and the real generated DROP migration together. What makes that safe is that no deployed query names the doomed columns anymore: every argless select()/returning()/relational read of user_stats, organization, and workflow_execution_logs (29 sites) now goes through omit(getTableColumns(...)) live-column maps, following the workspaceFileColumns precedent. scripts/check-pending-drop-tables.ts enforces this — it derives the guarded tables from the contract-pending markers themselves (picking up workspace_files' pending size drop too) and fails CI on any argless read, so the invariant holds between the two PRs and retires itself when the markers are deleted. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 5609775 commit 2bb6a6d

19 files changed

Lines changed: 602 additions & 20164 deletions

File tree

apps/sim/app/api/organizations/[id]/route.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
22
import { db } from '@sim/db'
3-
import { member, organization } from '@sim/db/schema'
3+
import { member, organization, organizationColumns } from '@sim/db/schema'
44
import { createLogger } from '@sim/logger'
55
import { isOrgAdminRole } from '@sim/platform-authz/workspace'
66
import { and, eq, ne } from 'drizzle-orm'
@@ -64,7 +64,7 @@ export const GET = withRouteHandler(
6464
}
6565

6666
const organizationEntry = await db
67-
.select()
67+
.select(organizationColumns)
6868
.from(organization)
6969
.where(eq(organization.id, organizationId))
7070
.limit(1)
@@ -156,7 +156,7 @@ export const PUT = withRouteHandler(
156156
if (name !== undefined || slug !== undefined || logo !== undefined) {
157157
if (slug !== undefined) {
158158
const existingSlug = await db
159-
.select()
159+
.select(organizationColumns)
160160
.from(organization)
161161
.where(and(eq(organization.slug, slug), ne(organization.id, organizationId)))
162162
.limit(1)
@@ -180,7 +180,7 @@ export const PUT = withRouteHandler(
180180
.update(organization)
181181
.set(updateData)
182182
.where(eq(organization.id, organizationId))
183-
.returning()
183+
.returning(organizationColumns)
184184

185185
if (updatedOrg.length === 0) {
186186
return NextResponse.json({ error: 'Organization not found' }, { status: 404 })

apps/sim/app/api/v1/admin/organizations/[id]/billing/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
*/
1717

1818
import { db, dbReplica } from '@sim/db'
19-
import { member, organization } from '@sim/db/schema'
19+
import { member, organization, organizationColumns } from '@sim/db/schema'
2020
import { createLogger } from '@sim/logger'
2121
import { count, eq } from 'drizzle-orm'
2222
import {
@@ -155,7 +155,7 @@ export const PATCH = withRouteHandler(
155155
if (!parsed.success) return parsed.response
156156

157157
const [orgData] = await db
158-
.select()
158+
.select(organizationColumns)
159159
.from(organization)
160160
.where(eq(organization.id, organizationId))
161161
.limit(1)

apps/sim/app/api/v1/admin/organizations/[id]/route.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ import {
3838
recordAuditBatch,
3939
} from '@sim/audit'
4040
import { db } from '@sim/db'
41-
import { member, organization, subscription } from '@sim/db/schema'
41+
import { member, organization, organizationColumns, subscription } from '@sim/db/schema'
4242
import { createLogger } from '@sim/logger'
4343
import { and, count, eq, inArray, isNull, not, or } from 'drizzle-orm'
4444
import {
@@ -92,7 +92,7 @@ export const GET = withRouteHandler(
9292

9393
try {
9494
const [orgData] = await db
95-
.select()
95+
.select(organizationColumns)
9696
.from(organization)
9797
.where(eq(organization.id, organizationId))
9898
.limit(1)
@@ -143,7 +143,7 @@ export const PATCH = withRouteHandler(
143143

144144
try {
145145
const [existing] = await db
146-
.select()
146+
.select(organizationColumns)
147147
.from(organization)
148148
.where(eq(organization.id, organizationId))
149149
.limit(1)
@@ -182,7 +182,7 @@ export const PATCH = withRouteHandler(
182182
.update(organization)
183183
.set(updateData)
184184
.where(eq(organization.id, organizationId))
185-
.returning()
185+
.returning(organizationColumns)
186186

187187
const updatedFields = auditUpdatedFields(updateData)
188188
logger.info(`Admin API: Updated organization ${organizationId}`, { updatedFields })

apps/sim/app/api/v1/admin/organizations/route.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323

2424
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
2525
import { db, dbReplica } from '@sim/db'
26-
import { member, organization, user } from '@sim/db/schema'
26+
import { member, organization, organizationColumns, user } from '@sim/db/schema'
2727
import { createLogger } from '@sim/logger'
2828
import { slugify } from '@sim/utils/string'
2929
import { count, eq } from 'drizzle-orm'
@@ -151,7 +151,7 @@ export const POST = withRouteHandler(
151151
})
152152

153153
const [createdOrg] = await db
154-
.select()
154+
.select(organizationColumns)
155155
.from(organization)
156156
.where(eq(organization.id, organizationId))
157157
.limit(1)

apps/sim/app/api/v1/admin/users/[id]/billing/route.ts

Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,14 @@
2020
*/
2121

2222
import { db } from '@sim/db'
23-
import { member, organization, subscription, user, userStats } from '@sim/db/schema'
23+
import {
24+
member,
25+
organization,
26+
subscription,
27+
user,
28+
userStats,
29+
userStatsColumns,
30+
} from '@sim/db/schema'
2431
import { createLogger } from '@sim/logger'
2532
import { generateShortId } from '@sim/utils/id'
2633
import { eq, or } from 'drizzle-orm'
@@ -78,7 +85,11 @@ export const GET = withRouteHandler(
7885
return notFoundResponse('User')
7986
}
8087

81-
const [stats] = await db.select().from(userStats).where(eq(userStats.userId, userId)).limit(1)
88+
const [stats] = await db
89+
.select(userStatsColumns)
90+
.from(userStats)
91+
.where(eq(userStats.userId, userId))
92+
.limit(1)
8293

8394
// Canonical current-period usage (attributed usage_log, refresh-adjusted)
8495
// comes from the same helper users see.
@@ -168,7 +179,7 @@ export const PATCH = withRouteHandler(
168179
}
169180

170181
const [existingStats] = await db
171-
.select()
182+
.select(userStatsColumns)
172183
.from(userStats)
173184
.where(eq(userStats.userId, userId))
174185
.limit(1)

apps/sim/lib/admin/dashboard.ts

Lines changed: 8 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { db } from '@sim/db'
33
import {
44
member,
55
organization,
6+
organizationColumns,
67
organizationMemberUsageLimit,
78
outboxEvent,
89
permissions,
@@ -651,7 +652,11 @@ export async function listDashboardUsers({ search, limit, offset }: PaginationIn
651652
async function getDashboardOrganizationSummary(organizationId: string) {
652653
const [[org], [memberCountRow], [externalCountRow], latestSubscription, provisionings] =
653654
await Promise.all([
654-
db.select().from(organization).where(eq(organization.id, organizationId)).limit(1),
655+
db
656+
.select(organizationColumns)
657+
.from(organization)
658+
.where(eq(organization.id, organizationId))
659+
.limit(1),
655660
db.select({ value: count() }).from(member).where(eq(member.organizationId, organizationId)),
656661
db
657662
.select({ value: countDistinct(permissions.userId) })
@@ -1277,7 +1282,7 @@ export async function updateDashboardOrganizationLimits(
12771282
const providerBacked = await db.transaction(async (tx) => {
12781283
await acquireOrganizationMutationLock(tx, organizationId)
12791284
const [org] = await tx
1280-
.select()
1285+
.select(organizationColumns)
12811286
.from(organization)
12821287
.where(eq(organization.id, organizationId))
12831288
.for('update')
@@ -1411,7 +1416,7 @@ export async function grantDashboardOrganizationBalance(
14111416
}),
14121417
operation: async () => {
14131418
const [org] = await tx
1414-
.select()
1419+
.select(organizationColumns)
14151420
.from(organization)
14161421
.where(eq(organization.id, organizationId))
14171422
.for('update')

apps/sim/lib/auth/anonymous.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export async function ensureAnonymousUserExists(): Promise<void> {
3232
}
3333

3434
const existingStats = await db.query.userStats.findFirst({
35+
columns: { id: true },
3536
where: eq(schema.userStats.userId, ANONYMOUS_USER_ID),
3637
})
3738

apps/sim/lib/billing/core/organization.ts

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
import { db } from '@sim/db'
2-
import { member, organization, usageLog, user, userStats } from '@sim/db/schema'
2+
import {
3+
member,
4+
organization,
5+
organizationColumns,
6+
usageLog,
7+
user,
8+
userStats,
9+
} from '@sim/db/schema'
310
import { createLogger } from '@sim/logger'
411
import { and, count, eq, gte, lt, sql } from 'drizzle-orm'
512
import { isOrganizationBillingBlocked } from '@/lib/billing/core/access'
@@ -189,7 +196,7 @@ export async function getOrganizationBillingData(
189196
try {
190197
// Get organization info
191198
const orgRecord = await executor
192-
.select()
199+
.select(organizationColumns)
193200
.from(organization)
194201
.where(eq(organization.id, organizationId))
195202
.limit(1)
@@ -368,7 +375,7 @@ export async function updateOrganizationUsageLimit(
368375
try {
369376
// Validate the organization exists
370377
const orgRecord = await db
371-
.select()
378+
.select(organizationColumns)
372379
.from(organization)
373380
.where(eq(organization.id, organizationId))
374381
.limit(1)

apps/sim/lib/billing/core/usage.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { db } from '@sim/db'
2-
import { member, organization, settings, user, userStats } from '@sim/db/schema'
2+
import { member, organization, settings, user, userStats, userStatsColumns } from '@sim/db/schema'
33
import { createLogger } from '@sim/logger'
44
import { isOrgAdminRole } from '@sim/platform-authz/workspace'
55
import { generateId } from '@sim/utils/id'
@@ -207,7 +207,7 @@ export async function getResolvedUserUsageData(
207207
// inserted, which a lagging replica can miss (this path throws on a
208208
// missing row). Stays on the primary deliberately.
209209
db
210-
.select()
210+
.select(userStatsColumns)
211211
.from(userStats)
212212
.where(eq(userStats.userId, userId))
213213
.limit(1),
@@ -324,7 +324,7 @@ export async function getUserUsageLimitInfo(userId: string): Promise<UsageLimitI
324324
try {
325325
const [subscription, userStatsRecord] = await Promise.all([
326326
getHighestPrioritySubscription(userId),
327-
db.select().from(userStats).where(eq(userStats.userId, userId)).limit(1),
327+
db.select(userStatsColumns).from(userStats).where(eq(userStats.userId, userId)).limit(1),
328328
])
329329

330330
if (userStatsRecord.length === 0) {
@@ -576,7 +576,7 @@ export async function checkUsageStatus(userId: string): Promise<{
576576
export async function syncUsageLimitsFromSubscription(userId: string): Promise<void> {
577577
const [subscription, currentUserStats] = await Promise.all([
578578
getHighestPrioritySubscription(userId),
579-
db.select().from(userStats).where(eq(userStats.userId, userId)).limit(1),
579+
db.select(userStatsColumns).from(userStats).where(eq(userStats.userId, userId)).limit(1),
580580
])
581581

582582
if (currentUserStats.length === 0) {

apps/sim/lib/billing/threshold-billing.ts

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,13 @@
11
import { AuditAction, AuditResourceType, recordAudit } from '@sim/audit'
22
import { db } from '@sim/db'
3-
import { member, organization, subscription, userStats } from '@sim/db/schema'
3+
import {
4+
member,
5+
organization,
6+
organizationColumns,
7+
subscription,
8+
userStats,
9+
userStatsColumns,
10+
} from '@sim/db/schema'
411
import { createLogger } from '@sim/logger'
512
import { toError } from '@sim/utils/errors'
613
import { and, eq, sql } from 'drizzle-orm'
@@ -342,7 +349,7 @@ export async function checkAndBillOverageThreshold(
342349
await tx.execute(sql.raw(`SET LOCAL lock_timeout = '${BILLING_LOCK_TIMEOUT_MS}ms'`))
343350

344351
const statsRecords = await tx
345-
.select()
352+
.select(userStatsColumns)
346353
.from(userStats)
347354
.where(eq(userStats.userId, userId))
348355
.for('update')
@@ -697,7 +704,7 @@ async function checkAndBillOrganizationOverageThreshold(
697704
}
698705

699706
const ownerStatsLock = await tx
700-
.select()
707+
.select(userStatsColumns)
701708
.from(userStats)
702709
.where(eq(userStats.userId, lockedOwnerId))
703710
.for('update')
@@ -724,7 +731,7 @@ async function checkAndBillOrganizationOverageThreshold(
724731
}
725732

726733
const orgLock = await tx
727-
.select()
734+
.select(organizationColumns)
728735
.from(organization)
729736
.where(eq(organization.id, organizationId))
730737
.for('update')

0 commit comments

Comments
 (0)