a homebrewed DnD campaign based in the Honkai: Star Rail universe
hsr honkaistarrail dnd
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

db: fix schema indexes and add checks

- indexes that already defined and then have definition changed are not re-evaluated on `drizzle-kit push`, but when dropping the entire schema and attempting to recreate anything, it fails. use inline sql function in the mean time (probably fixed in drizzle-kit/drizzle-orm v1 beta, upgrade currently blocked)
- add checks that some text columns are not empty strings

+183 -89
+21 -13
app/src/lib/server/background/db-schema.ts
··· 1 - import { index, integer, pgTable, primaryKey, text } from 'drizzle-orm/pg-core' 1 + import { ne, sql } from 'drizzle-orm' 2 + import { check, index, integer, pgTable, primaryKey, text } from 'drizzle-orm/pg-core' 2 3 import { publicId } from '$lib/unique-id' 3 - import { lower } from '$server/db/expressions' 4 + import { emptyString, lower } from '$server/db/expressions' 4 5 import { equipment, tool } from '$server/equipment/db-schema' 5 6 import { feat } from '$server/feat/db-schema' 6 7 import { ability, skill } from '$server/mechanic/db-schema' ··· 23 24 .notNull() 24 25 .references(() => feat.id), 25 26 }, 26 - (table) => [index('background_idx_lower_name').on(lower(table.name))], 27 + (table) => [ 28 + index('background_idx_lower_name').on(lower(table.name)), 29 + check('name_is_not_empty_string', ne(table.name, emptyString())), 30 + ], 27 31 ) 28 32 29 33 export const backgroundAbility = pgTable( ··· 59 63 .references(() => background.id, { onDelete: 'cascade' }), 60 64 }) 61 65 62 - export const equipmentPackageItem = pgTable('equipment_pack_item', { 63 - id: integer('id').primaryKey().generatedAlwaysAsIdentity(), 64 - equipmentPackageId: integer('equipment_package_id') 65 - .notNull() 66 - .references(() => equipmentPackage.id, { onDelete: 'cascade' }), 67 - equipmentId: integer('equipment_id') 68 - .notNull() 69 - .references(() => equipment.id), 70 - quantity: integer('quantity').notNull(), 71 - }) 66 + export const equipmentPackageItem = pgTable( 67 + 'equipment_pack_item', 68 + { 69 + id: integer('id').primaryKey().generatedAlwaysAsIdentity(), 70 + equipmentPackageId: integer('equipment_package_id') 71 + .notNull() 72 + .references(() => equipmentPackage.id, { onDelete: 'cascade' }), 73 + equipmentId: integer('equipment_id') 74 + .notNull() 75 + .references(() => equipment.id), 76 + quantity: integer('quantity').notNull(), 77 + }, 78 + (table) => [check('quantity_is_nonnegative', sql`${table.quantity} >= 0`)], 79 + )
+15 -2
app/src/lib/server/campaign/db-schema.ts
··· 1 - import { boolean, index, integer, pgTable, primaryKey, text, timestamp } from 'drizzle-orm/pg-core' 1 + import { ne, sql } from 'drizzle-orm' 2 + import { 3 + boolean, 4 + check, 5 + index, 6 + integer, 7 + pgTable, 8 + primaryKey, 9 + text, 10 + timestamp, 11 + } from 'drizzle-orm/pg-core' 2 12 import { publicId } from '$lib/unique-id' 3 13 import { user } from '$server/auth/db-schema' 4 14 import { lower } from '$server/db/expressions' ··· 17 27 createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), 18 28 endedAt: timestamp('ended_at', { withTimezone: true }), 19 29 }, 20 - (table) => [index('campaign_idx_lower_name').on(lower(table.name))], 30 + (table) => [ 31 + index('campaign_idx_lower_name').on(lower(table.name)), 32 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 33 + ], 21 34 ) 22 35 23 36 export const campaignMember = pgTable('campaign_member', {
+11 -10
app/src/lib/server/character/db-schema.ts
··· 1 - import { gte, sql } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 check, 4 4 index, ··· 52 52 }, 53 53 (table) => [ 54 54 index('character_idx_lower_name').on(lower(table.name)), 55 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 55 56 check('level_is_valid', sql`${table.level} >= 0 AND ${table.level} <= 20`), 56 57 check( 57 58 'current_hp_is_valid', 58 59 sql`${table.currentHitPoints} >= 0 AND ${table.currentHitPoints} <= ${table.totalHitPoints}`, 59 60 ), 60 - check('total_hp_is_nonnegative', gte(table.totalHitPoints, 0)), 61 - check('temp_hp_is_nonnegative', gte(table.tempHitPoints, 0)), 61 + check('total_hp_is_nonnegative', sql`${table.totalHitPoints} >= 0`), 62 + check('temp_hp_is_nonnegative', sql`${table.tempHitPoints} >= 0`), 62 63 ], 63 64 ) 64 65 ··· 116 117 cha: smallint('charisma').notNull(), 117 118 }, 118 119 (table) => [ 119 - check('str_is_nonnegative', gte(table.str, 0)), 120 - check('dex_is_nonnegative', gte(table.dex, 0)), 121 - check('con_is_nonnegative', gte(table.con, 0)), 122 - check('int_is_nonnegative', gte(table.int, 0)), 123 - check('wis_is_nonnegative', gte(table.wis, 0)), 124 - check('cha_is_nonnegative', gte(table.cha, 0)), 120 + check('str_is_nonnegative', sql`${table.str}, 0`), 121 + check('dex_is_nonnegative', sql`${table.dex}, 0`), 122 + check('con_is_nonnegative', sql`${table.con}, 0`), 123 + check('int_is_nonnegative', sql`${table.int}, 0`), 124 + check('wis_is_nonnegative', sql`${table.wis}, 0`), 125 + check('cha_is_nonnegative', sql`${table.cha}, 0`), 125 126 ], 126 127 ) 127 128 ··· 151 152 }, 152 153 (table) => [ 153 154 primaryKey({ columns: [table.characterId, table.equipmentId] }), 154 - check('amount_is_at_least_1', gte(table.amount, 1)), 155 + check('amount_is_at_least_1', sql`${table.amount} >= 1`), 155 156 ], 156 157 ) 157 158
+10 -3
app/src/lib/server/class/db-schema.ts
··· 1 - import { sql } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 check, 4 4 index, ··· 25 25 description: text('description').notNull(), 26 26 createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), 27 27 }, 28 - (table) => [index('character_class_idx_lower_name').on(lower(table.name))], 28 + (table) => [ 29 + index('character_class_idx_lower_name').on(lower(table.name)), 30 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 31 + ], 29 32 ) 30 33 31 34 export const characterClassFeature = pgTable( ··· 41 44 }, 42 45 (table) => [ 43 46 index('character_class_feature_idx_lower_name').on(lower(table.name)), 47 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 44 48 check('level_is_valid', sql`${table.level} >= 1 AND ${table.level} <= 20`), 45 49 ], 46 50 ) ··· 56 60 description: text('description').notNull(), 57 61 logic: jsonb('logic').notNull(), 58 62 }, 59 - (table) => [index(`character_class_feature_mod_idx_lower_name`).on(table.name)], 63 + (table) => [ 64 + index(`character_class_feature_mod_idx_lower_name`).on(table.name), 65 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 66 + ], 60 67 )
+4
app/src/lib/server/db/expressions.ts
··· 5 5 return sql<string>`lower(${expr})` 6 6 } 7 7 8 + export function emptyString(): SQL { 9 + return sql`''` 10 + } 11 + 8 12 export function isBetweenIncl(expr: SQLWrapper, min: unknown, max: unknown): SQL { 9 13 return and(gte(expr, min), lte(expr, max)) as SQL<unknown> 10 14 }
+6
app/src/lib/server/db/types.ts
··· 1 1 import type { InferInsertModel, InferSelectModel, Table } from 'drizzle-orm' 2 2 import type { QueryResult } from 'pg' 3 + import type { BrandedPublicId } from '$lib/unique-id' 3 4 import type { Sourcebook } from '$server/sourcebook/sourcebook-repo' 4 5 import type { db } from '.' 5 6 ··· 69 70 export interface RepositoryCountableWithSourceBook { 70 71 getCountFromSourcebook(id: Sourcebook['id']): Promise<number> 71 72 } 73 + 74 + export interface RepositoryGetByPublicId<TReturnOne, TReturnMany> { 75 + get(publicId: BrandedPublicId): TReturnOne 76 + getMany(publicIds: BrandedPublicId): TReturnMany 77 + }
+16 -7
app/src/lib/server/equipment/db-schema.ts
··· 1 - import { gte } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 boolean, 4 4 check, ··· 36 36 .$onUpdate(() => /* @__PURE__ */ new Date()) 37 37 .notNull(), 38 38 }, 39 - (table) => [index('equipment_idx_lower_name').on(lower(table.name))], 39 + (table) => [ 40 + index('equipment_idx_lower_name').on(lower(table.name)), 41 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 42 + ], 40 43 ) 41 44 42 45 export const tool = pgTable('tool', { ··· 68 71 stealthEffect: armorStealthEffect('stealth_effect'), 69 72 }, 70 73 (table) => [ 71 - check('required_strength_flat_is_nonnegative', gte(table.requiredMinStrengthFlat, 0)), 72 - check('armor_class_is_nonnegative', gte(table.armorClass, 0)), 73 - check('armor_class_dex_modifier_is_nonnegative', gte(table.armorClassDexModifier, 0)), 74 - check('armor_class_dex_modifier_max_is_nonnegative', gte(table.armorClassDexModifierMax, 0)), 74 + check('required_strength_flat_is_nonnegative', sql`${table.requiredMinStrengthFlat} >= 0`), 75 + check('armor_class_is_nonnegative', sql`${table.armorClass} >= 0`), 76 + check('armor_class_dex_modifier_is_nonnegative', sql`${table.armorClassDexModifier} >= 0`), 77 + check( 78 + 'armor_class_dex_modifier_max_is_nonnegative', 79 + sql`${table.armorClassDexModifierMax} >= 0`, 80 + ), 75 81 ], 76 82 ) 77 83 ··· 81 87 id: integer('id').primaryKey().generatedAlwaysAsIdentity(), 82 88 name: text('name').unique().notNull(), 83 89 }, 84 - (table) => [index('equipment_category_idx_lower_name').on(lower(table.name))], 90 + (table) => [ 91 + index('equipment_category_idx_lower_name').on(lower(table.name)), 92 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 93 + ], 85 94 ) 86 95 87 96 export const equipmentCategories = pgTable(
+3 -1
app/src/lib/server/faction/db-schema.ts
··· 1 - import { ne } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 check, 4 4 foreignKey, ··· 26 26 }, 27 27 (table) => [ 28 28 index('faction_idx_lower_name').on(table.name), 29 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 29 30 foreignKey({ columns: [table.parentFactionId], foreignColumns: [table.id] }), 30 31 ], 31 32 ) ··· 54 55 (table) => [ 55 56 index().on(table.factionId1, table.factionId2), 56 57 check('factions_are_different', ne(table.factionId1, table.factionId2)), 58 + check('reason_is_not_empty_string', ne(table.reason, sql`''`)), 57 59 ], 58 60 )
+6 -2
app/src/lib/server/feat/db-schema.ts
··· 1 - import { boolean, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 1 + import { ne, sql } from 'drizzle-orm' 2 + import { boolean, check, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 2 3 import { publicId } from '$lib/unique-id' 3 4 import { lower } from '$server/db/expressions' 4 5 ··· 11 12 description: text('description').notNull(), 12 13 isRepeatable: boolean('is_repeatable').default(false).notNull(), 13 14 }, 14 - (table) => [index('feat_idx_lower_name').on(lower(table.name))], 15 + (table) => [ 16 + index('feat_idx_lower_name').on(lower(table.name)), 17 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 18 + ], 15 19 )
+6 -2
app/src/lib/server/language/db-schema.ts
··· 1 - import { index, integer, pgTable, text } from 'drizzle-orm/pg-core' 1 + import { ne, sql } from 'drizzle-orm' 2 + import { check, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 2 3 import { lower } from '$server/db/expressions' 3 4 import { sourcebook } from '$server/sourcebook/db-schema' 4 5 ··· 11 12 .references(() => sourcebook.id), 12 13 name: text('name').notNull(), 13 14 }, 14 - (table) => [index('language_idx_lower_name').on(lower(table.name))], 15 + (table) => [ 16 + index('language_idx_lower_name').on(lower(table.name)), 17 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 18 + ], 15 19 )
+18 -5
app/src/lib/server/mechanic/db-schema.ts
··· 1 - import { boolean, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 1 + import { ne, sql } from 'drizzle-orm' 2 + import { boolean, check, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 2 3 import { lower } from '$server/db/expressions' 3 4 import { sourcebook } from '$server/sourcebook/db-schema' 4 5 ··· 12 13 name: text('name').notNull(), 13 14 hasLevels: boolean('has_levels').notNull(), 14 15 }, 15 - (table) => [index('mechanic_idx_lower_name').on(lower(table.name))], 16 + (table) => [ 17 + index('mechanic_idx_lower_name').on(lower(table.name)), 18 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 19 + ], 16 20 ) 17 21 18 22 export const damage = pgTable( ··· 25 29 name: text('name').notNull(), 26 30 description: text('description').notNull(), 27 31 }, 28 - (table) => [index('damage_idx_lower_name').on(lower(table.name))], 32 + (table) => [ 33 + index('damage_idx_lower_name').on(lower(table.name)), 34 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 35 + ], 29 36 ) 30 37 31 38 export const ability = pgTable( ··· 35 42 name: text('name').notNull(), 36 43 description: text('description').notNull(), 37 44 }, 38 - (table) => [index('ability_idx_lower_name').on(lower(table.name))], 45 + (table) => [ 46 + index('ability_idx_lower_name').on(lower(table.name)), 47 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 48 + ], 39 49 ) 40 50 41 51 export const skill = pgTable( ··· 48 58 name: text('name').notNull(), 49 59 description: text('description').notNull(), 50 60 }, 51 - (table) => [index('skill_idx_lower_name').on(lower(table.name))], 61 + (table) => [ 62 + index('skill_idx_lower_name').on(lower(table.name)), 63 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 64 + ], 52 65 )
+25 -24
app/src/lib/server/monster/db-schema.ts
··· 1 - import { gte } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 boolean, 4 4 check, ··· 35 35 }, 36 36 (table) => [ 37 37 index('monster_idx_lower_name').on(lower(table.name)), 38 - check('walk_speed_is_nonnegative', gte(table.walkSpeed, 0)), 39 - check('swim_speed_is_nonnegative', gte(table.swimSpeed, 0)), 40 - check('climb_speed_is_nonnegative', gte(table.climbSpeed, 0)), 41 - check('fly_speed_is_nonnegative', gte(table.flySpeed, 0)), 42 - check('burrow_speed_is_nonnegative', gte(table.burrowSpeed, 0)), 38 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 39 + check('walk_speed_is_nonnegative', sql`${table.walkSpeed} >= 0`), 40 + check('swim_speed_is_nonnegative', sql`${table.swimSpeed} >= 0`), 41 + check('climb_speed_is_nonnegative', sql`${table.climbSpeed} 0`), 42 + check('fly_speed_is_nonnegative', sql`${table.flySpeed} >= 0`), 43 + check('burrow_speed_is_nonnegative', sql`${table.burrowSpeed} >= 0`), 43 44 ], 44 45 ) 45 46 ··· 77 78 }, 78 79 (table) => [ 79 80 // base checks 80 - check('str_is_nonnegative', gte(table.str, 0)), 81 - check('dex_is_nonnegative', gte(table.dex, 0)), 82 - check('con_is_nonnegative', gte(table.con, 0)), 83 - check('int_is_nonnegative', gte(table.int, 0)), 84 - check('wis_is_nonnegative', gte(table.wis, 0)), 85 - check('cha_is_nonnegative', gte(table.cha, 0)), 81 + check('str_is_nonnegative', sql`${table.str} >= 0`), 82 + check('dex_is_nonnegative', sql`${table.dex} >= 0`), 83 + check('con_is_nonnegative', sql`${table.con} >= 0`), 84 + check('int_is_nonnegative', sql`${table.int} >= 0`), 85 + check('wis_is_nonnegative', sql`${table.wis} >= 0`), 86 + check('cha_is_nonnegative', sql`${table.cha} >= 0`), 86 87 // modifier checks 87 - check('str_mod_is_nonnegative', gte(table.strMod, 0)), 88 - check('dex_mod_is_nonnegative', gte(table.dexMod, 0)), 89 - check('con_mod_is_nonnegative', gte(table.conMod, 0)), 90 - check('int_mod_is_nonnegative', gte(table.intMod, 0)), 91 - check('wis_mod_is_nonnegative', gte(table.wisMod, 0)), 92 - check('cha_mod_is_nonnegative', gte(table.chaMod, 0)), 88 + check('str_mod_is_nonnegative', sql`${table.strMod} >= 0`), 89 + check('dex_mod_is_nonnegative', sql`${table.dexMod} >= 0`), 90 + check('con_mod_is_nonnegative', sql`${table.conMod} >= 0`), 91 + check('int_mod_is_nonnegative', sql`${table.intMod} >= 0`), 92 + check('wis_mod_is_nonnegative', sql`${table.wisMod} >= 0`), 93 + check('cha_mod_is_nonnegative', sql`${table.chaMod} >= 0`), 93 94 // saving throw checks 94 - check('str_save_is_nonnegative', gte(table.strSave, 0)), 95 - check('dex_save_is_nonnegative', gte(table.dexSave, 0)), 96 - check('con_save_is_nonnegative', gte(table.conSave, 0)), 97 - check('int_save_is_nonnegative', gte(table.intSave, 0)), 98 - check('wis_save_is_nonnegative', gte(table.wisSave, 0)), 99 - check('cha_save_is_nonnegative', gte(table.chaSave, 0)), 95 + check('str_save_is_nonnegative', sql`${table.strSave} >= 0`), 96 + check('dex_save_is_nonnegative', sql`${table.dexSave} >= 0`), 97 + check('con_save_is_nonnegative', sql`${table.conSave} >= 0`), 98 + check('int_save_is_nonnegative', sql`${table.intSave} >= 0`), 99 + check('wis_save_is_nonnegative', sql`${table.wisSave} >= 0`), 100 + check('cha_save_is_nonnegative', sql`${table.chaSave} >= 0`), 100 101 ], 101 102 ) 102 103
+9 -3
app/src/lib/server/readable/db-schema.ts
··· 1 - import { index, integer, pgTable, text } from 'drizzle-orm/pg-core' 1 + import { ne } from 'drizzle-orm' 2 + import { check, index, integer, pgTable, text } from 'drizzle-orm/pg-core' 2 3 import { publicId } from '$lib/unique-id' 3 - import { lower } from '$server/db/expressions' 4 + import { emptyString, lower } from '$server/db/expressions' 4 5 import { faction } from '$server/faction/db-schema' 5 6 import { sourcebook } from '$server/sourcebook/db-schema' 6 7 ··· 19 20 summary: text('summary').notNull(), 20 21 text: text('text').notNull(), 21 22 }, 22 - (table) => [index('readable_idx_lower_title').on(lower(table.title))], 23 + (table) => [ 24 + index('readable_idx_lower_title').on(lower(table.title)), 25 + check('name_is_not_empty_string', ne(table.title, emptyString())), 26 + check('summary_is_not_empty_string', ne(table.summary, emptyString())), 27 + check('text_is_not_empty_string', ne(table.text, emptyString())), 28 + ], 23 29 )
+19 -8
app/src/lib/server/sourcebook/db-schema.ts
··· 1 - import { index, integer, pgTable, timestamp, text, primaryKey } from 'drizzle-orm/pg-core' 1 + import { ne } from 'drizzle-orm' 2 + import { index, integer, pgTable, timestamp, text, primaryKey, check } from 'drizzle-orm/pg-core' 2 3 import { publicId } from '$lib/unique-id' 3 - import { lower } from '$server/db/expressions' 4 + import { emptyString, lower } from '$server/db/expressions' 4 5 5 6 export const sourcebook = pgTable( 6 7 'sourcebook', ··· 12 13 createdAt: timestamp('created_at', { withTimezone: true }).defaultNow().notNull(), 13 14 archivedAt: timestamp('archived_at', { withTimezone: true }), 14 15 }, 15 - (table) => [index('sourcebook_idx_lower_name').on(lower(table.name))], 16 + (table) => [ 17 + index('sourcebook_idx_lower_name').on(lower(table.name)), 18 + check('name_is_not_empty_string', ne(table.name, emptyString())), 19 + ], 16 20 ) 17 21 18 - export const genre = pgTable('genre', { 19 - id: integer('id').primaryKey().generatedAlwaysAsIdentity(), 20 - name: text('name').unique().notNull(), 21 - description: text('description').notNull(), 22 - }) 22 + export const genre = pgTable( 23 + 'genre', 24 + { 25 + id: integer('id').primaryKey().generatedAlwaysAsIdentity(), 26 + name: text('name').unique().notNull(), 27 + description: text('description').notNull(), 28 + }, 29 + (table) => [ 30 + index('genre_idx_lower_name').on(lower(table.name)), 31 + check('name_is_not_empty_string', ne(table.name, emptyString())), 32 + ], 33 + ) 23 34 24 35 export const sourcebookGenre = pgTable( 25 36 'sourcebook_genre',
+12 -8
app/src/lib/server/species/db-schema.ts
··· 1 - import { gte } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 boolean, 4 4 index, ··· 11 11 check, 12 12 } from 'drizzle-orm/pg-core' 13 13 import { publicId } from '$lib/unique-id' 14 - import { lower } from '$server/db/expressions' 14 + import { emptyString, lower } from '$server/db/expressions' 15 15 import { sourcebook } from '$server/sourcebook/db-schema' 16 16 17 17 export const species = pgTable( ··· 35 35 }, 36 36 (table) => [ 37 37 index('species_idx_lower_name').on(lower(table.name)), 38 - check('walk_speed_is_nonnegative', gte(table.walkSpeed, 0)), 39 - check('swim_speed_is_nonnegative', gte(table.swimSpeed, 0)), 40 - check('climb_speed_is_nonnegative', gte(table.climbSpeed, 0)), 41 - check('fly_speed_is_nonnegative', gte(table.flySpeed, 0)), 42 - check('burrow_speed_is_nonnegative', gte(table.burrowSpeed, 0)), 38 + check('name_is_not_empty_string', ne(table.name, emptyString())), 39 + check('walk_speed_is_nonnegative', sql`${table.walkSpeed} >= 0`), 40 + check('swim_speed_is_nonnegative', sql`${table.swimSpeed} >= 0`), 41 + check('climb_speed_is_nonnegative', sql`${table.climbSpeed} >= 0`), 42 + check('fly_speed_is_nonnegative', sql`${table.flySpeed} >= 0`), 43 + check('burrow_speed_is_nonnegative', sql`${table.burrowSpeed} >= 0`), 43 44 ], 44 45 ) 45 46 ··· 57 58 name: text('name').notNull(), 58 59 description: text('description').notNull(), 59 60 }, 60 - (table) => [index('species_feature_lower_name').on(lower(table.name))], 61 + (table) => [ 62 + index('species_feature_lower_name').on(lower(table.name)), 63 + check('name_is_not_empty_string', ne(table.name, emptyString())), 64 + ], 61 65 ) 62 66 63 67 export const speciesFeatureMod = pgTable('species_feature_mod', {
+2 -1
app/src/lib/server/spell/db-schema.ts
··· 1 - import { sql } from 'drizzle-orm' 1 + import { ne, sql } from 'drizzle-orm' 2 2 import { 3 3 boolean, 4 4 index, ··· 54 54 }, 55 55 (table) => [ 56 56 index('spell_idx_lower_name').on(lower(table.name)), 57 + check('name_is_not_empty_string', ne(table.name, sql`''`)), 57 58 check('level_is_valid', sql`${table.level} >= 0 AND ${table.level} <= 9`), 58 59 ], 59 60 )