From 01198d546a8bfb984cfb021475fc29e5dbc9da19 Mon Sep 17 00:00:00 2001 From: Tau Date: Sun, 22 Mar 2020 22:34:26 +0000 Subject: [PATCH] sql: Add NULL support to database wrappers --- src/aimedb/sql.ts | 4 ++-- src/checkdb.ts | 2 +- src/idz/sql/backgrounds.ts | 2 +- src/idz/sql/car.ts | 32 ++++++++++++++++---------------- src/idz/sql/chara.ts | 20 ++++++++++---------- src/idz/sql/coursePlays.ts | 4 ++-- src/idz/sql/missions.ts | 6 +++--- src/idz/sql/profile.ts | 18 +++++++++--------- src/idz/sql/settings.ts | 10 +++++----- src/idz/sql/story.ts | 12 ++++++------ src/idz/sql/team.ts | 10 +++++----- src/idz/sql/teamAuto.ts | 4 ++-- src/idz/sql/teamMember.ts | 4 ++-- src/idz/sql/teamReservation.ts | 4 ++-- src/idz/sql/tickets.ts | 2 +- src/idz/sql/timeAttack.ts | 28 ++++++++++++++-------------- src/idz/sql/titles.ts | 2 +- src/idz/sql/unlocks.ts | 10 +++++----- src/sql/api.ts | 2 +- src/sql/util.ts | 28 +++++++++++++++++++++------- 20 files changed, 109 insertions(+), 95 deletions(-) diff --git a/src/aimedb/sql.ts b/src/aimedb/sql.ts index 3ae5429..42dabfe 100644 --- a/src/aimedb/sql.ts +++ b/src/aimedb/sql.ts @@ -19,8 +19,8 @@ class CardRepositoryImpl implements CardRepository { return undefined; } - const id = row.id; - const extId = row.ext_id; + const id = row.id!; + const extId = row.ext_id!; const touchSql = sql .update("aime_player") diff --git a/src/checkdb.ts b/src/checkdb.ts index 8389821..8857842 100644 --- a/src/checkdb.ts +++ b/src/checkdb.ts @@ -111,7 +111,7 @@ export default async function checkdb(db: DataSource): Promise { const row = await db.transaction(txn => txn.fetchRow(stmt)); if (row !== undefined) { - maybe = parseInt(row.schemaver); + maybe = parseInt(row.schemaver!); } } catch (e) { return db.transaction(initdb); diff --git a/src/idz/sql/backgrounds.ts b/src/idz/sql/backgrounds.ts index 16d8866..e183b08 100644 --- a/src/idz/sql/backgrounds.ts +++ b/src/idz/sql/backgrounds.ts @@ -21,7 +21,7 @@ export class SqlBackgroundsRepository const result = new Set(); for (const row of rows) { - result.add(parseInt(row.background_no) as BackgroundCode); + result.add(parseInt(row.background_no!) as BackgroundCode); } return result; diff --git a/src/idz/sql/car.ts b/src/idz/sql/car.ts index 1b3d436..a36565e 100644 --- a/src/idz/sql/car.ts +++ b/src/idz/sql/car.ts @@ -8,21 +8,21 @@ import { Row, Transaction } from "../../sql"; function _extractRow(row: Row): Car { return { - selector: parseInt(row.selector) as CarSelector, - field_00: parseInt(row.field_00), - field_02: parseInt(row.field_02), - field_04: row.field_04.split(",").map((x: string) => parseInt(x)), - field_46: parseInt(row.field_46), - field_48: parseInt(row.field_48), - field_4a: parseInt(row.field_4a), - field_4c: parseInt(row.field_4c), - field_50_lo: parseInt(row.field_50_lo), - field_50_hi: parseInt(row.field_50_hi), - field_58: parseInt(row.field_58), - field_5a: parseInt(row.field_5a), - field_5b: parseInt(row.field_5b), - field_5c: parseInt(row.field_5c), - field_5e: parseInt(row.field_5e), + selector: parseInt(row.selector!) as CarSelector, + field_00: parseInt(row.field_00!), + field_02: parseInt(row.field_02!), + field_04: row.field_04!.split(",").map((x: string) => parseInt(x)), + field_46: parseInt(row.field_46!), + field_48: parseInt(row.field_48!), + field_4a: parseInt(row.field_4a!), + field_4c: parseInt(row.field_4c!), + field_50_lo: parseInt(row.field_50_lo!), + field_50_hi: parseInt(row.field_50_hi!), + field_58: parseInt(row.field_58!), + field_5a: parseInt(row.field_5a!), + field_5b: parseInt(row.field_5b!), + field_5c: parseInt(row.field_5c!), + field_5e: parseInt(row.field_5e!), }; } @@ -37,7 +37,7 @@ export class SqlCarRepository implements CarRepository { const row = await this._txn.fetchRow(countSql); - return parseInt(row!.result); + return parseInt(row!.result!); } async loadCars( diff --git a/src/idz/sql/chara.ts b/src/idz/sql/chara.ts index ca4722f..2203743 100644 --- a/src/idz/sql/chara.ts +++ b/src/idz/sql/chara.ts @@ -9,16 +9,16 @@ import { Row, Transaction } from "../../sql"; export function _extractChara(row: Row): Chara { return { - gender: row.gender as Gender, - field_02: parseInt(row.field_02), - field_04: parseInt(row.field_04), - field_06: parseInt(row.field_06), - field_08: parseInt(row.field_08), - field_0a: parseInt(row.field_0a), - field_0c: parseInt(row.field_0c), - field_0e: parseInt(row.field_0e), - title: parseInt(row.title) as TitleCode, - background: parseInt(row.background) as BackgroundCode, + gender: row.gender! as Gender, + field_02: parseInt(row.field_02!), + field_04: parseInt(row.field_04!), + field_06: parseInt(row.field_06!), + field_08: parseInt(row.field_08!), + field_0a: parseInt(row.field_0a!), + field_0c: parseInt(row.field_0c!), + field_0e: parseInt(row.field_0e!), + title: parseInt(row.title!) as TitleCode, + background: parseInt(row.background!) as BackgroundCode, }; } diff --git a/src/idz/sql/coursePlays.ts b/src/idz/sql/coursePlays.ts index 1cdab6c..b44559f 100644 --- a/src/idz/sql/coursePlays.ts +++ b/src/idz/sql/coursePlays.ts @@ -19,8 +19,8 @@ export class SqlCoursePlaysRepository implements CoursePlaysRepository { const result = new Map(); for (const row of rows) { - const courseNo = parseInt(row.course_no) as CourseNo; - const count = parseInt(row.count); + const courseNo = parseInt(row.course_no!) as CourseNo; + const count = parseInt(row.count!); result.set(courseNo, count); } diff --git a/src/idz/sql/missions.ts b/src/idz/sql/missions.ts index fea4182..ef33c94 100644 --- a/src/idz/sql/missions.ts +++ b/src/idz/sql/missions.ts @@ -36,9 +36,9 @@ export class SqlMissionsRepository implements FacetRepository { const rows = await this._txn.fetchRows(loadSoloSql); for (const row of rows) { - const gridNo = parseInt(row.grid_no); - const cellNo = parseInt(row.cell_no); - const value = parseInt(row.value); + const gridNo = parseInt(row.grid_no!); + const cellNo = parseInt(row.cell_no!); + const value = parseInt(row.value!); result.solo[gridNo].cells[cellNo] = value; } diff --git a/src/idz/sql/profile.ts b/src/idz/sql/profile.ts index ee409fb..4dfc550 100644 --- a/src/idz/sql/profile.ts +++ b/src/idz/sql/profile.ts @@ -7,15 +7,15 @@ import { Row, Transaction } from "../../sql"; export function _extractProfile(row: Row): Profile { return { - aimeId: parseInt(row.aime_id) as AimeId, - name: row.name, - lv: parseInt(row.lv), - exp: parseInt(row.exp), - fame: parseInt(row.fame), - dpoint: parseInt(row.dpoint), - mileage: parseInt(row.mileage), - accessTime: new Date(row.access_time), - registerTime: new Date(row.register_time), + aimeId: parseInt(row.aime_id!) as AimeId, + name: row.name!, + lv: parseInt(row.lv!), + exp: parseInt(row.exp!), + fame: parseInt(row.fame!), + dpoint: parseInt(row.dpoint!), + mileage: parseInt(row.mileage!), + accessTime: new Date(row.access_time!), + registerTime: new Date(row.register_time!), }; } diff --git a/src/idz/sql/settings.ts b/src/idz/sql/settings.ts index f7779c6..d90782f 100644 --- a/src/idz/sql/settings.ts +++ b/src/idz/sql/settings.ts @@ -22,11 +22,11 @@ export class SqlSettingsRepository implements FacetRepository { } return { - music: parseInt(row.music), - pack: parseInt(row.pack), - aura: parseInt(row.aura), - paperCup: parseInt(row.paper_cup), - gauges: parseInt(row.gauges), + music: parseInt(row.music!), + pack: parseInt(row.pack!), + aura: parseInt(row.aura!), + paperCup: parseInt(row.paper_cup!), + gauges: parseInt(row.gauges!), }; } diff --git a/src/idz/sql/story.ts b/src/idz/sql/story.ts index d799509..aad2d0f 100644 --- a/src/idz/sql/story.ts +++ b/src/idz/sql/story.ts @@ -20,8 +20,8 @@ export class SqlStoryRepository implements FacetRepository { // Must succeed even if nonexistent (required by save method below) const result = { - x: header !== undefined ? parseInt(header.x) : 0, - y: header !== undefined ? parseInt(header.y) : 0, + x: header !== undefined ? parseInt(header.x!) : 0, + y: header !== undefined ? parseInt(header.y!) : 0, rows: new Array(), }; @@ -43,12 +43,12 @@ export class SqlStoryRepository implements FacetRepository { const rows = await this._txn.fetchRows(loadCellSql); for (const row of rows) { - const rowNo = parseInt(row.row_no); - const colNo = parseInt(row.col_no); + const rowNo = parseInt(row.row_no!); + const colNo = parseInt(row.col_no!); const cell = result.rows[rowNo].cells[colNo]; - cell.a = parseInt(row.a); - cell.b = parseInt(row.b); + cell.a = parseInt(row.a!); + cell.b = parseInt(row.b!); } return result; diff --git a/src/idz/sql/team.ts b/src/idz/sql/team.ts index 368d83b..0d3b0f4 100644 --- a/src/idz/sql/team.ts +++ b/src/idz/sql/team.ts @@ -37,11 +37,11 @@ export class SqlTeamRepository implements TeamRepository { } return { - extId: parseInt(row.ext_id) as ExtId, - name: row.name, - nameBg: parseInt(row.name_bg), - nameFx: parseInt(row.name_fx), - registerTime: new Date(row.register_time), + extId: parseInt(row.ext_id!) as ExtId, + name: row.name!, + nameBg: parseInt(row.name_bg!), + nameFx: parseInt(row.name_fx!), + registerTime: new Date(row.register_time!), }; } diff --git a/src/idz/sql/teamAuto.ts b/src/idz/sql/teamAuto.ts index 5aed74b..4030686 100644 --- a/src/idz/sql/teamAuto.ts +++ b/src/idz/sql/teamAuto.ts @@ -20,8 +20,8 @@ export class SqlTeamAutoRepository implements TeamAutoRepository { return ( row && [ { - serialNo: parseInt(row.serial_no), - nameIdx: parseInt(row.name_idx), + serialNo: parseInt(row.serial_no!), + nameIdx: parseInt(row.name_idx!), }, row.id as Id, ] diff --git a/src/idz/sql/teamMember.ts b/src/idz/sql/teamMember.ts index 43286eb..abe93bf 100644 --- a/src/idz/sql/teamMember.ts +++ b/src/idz/sql/teamMember.ts @@ -57,7 +57,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { profile: _extractProfile(row), chara: _extractChara(row), leader: !!row.leader, - joinTime: new Date(row.join_time), + joinTime: new Date(row.join_time!), })); } @@ -92,7 +92,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository { const row = await this._txn.fetchRow(countSql); - if (parseInt(row!.count) >= 6) { + if (parseInt(row!.count!) >= 6) { throw new Error(`Team ${teamId} is full`); } diff --git a/src/idz/sql/teamReservation.ts b/src/idz/sql/teamReservation.ts index 9020360..e8babae 100644 --- a/src/idz/sql/teamReservation.ts +++ b/src/idz/sql/teamReservation.ts @@ -28,7 +28,7 @@ export class SqlTeamReservationRepository .where("tm.team_id", teamId); const memberRes = await this._txn.fetchRow(memberSql); - const memberCount = parseInt(memberRes!.count); + const memberCount = parseInt(memberRes!.count!); const reservSql = sql .select("count(*) as count") @@ -36,7 +36,7 @@ export class SqlTeamReservationRepository .where("tr.team_id", teamId); const reservRes = await this._txn.fetchRow(reservSql); - const reservCount = parseInt(reservRes!.count); + const reservCount = parseInt(reservRes!.count!); return memberCount + reservCount; } diff --git a/src/idz/sql/tickets.ts b/src/idz/sql/tickets.ts index 616c0f4..8d6821d 100644 --- a/src/idz/sql/tickets.ts +++ b/src/idz/sql/tickets.ts @@ -21,7 +21,7 @@ export class SqlTicketsRepository implements FacetRepository { return { freeCar: row && { - validFrom: new Date(row.valid_from), + validFrom: new Date(row.valid_from!), }, }; } diff --git a/src/idz/sql/timeAttack.ts b/src/idz/sql/timeAttack.ts index 0916cc9..f289065 100644 --- a/src/idz/sql/timeAttack.ts +++ b/src/idz/sql/timeAttack.ts @@ -11,13 +11,13 @@ import { Row, Transaction } from "../../sql"; function _extractRow(row: Row): TimeAttackScore { return { - routeNo: parseInt(row.route_no) as RouteNo, - timestamp: new Date(row.timestamp), - flags: parseInt(row.flags), - totalTime: parseFloat(row.total_time), - sectionTimes: row.section_times.split(",").map(parseFloat), - grade: parseInt(row.grade), - carSelector: parseInt(row.car_selector) as CarSelector, + routeNo: parseInt(row.route_no!) as RouteNo, + timestamp: new Date(row.timestamp!), + flags: parseInt(row.flags!), + totalTime: parseFloat(row.total_time!), + sectionTimes: row.section_times!.split(",").map(parseFloat), + grade: parseInt(row.grade!), + carSelector: parseInt(row.car_selector!) as CarSelector, }; } @@ -61,13 +61,13 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { const rows = await this._txn.fetchRows(loadSql); return rows.map(row => ({ - driverName: row.profile_name, + driverName: row.profile_name!, team: { - extId: parseInt(row.team_ext_id) as ExtId, - name: row.team_name, - nameBg: parseInt(row.team_name_bg), - nameFx: parseInt(row.team_name_fx), - registerTime: new Date(row.team_register_time), + extId: parseInt(row.team_ext_id!) as ExtId, + name: row.team_name!, + nameBg: parseInt(row.team_name_bg!), + nameFx: parseInt(row.team_name_fx!), + registerTime: new Date(row.team_register_time!), }, ta: _extractRow(row), })); @@ -122,7 +122,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository { await this._txn.modify(insertSql); } else { - if (score.totalTime < parseFloat(row.total_time)) { + if (score.totalTime < parseFloat(row.total_time!)) { const updateSql = sql .update("idz_ta_best", { total_time: score.totalTime, diff --git a/src/idz/sql/titles.ts b/src/idz/sql/titles.ts index 7a79071..d80a321 100644 --- a/src/idz/sql/titles.ts +++ b/src/idz/sql/titles.ts @@ -19,7 +19,7 @@ export class SqlTitlesRepository implements FlagRepository { const result = new Set(); for (const row of rows) { - result.add(parseInt(row.title_no) as TitleCode); + result.add(parseInt(row.title_no!) as TitleCode); } return result; diff --git a/src/idz/sql/unlocks.ts b/src/idz/sql/unlocks.ts index 7c046f1..95acbe5 100644 --- a/src/idz/sql/unlocks.ts +++ b/src/idz/sql/unlocks.ts @@ -22,11 +22,11 @@ export class SqlUnlocksRepository implements FacetRepository { } return { - auras: parseInt(row.auras), - cup: parseInt(row.cup), - gauges: parseInt(row.gauges), - music: parseInt(row.music), - lastMileageReward: parseInt(row.last_mileage_reward), + auras: parseInt(row.auras!), + cup: parseInt(row.cup!), + gauges: parseInt(row.gauges!), + music: parseInt(row.music!), + lastMileageReward: parseInt(row.last_mileage_reward!), }; } diff --git a/src/sql/api.ts b/src/sql/api.ts index bc540e1..8246cc4 100644 --- a/src/sql/api.ts +++ b/src/sql/api.ts @@ -3,7 +3,7 @@ import * as sql from "sql-bricks-postgres"; import { Id } from "../model"; export interface Row { - [key: string]: string; + [key: string]: string | null; } export interface Transaction { diff --git a/src/sql/util.ts b/src/sql/util.ts index c8669c0..7f180cd 100644 --- a/src/sql/util.ts +++ b/src/sql/util.ts @@ -3,15 +3,23 @@ import snakeCase from "snake-case"; import { Row } from "./api"; interface ColMapper { - _read(str: string): F; + _read(str: string | null): F; - _write(val: F): string; + _write(val: F): string | null; } type Spec = { [K in keyof R]: ColMapper; }; +function _nn(str: string | null): string { + if (str === null) { + throw new Error("Unexpected NULL returned from database"); + } + + return str; +} + /** * Function objects describing the precise way in which our SQL driver * transmits and receives values to the database as strings. Note that we could @@ -20,25 +28,31 @@ type Spec = { */ export const T = { bigint: { - _read: (str: string) => BigInt(str), + _read: (str: string) => BigInt(_nn(str)), _write: (val: bigint) => val.toString(), }, boolean: { - _read: (str: string) => str === "true", + _read: (str: string) => _nn(str) === "true", _write: (val: boolean) => val.toString(), }, number: { - _read: (str: string) => parseInt(str), + _read: (str: string) => parseInt(_nn(str)), _write: (val: number) => val.toString(), }, string: { - _read: (str: string) => str, + _read: (str: string) => _nn(str), _write: (val: string) => val, }, Date: { - _read: (str: string) => new Date(str), + _read: (str: string) => new Date(_nn(str)), _write: (val: Date) => val.toISOString(), }, + nullable: (inner: ColMapper) => ({ + _read: (str: string | null) => + str !== null ? inner._read(str) : undefined, + _write: (val: F | undefined) => + val !== undefined ? inner._write(val) : null, + }), }; /**