First version without typescript errors
This commit is contained in:
parent
9c8f6e20aa
commit
eb86e1803d
@ -25,9 +25,6 @@ export default function () {
|
||||
const dbSchema = global.DB_SCHEMA;
|
||||
|
||||
const { ROOT_DIR, BUN_MARIADB_TEMP_DB_FILE_PATH } = grabDirNames();
|
||||
const { db_file_path } = grabDBDir({ config });
|
||||
|
||||
cpSync(db_file_path, BUN_MARIADB_TEMP_DB_FILE_PATH);
|
||||
|
||||
try {
|
||||
const isVector = Boolean(opts.vector || opts.v);
|
||||
@ -66,7 +63,6 @@ export default function () {
|
||||
process.exit();
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
cpSync(BUN_MARIADB_TEMP_DB_FILE_PATH, db_file_path);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
@ -3,4 +3,5 @@ export const AppData = {
|
||||
MaxBackups: 10,
|
||||
DefaultBackupDirName: ".backups",
|
||||
DbSchemaManagerTableName: "__db_schema_manager__",
|
||||
DbSchemaFileName: "schema.ts",
|
||||
} as const;
|
||||
|
||||
@ -2,10 +2,11 @@ import path from "path";
|
||||
import fs from "fs";
|
||||
import { AppData } from "../data/app-data";
|
||||
import grabDirNames from "../data/grab-dir-names";
|
||||
import type {
|
||||
BunMariaDBConfig,
|
||||
BunMariaDBConfigReturn,
|
||||
BUN_MARIADB_DatabaseSchemaType,
|
||||
import {
|
||||
type BunMariaDBConfig,
|
||||
type BunMariaDBConfigReturn,
|
||||
type BUN_MARIADB_DatabaseSchemaType,
|
||||
RequiredENVs,
|
||||
} from "../types";
|
||||
|
||||
export default async function init(): Promise<BunMariaDBConfigReturn> {
|
||||
@ -25,27 +26,53 @@ export default async function init(): Promise<BunMariaDBConfigReturn> {
|
||||
const ConfigImport = await import(ConfigFilePath);
|
||||
const Config = ConfigImport["default"] as BunMariaDBConfig;
|
||||
|
||||
if (!Config) {
|
||||
console.error(
|
||||
`No default export from \`${ConfigFilePath}\`. Please export a default module.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!Config.db_name) {
|
||||
console.error(`\`db_name\` is required in your config`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!Config.db_schema_file_name) {
|
||||
console.error(`\`db_schema_file_name\` is required in your config`);
|
||||
RequiredENVs.forEach((env_key) => {
|
||||
if (
|
||||
env_key == "BUN_MARIADB_SERVER_PORT" ||
|
||||
env_key == "BUN_MARIADB_SERVER_SSL_KEY_PATH"
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!process.env[env_key]) {
|
||||
console.error(`\`${env_key}\` env variable is required.`);
|
||||
process.exit(1);
|
||||
}
|
||||
});
|
||||
|
||||
if (!Config.db_dir) {
|
||||
console.error(
|
||||
`\`db_dir\` is required in your config. This directory holds all database related configuration. Also note that a \`${AppData["DbSchemaFileName"]}\` file is also required in this directory to define your database schema`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let db_dir = ROOT_DIR;
|
||||
|
||||
if (Config.db_dir) {
|
||||
db_dir = path.resolve(ROOT_DIR, Config.db_dir);
|
||||
|
||||
const db_dir = path.resolve(ROOT_DIR, Config.db_dir);
|
||||
if (!fs.existsSync(Config.db_dir)) {
|
||||
fs.mkdirSync(Config.db_dir, { recursive: true });
|
||||
}
|
||||
|
||||
const DBSchemaFilePath = path.join(db_dir, AppData["DbSchemaFileName"]);
|
||||
|
||||
if (!fs.existsSync(DBSchemaFilePath)) {
|
||||
console.error(
|
||||
`Please create a schema file at \`${DBSchemaFilePath}\`. Don't forget to export a default module from this file.`,
|
||||
);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const DBSchemaFilePath = path.join(db_dir, Config.db_schema_file_name);
|
||||
const DbSchemaImport = await import(DBSchemaFilePath);
|
||||
const DbSchema = DbSchemaImport[
|
||||
"default"
|
||||
|
||||
@ -5,9 +5,6 @@ import grabDBConnection from "./grab-db-connection";
|
||||
type Param = {
|
||||
query: string;
|
||||
values?: string[] | object;
|
||||
noErrorLogs?: boolean;
|
||||
database?: string;
|
||||
tableSchema?: BUN_MARIADB_TableSchemaType;
|
||||
config?: ConnectionConfig;
|
||||
};
|
||||
|
||||
@ -17,18 +14,12 @@ type Param = {
|
||||
*/
|
||||
export default async function dbHandler<
|
||||
T extends { [k: string]: any } = { [k: string]: any },
|
||||
>({
|
||||
query,
|
||||
values,
|
||||
noErrorLogs,
|
||||
database,
|
||||
config,
|
||||
}: Param): Promise<DBResponseObject> {
|
||||
>({ query, values, config }: Param): Promise<DBResponseObject> {
|
||||
let CONNECTION: Connection | undefined;
|
||||
let results: T | null = null;
|
||||
|
||||
try {
|
||||
CONNECTION = await grabDBConnection({ database, config });
|
||||
CONNECTION = await grabDBConnection({ config });
|
||||
|
||||
if (query && values) {
|
||||
const queryResults = await CONNECTION.query(query, values);
|
||||
@ -46,10 +37,6 @@ export default async function dbHandler<
|
||||
throw new Error("Authentication Failed!");
|
||||
}
|
||||
|
||||
if (!noErrorLogs) {
|
||||
console.log(error);
|
||||
}
|
||||
|
||||
results = null;
|
||||
} finally {
|
||||
await CONNECTION?.end();
|
||||
|
||||
@ -8,7 +8,7 @@ type Return = ConnectionConfig["ssl"] | undefined;
|
||||
* # Grab SSL
|
||||
*/
|
||||
export default function grabDbSSL(): Return {
|
||||
const caProivdedPath = process.env.DSQL_SSL_CA_CERT;
|
||||
const caProivdedPath = process.env.BUN_MARIADB_SERVER_SSL_KEY_PATH;
|
||||
|
||||
if (!caProivdedPath?.match(/./)) {
|
||||
return undefined;
|
||||
|
||||
@ -8,17 +8,18 @@ import grabDbSSL from "./grab-db-ssl";
|
||||
export default function grabDSQLConnectionConfig(
|
||||
param?: DsqlConnectionParam,
|
||||
): ConnectionConfig {
|
||||
const CONN_TIMEOUT = 10000;
|
||||
const configData = global.CONFIG;
|
||||
const CONN_TIMEOUT = configData?.connection_timeout || 10000;
|
||||
|
||||
const config: ConnectionConfig = {
|
||||
host: process.env.DSQL_DB_HOST,
|
||||
user: process.env.DSQL_DB_USERNAME,
|
||||
password: process.env.DSQL_DB_PASSWORD,
|
||||
// database: param?.dbFullName,
|
||||
port: process.env.DSQL_DB_PORT
|
||||
? Number(process.env.DSQL_DB_PORT)
|
||||
host: process.env.BUN_MARIADB_SERVER_HOST,
|
||||
user: process.env.BUN_MARIADB_SERVER_USERNAME,
|
||||
password: process.env.BUN_MARIADB_SERVER_PASSWORD,
|
||||
database: configData?.db_name,
|
||||
port: process.env.BUN_MARIADB_SERVER_PORT
|
||||
? Number(process.env.BUN_MARIADB_SERVER_PORT)
|
||||
: undefined,
|
||||
charset: "utf8mb4",
|
||||
charset: configData?.charset || "utf8mb4",
|
||||
ssl: grabDbSSL(),
|
||||
bigIntAsNumber: true,
|
||||
supportBigNumbers: true,
|
||||
|
||||
@ -4,37 +4,45 @@ type Params = {
|
||||
data: any | any[];
|
||||
};
|
||||
|
||||
export default async function ({ sql: passed_sql, table, data }: Params) {
|
||||
let sql = passed_sql;
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
export default async function ({ sql: passedSql, table, data }: Params) {
|
||||
const config = global.CONFIG;
|
||||
const dbSchema = global.DB_SCHEMA;
|
||||
|
||||
const table_schema = dbSchema.tables.find((t) => t.tableName == table);
|
||||
const now = Date.now();
|
||||
const tableSchema = dbSchema?.tables.find(
|
||||
(t: { tableName: string }) => t.tableName == table,
|
||||
);
|
||||
|
||||
if (table_schema?.tableName) {
|
||||
const set_sql_arr = Object.keys(
|
||||
Array.isArray(data) ? data[0] : data,
|
||||
).map((field) => `${field} = excluded.${field}`);
|
||||
|
||||
set_sql_arr.push(`updated_at = ${now}`);
|
||||
|
||||
const set_sql = set_sql_arr.join(", ");
|
||||
|
||||
const unique_fields = table_schema.fields.filter((f) => f.unique);
|
||||
|
||||
for (let i = 0; i < unique_fields.length; i++) {
|
||||
const field = unique_fields[i];
|
||||
sql += ` ON CONFLICT(${field?.fieldName}) DO UPDATE SET ${set_sql}`;
|
||||
if (!tableSchema?.tableName) {
|
||||
return passedSql;
|
||||
}
|
||||
|
||||
if (table_schema.uniqueConstraints?.[0]) {
|
||||
for (let i = 0; i < table_schema.uniqueConstraints.length; i++) {
|
||||
const constraint = table_schema.uniqueConstraints[i];
|
||||
sql += ` ON CONFLICT(${constraint?.constraintTableFields?.map((c) => c.value)?.join(", ")}) DO UPDATE SET ${set_sql}`;
|
||||
}
|
||||
}
|
||||
const dataObject = Array.isArray(data) ? data[0] : data;
|
||||
const dataKeys = Object.keys(dataObject || {});
|
||||
|
||||
if (dataKeys.length === 0) {
|
||||
return passedSql;
|
||||
}
|
||||
|
||||
return sql;
|
||||
const hasUniqueField =
|
||||
tableSchema.fields.some((field) => field.unique) ||
|
||||
Boolean(tableSchema.uniqueConstraints?.length);
|
||||
|
||||
if (!hasUniqueField) {
|
||||
return passedSql;
|
||||
}
|
||||
|
||||
const setSql = dataKeys
|
||||
.filter((field: string) => field !== "updated_at")
|
||||
.map(
|
||||
(field: string) =>
|
||||
`${quoteIdentifier(field)} = VALUES(${quoteIdentifier(field)})`,
|
||||
);
|
||||
|
||||
setSql.push(`updated_at = ${Date.now()}`);
|
||||
|
||||
return `${passedSql} ON DUPLICATE KEY UPDATE ${setSql.join(", ")}`;
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import DbClient from ".";
|
||||
import dbHandler from "../db-handler";
|
||||
import _ from "lodash";
|
||||
import type { APIResponseObject, ServerQueryParam } from "../../types";
|
||||
import sqlGenerator from "../../utils/sql-generator";
|
||||
@ -12,6 +12,10 @@ type Params<
|
||||
targetId?: number | string;
|
||||
};
|
||||
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
export default async function DbDelete<
|
||||
Schema extends { [k: string]: any } = { [k: string]: any },
|
||||
Table extends string = string,
|
||||
@ -39,30 +43,13 @@ export default async function DbDelete<
|
||||
}
|
||||
|
||||
sqlObj = sqlGenerator({
|
||||
tableName: table,
|
||||
tableName: quoteIdentifier(table),
|
||||
genObject: finalQuery,
|
||||
});
|
||||
|
||||
const whereClause = sqlObj.string.match(/WHERE .*/)?.[0];
|
||||
|
||||
if (whereClause) {
|
||||
let sql = `DELETE FROM ${table} ${whereClause}`;
|
||||
|
||||
sqlObj.string = sql;
|
||||
|
||||
const res = DbClient.run(sql, sqlObj.values);
|
||||
|
||||
return {
|
||||
success: Boolean(res.changes),
|
||||
postInsertReturn: {
|
||||
affectedRows: res.changes,
|
||||
insertId: Number(res.lastInsertRowid),
|
||||
},
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
if (!whereClause) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `No WHERE clause`,
|
||||
@ -71,6 +58,38 @@ export default async function DbDelete<
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const sql = `DELETE FROM ${quoteIdentifier(table)} ${whereClause}`;
|
||||
|
||||
sqlObj.string = sql;
|
||||
|
||||
const res = await dbHandler({
|
||||
query: sql,
|
||||
values: sqlObj.values as any,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "Database delete failed",
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const singleRes = res.single_res as any;
|
||||
|
||||
return {
|
||||
success: Boolean(singleRes?.affectedRows),
|
||||
postInsertReturn: {
|
||||
affectedRows: Number(singleRes?.affectedRows),
|
||||
insertId: Number(singleRes?.insertId),
|
||||
},
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import DbClient from ".";
|
||||
import dbHandler from "../db-handler";
|
||||
import type { APIResponseObject, SQLInsertGenReturn } from "../../types";
|
||||
import sqlInsertGenerator from "../../utils/sql-insert-generator";
|
||||
import grabDuplicateSafeInsertSql from "../grab-duplicate-safe-insert-sql";
|
||||
@ -35,21 +35,41 @@ export default async function DbInsert<
|
||||
data: finalData as any[],
|
||||
}) || null;
|
||||
|
||||
let sql = sqlObj?.query || "";
|
||||
if (!sqlObj) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "No insert SQL generated",
|
||||
};
|
||||
}
|
||||
|
||||
let sql = sqlObj.query;
|
||||
|
||||
if (update_on_duplicate && data[0]) {
|
||||
sql = await grabDuplicateSafeInsertSql({ data, table, sql });
|
||||
}
|
||||
|
||||
(sqlObj || ({} as any)).query = sql;
|
||||
const res = await dbHandler({
|
||||
query: sql,
|
||||
values: sqlObj.values as any,
|
||||
});
|
||||
|
||||
const res = DbClient.run(sql, sqlObj?.values || []);
|
||||
if (!res.success) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "Database insert failed",
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const singleRes = res.single_res as any;
|
||||
|
||||
return {
|
||||
success: Boolean(Number(res.lastInsertRowid)),
|
||||
success: Boolean(singleRes?.affectedRows || singleRes?.insertId),
|
||||
postInsertReturn: {
|
||||
affectedRows: res.changes,
|
||||
insertId: Number(res.lastInsertRowid),
|
||||
affectedRows: Number(singleRes?.affectedRows),
|
||||
insertId: Number(singleRes?.insertId),
|
||||
},
|
||||
debug: {
|
||||
sqlObj,
|
||||
|
||||
@ -59,10 +59,6 @@ class MariaDBSchemaManager {
|
||||
console.log("Schema synchronization complete!");
|
||||
}
|
||||
|
||||
private getDatabaseName(): string | undefined {
|
||||
return this.db_schema.dbName || this.db_schema.dbSlug;
|
||||
}
|
||||
|
||||
private quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
@ -71,7 +67,8 @@ class MariaDBSchemaManager {
|
||||
where: string;
|
||||
values: QueryValues;
|
||||
} {
|
||||
const databaseName = this.getDatabaseName();
|
||||
const config = global.CONFIG;
|
||||
const databaseName = config?.db_name;
|
||||
|
||||
if (databaseName) {
|
||||
return {
|
||||
@ -87,12 +84,11 @@ class MariaDBSchemaManager {
|
||||
}
|
||||
|
||||
private async run(query: string, values?: QueryValues): Promise<void> {
|
||||
const config = global.CONFIG;
|
||||
const res = await dbHandler({
|
||||
query,
|
||||
values: values as any,
|
||||
config: this.getDatabaseName()
|
||||
? { database: this.getDatabaseName() }
|
||||
: undefined,
|
||||
config: config?.db_config,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
@ -107,9 +103,7 @@ class MariaDBSchemaManager {
|
||||
const res = await dbHandler<T>({
|
||||
query,
|
||||
values: values as any,
|
||||
config: this.getDatabaseName()
|
||||
? { database: this.getDatabaseName() }
|
||||
: undefined,
|
||||
config: global.CONFIG.db_config,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
@ -195,7 +189,9 @@ class MariaDBSchemaManager {
|
||||
|
||||
for (const tableName of tablesToDrop) {
|
||||
console.log(`Dropping table: ${tableName}`);
|
||||
await this.run(`DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`);
|
||||
await this.run(
|
||||
`DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`,
|
||||
);
|
||||
await this.removeDbManagerTable(tableName);
|
||||
}
|
||||
}
|
||||
@ -308,7 +304,10 @@ class MariaDBSchemaManager {
|
||||
const options = ["ENGINE=InnoDB"];
|
||||
|
||||
if (table.collation) {
|
||||
options.push("DEFAULT CHARSET=utf8mb4", `COLLATE ${table.collation}`);
|
||||
options.push(
|
||||
"DEFAULT CHARSET=utf8mb4",
|
||||
`COLLATE ${table.collation}`,
|
||||
);
|
||||
}
|
||||
|
||||
return ` ${options.join(" ")}`;
|
||||
@ -321,11 +320,12 @@ class MariaDBSchemaManager {
|
||||
await this.recreateTable(table);
|
||||
}
|
||||
|
||||
private async getTableColumns(
|
||||
tableName: string,
|
||||
): Promise<ColumnInfoRow[]> {
|
||||
private async getTableColumns(tableName: string): Promise<ColumnInfoRow[]> {
|
||||
const tableSchemaWhere = this.tableSchemaWhere(tableName);
|
||||
const rows = await this.query<{ COLUMN_NAME: string; COLUMN_TYPE: string }>(
|
||||
const rows = await this.query<{
|
||||
COLUMN_NAME: string;
|
||||
COLUMN_TYPE: string;
|
||||
}>(
|
||||
`SELECT COLUMN_NAME AS name, COLUMN_TYPE AS type FROM information_schema.COLUMNS WHERE ${tableSchemaWhere.where} AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION`,
|
||||
tableSchemaWhere.values,
|
||||
);
|
||||
@ -379,7 +379,9 @@ class MariaDBSchemaManager {
|
||||
existingRows = await this.query<Record<string, any>>(
|
||||
`SELECT * FROM ${this.quoteIdentifier(table.tableName)}`,
|
||||
);
|
||||
await this.run(`DROP TABLE ${this.quoteIdentifier(table.tableName)}`);
|
||||
await this.run(
|
||||
`DROP TABLE ${this.quoteIdentifier(table.tableName)}`,
|
||||
);
|
||||
}
|
||||
|
||||
await this.createTable(table);
|
||||
@ -395,7 +397,9 @@ class MariaDBSchemaManager {
|
||||
const existingColumns = await this.getTableColumns(table.tableName);
|
||||
const columnsToKeep = table.fields
|
||||
.filter((field) =>
|
||||
existingColumns.some((column) => column.name === field.fieldName),
|
||||
existingColumns.some(
|
||||
(column) => column.name === field.fieldName,
|
||||
),
|
||||
)
|
||||
.map((field) => field.fieldName)
|
||||
.filter((fieldName): fieldName is string => Boolean(fieldName));
|
||||
@ -624,7 +628,9 @@ class MariaDBSchemaManager {
|
||||
|
||||
if (!stillExists) {
|
||||
console.log(`Dropping index: ${indexName}`);
|
||||
await this.run(`DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(table.tableName)}`);
|
||||
await this.run(
|
||||
`DROP INDEX ${this.quoteIdentifier(indexName)} ON ${this.quoteIdentifier(table.tableName)}`,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import mysql from "mysql";
|
||||
import DbClient from ".";
|
||||
import dbHandler from "../db-handler";
|
||||
import _ from "lodash";
|
||||
import type { APIResponseObject, ServerQueryParam } from "../../types";
|
||||
import sqlGenerator from "../../utils/sql-generator";
|
||||
@ -14,6 +13,10 @@ type Params<
|
||||
targetId?: number | string;
|
||||
};
|
||||
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
export default async function DbSelect<
|
||||
Schema extends { [k: string]: any } = { [k: string]: any },
|
||||
Table extends string = string,
|
||||
@ -42,44 +45,75 @@ export default async function DbSelect<
|
||||
}
|
||||
|
||||
sqlObj = sqlGenerator({
|
||||
tableName: table,
|
||||
tableName: quoteIdentifier(table),
|
||||
genObject: finalQuery,
|
||||
});
|
||||
|
||||
let sql = mysql.format(sqlObj.string, sqlObj.values);
|
||||
const res = await dbHandler<Schema>({
|
||||
query: sqlObj.string,
|
||||
values: sqlObj.values as any,
|
||||
});
|
||||
|
||||
const res = DbClient.query<Schema, Schema[]>(sql);
|
||||
const batchRes = res.all();
|
||||
if (!res.success) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "Database select failed",
|
||||
debug: {
|
||||
sqlObj,
|
||||
sql: sqlObj.string,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const batchRes = (res.payload || []) as Schema[];
|
||||
|
||||
let resp: APIResponseObject<Schema> = {
|
||||
success: Boolean(batchRes[0]),
|
||||
success: true,
|
||||
payload: batchRes,
|
||||
singleRes: batchRes[0],
|
||||
debug: {
|
||||
sqlObj,
|
||||
sql,
|
||||
sql: sqlObj.string,
|
||||
},
|
||||
};
|
||||
|
||||
if (count) {
|
||||
let count_sql_object = sqlGenerator({
|
||||
tableName: table,
|
||||
const countSqlObject = sqlGenerator({
|
||||
tableName: quoteIdentifier(table),
|
||||
genObject: finalQuery,
|
||||
count,
|
||||
});
|
||||
|
||||
let count_sql = mysql.format(
|
||||
count_sql_object.string,
|
||||
count_sql_object.values,
|
||||
);
|
||||
const countSql = `SELECT COUNT(*) AS count FROM (${countSqlObject.string}) AS c`;
|
||||
|
||||
count_sql = `SELECT COUNT(*) FROM (${count_sql}) as c`;
|
||||
const countRes = await dbHandler<{ count: number }>({
|
||||
query: countSql,
|
||||
values: countSqlObject.values as any,
|
||||
});
|
||||
|
||||
const count_res = DbClient.query<Schema, Schema[]>(count_sql).all();
|
||||
if (!countRes.success) {
|
||||
return {
|
||||
...resp,
|
||||
success: false,
|
||||
msg: "Database count failed",
|
||||
debug: {
|
||||
...resp.debug,
|
||||
count_sql: countSql,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const count_val = count_res[0]?.["COUNT(*)"];
|
||||
resp["count"] = Number(count_val);
|
||||
resp["debug"]["count_sql"] = count_sql;
|
||||
const countRows = countRes.payload || [];
|
||||
const countVal = countRows[0]?.count ?? countRows[0]?.["COUNT(*)"];
|
||||
|
||||
resp = {
|
||||
...resp,
|
||||
count: Number(countVal),
|
||||
debug: {
|
||||
...resp.debug,
|
||||
count_sql: countSql,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
return resp;
|
||||
|
||||
@ -1,5 +1,4 @@
|
||||
import DbClient from ".";
|
||||
import _ from "lodash";
|
||||
import dbHandler from "../db-handler";
|
||||
import type { APIResponseObject, SQLInsertGenValueType } from "../../types";
|
||||
|
||||
type Params = {
|
||||
@ -11,25 +10,45 @@ export default async function DbSQL<
|
||||
T extends { [k: string]: any } = { [k: string]: any },
|
||||
>({ sql, values }: Params): Promise<APIResponseObject<T>> {
|
||||
try {
|
||||
const trimmed_sql = sql.trim();
|
||||
const trimmedSql = sql.trim();
|
||||
const isSelect = trimmedSql.match(/^select/i);
|
||||
|
||||
const res = trimmed_sql.match(/^select/i)
|
||||
? DbClient.query(trimmed_sql).all(...(values || []))
|
||||
: DbClient.run(trimmed_sql, values || []);
|
||||
const res = await dbHandler<T>({
|
||||
query: trimmedSql,
|
||||
values: values as any,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "Database query failed",
|
||||
debug: {
|
||||
sqlObj: {
|
||||
sql: trimmedSql,
|
||||
values,
|
||||
},
|
||||
sql,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const payload = isSelect ? ((res.payload || []) as T[]) : undefined;
|
||||
const singleRes = isSelect ? payload?.[0] : (res.single_res as T);
|
||||
const singleRaw = res.single_res as any;
|
||||
|
||||
return {
|
||||
success: true,
|
||||
payload: Array.isArray(res) ? (res as T[]) : undefined,
|
||||
singleRes: Array.isArray(res) ? (res as T[])?.[0] : undefined,
|
||||
postInsertReturn: Array.isArray(res)
|
||||
payload,
|
||||
singleRes,
|
||||
postInsertReturn: isSelect
|
||||
? undefined
|
||||
: {
|
||||
affectedRows: res.changes,
|
||||
insertId: Number(res.lastInsertRowid),
|
||||
affectedRows: Number(singleRaw?.affectedRows),
|
||||
insertId: Number(singleRaw?.insertId),
|
||||
},
|
||||
debug: {
|
||||
sqlObj: {
|
||||
sql: trimmed_sql,
|
||||
sql: trimmedSql,
|
||||
values,
|
||||
},
|
||||
sql,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import DbClient from ".";
|
||||
import dbHandler from "../db-handler";
|
||||
import _ from "lodash";
|
||||
import type {
|
||||
APIResponseObject,
|
||||
@ -17,6 +17,10 @@ type Params<
|
||||
targetId?: number | string;
|
||||
};
|
||||
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
export default async function DbUpdate<
|
||||
Schema extends { [k: string]: any } = { [k: string]: any },
|
||||
Table extends string = string,
|
||||
@ -45,16 +49,21 @@ export default async function DbUpdate<
|
||||
}
|
||||
|
||||
const sqlQueryObj = sqlGenerator({
|
||||
tableName: table,
|
||||
tableName: quoteIdentifier(table),
|
||||
genObject: finalQuery,
|
||||
});
|
||||
|
||||
let values: SQLInsertGenValueType[] = [];
|
||||
|
||||
const whereClause = sqlQueryObj.string.match(/WHERE .*/)?.[0];
|
||||
|
||||
if (whereClause) {
|
||||
let sql = `UPDATE ${table} SET`;
|
||||
if (!whereClause) {
|
||||
return {
|
||||
success: false,
|
||||
msg: `No WHERE clause`,
|
||||
};
|
||||
}
|
||||
|
||||
let values: SQLInsertGenValueType[] = [];
|
||||
let sql = `UPDATE ${quoteIdentifier(table)} SET`;
|
||||
|
||||
const finalData: { [k: string]: SQLInsertGenValueType } = {
|
||||
updated_at: Date.now(),
|
||||
@ -69,9 +78,8 @@ export default async function DbUpdate<
|
||||
|
||||
const isLast = i == keys.length - 1;
|
||||
|
||||
sql += ` ${key}=?`;
|
||||
const value = finalData[key];
|
||||
values.push(value || null);
|
||||
sql += ` ${quoteIdentifier(key)}=?`;
|
||||
values.push(finalData[key] ?? null);
|
||||
|
||||
if (!isLast) {
|
||||
sql += `,`;
|
||||
@ -84,24 +92,33 @@ export default async function DbUpdate<
|
||||
sqlObj.string = sql;
|
||||
sqlObj.values = values as any[];
|
||||
|
||||
const res = DbClient.run(sql, values);
|
||||
const res = await dbHandler({
|
||||
query: sql,
|
||||
values: values as any,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
return {
|
||||
success: false,
|
||||
msg: "Database update failed",
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const singleRes = res.single_res as any;
|
||||
|
||||
return {
|
||||
success: Boolean(res.changes),
|
||||
success: Boolean(singleRes?.affectedRows || singleRes?.changedRows),
|
||||
postInsertReturn: {
|
||||
affectedRows: res.changes,
|
||||
insertId: Number(res.lastInsertRowid),
|
||||
affectedRows: Number(singleRes?.affectedRows),
|
||||
insertId: Number(singleRes?.insertId),
|
||||
},
|
||||
debug: {
|
||||
sqlObj,
|
||||
},
|
||||
};
|
||||
} else {
|
||||
return {
|
||||
success: false,
|
||||
msg: `No WHERE clause`,
|
||||
};
|
||||
}
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
|
||||
@ -1,7 +0,0 @@
|
||||
import _ from "lodash";
|
||||
import type { BUN_MARIADB_DatabaseSchemaType } from "../../types";
|
||||
|
||||
export const DbSchema: BUN_MARIADB_DatabaseSchemaType = {
|
||||
dbName: "travis-ai",
|
||||
tables: [],
|
||||
};
|
||||
@ -28,18 +28,8 @@ export const UsersOmitedFields = [
|
||||
* database metadata.
|
||||
*/
|
||||
export interface BUN_MARIADB_DatabaseSchemaType {
|
||||
id?: string | number;
|
||||
dbName?: string;
|
||||
dbSlug?: string;
|
||||
dbFullName?: string;
|
||||
dbDescription?: string;
|
||||
dbImage?: string;
|
||||
tables: BUN_MARIADB_TableSchemaType[];
|
||||
childrenDatabases?: BUN_MARIADB_ChildrenDatabaseObject[];
|
||||
childDatabase?: boolean;
|
||||
childDatabaseDbId?: string | number;
|
||||
updateData?: boolean;
|
||||
collation?: (typeof MariaDBCollations)[number];
|
||||
collation?: MariaDbCollationsType;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -58,6 +48,12 @@ export const MariaDBCollations = [
|
||||
"utf8mb4_unicode_520_ci",
|
||||
] as const;
|
||||
|
||||
export type MariaDbCollationsType = (typeof MariaDBCollations)[number];
|
||||
|
||||
export const MariaDBCharsets = ["utf8mb4"] as const;
|
||||
|
||||
export type MariaDbCharsetType = (typeof MariaDBCharsets)[number];
|
||||
|
||||
/**
|
||||
* Describes a single table within a database schema, including its fields,
|
||||
* indexes, unique constraints, and parent/child table relationships.
|
||||
@ -1498,11 +1494,6 @@ export type SQLInsertGenParams = {
|
||||
*/
|
||||
export type BunMariaDBConfig = {
|
||||
db_name: string;
|
||||
/**
|
||||
* The Name of the Database Schema File. Eg `db_schema.ts`. This is
|
||||
* relative to `db_dir`, or root dir if `db_dir` is not provided
|
||||
*/
|
||||
db_schema_file_name: string;
|
||||
/**
|
||||
* The Directory for backups. Relative to db_dir.
|
||||
*/
|
||||
@ -1518,9 +1509,17 @@ export type BunMariaDBConfig = {
|
||||
*/
|
||||
typedef_file_path?: string;
|
||||
/**
|
||||
* Whether to enable `WAL` mode
|
||||
* Configuration for the MariaDB Connection
|
||||
*/
|
||||
wal_mode?: boolean;
|
||||
db_config?: ConnectionConfig;
|
||||
/**
|
||||
* Database charset. Defaults to `utf8mb4`
|
||||
*/
|
||||
charset?: MariaDbCharsetType;
|
||||
/**
|
||||
* Database Connection timeout. Defaults to `10000`
|
||||
*/
|
||||
connection_timeout?: number;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -1589,3 +1588,11 @@ export type DBResponseObject<
|
||||
payload?: T[];
|
||||
single_res?: T;
|
||||
};
|
||||
|
||||
export const RequiredENVs = [
|
||||
"BUN_MARIADB_SERVER_HOST",
|
||||
"BUN_MARIADB_SERVER_PORT",
|
||||
"BUN_MARIADB_SERVER_USERNAME",
|
||||
"BUN_MARIADB_SERVER_PASSWORD",
|
||||
"BUN_MARIADB_SERVER_SSL_KEY_PATH",
|
||||
] as const;
|
||||
|
||||
@ -4,6 +4,10 @@ import type {
|
||||
SQLInsertGenValueType,
|
||||
} from "../types";
|
||||
|
||||
function quoteIdentifier(identifier: string): string {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
/**
|
||||
* # SQL Insert Generator
|
||||
*/
|
||||
@ -12,7 +16,7 @@ export default function sqlInsertGenerator({
|
||||
data,
|
||||
dbFullName,
|
||||
}: SQLInsertGenParams): SQLInsertGenReturn | undefined {
|
||||
const finalDbName = dbFullName ? `${dbFullName}.` : "";
|
||||
const finalDbName = dbFullName ? `${quoteIdentifier(dbFullName)}.` : "";
|
||||
|
||||
try {
|
||||
if (Array.isArray(data) && data?.[0]) {
|
||||
@ -64,9 +68,9 @@ export default function sqlInsertGenerator({
|
||||
.join(",")})`,
|
||||
);
|
||||
});
|
||||
let query = `INSERT INTO ${finalDbName}${tableName} (${insertKeys.join(
|
||||
",",
|
||||
)}) VALUES ${queryBatches.join(",")}`;
|
||||
|
||||
const insertColumns = insertKeys.map(quoteIdentifier).join(",");
|
||||
let query = `INSERT INTO ${finalDbName}${quoteIdentifier(tableName)} (${insertColumns}) VALUES ${queryBatches.join(",")}`;
|
||||
|
||||
return {
|
||||
query: query,
|
||||
|
||||
Loading…
Reference in New Issue
Block a user