sql: Add NULL support to database wrappers
This commit is contained in:
+2
-2
@@ -19,8 +19,8 @@ class CardRepositoryImpl implements CardRepository {
|
|||||||
return undefined;
|
return undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const id = row.id;
|
const id = row.id!;
|
||||||
const extId = row.ext_id;
|
const extId = row.ext_id!;
|
||||||
|
|
||||||
const touchSql = sql
|
const touchSql = sql
|
||||||
.update("aime_player")
|
.update("aime_player")
|
||||||
|
|||||||
+1
-1
@@ -111,7 +111,7 @@ export default async function checkdb(db: DataSource): Promise<void> {
|
|||||||
const row = await db.transaction(txn => txn.fetchRow(stmt));
|
const row = await db.transaction(txn => txn.fetchRow(stmt));
|
||||||
|
|
||||||
if (row !== undefined) {
|
if (row !== undefined) {
|
||||||
maybe = parseInt(row.schemaver);
|
maybe = parseInt(row.schemaver!);
|
||||||
}
|
}
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
return db.transaction(initdb);
|
return db.transaction(initdb);
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class SqlBackgroundsRepository
|
|||||||
const result = new Set<BackgroundCode>();
|
const result = new Set<BackgroundCode>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
result.add(parseInt(row.background_no) as BackgroundCode);
|
result.add(parseInt(row.background_no!) as BackgroundCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+16
-16
@@ -8,21 +8,21 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
function _extractRow(row: Row): Car {
|
function _extractRow(row: Row): Car {
|
||||||
return {
|
return {
|
||||||
selector: parseInt(row.selector) as CarSelector,
|
selector: parseInt(row.selector!) as CarSelector,
|
||||||
field_00: parseInt(row.field_00),
|
field_00: parseInt(row.field_00!),
|
||||||
field_02: parseInt(row.field_02),
|
field_02: parseInt(row.field_02!),
|
||||||
field_04: row.field_04.split(",").map((x: string) => parseInt(x)),
|
field_04: row.field_04!.split(",").map((x: string) => parseInt(x)),
|
||||||
field_46: parseInt(row.field_46),
|
field_46: parseInt(row.field_46!),
|
||||||
field_48: parseInt(row.field_48),
|
field_48: parseInt(row.field_48!),
|
||||||
field_4a: parseInt(row.field_4a),
|
field_4a: parseInt(row.field_4a!),
|
||||||
field_4c: parseInt(row.field_4c),
|
field_4c: parseInt(row.field_4c!),
|
||||||
field_50_lo: parseInt(row.field_50_lo),
|
field_50_lo: parseInt(row.field_50_lo!),
|
||||||
field_50_hi: parseInt(row.field_50_hi),
|
field_50_hi: parseInt(row.field_50_hi!),
|
||||||
field_58: parseInt(row.field_58),
|
field_58: parseInt(row.field_58!),
|
||||||
field_5a: parseInt(row.field_5a),
|
field_5a: parseInt(row.field_5a!),
|
||||||
field_5b: parseInt(row.field_5b),
|
field_5b: parseInt(row.field_5b!),
|
||||||
field_5c: parseInt(row.field_5c),
|
field_5c: parseInt(row.field_5c!),
|
||||||
field_5e: parseInt(row.field_5e),
|
field_5e: parseInt(row.field_5e!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,7 +37,7 @@ export class SqlCarRepository implements CarRepository {
|
|||||||
|
|
||||||
const row = await this._txn.fetchRow(countSql);
|
const row = await this._txn.fetchRow(countSql);
|
||||||
|
|
||||||
return parseInt(row!.result);
|
return parseInt(row!.result!);
|
||||||
}
|
}
|
||||||
|
|
||||||
async loadCars(
|
async loadCars(
|
||||||
|
|||||||
+10
-10
@@ -9,16 +9,16 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
export function _extractChara(row: Row): Chara {
|
export function _extractChara(row: Row): Chara {
|
||||||
return {
|
return {
|
||||||
gender: row.gender as Gender,
|
gender: row.gender! as Gender,
|
||||||
field_02: parseInt(row.field_02),
|
field_02: parseInt(row.field_02!),
|
||||||
field_04: parseInt(row.field_04),
|
field_04: parseInt(row.field_04!),
|
||||||
field_06: parseInt(row.field_06),
|
field_06: parseInt(row.field_06!),
|
||||||
field_08: parseInt(row.field_08),
|
field_08: parseInt(row.field_08!),
|
||||||
field_0a: parseInt(row.field_0a),
|
field_0a: parseInt(row.field_0a!),
|
||||||
field_0c: parseInt(row.field_0c),
|
field_0c: parseInt(row.field_0c!),
|
||||||
field_0e: parseInt(row.field_0e),
|
field_0e: parseInt(row.field_0e!),
|
||||||
title: parseInt(row.title) as TitleCode,
|
title: parseInt(row.title!) as TitleCode,
|
||||||
background: parseInt(row.background) as BackgroundCode,
|
background: parseInt(row.background!) as BackgroundCode,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,8 +19,8 @@ export class SqlCoursePlaysRepository implements CoursePlaysRepository {
|
|||||||
const result = new Map<CourseNo, number>();
|
const result = new Map<CourseNo, number>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const courseNo = parseInt(row.course_no) as CourseNo;
|
const courseNo = parseInt(row.course_no!) as CourseNo;
|
||||||
const count = parseInt(row.count);
|
const count = parseInt(row.count!);
|
||||||
|
|
||||||
result.set(courseNo, count);
|
result.set(courseNo, count);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,9 +36,9 @@ export class SqlMissionsRepository implements FacetRepository<MissionState> {
|
|||||||
const rows = await this._txn.fetchRows(loadSoloSql);
|
const rows = await this._txn.fetchRows(loadSoloSql);
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const gridNo = parseInt(row.grid_no);
|
const gridNo = parseInt(row.grid_no!);
|
||||||
const cellNo = parseInt(row.cell_no);
|
const cellNo = parseInt(row.cell_no!);
|
||||||
const value = parseInt(row.value);
|
const value = parseInt(row.value!);
|
||||||
|
|
||||||
result.solo[gridNo].cells[cellNo] = value;
|
result.solo[gridNo].cells[cellNo] = value;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,15 +7,15 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
export function _extractProfile(row: Row): Profile {
|
export function _extractProfile(row: Row): Profile {
|
||||||
return {
|
return {
|
||||||
aimeId: parseInt(row.aime_id) as AimeId,
|
aimeId: parseInt(row.aime_id!) as AimeId,
|
||||||
name: row.name,
|
name: row.name!,
|
||||||
lv: parseInt(row.lv),
|
lv: parseInt(row.lv!),
|
||||||
exp: parseInt(row.exp),
|
exp: parseInt(row.exp!),
|
||||||
fame: parseInt(row.fame),
|
fame: parseInt(row.fame!),
|
||||||
dpoint: parseInt(row.dpoint),
|
dpoint: parseInt(row.dpoint!),
|
||||||
mileage: parseInt(row.mileage),
|
mileage: parseInt(row.mileage!),
|
||||||
accessTime: new Date(row.access_time),
|
accessTime: new Date(row.access_time!),
|
||||||
registerTime: new Date(row.register_time),
|
registerTime: new Date(row.register_time!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ export class SqlSettingsRepository implements FacetRepository<Settings> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
music: parseInt(row.music),
|
music: parseInt(row.music!),
|
||||||
pack: parseInt(row.pack),
|
pack: parseInt(row.pack!),
|
||||||
aura: parseInt(row.aura),
|
aura: parseInt(row.aura!),
|
||||||
paperCup: parseInt(row.paper_cup),
|
paperCup: parseInt(row.paper_cup!),
|
||||||
gauges: parseInt(row.gauges),
|
gauges: parseInt(row.gauges!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class SqlStoryRepository implements FacetRepository<Story> {
|
|||||||
// Must succeed even if nonexistent (required by save method below)
|
// Must succeed even if nonexistent (required by save method below)
|
||||||
|
|
||||||
const result = {
|
const result = {
|
||||||
x: header !== undefined ? parseInt(header.x) : 0,
|
x: header !== undefined ? parseInt(header.x!) : 0,
|
||||||
y: header !== undefined ? parseInt(header.y) : 0,
|
y: header !== undefined ? parseInt(header.y!) : 0,
|
||||||
rows: new Array<StoryRow>(),
|
rows: new Array<StoryRow>(),
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -43,12 +43,12 @@ export class SqlStoryRepository implements FacetRepository<Story> {
|
|||||||
const rows = await this._txn.fetchRows(loadCellSql);
|
const rows = await this._txn.fetchRows(loadCellSql);
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
const rowNo = parseInt(row.row_no);
|
const rowNo = parseInt(row.row_no!);
|
||||||
const colNo = parseInt(row.col_no);
|
const colNo = parseInt(row.col_no!);
|
||||||
const cell = result.rows[rowNo].cells[colNo];
|
const cell = result.rows[rowNo].cells[colNo];
|
||||||
|
|
||||||
cell.a = parseInt(row.a);
|
cell.a = parseInt(row.a!);
|
||||||
cell.b = parseInt(row.b);
|
cell.b = parseInt(row.b!);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+5
-5
@@ -37,11 +37,11 @@ export class SqlTeamRepository implements TeamRepository {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
extId: parseInt(row.ext_id) as ExtId<Team>,
|
extId: parseInt(row.ext_id!) as ExtId<Team>,
|
||||||
name: row.name,
|
name: row.name!,
|
||||||
nameBg: parseInt(row.name_bg),
|
nameBg: parseInt(row.name_bg!),
|
||||||
nameFx: parseInt(row.name_fx),
|
nameFx: parseInt(row.name_fx!),
|
||||||
registerTime: new Date(row.register_time),
|
registerTime: new Date(row.register_time!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,8 +20,8 @@ export class SqlTeamAutoRepository implements TeamAutoRepository {
|
|||||||
return (
|
return (
|
||||||
row && [
|
row && [
|
||||||
{
|
{
|
||||||
serialNo: parseInt(row.serial_no),
|
serialNo: parseInt(row.serial_no!),
|
||||||
nameIdx: parseInt(row.name_idx),
|
nameIdx: parseInt(row.name_idx!),
|
||||||
},
|
},
|
||||||
row.id as Id<Team>,
|
row.id as Id<Team>,
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -57,7 +57,7 @@ export class SqlTeamMemberRepository implements TeamMemberRepository {
|
|||||||
profile: _extractProfile(row),
|
profile: _extractProfile(row),
|
||||||
chara: _extractChara(row),
|
chara: _extractChara(row),
|
||||||
leader: !!row.leader,
|
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);
|
const row = await this._txn.fetchRow(countSql);
|
||||||
|
|
||||||
if (parseInt(row!.count) >= 6) {
|
if (parseInt(row!.count!) >= 6) {
|
||||||
throw new Error(`Team ${teamId} is full`);
|
throw new Error(`Team ${teamId} is full`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ export class SqlTeamReservationRepository
|
|||||||
.where("tm.team_id", teamId);
|
.where("tm.team_id", teamId);
|
||||||
|
|
||||||
const memberRes = await this._txn.fetchRow(memberSql);
|
const memberRes = await this._txn.fetchRow(memberSql);
|
||||||
const memberCount = parseInt(memberRes!.count);
|
const memberCount = parseInt(memberRes!.count!);
|
||||||
|
|
||||||
const reservSql = sql
|
const reservSql = sql
|
||||||
.select("count(*) as count")
|
.select("count(*) as count")
|
||||||
@@ -36,7 +36,7 @@ export class SqlTeamReservationRepository
|
|||||||
.where("tr.team_id", teamId);
|
.where("tr.team_id", teamId);
|
||||||
|
|
||||||
const reservRes = await this._txn.fetchRow(reservSql);
|
const reservRes = await this._txn.fetchRow(reservSql);
|
||||||
const reservCount = parseInt(reservRes!.count);
|
const reservCount = parseInt(reservRes!.count!);
|
||||||
|
|
||||||
return memberCount + reservCount;
|
return memberCount + reservCount;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ export class SqlTicketsRepository implements FacetRepository<Tickets> {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
freeCar: row && {
|
freeCar: row && {
|
||||||
validFrom: new Date(row.valid_from),
|
validFrom: new Date(row.valid_from!),
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
+14
-14
@@ -11,13 +11,13 @@ import { Row, Transaction } from "../../sql";
|
|||||||
|
|
||||||
function _extractRow(row: Row): TimeAttackScore {
|
function _extractRow(row: Row): TimeAttackScore {
|
||||||
return {
|
return {
|
||||||
routeNo: parseInt(row.route_no) as RouteNo,
|
routeNo: parseInt(row.route_no!) as RouteNo,
|
||||||
timestamp: new Date(row.timestamp),
|
timestamp: new Date(row.timestamp!),
|
||||||
flags: parseInt(row.flags),
|
flags: parseInt(row.flags!),
|
||||||
totalTime: parseFloat(row.total_time),
|
totalTime: parseFloat(row.total_time!),
|
||||||
sectionTimes: row.section_times.split(",").map(parseFloat),
|
sectionTimes: row.section_times!.split(",").map(parseFloat),
|
||||||
grade: parseInt(row.grade),
|
grade: parseInt(row.grade!),
|
||||||
carSelector: parseInt(row.car_selector) as CarSelector,
|
carSelector: parseInt(row.car_selector!) as CarSelector,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -61,13 +61,13 @@ export class SqlTimeAttackRepository implements TimeAttackRepository {
|
|||||||
const rows = await this._txn.fetchRows(loadSql);
|
const rows = await this._txn.fetchRows(loadSql);
|
||||||
|
|
||||||
return rows.map(row => ({
|
return rows.map(row => ({
|
||||||
driverName: row.profile_name,
|
driverName: row.profile_name!,
|
||||||
team: {
|
team: {
|
||||||
extId: parseInt(row.team_ext_id) as ExtId<Team>,
|
extId: parseInt(row.team_ext_id!) as ExtId<Team>,
|
||||||
name: row.team_name,
|
name: row.team_name!,
|
||||||
nameBg: parseInt(row.team_name_bg),
|
nameBg: parseInt(row.team_name_bg!),
|
||||||
nameFx: parseInt(row.team_name_fx),
|
nameFx: parseInt(row.team_name_fx!),
|
||||||
registerTime: new Date(row.team_register_time),
|
registerTime: new Date(row.team_register_time!),
|
||||||
},
|
},
|
||||||
ta: _extractRow(row),
|
ta: _extractRow(row),
|
||||||
}));
|
}));
|
||||||
@@ -122,7 +122,7 @@ export class SqlTimeAttackRepository implements TimeAttackRepository {
|
|||||||
|
|
||||||
await this._txn.modify(insertSql);
|
await this._txn.modify(insertSql);
|
||||||
} else {
|
} else {
|
||||||
if (score.totalTime < parseFloat(row.total_time)) {
|
if (score.totalTime < parseFloat(row.total_time!)) {
|
||||||
const updateSql = sql
|
const updateSql = sql
|
||||||
.update("idz_ta_best", {
|
.update("idz_ta_best", {
|
||||||
total_time: score.totalTime,
|
total_time: score.totalTime,
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ export class SqlTitlesRepository implements FlagRepository<TitleCode> {
|
|||||||
const result = new Set<TitleCode>();
|
const result = new Set<TitleCode>();
|
||||||
|
|
||||||
for (const row of rows) {
|
for (const row of rows) {
|
||||||
result.add(parseInt(row.title_no) as TitleCode);
|
result.add(parseInt(row.title_no!) as TitleCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ export class SqlUnlocksRepository implements FacetRepository<Unlocks> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
auras: parseInt(row.auras),
|
auras: parseInt(row.auras!),
|
||||||
cup: parseInt(row.cup),
|
cup: parseInt(row.cup!),
|
||||||
gauges: parseInt(row.gauges),
|
gauges: parseInt(row.gauges!),
|
||||||
music: parseInt(row.music),
|
music: parseInt(row.music!),
|
||||||
lastMileageReward: parseInt(row.last_mileage_reward),
|
lastMileageReward: parseInt(row.last_mileage_reward!),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+1
-1
@@ -3,7 +3,7 @@ import * as sql from "sql-bricks-postgres";
|
|||||||
import { Id } from "../model";
|
import { Id } from "../model";
|
||||||
|
|
||||||
export interface Row {
|
export interface Row {
|
||||||
[key: string]: string;
|
[key: string]: string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Transaction {
|
export interface Transaction {
|
||||||
|
|||||||
+21
-7
@@ -3,15 +3,23 @@ import snakeCase from "snake-case";
|
|||||||
import { Row } from "./api";
|
import { Row } from "./api";
|
||||||
|
|
||||||
interface ColMapper<F> {
|
interface ColMapper<F> {
|
||||||
_read(str: string): F;
|
_read(str: string | null): F;
|
||||||
|
|
||||||
_write(val: F): string;
|
_write(val: F): string | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
type Spec<R> = {
|
type Spec<R> = {
|
||||||
[K in keyof R]: ColMapper<R[K]>;
|
[K in keyof R]: ColMapper<R[K]>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
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
|
* Function objects describing the precise way in which our SQL driver
|
||||||
* transmits and receives values to the database as strings. Note that we could
|
* transmits and receives values to the database as strings. Note that we could
|
||||||
@@ -20,25 +28,31 @@ type Spec<R> = {
|
|||||||
*/
|
*/
|
||||||
export const T = {
|
export const T = {
|
||||||
bigint: {
|
bigint: {
|
||||||
_read: (str: string) => BigInt(str),
|
_read: (str: string) => BigInt(_nn(str)),
|
||||||
_write: (val: bigint) => val.toString(),
|
_write: (val: bigint) => val.toString(),
|
||||||
},
|
},
|
||||||
boolean: {
|
boolean: {
|
||||||
_read: (str: string) => str === "true",
|
_read: (str: string) => _nn(str) === "true",
|
||||||
_write: (val: boolean) => val.toString(),
|
_write: (val: boolean) => val.toString(),
|
||||||
},
|
},
|
||||||
number: {
|
number: {
|
||||||
_read: (str: string) => parseInt(str),
|
_read: (str: string) => parseInt(_nn(str)),
|
||||||
_write: (val: number) => val.toString(),
|
_write: (val: number) => val.toString(),
|
||||||
},
|
},
|
||||||
string: {
|
string: {
|
||||||
_read: (str: string) => str,
|
_read: (str: string) => _nn(str),
|
||||||
_write: (val: string) => val,
|
_write: (val: string) => val,
|
||||||
},
|
},
|
||||||
Date: {
|
Date: {
|
||||||
_read: (str: string) => new Date(str),
|
_read: (str: string) => new Date(_nn(str)),
|
||||||
_write: (val: Date) => val.toISOString(),
|
_write: (val: Date) => val.toISOString(),
|
||||||
},
|
},
|
||||||
|
nullable: <F>(inner: ColMapper<F>) => ({
|
||||||
|
_read: (str: string | null) =>
|
||||||
|
str !== null ? inner._read(str) : undefined,
|
||||||
|
_write: (val: F | undefined) =>
|
||||||
|
val !== undefined ? inner._write(val) : null,
|
||||||
|
}),
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user