const { throwTypeError, throwRangeError } = require("./Errors"); /** * Prepares a new alteration of a table */ 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){ if (!database) throw new Error("database must not be empty"); if (typeof database !== "string") throwTypeError("string", 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 {this} */ structure(struc){ if (!struc) throw new Error("struc must not be empty"); if (typeof struc !== "object") throwTypeError("object", struc); this.#structure = struc; return this; } /** * Executes the prepared querry * @returns {import("../index").CheckResult} */ async execute(){ 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!==null?` 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": 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!==null?` DEFAULT '${col.Default}'`:""}`); if (oldStrucCol.Key){ switch(oldStrucCol.Key){ case "PRI": await this.#instance.queryRaw(`ALTER TABLE ${this.#database}.${this.#name} DROP PRIMARY KEY;`); 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); } } /** * Prepares creation of a new table */ 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){ if (!database) throw new Error("database must not be empty"); if (typeof database !== "string") throwTypeError("string", database); this.#database = database; return this; } /** * Sets a new name * @param {String} name * @returns {this} */ name(name){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); this.#name = name; return this; } /** * Defines the structure for the table * @param {Structure} struc - Instance of Structure * @returns {this} */ structure(struc){ if (!struc) throw new Error("struc must not be empty"); if (typeof struc !== "object") throwTypeError("object", struc); this.#structure = struc; return this; } /** * Executes the prepared querry * @returns {import("../index").OkPacket} */ async execute(){ 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 */ /** * Creates a new table-structure */ class Structure { #columns = []; constructor(tableDescription) { if (!tableDescription) return; this.#columns = tableDescription; } /** * Drops (Removes) a column name * @param {String} name - The name of the column * @returns {this} */ drop(name){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", 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; } /** * Returns the columns * @returns {Array} */ 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 * @returns {this} */ char(name, size=1, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 0 || size > 255){ throwRangeError(0, 255, 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 * @returns {this} */ varchar(name, size=8, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 0 || size > 255){ throwRangeError(0, 255, 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 {this} */ binary(name, size=1, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1){ throwRangeError(1, "*", 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 {this} */ varbinary(name, size=1, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1){ throwRangeError(1, "*", 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 {this} */ tinyblob(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ tinytext(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ text(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ blob(name, size=65535, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 65535) throwRangeError(1, 65535, 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 {this} */ mediumtext(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ longtext(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ longblob(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", 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 {this} */ enum(name, vals=[], options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (!Array.isArray(vals)) throwTypeError("array", vals); if (vals.length===0) throw new Error("vals must not be empty"); if (typeof options !== "object") throwTypeError("object", 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 {this} */ set(name, vals=[], options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (!Array.isArray(vals)) throwTypeError("array", vals); if (vals.length===0) throw new Error("vals must not be empty"); if (typeof options !== "object") throwTypeError("object", 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 {this} */ bit(name, size=1, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 64) throwRangeError(1, 64, 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 {this} */ tinyint(name, size=255, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 255) throwRangeError(1, 255, 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 {this} */ bool(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", options); this.#columns.push(parseColumnData(name, `tinyint(1)`, 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 {this} */ smallint(name, size=255, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 255) throwRangeError(1, 255, 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 {this} */ mediumint(name, size=255, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 255) throwRangeError(1, 255, 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 {this} */ int(name, size=255, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 255) throwRangeError(1, 255, 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 {this} */ bigint(name, size=255, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 255) throwRangeError(1, 255, 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 {this} */ float(name, p=25, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof p !== "number") throwTypeError("number", p); if (typeof options !== "object") throwTypeError("object", options); if (p < 1 || p > 53) throwRangeError(1, 53, 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 {this} */ double(name, size=16, d=8, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof d !== "number") throwTypeError("number", d); if (typeof options !== "object") throwTypeError("object", options); if (size < 1) throwRangeError(1, "*", size); if (d < 1) throwRangeError(1, "*", d); 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 {this} */ decimal(name, size=10, d=0, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof size !== "number") throwTypeError("number", size); if (typeof d !== "number") throwTypeError("number", d); if (typeof options !== "object") throwTypeError("object", options); if (size < 1 || size > 65) throwRangeError(1, 65, size); if (d < 0) throwRangeError(0, "*", d); this.#columns.push(parseColumnData(name, `decimal(${size},${d})`, options)); return this; } /** * Adds a new column of data type 'date' to this structure * @param {String} name - Name of the column * @param {ConstraintOptions} [options] - Extra constraint options * @returns {this} */ date(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", options); this.#columns.push(parseColumnData(name, `date`, options)); return this; } /** * Adds a new column of data type 'datetime' to this structure * @param {String} name - Name of the column * @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0 * @param {ConstraintOptions} [options] - Extra constraint options * @returns {this} */ datetime(name, fsp=0, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof fsp !== "number") throwTypeError("number", fsp); if (typeof options !== "object") throwTypeError("object", options); if (fsp < 0 || fsp > 6) throwRangeError(0, 6, fsp); this.#columns.push(parseColumnData(name, `datetime(${fsp})`, options)); return this; } /** * Adds a new column of data type 'timestamp' to this structure * @param {String} name - Name of the column * @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0 * @param {ConstraintOptions} [options] - Extra constraint options * @returns {this} */ timestamp(name, fsp=0, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof fsp !== "number") throwTypeError("number", fsp); if (typeof options !== "object") throwTypeError("object", options); if (fsp < 0 || fsp > 6) throwRangeError(0, 6, fsp); this.#columns.push(parseColumnData(name, `timestamp(${fsp})`, options)); return this; } /** * Adds a new column of data type 'time' to this structure * @param {String} name - Name of the column * @param {Number} [fsp] - Fractional second precision. Min 0, Max 6. Defaults to 0 * @param {ConstraintOptions} [options] - Extra constraint options * @returns {this} */ time(name, fsp=0, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof fsp !== "number") throwTypeError("number", fsp); if (typeof options !== "object") throwTypeError("object", options); if (fsp < 0 || fsp > 6) throwRangeError(0, 6, fsp); this.#columns.push(parseColumnData(name, `time(${fsp})`, options)); return this; } /** * Adds a new column of data type 'year' to this structure * @param {String} name - Name of the column * @param {ConstraintOptions} [options] - Extra constraint options * @returns {this} */ year(name, options={}){ if (!name) throw new Error("name must not be empty"); if (typeof name !== "string") throwTypeError("string", name); if (typeof options !== "object") throwTypeError("object", options); this.#columns.push(parseColumnData(name, `time`, options)); return this; } } /** * Parses the given column data * @private * @param {String} name - Column name * @param {String} type - Column type * @param {ConstraintOptions} options * @returns {ColumnStructure} */ 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!==undefined?options.default.toString():null, Extra: options.auto_increment?"auto_increment":"" } } /** * @typedef {Object} ColumnStructure * @property {String} Field - Column name * @property {String} Type - Column type * @property {"YES"|"NO"} Null - Whether the column allows null-values * @property {""|"PRI"|"MUL"|"UNI"} Key - PRI = Primary, MUL = Indexed, UNI = Unique * @property {String|null} Default - Default values for this row * @property {""|"auto_increment"} Extra - Any extra */ module.exports = { CreateTable, Structure, AlterTable }