UNPKG

tspace-mysql

Version:

Tspace MySQL is a promise-based ORM for Node.js, designed with modern TypeScript and providing type safety for schema databases.

514 lines 17.8 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MysqlQueryBuilder = void 0; const __1 = require(".."); class MysqlQueryBuilder extends __1.QueryBuilder { constructor(state) { super(state); } select = () => { const combindSQL = [ this.bindSelect(this.$state.get("SELECT")), this.bindFrom({ from: !this.$state.get("FROM").length ? [this.$state.get("TABLE_NAME")].map(String) : [this.$state.get("TABLE_NAME"), ...this.$state.get("FROM")].map(String), alias: this.$state.get("ALIAS"), rawAlias: this.$state.get("RAW_ALIAS"), }), this.bindJoin(this.$state.get("JOIN")), this.bindWhere(this.$state.get("WHERE")), this.bindGroupBy(this.$state.get("GROUP_BY")), this.bindHaving(this.$state.get("HAVING")), this.bindOrderBy(this.$state.get("ORDER_BY")), this.bindLimit(this.$state.get("LIMIT")), this.bindOffset(this.$state.get("OFFSET")), this.bindRowLevelLock(this.$state.get("ROW_LEVEL_LOCK")), ]; let sql = this.format(combindSQL).trimEnd(); if (this.$state.get("UNION").length) { sql = `(${sql}) ${this.$state .get("UNION") .map((union) => `${this.$constants("UNION")} (${union})`) .join(" ")}`; } if (this.$state.get("UNION_ALL").length) { sql = `(${sql}) ${this.$state .get("UNION_ALL") .map((union) => `${this.$constants("UNION_ALL")} (${union})`) .join(" ")}`; } if (this.$state.get("CTE").length) { sql = `${this.$constants("WITH")} ${this.$state .get("CTE") .join(", ")} ${sql}`; } return sql; }; insert() { const query = this.$state.get("INSERT"); if (!query) return ''; const table = this.$state.get("TABLE_NAME"); const columns = `(${query.columns})`; const values = query.values.map(v => `(${v})`).join(', '); const sql = this.format([ this.$constants("INSERT"), table, columns, this.$constants("VALUES"), values ]); return sql; } update() { const query = this.$state.get("UPDATE"); if (query == null) { return ''; } const sql = this.format([ `${this.$constants("UPDATE")}`, `${this.$state.get("TABLE_NAME")}`, `${this.$constants("SET")}`, `${query}`, this.bindWhere(this.$state.get("WHERE")), this.bindOrderBy(this.$state.get("ORDER_BY")), this.bindLimit(this.$state.get("LIMIT")), ]); return sql; } remove() { const query = this.$state.get("DELETE"); if (!query) { throw new Error("Bad query builder: DELETE state not found. Please check your query configuration."); } let sql = this.format([ this.$constants("DELETE"), this.$constants("FROM"), this.$state.get("TABLE_NAME"), this.bindWhere(this.$state.get("WHERE")), this.bindOrderBy(this.$state.get("ORDER_BY")), this.bindLimit(this.$state.get("LIMIT")), ]); if (this.$state.get("CTE").length) { sql = `${this.$constants("WITH")} ${this.$state .get("CTE") .join(", ")} ${sql}`; } return sql; } any() { if (this.$state.get("INSERT")) return this.insert(); if (this.$state.get("UPDATE")) return this.update(); if (this.$state.get("DELETE")) return this.remove(); return this.select(); } getColumns({ database, table }) { const sql = [ `SELECT COLUMN_NAME as "Field", COLUMN_TYPE as "ColumnType", DATA_TYPE as "Type", IS_NULLABLE as "Nullable", COLUMN_DEFAULT as "Default" `, `FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${table.replace(/\`/g, "")}' AND TABLE_SCHEMA = '${database}' ORDER BY ORDINAL_POSITION `, ]; return this.format(sql); } getSchema({ database, table }) { const sql = [ ` SELECT COLUMN_NAME AS "Field", CASE WHEN COLUMN_KEY = '' THEN NULL ELSE COLUMN_KEY END AS "Key", COALESCE(NULLIF(COLUMN_TYPE, ''), DATA_TYPE) AS "Type", IS_NULLABLE AS "Nullable", CASE WHEN COLUMN_DEFAULT = 'CURRENT_TIMESTAMP' THEN 'IS_CONST:CURRENT_TIMESTAMP' ELSE COLUMN_DEFAULT END AS "Default", CASE WHEN EXTRA = 'DEFAULT_GENERATED' OR EXTRA = '' THEN NULL ELSE EXTRA END AS "Extra", CASE WHEN DATA_TYPE = 'enum' THEN REPLACE(SUBSTRING(COLUMN_TYPE, 6, LENGTH(COLUMN_TYPE)-6), '''', '') WHEN COLUMN_TYPE LIKE '%(%)%' THEN SUBSTRING_INDEX(SUBSTRING_INDEX(COLUMN_TYPE, '(', -1), ')', 1) ELSE NULL END AS "TypeValue" FROM INFORMATION_SCHEMA.COLUMNS WHERE TABLE_NAME = '${table.replace(/\`/g, "")}' AND TABLE_SCHEMA = '${database}' ORDER BY ORDINAL_POSITION `, ]; return this.format(sql); } getTables(database) { const sql = [ ` SELECT TABLE_NAME AS "Tables" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${database.replace(/\`/g, "")}' AND TABLE_TYPE = 'BASE TABLE' `, ]; return this.format(sql); } getTable({ database, table }) { const sql = [ ` SELECT TABLE_NAME AS "Table" FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${database.replace(/\`/g, "")}' AND TABLE_NAME = '${table.replace(/\`/g, "")}' AND TABLE_TYPE = 'BASE TABLE' `, ]; return this.format(sql); } createTable({ database, table, schema, }) { let columns = []; if (Array.isArray(schema)) { const sql = [ `${this.$constants("CREATE_TABLE_NOT_EXISTS")}`, `\`${database.replace(/`/g, "")}\`.\`${table.replace(/`/g, "")}\``, `(${schema.join(", ")})`, `${this.$constants("ENGINE")}`, ]; return this.format(sql); } const detectSchema = (schema) => { try { return { type: schema?.type ?? schema["_type"] ?? null, attributes: schema?.attributes ?? schema["_attributes"] ?? null, }; } catch (e) { return { type: null, attributes: null, }; } }; for (const key in schema) { const data = schema[key]; const { type, attributes } = detectSchema(data); if (type == null || attributes == null) continue; columns = [...columns, `\`${key}\` ${type} ${attributes.join(" ")}`]; } const sql = [ `${this.$constants("CREATE_TABLE_NOT_EXISTS")}`, `\`${table.replace(/`/g, "")}\` (${columns.join(", ")})`, `${this.$constants("ENGINE")}`, ]; return this.format(sql); } addColumn({ table, column, type, attributes, after, }) { const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("ADD"), `\`${column}\` ${type} ${attributes != null && attributes.length ? `${attributes.join(" ")}` : ""}`, this.$constants("AFTER"), `\`${after}\``, ]; return this.format(sql); } changeColumn({ table, column, type, attributes, }) { const sql = [ this.$constants("ALTER_TABLE"), `\`${table.replace(/`/g, "")}\``, this.$constants("CHANGE"), `\`${column}\``, `\`${column}\` ${type} ${attributes != null && attributes.length ? `${attributes .filter((v) => !["PRIMARY KEY"].includes(v)) .join(" ")}` : ""}`, ]; return this.format(sql); } getChildFKs({ database, table }) { const sql = [ ` SELECT CONSTRAINT_NAME AS "Constraint", TABLE_NAME AS "ChildTable", COLUMN_NAME AS "ChildColumn", REFERENCED_TABLE_NAME AS "ParentTable", REFERENCED_COLUMN_NAME AS "ParentColumn" FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME = '${table.replace(/`/g, "")}' AND TABLE_SCHEMA = '${database.replace(/`/g, "")}' `, ]; return this.format(sql); } getFKs({ database, table }) { const sql = [ ` SELECT REFERENCED_TABLE_NAME AS "RefTable", REFERENCED_COLUMN_NAME AS "RefColumn", COLUMN_NAME AS "Column", CONSTRAINT_NAME As "Constraint" FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_SCHEMA = '${database.replace(/`/g, "")}' AND TABLE_NAME = '${table.replace(/`/g, "")}' `, ]; return this.format(sql); } hasFK({ database, table, constraint, }) { const sql = [ ` SELECT EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE WHERE REFERENCED_TABLE_NAME IS NOT NULL AND TABLE_SCHEMA = '${database.replace(/`/g, "")}' AND TABLE_NAME = '${table.replace(/`/g, "")}' AND CONSTRAINT_NAME = '${constraint}' ) AS "IS_EXISTS" `, ]; return this.format(sql); } createFK({ table, tableRef, key, constraint, foreign, }) { const sql = [ `${this.$constants("ALTER_TABLE")}`, `\`${table}\``, `${this.$constants("ADD_CONSTRAINT")}`, `\`${constraint}\``, `${this.$constants("FOREIGN_KEY")}(\`${key}\`)`, `${this.$constants("REFERENCES")} \`${tableRef}\`(\`${foreign.references}\`)`, `${this.$constants("ON_DELETE")} ${foreign.onDelete}`, `${this.$constants("ON_UPDATE")} ${foreign.onUpdate}`, ].join(" "); return this.format(sql); } dropFK({ table, constraint, }) { const sql = [ `${this.$constants("ALTER_TABLE")}`, `\`${table}\``, `DROP FOREIGN KEY`, `\`${constraint}\``, ].join(" "); return this.format(sql); } getIndexes({ database, table }) { const sql = [ ` SELECT s.COLUMN_NAME AS "Column", s.INDEX_NAME AS "IndexName", s.INDEX_TYPE AS "IndexType", CASE WHEN s.NULLABLE = 'YES' THEN 'YES' ELSE 'NO' END AS "Nullable", CASE WHEN s.NON_UNIQUE = 0 THEN 'YES' ELSE 'NO' END AS "Unique" FROM INFORMATION_SCHEMA.STATISTICS s LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k ON s.TABLE_SCHEMA = k.TABLE_SCHEMA AND s.TABLE_NAME = k.TABLE_NAME AND s.COLUMN_NAME = k.COLUMN_NAME AND k.REFERENCED_TABLE_NAME IS NOT NULL WHERE k.REFERENCED_TABLE_NAME IS NULL AND s.TABLE_SCHEMA = '${database.replace(/`/g, "")}' AND s.TABLE_NAME = '${table.replace(/`/g, "")}' `, ]; return this.format(sql); } hasIndex({ database, table, index, }) { const sql = [ ` SELECT EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.STATISTICS s LEFT JOIN INFORMATION_SCHEMA.KEY_COLUMN_USAGE k ON s.TABLE_SCHEMA = k.TABLE_SCHEMA AND s.TABLE_NAME = k.TABLE_NAME AND s.COLUMN_NAME = k.COLUMN_NAME AND k.REFERENCED_TABLE_NAME IS NOT NULL WHERE k.REFERENCED_TABLE_NAME IS NULL AND s.TABLE_SCHEMA = '${database.replace(/`/g, "")}' AND s.TABLE_NAME = '${table.replace(/`/g, "")}' AND s.INDEX_NAME = '${index}' ) AS "IS_EXISTS" `, ]; return this.format(sql); } createIndex({ table, index, key, }) { const sql = [ `${this.$constants("CREATE_INDEX")}`, `\`${index}\``, `${this.$constants("ON")}`, `${table}(\`${key}\`)`, ]; return this.format(sql); } getDatabase(database) { const sql = [ `${this.$constants("SHOW_DATABASES")}`, `${this.$constants("LIKE")}`, `'${database.replace(/`/g, "")}'`, ].join(" "); return this.format(sql); } dropDatabase(database) { const sql = [ `${this.$constants("DROP_DATABASE")}`, `\`${database.replace(/`/g, "")}\`` ].join(" "); return this.format(sql); } dropView(view) { const sql = [ `${this.$constants("DROP_VIEW")}`, `\`${view.replace(/`/g, "")}\`` ].join(" "); return this.format(sql); } dropTable(table) { const sql = [ `${this.$constants("DROP_TABLE")}`, `\`${table.replace(/`/g, "")}\`` ].join(" "); return this.format(sql); } truncate(table) { const sql = [ `${this.$constants("TRUNCATE_TABLE")}`, `\`${table.replace(/`/g, "")}\`` ].join(" "); return this.format(sql); } sleep(second) { return `SLEEP(${second})`; } format(sql) { if (typeof sql === "string") sql = [sql]; return sql .filter((s) => s !== "" || s == null) .join(" ") .replace(/\s+/g, " "); } bindJoin(values) { if (!Array.isArray(values) || !values.length) return null; return values.join(" "); } bindWhere(values) { if (!Array.isArray(values) || !values.length) return null; return `${this.$constants("WHERE")} ${values .map((v) => v.replace(/^\s/, "").replace(/\s+/g, " ")) .join(" ")}`; } bindOrderBy(values) { if (!Array.isArray(values) || !values.length) return null; return `${this.$constants("ORDER_BY")} ${values .map((v) => v.replace(/^\s/, "").replace(/\s+/g, " ")) .join(", ")}`; } bindGroupBy(values) { if (!Array.isArray(values) || !values.length) return null; return `${this.$constants("GROUP_BY")} ${values .map((v) => v.replace(/^\s/, "").replace(/\s+/g, " ")) .join(", ")}`; } bindSelect(values, { distinct } = {}) { if (!values.length) { if (!distinct) return `${this.$constants("SELECT")} *`; return `${this.$constants("SELECT")} ${this.$constants("DISTINCT")} *`; } const findIndex = values.indexOf("*"); if (findIndex > -1) { const removed = values.splice(findIndex, 1); values.unshift(removed[0]); } return `${this.$constants("SELECT")} ${values.join(", ")}`; } bindFrom({ from, alias, rawAlias, }) { if (!from.length || from.every((f) => f == null || f === "")) { return ""; } if (alias != null && alias !== "") { if (rawAlias != null && rawAlias !== "") { const raw = String(rawAlias) .replace(/^\(\s*|\s*\)$/g, "") .trim(); const normalizedRawAlias = raw.startsWith("(") && raw.endsWith(")") ? raw.slice(1, -1) : raw; raw.startsWith("(") && raw.endsWith(")") ? raw.slice(1, -1) : raw; return `${this.$constants("FROM")} (${normalizedRawAlias}) ${this.$constants("AS")} \`${alias}\``; } return `${this.$constants("FROM")} ${from.join(", ")} ${this.$constants("AS")} \`${alias}\``; } return `${this.$constants("FROM")} ${from.join(", ")}`; } bindLimit(limit) { if (limit === "" || limit == null) return ""; return `${this.$constants("LIMIT")} ${limit}`; } bindOffset(offset) { if (offset === "" || offset == null) return ""; return `${this.$constants("OFFSET")} ${offset}`; } bindHaving(having) { if (having == null || having === '') return ""; return `${this.$constants("HAVING")} ${having}`; } bindRowLevelLock(mode) { if (mode == null) return ''; const modeLock = mode === "FOR_UPDATE" ? this.$constants("ROW_LEVEL_LOCK").update : this.$constants("ROW_LEVEL_LOCK").share; return modeLock; } } exports.MysqlQueryBuilder = MysqlQueryBuilder; //# sourceMappingURL=MysqlQueryBuilder.js.map