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.

643 lines 22.1 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.MariadbQueryBuilder = void 0; const __1 = require(".."); class MariadbQueryBuilder 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")].filter(Boolean).map(String) : [this.$state.get("TABLE_NAME"), ...this.$state.get("FROM")].filter(Boolean).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 = Array.isArray(query.values) ? query.values.map(v => `(${v})`).join(', ') : `(${query.values})`; 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", 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}' `, ]; 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); } hasTable({ database, table }) { const sql = [ `SELECT EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '${database.replace(/\`/g, "")}' AND TABLE_NAME = '${table.replace(/\`/g, "")}' AND TABLE_TYPE = 'BASE TABLE' ) AS "IS_EXISTS"`, ]; return this.format(sql); } createDatabase(database) { const sql = `CREATE DATABASE IF NOT EXISTS \`${database}\``; 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, "")}\``, `${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} (${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}\``, 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); } addFK({ 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, name, }) { 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 = '${name}' ) AS "IS_EXISTS" `, ]; return this.format(sql); } addIndex({ table, name, columns, }) { const cols = columns .map(col => `\`${col}\``) .join(", "); const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("ADD_INDEX"), `\`${name}\``, `(${cols})` ]; return this.format(sql); } dropIndex({ table, name, }) { const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("DROP"), this.$constants("INDEX"), `\`${name}\`` ]; return this.format(sql); } hasUnique({ database, table, name, }) { const sql = [ ` SELECT EXISTS( SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = '${database.replace(/`/g, "")}' AND TABLE_NAME = '${table.replace(/`/g, "")}' AND CONSTRAINT_TYPE = '${name}' ) AS "IS_EXISTS" `, ]; return this.format(sql); } addUnique({ table, name, columns }) { const cols = columns .map(col => `\`${col}\``) .join(", "); const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("ADD_CONSTRAINT"), `\`${name}\``, this.$constants("UNIQUE"), `(${cols})` ]; return this.format(sql); } dropUnique({ table, name, }) { const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("DROP"), this.$constants("INDEX"), `\`${name}\`` ]; return this.format(sql); } hasPrimaryKey({ database, table, }) { const sql = ` SELECT EXISTS ( SELECT 1 FROM INFORMATION_SCHEMA.TABLE_CONSTRAINTS WHERE TABLE_SCHEMA = '${database.replace(/['"`]/g, "")}' AND TABLE_NAME = '${table.replace(/['"`]/g, "")}' AND CONSTRAINT_TYPE = '${this.$constants('PRIMARY_KEY')}' ) AS IS_EXISTS `; return this.format(sql); } addPrimaryKey({ table, columns, }) { const cols = columns .map(col => `\`${col}\``) .join(", "); const sql = ` ${this.$constants('ALTER_TABLE')} \`${table}\` ${this.$constants('ADD')} ${this.$constants('PRIMARY_KEY')} (${cols}) `; return this.format(sql); } dropPrimaryKey({ table }) { const sql = [ this.$constants("ALTER_TABLE"), `\`${table}\``, this.$constants("DROP"), this.$constants("PRIMARY_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, " "); } getActiveConnections() { const sql = ` SELECT VARIABLE_VALUE AS Connections FROM PERFORMANCE_SCHEMA.GLOBAL_STATUS WHERE VARIABLE_NAME = 'Threads_connected' `; return this.format(sql); } getMaxConnections() { const sql = `SELECT @@max_connections AS MaxConnections;`; return this.format(sql); } bindJoin(values) { if (!Array.isArray(values) || !values.length) return null; return values.join(" "); } bindWhere(values) { if (!Array.isArray(values) || !values.length) return null; const serializeWhere = (wheres) => { const resolveValue = ({ operator, value }) => { let valueStr = ''; if (operator?.toUpperCase() === this.$constants('IN') && Array.isArray(value)) { valueStr = `(${value.map((v) => v).join(',')})`; } else if (operator?.toUpperCase() === this.$constants('IS_NULL') || operator?.toUpperCase() === this.$constants('IS_NOT_NULL')) { valueStr = ''; } else { valueStr = `${value}`; } return valueStr; }; const conditionToSQL = (cond, isFirst = false) => { const { column = '', operator = '', condition, value, nested } = cond; if (nested && nested.length) { const nestedSQL = nested .map((c) => conditionToSQL(c)) .join(' '); const valueStr = resolveValue({ operator, value }); if (!isFirst) { return `${condition ?? this.$constants('AND')} (${column} ${operator} ${valueStr} ${nestedSQL})`; } return `(${column} ${operator} ${valueStr} ${nestedSQL})`; } const valueStr = resolveValue({ operator, value }); if (!isFirst) { return `${condition ?? this.$constants('AND')} ${column} ${operator} ${valueStr}`.trim(); } return `${column} ${operator} ${valueStr}`.trim(); }; return wheres.map((cond, i) => conditionToSQL(cond, !i)).join(' '); }; return `${this.$constants("WHERE")} ${serializeWhere(values)}`; } 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(rowLevelLock) { if (rowLevelLock.mode == null) return ''; let modeLock = rowLevelLock.mode === "FOR_UPDATE" ? this.$constants("ROW_LEVEL_LOCK").update : this.$constants("ROW_LEVEL_LOCK").share; if (rowLevelLock.skipLocked) { modeLock += ` ${this.$constants("ROW_LEVEL_LOCK").skipLocked}`; } if (rowLevelLock.nowait) { modeLock += ` ${this.$constants("ROW_LEVEL_LOCK").nowait}`; } return modeLock; } } exports.MariadbQueryBuilder = MariadbQueryBuilder; //# sourceMappingURL=MariadbQueryBuilder.js.map