/*
* 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 { ObjectifyOptions } from '../cell/serialization/ObjectIteration';
import { CellSystem } from '../cell/systems/CellSystem';
import { Row } from '../table/Row';
import { SqlTable } from './SQLTable';
import { translate } from './SQLTranslate';
/**
* Represents a single row in the SQL table. If this row was loaded directly from the db,
* changes made to it will automatically be written to the database once your patch
* is finished.
*
* @motivation I wanted SqlRows to behave as similar to DBCRows as possible, which is
* why we write changes to things read directly from source to be applied automatically.
*
* @details To automatically apply changes, we keep all loaded SqlRows in the current connection,
* and keep a dirty tag on each which enables if we change any property on it. When a patch has been applied,
* the sql connection checks through all loaded rows and writes them if they're dirty.
*/
export abstract class SqlRow extends Row {
protected obj: {[key: string]: any};
protected _dirty = false;
protected _isDeleted = false;
constructor(table: SqlTable>, obj: {[key: string]: any}) {
super(table);
this.obj = obj;
}
/**
* @warning Deleted base SQL rows (from the core) are not automatically restored when
* building datascripts without the --rebuild flag. For example,
* deleting creature_template entry=25 and later removing the deletion statement
* will keep entry=25 deleted until it is explicitly undeleted
* (OR MODIFIED IN ANY WAY) by a script,
* OR datascripts are built with the --rebuild flag
*/
delete() {
this._isDeleted = true;
this._dirty = true;
return this;
}
undelete() {
this._isDeleted = false;
this._dirty = true;
return this;
}
isDeleted() {
return this._isDeleted;
}
static markDirty(row: SqlRow) {
row._dirty = true;
}
static isDirty(row: SqlRow) {
return row._dirty;
}
static getSql(row: SqlRow): string {
return row.generateSql();
}
static getRowObj(row: SqlRow): {[key: string]: any} {
return row.obj;
}
protected writePrimaryKeys(keys: any[]) {
this._dirty = true;
Row.primaryKeyFields(this).forEach((x: any, i: number) => {
this.obj[x] = keys[i];
});
}
// TODO: This doesn't work, don't know why.
objectify(options?: ObjectifyOptions) {
// ???????
return Object.assign({}, this.obj);
}
protected _generatePreparedDeleteStatement() {
const pkFields: string[] = Row.primaryKeyFields(this);
const text = `DELETE FROM ${this.table.name} WHERE `
+ `${pkFields.map((x)=>{
return `${x} = ?`
}).join(' AND ')};`
return text;
}
protected _generatePreparedStatement() {
const obj = this.objectify();
return `REPLACE INTO ${this.table.name} ` +
`(${Object.keys(obj).map(x=>`\`${x}\``).join(',')}) ` +
`VALUES (${Object.values(obj).map(() => '?')})`
}
protected _getPreparedStatements() {
return Object.values(this.objectify())
}
protected _getPreparedDeleteValues() {
return this.primaryKeys();
}
static generatePreparedStatement(row: SqlRow) {
return row._generatePreparedStatement();
}
static generatePreparedDeleteStatement(row: SqlRow) {
return row._generatePreparedDeleteStatement();
}
static getPreparedStatement(row: SqlRow) {
return row._getPreparedStatements();
}
static getPreparedDeleteStatement(row: SqlRow) {
return row._getPreparedDeleteValues();
}
protected generateSql() {
const obj = this.objectify();
translate(this.table.name,obj,'OUT')
for(let key in obj) {
if(typeof(obj[key]) == 'string') {
obj[key] = obj[key].split('\\').join('\\\\').split('"').join('\\"')
}
}
if(this.isDeleted()) {
const pkFields: string[] = Row.primaryKeyFields(this);
const text = `DELETE FROM ${this.table.name} WHERE `
+ `${pkFields.map((x)=>{
const value = obj[x];
return `${x} = ${typeof(value)=='string'?`"${value}"`:value}`
}).join(' AND ')};`
return text;
}
return `REPLACE INTO ${this.table.name} ` +
`(${Object.keys(obj).map(x=>`\`${x}\``).join(',')}) ` +
`VALUES (${Object.values(obj).map(x => x === null ? 'null' : typeof(x) === 'string' ? `"${x}"` : x)})`;
}
protected cloneInternal(keys: any[], c?: C) {
const row = SqlTable.createRow(this.table as SqlTable, {});
CellSystem.cloneFrom(row, this);
if (c) {
CellSystem.cloneFrom(row, c);
}
row.writePrimaryKeys(keys);
SqlTable.addRow(this.table as SqlTable, row);
return row;
}
}