first release
This commit is contained in:
commit
aa0b62be09
3
.gitignore
vendored
Normal file
3
.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
**/node_modules
|
||||
**/out
|
||||
package-lock.json
|
||||
310
index.js
Normal file
310
index.js
Normal file
@ -0,0 +1,310 @@
|
||||
const mysql = require("mysql");
|
||||
const Select = require("./lib/Select");
|
||||
const Insert = require("./lib/Insert");
|
||||
const Delete = require("./lib/Delete");
|
||||
const Update = require("./lib/Update");
|
||||
const { CreateTable, Structure, AlterTable } = require("./lib/Tables");
|
||||
|
||||
class awSQL {
|
||||
#instances = {};
|
||||
#default;
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {*} hostname
|
||||
* @param {*} username
|
||||
* @param {*} password
|
||||
* @param {*} options
|
||||
* @returns {Instance}
|
||||
*/
|
||||
createInstance = (hostname="localhost", username, password, options = {
|
||||
charset:"utf8mb4",
|
||||
defaultDatabase: false,
|
||||
multipleStatements: false,
|
||||
insecureAuth: false,
|
||||
customIdentifier: false,
|
||||
isDefault: false,
|
||||
})=>{
|
||||
const identifier = options.customIdentifier||`${username}@${hostname}`;
|
||||
if (this.#instances[identifier]) throw new Error(`Can't create new instance with identifier "${identifier}": An instance with the same name already exists`);
|
||||
const instance = new Instance(hostname, username, password, options.charset, options.defaultDatabase, options.multipleStatements, options.insecureAuth);
|
||||
this.#instances[identifier] = instance;
|
||||
if (options.createAsDefault) this.#default = identifier;
|
||||
return instance;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} identifier
|
||||
* @returns {Instance}
|
||||
*/
|
||||
getInstance = (identifier) => {
|
||||
if (Object.keys(this.#instances).length===0) return undefined;
|
||||
if (!identifier) return this.#default?this.#instances[this.#default]:this.#instances[Object.keys(this.#instances)[0]]; // If no identifier is set return default or first instance
|
||||
return this.#instances[identifier];
|
||||
}
|
||||
|
||||
listInstances = () => {
|
||||
return Object.keys(this.#instances);
|
||||
}
|
||||
|
||||
deleteInstance = (identifier) => {
|
||||
if (!identifier) throw new Error("Can't delete Instance: No identifier set");
|
||||
if (!this.#instances[identifier]) throw new Error(`Can't delete Instance '${identifier}': No Instance`);
|
||||
this.#instances[identifier].destroy();
|
||||
if (this.#default === identifier) this.#default = undefined;
|
||||
delete this.#instances[identifier];
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
class Instance {
|
||||
#user;
|
||||
#password;
|
||||
#host;
|
||||
#insecureAuth;
|
||||
#multipleStatements;
|
||||
#charset;
|
||||
#connection;
|
||||
|
||||
#selectedDatabase;
|
||||
|
||||
constructor(hostname="localhost", username, password, charset="utf8mb4", defaultDatabase=false, multipleStatements=false, insecureAuth=false){
|
||||
this.#host = hostname;
|
||||
this.#user = username;
|
||||
this.#password = password;
|
||||
this.#charset = charset;
|
||||
this.#multipleStatements = multipleStatements;
|
||||
this.#insecureAuth = insecureAuth;
|
||||
this.#selectedDatabase = defaultDatabase||username;
|
||||
}
|
||||
|
||||
/**
|
||||
* Connects the instance
|
||||
*
|
||||
*/
|
||||
connect = () => {
|
||||
return new Promise((resolve, reject) => {
|
||||
const connection = mysql.createConnection({
|
||||
user: this.#user,
|
||||
password: this.#password,
|
||||
host: this.#host,
|
||||
insecureAuth: this.#insecureAuth,
|
||||
multipleStatements: this.#multipleStatements,
|
||||
charset: this.#charset
|
||||
});
|
||||
this.#connection = connection;
|
||||
connection.connect((err) =>{
|
||||
if (err) throw err;
|
||||
resolve(`Connected to ${this.#host} with user ${this.#user}`);
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys the instance
|
||||
*/
|
||||
destroy = () => {
|
||||
if (this.#connection) this.#connection.end();
|
||||
this.#connection = undefined;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Performs a raw query
|
||||
* @param {String} queryString The sql query string to perform.
|
||||
* @param {Array} values - An array holding all replacable ?-values from left to right.
|
||||
* @returns {Any} - The individual result of your query
|
||||
*/
|
||||
queryRaw = (queryString, values) => {
|
||||
return new Promise((resolve, reject) => {
|
||||
if (!this.#connection) throw new Error("Querying failed: No connection");
|
||||
this.#connection.query(queryString, values, (err, result) => {
|
||||
if (err) throw err;
|
||||
resolve(result);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of database names the user has access to
|
||||
* @param {Boolean} excludeSchema - Whether to exclude the default database 'information_schema'
|
||||
* @returns {Array}
|
||||
*/
|
||||
getDatabases = async (excludeSchema=false) => {
|
||||
let dbs = await this.queryRaw("SHOW DATABASES;");
|
||||
if (excludeSchema) dbs = dbs.filter((db)=>db.Database!=="information_schema")
|
||||
return dbs.map(db => db.Database);
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a default database for future queries
|
||||
* @param {String} name - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (name) => {
|
||||
this.#selectedDatabase = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of tables for the selected database
|
||||
* - 'multipleStatements' must be active for this to work
|
||||
* @param {String} [database] - Database to select. Can be empty as long as a default database was set with 'selectDatabase'
|
||||
* @returns {Array}
|
||||
*/
|
||||
getTables = async (database) => {
|
||||
if (!this.#multipleStatements) throw new Error("getTables: multipleStatements must be set to 'true' in instance options");
|
||||
if (!this.#selectedDatabase && !database) throw new Error("getTables: No database selected");
|
||||
const tables = (await this.queryRaw(`USE ${database||this.#selectedDatabase}; SHOW TABLES;`))[1];
|
||||
return tables.map(table => table[`Tables_in_${database||this.#selectedDatabase}`]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a new select query
|
||||
* @param {String} from - Name of the table
|
||||
* @param {...String} [columns] - Name of columns to select. Leave empty to select all
|
||||
* @returns {Select}
|
||||
*/
|
||||
select = (from, ...columns) => {
|
||||
return new Select(this, this.#selectedDatabase, from, columns);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepares a new query to insert data
|
||||
* @param {String} into - Name of the table to insert into
|
||||
* @returns {Insert}
|
||||
*/
|
||||
insert = (into) => {
|
||||
return new Insert(this, this.#selectedDatabase, into);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a new delete query
|
||||
* @param {String} from - Name of the table to delete from
|
||||
* @returns {Delete}
|
||||
*/
|
||||
delete = (from) => {
|
||||
return new Delete(this, this.#selectedDatabase, from);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares a new update query
|
||||
* @param {String} table - Name of the table to update data in
|
||||
* @returns {Update}
|
||||
*/
|
||||
update = (table) => {
|
||||
return new Update(this, this.#selectedDatabase, table);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a whole database
|
||||
* - Requires admin privileges
|
||||
* @param {String} database - Name of the database to drop
|
||||
* @returns
|
||||
*/
|
||||
dropDatabase = async (database) => {
|
||||
return await this.queryRaw(`DROP DATABASE ${database};`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops a whole table
|
||||
* @param {String} table - Name of the table to drop
|
||||
*/
|
||||
dropTable = async (table) => {
|
||||
if (!this.#selectedDatabase) throw new Error(`Can't drop table '${table}': Database not set`);
|
||||
return await this.queryRaw(`DROP TABLE ${this.#selectedDatabase}.${table}`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new database
|
||||
* - Requires admin privileges
|
||||
* @param {String} name - Name of the database to create
|
||||
* @returns
|
||||
*/
|
||||
createDatabase = async (name) => {
|
||||
return await this.queryRaw(`CREATE DATABASE ${name};`);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares to create a new table
|
||||
* @param {String} name - Name of the table to create
|
||||
* @returns {CreateTable}
|
||||
*/
|
||||
createTable = (name) => {
|
||||
return new CreateTable(this, this.#selectedDatabase, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Alters a table and updates to the new given structure.
|
||||
*
|
||||
* @param {*} name
|
||||
*/
|
||||
alterTable = (name) => {
|
||||
return new AlterTable(this, this.#selectedDatabase, name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares to create a new table-structure
|
||||
* @returns {Structure}
|
||||
*/
|
||||
createStructure = () => {
|
||||
return new Structure();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the structure object of a table
|
||||
* @param {String} table - Name of table to get structure of
|
||||
* @param {String} [database] - Name of the underlying database
|
||||
* @returns {Structure}
|
||||
*/
|
||||
getStructure = async (table, database) => {
|
||||
if (!this.#selectedDatabase && !database) throw new Error(`Can't get structure of table ${table}: Database not selected`);
|
||||
return new Structure(await this.queryRaw(`DESCRIBE ${database||this.#selectedDatabase}.${table};`));
|
||||
}
|
||||
|
||||
checkStructure = async (table, desiredStructure, database) => {
|
||||
if (!this.#selectedDatabase && !database) throw new Error(`Can't get structure of table ${table}: Database not selected`);
|
||||
const dbStruc = (await this.getStructure(table, database||this.#selectedDatabase)).get();
|
||||
const result = {
|
||||
errors: [],
|
||||
passed: []
|
||||
}
|
||||
for (let col of dbStruc){
|
||||
const desiredCol = desiredStructure.find((desCol) => desCol.Field === col.Field);
|
||||
if (!desiredCol) {
|
||||
result.errors.push(`${col.Field}: Missing completely`);
|
||||
continue;
|
||||
}
|
||||
let breakOut = false;
|
||||
for (let key in col){
|
||||
if (col[key] !== desiredCol[key]){
|
||||
result.errors.push(`${col.Field}.${key}: Required ${desiredCol[key]}, got ${col[key]}`);
|
||||
breakOut=true;
|
||||
}
|
||||
}
|
||||
if (!breakOut){
|
||||
result.passed.push(`${col.Field}`);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns total amount of rows of a table
|
||||
* @param {String} table - Table name
|
||||
* @returns
|
||||
*/
|
||||
total = async (table) => {
|
||||
return await new Select(this, this.#selectedDatabase, table).count(true).execute();
|
||||
}
|
||||
|
||||
isConnected = () => {
|
||||
if (this.#connection) return true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = {awSQL: new awSQL(), Structure};
|
||||
64
lib/Delete.js
Normal file
64
lib/Delete.js
Normal file
@ -0,0 +1,64 @@
|
||||
class Delete {
|
||||
#instance;
|
||||
#database;
|
||||
#from;
|
||||
|
||||
#where;
|
||||
#whereValues;
|
||||
#force;
|
||||
|
||||
|
||||
constructor(instance, defaultDatabase, from){
|
||||
this.#instance = instance;
|
||||
this.#database = defaultDatabase;
|
||||
this.#from = from;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where-clause to the query
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables deletion of all rows
|
||||
*/
|
||||
force = () => {
|
||||
this.#force = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
if (!this.#where && !this.#force) throw new Error("Delete: Tried to delete all rows. If this was intentional, use .force() on Delete");
|
||||
|
||||
const values = [];
|
||||
const whereString = this.#where?` WHERE ${this.#where}`:"";
|
||||
this.#where&&values.push(...this.#whereValues);
|
||||
|
||||
const queryString = `DELETE FROM ${this.#database}.${this.#from}${whereString}`;
|
||||
|
||||
return await this.#instance.queryRaw(queryString, values);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Delete;
|
||||
55
lib/Insert.js
Normal file
55
lib/Insert.js
Normal file
@ -0,0 +1,55 @@
|
||||
class Insert {
|
||||
#instance;
|
||||
#database;
|
||||
#into;
|
||||
|
||||
#data;
|
||||
|
||||
constructor(instance, defaultDatabase, into){
|
||||
this.#instance = instance;
|
||||
this.#database = defaultDatabase;
|
||||
this.#into = into;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The data (rows) to insert
|
||||
* @param {Array} objects - Array containing objects to insert, where the key represents the column-name. All objects must have the same structure!
|
||||
* @returns
|
||||
*/
|
||||
data = (objects) => {
|
||||
this.#data = objects;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
if (!this.#data) throw new Error("Insert: tried to insert without data");
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
const columns = Object.keys(this.#data[0]);
|
||||
const queryString = `INSERT INTO ${this.#database}.${this.#into} (${columns.toString()}) VALUES ?`;
|
||||
|
||||
const values = [];
|
||||
for (let obj of this.#data){
|
||||
let thisRow = [];
|
||||
for (let key of columns){
|
||||
thisRow.push(obj[key]);
|
||||
}
|
||||
values.push(thisRow);
|
||||
}
|
||||
|
||||
return await this.#instance.queryRaw(queryString, [values]);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Insert;
|
||||
245
lib/Select.js
Normal file
245
lib/Select.js
Normal file
@ -0,0 +1,245 @@
|
||||
|
||||
class Select {
|
||||
#instance;
|
||||
#database;
|
||||
#from;
|
||||
#columns = [];
|
||||
|
||||
#distinct = false;
|
||||
|
||||
#where;
|
||||
#whereValues;
|
||||
#order = {
|
||||
asc: [],
|
||||
desc: []
|
||||
};
|
||||
#group;
|
||||
|
||||
#aggregator;
|
||||
#join;
|
||||
#having;
|
||||
#havingValues;
|
||||
#limit;
|
||||
#aggregatorParse;
|
||||
constructor(instance, defaultDatabase, from, columns){
|
||||
this.#database = defaultDatabase;
|
||||
this.#from = from;
|
||||
this.#columns = columns||[];
|
||||
this.#instance = instance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the 'distinct' keyword for this query
|
||||
* Should be called on only selected columns.
|
||||
* - With 'distinct' only unique values are returned
|
||||
* @returns {this}
|
||||
*/
|
||||
distinct = () => {
|
||||
this.#distinct = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where-clause to the query
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* - If you are using joins, specify the table and column together: table.column
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Same as a where-clause, but allows for aggregation
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* - If you are using joins, specify the table and column together: table.column
|
||||
* @param {String} string - The having-clause with possible aggregation and ? representing each values
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
having = (string, values = []) => {
|
||||
this.#having = string;
|
||||
this.#havingValues = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new sort order
|
||||
* - Can be used multiple times to order by multiple columns
|
||||
* @param {String} column - Column to order by
|
||||
* @param {Boolean} desc - Sorty descending
|
||||
* @param {"MIN"|"MAX"|"COUNT"|"SUM"|"AVG"} aggregation - The aggregation type to use
|
||||
* @returns
|
||||
*/
|
||||
order = (column, desc=false, aggregation) => {
|
||||
if (["MIN", "MAX", "COUNT", "SUM", "AVG"].includes(aggregation)){
|
||||
switch(aggregation){
|
||||
case "MIN":
|
||||
column = `MIN(${column})`;
|
||||
break;
|
||||
case "MAX":
|
||||
column = `MAX(${column})`;
|
||||
break;
|
||||
case "COUNT":
|
||||
column = `COUNT(${column})`;
|
||||
break;
|
||||
case "SUM":
|
||||
column = `SUM(${column})`;
|
||||
break;
|
||||
case "AVG":
|
||||
column = `AVG(${column})`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!desc){
|
||||
this.#order.asc.push(column);
|
||||
}else{
|
||||
this.#order.desc.push(column);
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts number of entries of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
*/
|
||||
count = (doParse=false) => {
|
||||
this.#aggregator = "COUNT";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sums numerical rows of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
* @returns
|
||||
*/
|
||||
sum = (doParse=false) => {
|
||||
this.#aggregator ="SUM";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Averages numerical rows of the first selected column
|
||||
* @param {Boolean} doParse - Return only an integer, not the full query result
|
||||
* @returns
|
||||
*/
|
||||
avg = (doParse=false) => {
|
||||
this.#aggregator = "AVG";
|
||||
this.#aggregatorParse = doParse;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Groups rows that have the same values into summary rows
|
||||
* @param {...String} columns - The columns to group by
|
||||
*/
|
||||
group = (...columns) => {
|
||||
this.#group = columns;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {"LEFT"|"INNER"|"RIGHT"|"FULL OUTER"} type
|
||||
* @param {*} table
|
||||
* @param {*} onOriginalColumn
|
||||
* @param {*} onJoinedColumn
|
||||
* @param {...any} columns
|
||||
*/
|
||||
join = (type, table, onOriginalColumn, onJoinedColumn, ...columns) => {
|
||||
this.#join = {
|
||||
type,
|
||||
on: `%%FROM%%.${onOriginalColumn}=${table}.${onJoinedColumn}`,
|
||||
columns,
|
||||
table
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Limits the query and specifies an offset
|
||||
* @param {Number} number - Limits the query by specified rows
|
||||
* @param {*} offset - Offset to start at
|
||||
* @returns
|
||||
*/
|
||||
limit = (number, offset) => {
|
||||
this.#limit = {
|
||||
number,
|
||||
offset
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Paginates the query
|
||||
* @param {Number} page - The page to get (Minimum 1)
|
||||
* @param {Number} itemsPerPage - How many items a page should have
|
||||
* @returns
|
||||
*/
|
||||
pagination = (page, itemsPerPage) => {
|
||||
if (page<1) page=1;
|
||||
this.#limit = {
|
||||
number: itemsPerPage,
|
||||
offset: itemsPerPage*(page-1)
|
||||
}
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
* @returns
|
||||
*/
|
||||
execute = async () => {
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
|
||||
const values = [];
|
||||
let columnString;
|
||||
if (this.#join && this.#columns.length>0){
|
||||
columnString = `${this.#columns.toString()},${this.#join.columns.toString()}`
|
||||
}else{
|
||||
columnString = this.#columns.length>0?this.#columns.toString():"*"
|
||||
}
|
||||
const distinctString = this.#distinct?"DISTINCT ":"";
|
||||
|
||||
|
||||
const whereString = this.#where?` WHERE ${this.#where}`:"";
|
||||
this.#where&&values.push(...this.#whereValues);
|
||||
const havingString = this.#having?` HAVING ${this.#having}`:"";
|
||||
this.#having&&values.push(...this.#havingValues);
|
||||
|
||||
const orderString = (this.#order.asc.length>0||this.#order.desc.length>0)?` ORDER BY ${this.#order.asc.length>0?this.#order.asc.toString()+" ASC,":""}${this.#order.desc.length>0?this.#order.desc.toString()+" DESC":""}`:"";
|
||||
|
||||
const groupString = this.#group?` GROUP BY ${this.#group.toString()}`:"";
|
||||
|
||||
const joinString = this.#join?` ${this.#join.type} JOIN ${this.#database}.${this.#join.table} ON ${this.#join.on.replace("%%FROM%%", this.#from)}`:"";
|
||||
|
||||
const limitString = this.#limit?` LIMIT ${this.#limit.number}${this.#limit.offset?` OFFSET ${this.#limit.offset}`:""}`:"";
|
||||
|
||||
const queryString = `SELECT ${this.#aggregator?this.#aggregator+"(":""}${distinctString}${columnString}${this.#aggregator?")":""} FROM ${this.#database}.${this.#from}${joinString}${whereString}${havingString}${limitString}${groupString}${orderString};`;
|
||||
|
||||
if (this.#aggregatorParse){
|
||||
const result = await this.#instance.queryRaw(queryString, values);
|
||||
return result[0][Object.keys(result[0])];
|
||||
}
|
||||
return await this.#instance.queryRaw(queryString, values);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = Select;
|
||||
529
lib/Tables.js
Normal file
529
lib/Tables.js
Normal file
@ -0,0 +1,529 @@
|
||||
|
||||
class AlterTable {
|
||||
#instance;
|
||||
#database;
|
||||
#name;
|
||||
#structure;
|
||||
constructor(instance, defaultDatabase, name){
|
||||
this.#instance = instance;
|
||||
this.#database = defaultDatabase;
|
||||
this.#name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* The new desired structure.
|
||||
* - Drops columns that are existing in the current table but not in this structure
|
||||
* - Adds columns that are missing in the current table
|
||||
* - Modifies all other columns where at least one datatype is not matching
|
||||
* @param {Structure} struc - New structure for the table
|
||||
* @returns
|
||||
*/
|
||||
structure = (struc) => {
|
||||
this.#structure = struc;
|
||||
return this;
|
||||
}
|
||||
|
||||
execute = async () => {
|
||||
if (!this.#database) throw new Error(`Can't alter table ${this.#name}: Database not selected`);
|
||||
if (!this.#name) throw new Error(`Can't alter table on ${this.#database}: No name given`);
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#structure) throw new Error(`Can't alter table ${this.#database}.${this.#name}: No new structure given`);
|
||||
const currentStruc = (await this.#instance.getStructure(this.#name, this.#database)).get();
|
||||
const newStruc = this.#structure.get();
|
||||
if (currentStruc.length===0) throw new Error(`Can't alter table ${this.#name}: Table does not exist`);
|
||||
for (let col of currentStruc){
|
||||
if (!newStruc.find(newCol => newCol.Field === col.Field)){
|
||||
// DELETE COLUMN
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP COLUMN \`${col.Field}\``);
|
||||
}
|
||||
}
|
||||
for (let col of newStruc){
|
||||
const oldStrucCol = currentStruc.find((oldCol) => oldCol.Field === col.Field);
|
||||
if (!oldStrucCol){
|
||||
// ADD COLUMN
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} ADD COLUMN \`${col.Field}\` ${col.Type}${col.Extra?` ${col.Extra}`:""}${col.Null==="YES"?" NULL":" NOT NULL"}${col.Default?` DEFAULT '${col.Default}'`:""}`);
|
||||
if (col.Key){
|
||||
switch(col.Key){
|
||||
case "PRI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} ADD PRIMARY KEY (${col.Field});`);
|
||||
break;
|
||||
case "MUL":
|
||||
console.log("HEY");
|
||||
await this.#instance.queryRaw(`CREATE INDEX ${col.Field} ON ${this.#database}.${this.#name} (${col.Field})`);
|
||||
break;
|
||||
case "UNI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} ADD UNIQUE (${col.Field});`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
for (let key in col){
|
||||
if (oldStrucCol[key] !== col[key]){
|
||||
// UPDATE COLUMN
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} MODIFY COLUMN \`${col.Field}\` ${col.Type}${col.Extra?` ${col.Extra}`:""}${col.Null==="YES"?" NULL":" NOT NULL"}${col.Default?` DEFAULT '${col.Default}'`:""}`);
|
||||
if (oldStrucCol.Key){
|
||||
switch(oldStrucCol.Key){
|
||||
case "PRI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP PRIMARY KEY (${col.Field});`);
|
||||
break;
|
||||
case "MUL":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP INDEX ${col.Field};`);
|
||||
break;
|
||||
case "UNI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP UNIQUE (${col.Field});`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (col.Key){
|
||||
switch(col.Key){
|
||||
case "PRI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} ADD PRIMARY KEY (${col.Field});`);
|
||||
break;
|
||||
case "MUL":
|
||||
await this.#instance.queryRaw(`CREATE INDEX ${col.Field} ON ${this.#database}.${this.#name} (${col.Field})`);
|
||||
break;
|
||||
case "UNI":
|
||||
await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} ADD UNIQUE (${col.Field});`);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return await this.#instance.checkStructure(this.#name, newStruc, this.#database);
|
||||
}
|
||||
}
|
||||
|
||||
class CreateTable {
|
||||
#instance;
|
||||
#database;
|
||||
#name;
|
||||
#structure;
|
||||
constructor(instance, defaultDatabase, name) {
|
||||
this.#instance = instance;
|
||||
this.#database = defaultDatabase;
|
||||
this.#name = name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new name
|
||||
* @param {String} name
|
||||
*/
|
||||
name = (name) => {
|
||||
this.#name = name;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines the structure for the table
|
||||
* @param {Structure} struc - Instance of Structure
|
||||
*/
|
||||
structure = (struc) => {
|
||||
this.#structure = struc;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
* @returns
|
||||
*/
|
||||
execute = async () => {
|
||||
if (!this.#database) throw new Error(`Can't create table ${this.#name}: Database not selected`);
|
||||
if (!this.#name) throw new Error(`Can't create table on ${this.#database}: No name given`);
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#structure) throw new Error(`Can't create table ${this.#database}.${this.#name}: No structure given`);
|
||||
const colObjs = this.#structure.get();
|
||||
|
||||
const coltypes = colObjs.map(obj => `\`${obj.Field}\` ${obj.Type}${obj.Null==="YES"?" NULL":" NOT NULL"}${obj.Default?` DEFAULT '${obj.Default}'`:""}${obj.Extra?` ${obj.Extra}`:""}`);
|
||||
const constraints = colObjs.map(obj => `${obj.Key?obj.Key==="PRI"?` PRIMARY KEY (${obj.Field})`:obj.Key==="MUL"?` INDEX (${obj.Field})`:obj.Key==="UNI"?` UNIQUE (${obj.Key})`:"":""}`)
|
||||
.filter(str => str !== ""); // Filter out empty
|
||||
|
||||
const queryString = `CREATE TABLE ${this.#database}.${this.#name} (${coltypes}${constraints.length>0?",":""}${constraints})`;
|
||||
return await this.#instance.queryRaw(queryString);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @typedef {Object} ConstraintOptions
|
||||
* @property {Boolean} [primary] - Whether this column should be primary
|
||||
* @property {Boolean} [index] - Whether this column should be indexable (Faster query, slower insertion)
|
||||
* @property {Boolean} [null] - Whether this column is null per default
|
||||
* @property {Boolean} [unique] - Whether this column data should be unique
|
||||
* @property {String} [default] - Set's the default data for this column
|
||||
* @property {Boolean} [auto_increment] - Whether this column should be numerical auto_increment
|
||||
* @property {Boolean} [unsigned] - Only on numerical: Whether this numerical field should be unsigned
|
||||
*/
|
||||
|
||||
class Structure {
|
||||
#columns = [];
|
||||
constructor(tableDescription) {
|
||||
if (!tableDescription) return;
|
||||
this.#columns = tableDescription;
|
||||
}
|
||||
|
||||
/**
|
||||
* Drops (Removes) a column name
|
||||
* @param {String} name - The name of the column
|
||||
*/
|
||||
drop = (name) => {
|
||||
let index;
|
||||
for (let i = 0; i < this.#columns.length; i++){
|
||||
if (this.#columns[i].Field === name) {
|
||||
index=i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
this.#columns.splice(index, 1);
|
||||
return this;
|
||||
}
|
||||
|
||||
get(){
|
||||
return this.#columns;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'char' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
*/
|
||||
char = (name, size=1, options) => {
|
||||
if (size < 0 || size > 255){
|
||||
throw new Error(`Column datatype 'char' size must be a number between 0 and 255. Received: ${size}`);
|
||||
}
|
||||
this.#columns.push(parseColumnData(name, `char(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'varchar' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of characters. Min 0, Max 255. Default 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
*/
|
||||
varchar = (name, size=8, options) => {
|
||||
if (size < 0 || size > 255){
|
||||
throw new Error(`Column datatype 'varchar' size must be a number between 0 and 65535. Received: ${size}`);
|
||||
}
|
||||
this.#columns.push(parseColumnData(name, `varchar(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'binary' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of data. Min 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
binary = (name, size=1, options) => {
|
||||
if (size < 1){
|
||||
throw new Error(`Column datatype 'binary' size must be a number above 0. Received: ${size}`);
|
||||
}
|
||||
this.#columns.push(parseColumnData(name, `binary(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'varbinary' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Length of data. Min 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
varbinary = (name, size=1, options) => {
|
||||
if (size < 1){
|
||||
throw new Error(`Column datatype 'varbinary' size must be a number above 0. Received: ${size}`);
|
||||
}
|
||||
|
||||
this.#columns.push(parseColumnData(name, `varbinary(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'tinyblob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
tinyblob = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `tinyblob`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'tinytext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
tinytext = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `tinytext`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'text' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
text = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `text`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'blob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Size in bytes. Min 1, Max 65535. Defaults to 65535
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
blob = (name, size=65535, options) => {
|
||||
if (size < 1 || size > 65535) throw new Error(`Column datatype 'blob' size must be a number between 1 and 65535. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `blob(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'mediumtext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
mediumtext = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `mediumtext`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'longtext' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
longtext = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `longtext`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'longblob' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
longblob = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `longblob`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'enum' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Array} vals - Array of possible values
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
enum = (name, vals=[], options) => {
|
||||
if (!Array.isArray(vals)) throw new Error(`Column datatype 'enum': 'vals' must be of type Array`);
|
||||
if (vals.length<=0) throw new Error(`Column datatype 'enum' must contain a list of possible values. Received undefined`);
|
||||
this.#columns.push(parseColumnData(name, `enum(${vals.map(val=>`"${val}"`)})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'enum' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Array} vals - Array of possible values
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
set = (name, vals=[], options) => {
|
||||
if (!Array.isArray(vals)) throw new Error(`Column datatype 'set': 'vals' must be of type Array`);
|
||||
if (vals.length<=0) throw new Error(`Column datatype 'set' must contain a list of possible values. Received undefined`);
|
||||
this.#columns.push(parseColumnData(name, `set(${vals.map(val=>`"${val}"`)})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'bit' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 64. Default to 1
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
bit = (name, size=1, options) => {
|
||||
if (size < 1 || size > 64) throw new Error(`Column datatype 'bit' size must be a number between 1 and 64. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `bit(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'tinyint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
tinyint = (name, size=255, options) => {
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'tinyint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `tinyint(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'bool' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
bool = (name, options) => {
|
||||
this.#columns.push(parseColumnData(name, `bool`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'smallint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
smallint = (name, size=255, options) => {
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'smallint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `smallint(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'mediumint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
mediumint = (name, size=255, options) => {
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'mediumint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `mediumint(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'int' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
int = (name, size=255, options) => {
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'int' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `int(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'bigint' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size Min 1, Max 255. Defaults to 255
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
bigint = (name, size=255, options) => {
|
||||
if (size < 1 || size > 255) throw new Error(`Column datatype 'bigint' size must be a number between 1 and 255. Received: ${size}`);
|
||||
this.#columns.push(parseColumnData(name, `bigint(${size})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'float' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} p - Precision. Min 1, Max 53. Defaults to 25
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
float = (name, p=25, options) => {
|
||||
if (p < 1 || p > 53) throw new Error(`Column datatype 'float' size must be a number between 1 and 53. Received: ${p}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `float(${p})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'double' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1. Defaults to 16
|
||||
* @param {Number} d - Double precision. Min 1. Defaults to 8
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
double = (name, size=16, d=8, options) => {
|
||||
if (size < 1) throw new Error(`Column datatype 'double' size must be greater than 0. Received: ${p}`);
|
||||
if (d < 1) throw new Error(`Column datatype 'double' d must be greater than 0. Received: ${p}`);
|
||||
this.#columns.push(parseColumnData(name, `double(${size},${d})`, options));
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a new column of data type 'decimal' to this structure
|
||||
* @param {String} name - Name of the column
|
||||
* @param {Number} size - Min 1, Max 65. Defaults to 10
|
||||
* @param {Number} d - Double precision. Min 0. Defaults to 0.
|
||||
* @param {ConstraintOptions} options - Extra constraint options
|
||||
* @returns
|
||||
*/
|
||||
decimal = (name, size=10, d=0, options) => {
|
||||
if (size < 1 || size > 65) throw new Error(`Column datatype 'decimal' size must be a number between 1 and 65. Received: ${size}`);
|
||||
if (d < 0) throw new Error(`Column datatype 'decimal' d must be positive. Received: ${d}`);
|
||||
|
||||
this.#columns.push(parseColumnData(name, `decimal(${size},${d})`, options));
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param {String} name
|
||||
* @param {String} type
|
||||
* @param {ConstraintOptions} options
|
||||
* @returns
|
||||
*/
|
||||
function parseColumnData(name, type, options={}){
|
||||
if (options.primary + options.unique + options.index > 1) throw new Error(`ConstrainError: Only one of 'primary', 'unique' or 'index' allowed`);
|
||||
return {
|
||||
Field: name,
|
||||
Type: `${type}${options.unsigned?" UNSIGNED":""}`,
|
||||
Null: options.null?"YES":"NO",
|
||||
Key: options.primary?"PRI":options.index?"MUL":options.unique?"UNI":"",
|
||||
Default: options.default?options.default:null,
|
||||
Extra: options.auto_increment?"auto_increment":""
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
module.exports = { CreateTable, Structure, AlterTable }
|
||||
70
lib/Update.js
Normal file
70
lib/Update.js
Normal file
@ -0,0 +1,70 @@
|
||||
class Update{
|
||||
#instance;
|
||||
#database;
|
||||
#table;
|
||||
|
||||
#data;
|
||||
#where;
|
||||
#whereValues;
|
||||
#force;
|
||||
constructor(instance, defaultDatabase, table){
|
||||
this.#instance = instance;
|
||||
this.#database = defaultDatabase;
|
||||
this.#table = table;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates all matching rows with the given object
|
||||
* @param {Object} object - The object with the data to update. Keys represent column names
|
||||
*/
|
||||
data = (object) => {
|
||||
this.#data = object;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Selects a database for this query
|
||||
* @param {String} database - Name of the database
|
||||
* @returns {this}
|
||||
*/
|
||||
selectDatabase = (database) => {
|
||||
this.#database = database;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables update of all rows
|
||||
*/
|
||||
force = () => {
|
||||
this.#force = true;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a where-clause to the query
|
||||
* - Values should be set as ? in the string and given in left-to-right order via the 'values'-array to minimize the risk of sql-injection
|
||||
* @param {String} string - The where-clause as a string with ? representing each values.
|
||||
* @param {Array} values - Array containing values replacing the ? in the string (from left to right)
|
||||
* @returns {this}
|
||||
*/
|
||||
where = (string, values=[]) => {
|
||||
this.#where = string;
|
||||
this.#whereValues = values;
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Executes the prepared query
|
||||
*/
|
||||
execute = async () => {
|
||||
if (!this.#instance.isConnected()) throw new Error(`Can't execute query: Instance has no connection`);
|
||||
if (!this.#database) throw new Error(`Can't execute query: Database not selected`);
|
||||
if (!this.#where && !this.#force) throw new Error("Update: Tried to update all rows. If this was intentional, use .force() on Update");
|
||||
const values = [];
|
||||
const queryString = `UPDATE ${this.#database}.${this.#table} SET ${Object.keys(this.#data).map(col=>{values.push(this.#data[col]);return `${col}=?`})}${this.#where&&` WHERE ${this.#where}`}`;
|
||||
this.#where&&values.push(...this.#whereValues);
|
||||
console.log(queryString);
|
||||
return await this.#instance.queryRaw(queryString, values);
|
||||
}
|
||||
}
|
||||
module.exports = Update;
|
||||
18
package.json
Normal file
18
package.json
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"name": "awsql_refined",
|
||||
"version": "1.0.0",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
},
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"mysql": "^2.18.1"
|
||||
},
|
||||
"devDependencies": {},
|
||||
"directories": {
|
||||
"lib": "lib"
|
||||
},
|
||||
"description": ""
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user