const tuplify = s => [s.split(" ")[0], s.split(" ").slice(1).join(" ")];
const DefaultDataTypes = {
String: "TEXT",
Integer: "INTEGER",
Float: "INTEGER",
AutoID: "INTEGER PRIMARY KEY AUTOINCREMENT UNIQUE",
Timestamp: "DATETIME",
AutoTS: "DATETIME DEFAULT CURRENT_TIMESTAMP NOT NULL",
};
/* eslint-disable no-restricted-syntax */
/**
* SQL driver for compatibility with SQL providers. Do NOT use this directly.
* @class SQL
*/
class SQL {
/**
* Creates an instance of SQL.
* @param {KomadaClient} client The Komada Client.
* @param {SettingGateway} gateway The SettingGateway instance which initialized this instance.
*/
constructor(client, gateway) {
/**
* The client this SettingsCache was created with.
* @name SQL#client
* @type {KomadaClient}
* @readonly
*/
Object.defineProperty(this, "client", { value: client });
/**
* The gateway which initiated this instance.
* @name SQL#gateway
* @type {SettingGateway}
* @readonly
*/
Object.defineProperty(this, "gateway", { value: gateway });
}
/**
* Generate an automatic SQL schema for a single row.
* @param {Object} value The Schema<Value> object.
* @returns {string}
*/
buildSingleSQLSchema(value) {
const selectType = schemaKey => this.constants[schemaKey] || "TEXT";
const type = value.sql || value.default ? ` DEFAULT ${this.sanitizer(value.default)}` : "";
return `${selectType(value.type)}${type}`;
}
/**
* Generate an automatic SQL schema for all rows.
* @param {any} schema The Schema Object.
* @returns {string[]}
*/
buildSQLSchema(schema) {
const output = ["id TEXT NOT NULL UNIQUE"];
for (const [key, value] of Object.entries(schema)) {
output.push(`${key} ${this.buildSingleSQLSchema(key, value)}`);
}
return output;
}
/**
* Init the deserialization keys for SQL providers.
*/
initDeserialize() {
this.deserializeKeys = [];
for (const [key, value] of Object.entries(this.schema)) {
if (value.array === true) this.deserializeKeys.push(key);
}
}
/**
* Deserialize stringified objects.
* @param {Object} data The GuildSettings object.
*/
deserializer(data) {
const deserialize = this.deserializeKeys;
for (let i = 0; i < deserialize.length; i++) data[deserialize[i]] = JSON.parse(data[deserialize[i]]);
}
/**
* Create/Remove columns from a SQL database, by the current Schema.
* @param {Object} schema The Schema object.
* @param {Object} defaults The Schema<Defaults> object.
* @param {string} key The key which is updated.
* @returns {Promise<boolean>}
*/
async updateColumns(schema, defaults, key) {
if (!this.provider.updateColumns) {
this.client.emit("log", "This SQL Provider does not seem to have a updateColumns exports. Force action cancelled.", "error");
return false;
}
const newSQLSchema = this.buildSQLSchema(schema).map(tuplify);
const keys = Object.keys(defaults);
if (!keys.includes("id")) keys.push("id");
const columns = keys.filter(k => k !== key);
await this.provider.updateColumns(this.gateway.type, columns, newSQLSchema);
this.initDeserialize();
return true;
}
/**
* The constants this instance will use to build the SQL schemas.
* @name SQL#constants
* @type {Object}
* @readonly
*/
get constants() {
return this.provider.CONSTANTS || DefaultDataTypes;
}
/**
* Sanitize and prepare the strings for SQL input.
* @name SQL#sanitizer
* @type {Function}
* @readonly
*/
get sanitizer() {
return this.provider.sanitize || (value => `'${value}'`);
}
/**
* Shortcut for Schema.
* @name SQL#schema
* @type {Object}
* @readonly
*/
get schema() {
return this.gateway.schema;
}
/**
* Shortcut for Schema<Defaults>
* @name SQL#defaults
* @type {Object}
* @readonly
*/
get defaults() {
return this.gateway.defaults;
}
/**
* The provider this SettingGateway instance uses for the persistent data operations.
* @name SQL#provider
* @type {Resolver}
* @readonly
*/
get provider() {
return this.gateway.provider;
}
}
module.exports = SQL;