const frappe = require('frappejs'); const mysql = require('mysql'); const Database = require('./database'); const debug = false; module.exports = class mysqlDatabase extends Database{ constructor({ db_name, username, password, host }) { super(); this.db_name = db_name; this.username = username; this.password = password; this.host = host; this.init_type_map(); } connect(db_name) { if (db_name) { this.db_name = db_name; } return new Promise(resolve => { this.conn = new mysql.createConnection({ host : this.host, user : this.username, password : this.password, database : this.db_name }); () => { if (debug) { this.conn.on('trace', (trace) => console.log(trace)); } }; resolve(); }); } async tableExists(table) { const name = await this.sql(`SELECT table_name FROM information_schema.tables WHERE table_schema = '${this.db_name}' AND table_name = '${table}'`); return (name && name.length) ? true : false; } async runCreateTableQuery(doctype, columns, values){ const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} ( ${columns.join(", ")})`; return await this.run(query, values); } getColumnDefinition(df) { return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}` } async getTableColumns(doctype) { return (await this.sql(`SHOW COLUMNS FROM ${doctype}`)).map(d => d.Field); } async runAlterTableQuery(doctype) { await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values); } getOne(doctype, name, fields = '*') { fields = this.prepareFields(fields); return new Promise((resolve, reject) => { this.conn.get(`select ${fields} from ${frappe.slug(doctype)} where name = ?`, name, (err, row) => { resolve(row || {}); }); }); } async insertOne(doctype, doc) { let fields = this.get_keys(doctype); let placeholders = fields.map(d => '?').join(', '); if (!doc.name) { doc.name = frappe.getRandomName(); } return await this.run(`insert into ${frappe.slug(doctype)} (${fields.map(field => field.fieldname).join(", ")}) values (${placeholders})`, this.getFormattedValues(fields, doc)); } async updateOne(doctype, doc) { let fields = this.getKeys(doctype); let assigns = fields.map(field => `${field.fieldname} = ?`); let values = this.getFormattedValues(fields, doc); // additional name for where clause values.push(doc.name); return await this.run(`update ${frappe.slug(doctype)} set ${assigns.join(", ")} where name=?`, values); } async runDeleteOtherChildren(field, added) { await this.run(`delete from ${frappe.slug(field.childtype)} where parent = ? and name not in (${added.slice(1).map(d => '?').join(', ')})`, added); } async deleteOne(doctype, name) { return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name); } async deleteChildren(parenttype, parent) { await this.run(`delete from ${parent} where parent=?`, parent); } getAll({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) { if (!fields) { fields = frappe.getMeta(doctype).getKeywordFields(); } return new Promise((resolve, reject) => { let conditions = this.getFilterConditions(filters); this.conn.all(`select ${fields.join(", ")} from ${frappe.slug(doctype)} ${conditions.conditions ? "where" : ""} ${conditions.conditions} ${order_by ? ("order by " + order_by) : ""} ${order_by ? (order || "asc") : ""} ${limit ? ("limit " + limit) : ""} ${start ? ("offset " + start) : ""}`, conditions.values, (err, rows) => { if (err) { reject(err); } else { resolve(rows); } }); }); } run(query, params) { // TODO promisify return new Promise((resolve, reject) => { this.conn.query(query, params, (err) => { if (err) { if (debug) { console.error(err); } reject(err); } else { resolve(); } }); }); } sql(query, params) { return new Promise((resolve) => { this.conn.query(query, params, (err, rows) => { resolve(rows); }); }); } async commit() { try { await this.run('commit'); } catch (e) { if (e.errno !== 1) { throw e; } } } init_type_map() { this.type_map = { 'Currency': 'real' , 'Int': 'INT' , 'Float': 'decimal(18,6)' , 'Percent': 'real' , 'Check': 'INT(1)' , 'Small Text': 'text' , 'Long Text': 'text' , 'Code': 'text' , 'Text Editor': 'text' , 'Date': 'DATE' , 'Datetime': 'DATETIME' , 'Time': 'TIME' , 'Text': 'text' , 'Data': 'VARCHAR(140)' , 'Link': ' varchar(140)' , 'Dynamic Link': 'text' , 'Password': 'varchar(140)' , 'Select': 'VARCHAR(140)' , 'Read Only': 'varchar(140)' , 'Attach': 'text' , 'Attach Image': 'text' , 'Signature': 'text' , 'Color': 'text' , 'Barcode': 'text' , 'Geolocation': 'text' } } }