Task 2.3: Replace single-column unique constraints with composite (user_id, name) unique indexes

- charts.name → uniqueIndex('charts_user_id_name_unique').on(user_id, name)
- annotation_types.name → uniqueIndex('annotation_types_user_id_name_unique').on(user_id, name)
- span_label_types.name → uniqueIndex('span_label_types_user_id_name_unique').on(user_id, name)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marko Djordjevic 2026-02-20 09:48:50 +01:00
parent 0cd74ebedd
commit b633a3f52a
2 changed files with 13 additions and 7 deletions

View file

@ -16,9 +16,11 @@ export const users = pgTable('users', {
export const charts = pgTable('charts', {
id: serial('id').primaryKey(),
user_id: uuid('user_id').references(() => users.id), // nullable for now; will be NOT NULL after data migration (task 2.5)
name: text('name').notNull().unique(),
name: text('name').notNull(),
created_at: timestamp('created_at').notNull().defaultNow(),
});
}, (table) => [
uniqueIndex('charts_user_id_name_unique').on(table.user_id, table.name),
]);
export const candles = pgTable('candles', {
id: serial('id').primaryKey(),
@ -35,14 +37,16 @@ export const candles = pgTable('candles', {
export const annotationTypes = pgTable('annotation_types', {
id: serial('id').primaryKey(),
user_id: uuid('user_id').references(() => users.id), // nullable for now; will be NOT NULL after data migration (task 2.5)
name: text('name').notNull().unique(), // internal name (e.g., 'break_up')
name: text('name').notNull(), // internal name (e.g., 'break_up')
display_name: text('display_name').notNull(), // display name (e.g., 'Break Up')
color: text('color').notNull(), // hex color code
category: text('category').notNull(), // 'marker' or 'line'
icon: text('icon'), // icon name or symbol
is_active: boolean('is_active').notNull().default(true), // true = active, false = inactive
created_at: timestamp('created_at').notNull().defaultNow(),
});
}, (table) => [
uniqueIndex('annotation_types_user_id_name_unique').on(table.user_id, table.name),
]);
export const annotations = pgTable('annotations', {
id: serial('id').primaryKey(),
@ -58,14 +62,16 @@ export const annotations = pgTable('annotations', {
export const spanLabelTypes = pgTable('span_label_types', {
id: serial('id').primaryKey(),
user_id: uuid('user_id').references(() => users.id), // nullable for now; will be NOT NULL after data migration (task 2.5)
name: text('name').notNull().unique(), // internal name (e.g., 'bull_flag')
name: text('name').notNull(), // internal name (e.g., 'bull_flag')
display_name: text('display_name').notNull(), // UI label (e.g., 'Bull Flag')
color: text('color').notNull(), // hex color for rectangle fill
hotkey: text('hotkey'), // keyboard shortcut (e.g., '1')
is_active: boolean('is_active').notNull().default(true), // true = active, false = inactive
sort_order: integer('sort_order').notNull().default(0), // display order
created_at: timestamp('created_at').notNull().defaultNow(),
});
}, (table) => [
uniqueIndex('span_label_types_user_id_name_unique').on(table.user_id, table.name),
]);
export const spanAnnotations = pgTable('span_annotations', {
id: serial('id').primaryKey(),