/* * This file is part of tswow (https://github.com/tswow) * * Copyright (C) 2020 tswow * This program is free software: you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation, version 3. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * See the GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import { inMemory } from '../query/Query'; import { Row } from '../table/Row'; import { Table } from '../table/Table'; import { SqlConnection } from './SQLConnection'; import { SqlRow } from './SQLRow'; export type SqlRowCreator> = (table: SqlTable, obj: {[key: string]: any}) => R; export class SqlTable> extends Table { private cachedRows: {[key: string]: R} = {}; private cachedFirst: R | undefined; protected rowCreator: SqlRowCreator; private get cachedValues() { return Object.values(this.cachedRows); } static cachedRowCount(table: SqlTable) { return Object.values(table.cachedRows).length; } static cachedValues(table: SqlTable) { return table.cachedValues; } static createRow>(table: SqlTable, obj: {[key: string]: any}): R { return table.rowCreator(table, obj); } static addRow>(table: SqlTable, row: R) { table.cachedRows[Row.fullKey(row)] = row; } constructor(name: string, rowCreator: SqlRowCreator) { super(name); this.rowCreator = rowCreator; } first(): R { if (this.cachedFirst) { return this.cachedFirst; } this.cachedFirst = this.filterInt({} as any, true)[0]; if (!this.cachedFirst) { this.cachedFirst = this.rowCreator(this, {}); } return this.cachedFirst; } queryAll(where: Q): R[] { return this.filterInt(where, false); } private isPkLookup(where: Q): string { let fields: string[] = Row.primaryKeyFields(this.rowCreator(this,{})); if(fields.length != Object.entries(where).length) { return undefined; } for(let field of fields) { if(where[field] === undefined) { return undefined; } if(typeof(where[field]) == 'object') { return undefined; } } return fields.map(x=>where[x]).join('_') } private filterInt(where: Q, firstOnly = false): R[] { // Try looking up using only primary key let pkLookup = this.isPkLookup(where); let cacheMatches: R[] = [] if(pkLookup) { let row = this.cachedRows[pkLookup]; if(row) { return [row]; } } else { for(let key in this.cachedRows) { let value = this.cachedRows[key]; if(inMemory(where, value)) { cacheMatches.push(value); } } } const dbMatches = SqlConnection.getRows(this, where, firstOnly) .filter(x => !this.cachedRows[Row.fullKey(x)]); dbMatches.forEach(x => this.cachedRows[Row.fullKey(x)] = x); return cacheMatches.concat(dbMatches); } addRow(row: R) { this.cachedRows[Row.fullKey(row)] = row; } // TODO/sqltable protected writeToFile(sqlfile: string): string { const dirtyRows = this.cachedValues.filter(SqlRow.isDirty); if (dirtyRows.length === 0) { return sqlfile; } return sqlfile + `--${this.name}\n` + dirtyRows.map(x => SqlRow.getSql(x)).join('\n') + '\n'; } static writeSQL(table: SqlTable) { // Very stupid let dummyRow: SqlRow for(let row in table.cachedRows) { dummyRow = table.cachedRows[row]; break; } if(!dummyRow) { return; } const normalQuery = SqlRow.generatePreparedStatement(dummyRow); const deleteQuery = SqlRow.generatePreparedDeleteStatement(dummyRow); let normalStatement = SqlConnection.world_dst.prepare(normalQuery) let deleteStatement = SqlConnection.world_dst.prepare(deleteQuery) SqlConnection.world_dst.prepare(table.rowCreator(table,{})) let values = table.cachedValues.filter(SqlRow.isDirty) values.forEach((x: SqlRow)=>{ if(x.isDeleted()) { deleteStatement.writeNormal(SqlRow.getPreparedDeleteStatement(x)) } else { normalStatement.writeNormal(SqlRow.getPreparedStatement(x)) } //SqlConnection.world_dst.write(SqlRow.getSql(x)) }); table.cachedRows = {}; } }