Refactor DB Handler. Use Bun native SQL adapter.
This commit is contained in:
@@ -13,18 +13,6 @@ declare global {
|
||||
var DB_SCHEMA: BUN_MARIADB_DatabaseSchemaType;
|
||||
}
|
||||
|
||||
await init();
|
||||
|
||||
if (!global.CONFIG) {
|
||||
console.error(`Couldn't grab global Config.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!global.DB_SCHEMA) {
|
||||
console.error(`Couldn't grab Database Schema.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const BunMariaDB = {
|
||||
select: DbSelect,
|
||||
insert: DbInsert,
|
||||
|
||||
+28
-33
@@ -1,11 +1,9 @@
|
||||
import type { Connection, ConnectionConfig } from "mariadb";
|
||||
import type { BUN_MARIADB_TableSchemaType, DBResponseObject } from "../types";
|
||||
import grabDBConnection from "./grab-db-connection";
|
||||
import type { DBInsertReturn, DBResponseObject } from "../types";
|
||||
import MariaDBClient from "./mariadb";
|
||||
|
||||
type Param = {
|
||||
query: string;
|
||||
values?: string[] | object;
|
||||
config?: ConnectionConfig;
|
||||
values?: any[];
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -14,43 +12,40 @@ type Param = {
|
||||
*/
|
||||
export default async function dbHandler<
|
||||
T extends { [k: string]: any } = { [k: string]: any },
|
||||
>({ query, values, config }: Param): Promise<DBResponseObject> {
|
||||
let CONNECTION: Connection | undefined;
|
||||
let results: T | null = null;
|
||||
|
||||
>({ query, values }: Param): Promise<DBResponseObject> {
|
||||
try {
|
||||
CONNECTION = await grabDBConnection({ config });
|
||||
const res = await MariaDBClient.unsafe(query, values);
|
||||
|
||||
if (query && values) {
|
||||
const queryResults = await CONNECTION.query(query, values);
|
||||
results = queryResults[0];
|
||||
} else {
|
||||
const queryResults = await CONNECTION.query(query);
|
||||
results = queryResults[0];
|
||||
}
|
||||
} catch (error: any) {
|
||||
if (
|
||||
error.message &&
|
||||
typeof error.message == "string" &&
|
||||
error.message.match(/Access denied for user.*password/i)
|
||||
) {
|
||||
throw new Error("Authentication Failed!");
|
||||
}
|
||||
const count = res.count;
|
||||
const last_insert_id = res.lastInsertRowid;
|
||||
const affected_rows = res.affectedRows;
|
||||
|
||||
results = null;
|
||||
} finally {
|
||||
await CONNECTION?.end();
|
||||
}
|
||||
const res_array = (() => {
|
||||
try {
|
||||
return JSON.parse(JSON.stringify(res)) as T[];
|
||||
} catch (error) {
|
||||
return undefined;
|
||||
}
|
||||
})();
|
||||
|
||||
const insert_return: DBInsertReturn = {
|
||||
count,
|
||||
last_insert_id,
|
||||
affected_rows,
|
||||
};
|
||||
|
||||
if (results) {
|
||||
return {
|
||||
success: true,
|
||||
payload: Array.isArray(results) ? results : undefined,
|
||||
single_res: Array.isArray(results) ? undefined : results,
|
||||
payload: res_array,
|
||||
single_res: res_array?.[0],
|
||||
insert_return,
|
||||
};
|
||||
} else {
|
||||
} catch (error: any) {
|
||||
return {
|
||||
success: false,
|
||||
error: "DB Handler Error => " + error.message,
|
||||
msg: "DB Handler Error => " + error.message,
|
||||
};
|
||||
} finally {
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import * as mariadb from "mariadb";
|
||||
import type { Connection } from "mariadb";
|
||||
import grabDSQLConnectionConfig from "./grab-dsql-connection-config";
|
||||
import type { DsqlConnectionParam } from "../types";
|
||||
import grabDbSSL from "./grab-db-ssl";
|
||||
|
||||
/**
|
||||
* # Grab General CONNECTION for DSQL
|
||||
@@ -9,7 +9,29 @@ import type { DsqlConnectionParam } from "../types";
|
||||
export default async function grabDBConnection(
|
||||
param?: DsqlConnectionParam,
|
||||
): Promise<Connection> {
|
||||
const config = grabDSQLConnectionConfig(param);
|
||||
const configData = global.CONFIG;
|
||||
const CONN_TIMEOUT = configData?.connection_timeout || 10000;
|
||||
|
||||
const config: mariadb.ConnectionConfig = {
|
||||
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: configData?.charset || "utf8mb4",
|
||||
ssl: grabDbSSL(),
|
||||
bigIntAsNumber: true,
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
dateStrings: true,
|
||||
metaAsArray: true,
|
||||
socketTimeout: CONN_TIMEOUT,
|
||||
connectTimeout: CONN_TIMEOUT,
|
||||
compress: true,
|
||||
...param?.config,
|
||||
};
|
||||
|
||||
try {
|
||||
return await mariadb.createConnection(config);
|
||||
|
||||
+10
-9
@@ -2,27 +2,28 @@ import fs from "fs";
|
||||
import type { ConnectionConfig } from "mariadb";
|
||||
import path from "path";
|
||||
|
||||
type Return = ConnectionConfig["ssl"] | undefined;
|
||||
|
||||
/**
|
||||
* # Grab SSL
|
||||
*/
|
||||
export default function grabDbSSL(): Return {
|
||||
const caProivdedPath = process.env.BUN_MARIADB_SERVER_SSL_KEY_PATH;
|
||||
export default function grabDbSSL(): ConnectionConfig["ssl"] {
|
||||
const caProivdedPath =
|
||||
global.CONFIG.ssl_ca || process.env.BUN_MARIADB_SERVER_SSL_KEY_PATH;
|
||||
|
||||
if (!caProivdedPath?.match(/./)) {
|
||||
return undefined;
|
||||
return {
|
||||
rejectUnauthorized: false,
|
||||
};
|
||||
}
|
||||
|
||||
const caFilePath = path.resolve(process.cwd(), caProivdedPath);
|
||||
const ca_file_path = path.resolve(process.cwd(), caProivdedPath);
|
||||
|
||||
if (!fs.existsSync(caFilePath)) {
|
||||
console.log(`${caFilePath} does not exist`);
|
||||
if (!fs.existsSync(ca_file_path)) {
|
||||
console.log(`${ca_file_path} does not exist`);
|
||||
return undefined;
|
||||
}
|
||||
|
||||
return {
|
||||
ca: fs.readFileSync(caFilePath),
|
||||
ca: fs.readFileSync(ca_file_path),
|
||||
rejectUnauthorized: false,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
import { type ConnectionConfig } from "mariadb";
|
||||
import type { DsqlConnectionParam } from "../types";
|
||||
import grabDbSSL from "./grab-db-ssl";
|
||||
|
||||
/**
|
||||
* # Grab General CONNECTION for DSQL
|
||||
*/
|
||||
export default function grabDSQLConnectionConfig(
|
||||
param?: DsqlConnectionParam,
|
||||
): ConnectionConfig {
|
||||
const configData = global.CONFIG;
|
||||
const CONN_TIMEOUT = configData?.connection_timeout || 10000;
|
||||
|
||||
const config: ConnectionConfig = {
|
||||
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: configData?.charset || "utf8mb4",
|
||||
ssl: grabDbSSL(),
|
||||
bigIntAsNumber: true,
|
||||
supportBigNumbers: true,
|
||||
bigNumberStrings: false,
|
||||
dateStrings: true,
|
||||
metaAsArray: true,
|
||||
socketTimeout: CONN_TIMEOUT,
|
||||
connectTimeout: CONN_TIMEOUT,
|
||||
compress: true,
|
||||
...param?.config,
|
||||
};
|
||||
|
||||
return config;
|
||||
}
|
||||
@@ -28,8 +28,9 @@ export default async function ({ sql: passedSql, table, data }: Params) {
|
||||
}
|
||||
|
||||
const hasUniqueField =
|
||||
tableSchema.fields.some((field) => field.unique) ||
|
||||
Boolean(tableSchema.uniqueConstraints?.length);
|
||||
tableSchema.fields.some(
|
||||
(field: { unique?: boolean }) => field.unique,
|
||||
) || Boolean(tableSchema.uniqueConstraints?.length);
|
||||
|
||||
if (!hasUniqueField) {
|
||||
return passedSql;
|
||||
|
||||
@@ -27,18 +27,10 @@ type IndexInfoRow = {
|
||||
|
||||
class MariaDBSchemaManager {
|
||||
private db_manager_table_name: string;
|
||||
private recreate_vector_table: boolean;
|
||||
private db_schema: BUN_MARIADB_DatabaseSchemaType;
|
||||
|
||||
constructor({
|
||||
schema,
|
||||
recreate_vector_table = false,
|
||||
}: {
|
||||
schema: BUN_MARIADB_DatabaseSchemaType;
|
||||
recreate_vector_table?: boolean;
|
||||
}) {
|
||||
constructor({ schema }: { schema: BUN_MARIADB_DatabaseSchemaType }) {
|
||||
this.db_manager_table_name = AppData["DbSchemaManagerTableName"];
|
||||
this.recreate_vector_table = recreate_vector_table;
|
||||
this.db_schema = schema;
|
||||
}
|
||||
|
||||
@@ -63,32 +55,27 @@ class MariaDBSchemaManager {
|
||||
return `\`${identifier.replace(/`/g, "``")}\``;
|
||||
}
|
||||
|
||||
private tableSchemaWhere(tableName: string): {
|
||||
where: string;
|
||||
values: QueryValues;
|
||||
} {
|
||||
private schemaCondition(): { where: string; values: string[] } {
|
||||
const config = global.CONFIG;
|
||||
const databaseName = config?.db_name;
|
||||
|
||||
if (databaseName) {
|
||||
return {
|
||||
where: "TABLE_SCHEMA = ?",
|
||||
values: [databaseName, tableName],
|
||||
values: [databaseName],
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
where: "TABLE_SCHEMA = DATABASE()",
|
||||
values: [tableName],
|
||||
values: [],
|
||||
};
|
||||
}
|
||||
|
||||
private async run(query: string, values?: QueryValues): Promise<void> {
|
||||
const config = global.CONFIG;
|
||||
const res = await dbHandler({
|
||||
query,
|
||||
values: values as any,
|
||||
config: config?.db_config,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
@@ -103,10 +90,12 @@ class MariaDBSchemaManager {
|
||||
const res = await dbHandler<T>({
|
||||
query,
|
||||
values: values as any,
|
||||
config: global.CONFIG.db_config,
|
||||
});
|
||||
|
||||
if (!res.success) {
|
||||
console.log("res", res);
|
||||
console.log("query", query);
|
||||
console.log("values", values);
|
||||
throw new Error(`Database query failed: ${query}`);
|
||||
}
|
||||
|
||||
@@ -148,10 +137,10 @@ class MariaDBSchemaManager {
|
||||
}
|
||||
|
||||
private async getLiveTableNames(): Promise<string[]> {
|
||||
const tableSchemaWhere = this.tableSchemaWhere("");
|
||||
const schemaCond = this.schemaCondition();
|
||||
const rows = await this.query<{ TABLE_NAME: string }>(
|
||||
`SELECT TABLE_NAME FROM information_schema.TABLES WHERE ${tableSchemaWhere.where} AND TABLE_TYPE = 'BASE TABLE'`,
|
||||
tableSchemaWhere.values,
|
||||
`SELECT TABLE_NAME FROM information_schema.TABLES WHERE ${schemaCond.where} AND TABLE_TYPE = 'BASE TABLE'`,
|
||||
schemaCond.values,
|
||||
);
|
||||
|
||||
return rows.map((row) => row.TABLE_NAME);
|
||||
@@ -163,7 +152,7 @@ class MariaDBSchemaManager {
|
||||
): Promise<void> {
|
||||
console.log(`Cleaning up tables ...`);
|
||||
|
||||
const tablesToDrop = existingTables.filter(
|
||||
const tablesToDrop: string[] = existingTables.filter(
|
||||
(tableName) =>
|
||||
!schemaTables.includes(tableName) &&
|
||||
!schemaTables.some((schemaTable) =>
|
||||
@@ -187,7 +176,9 @@ class MariaDBSchemaManager {
|
||||
}
|
||||
}
|
||||
|
||||
for (const tableName of tablesToDrop) {
|
||||
const uniqueTablesToDrop = _.uniq(tablesToDrop);
|
||||
|
||||
for (const tableName of uniqueTablesToDrop) {
|
||||
console.log(`Dropping table: ${tableName}`);
|
||||
await this.run(
|
||||
`DROP TABLE IF EXISTS ${this.quoteIdentifier(tableName)}`,
|
||||
@@ -202,6 +193,7 @@ class MariaDBSchemaManager {
|
||||
): Promise<void> {
|
||||
let tableExists = existingTables.includes(table.tableName);
|
||||
const liveTables = await this.getLiveTableNames();
|
||||
let wasRenamed = false;
|
||||
|
||||
if (table.tableNameOld && table.tableNameOld !== table.tableName) {
|
||||
if (liveTables.includes(table.tableNameOld)) {
|
||||
@@ -214,13 +206,14 @@ class MariaDBSchemaManager {
|
||||
await this.insertDbManagerTable(table.tableName);
|
||||
await this.removeDbManagerTable(table.tableNameOld);
|
||||
tableExists = true;
|
||||
wasRenamed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!tableExists) {
|
||||
await this.createTable(table);
|
||||
await this.insertDbManagerTable(table.tableName);
|
||||
} else {
|
||||
} else if (!wasRenamed) {
|
||||
await this.updateTable(table);
|
||||
await this.insertDbManagerTable(table.tableName);
|
||||
}
|
||||
@@ -228,7 +221,9 @@ class MariaDBSchemaManager {
|
||||
await this.syncIndexes(table);
|
||||
}
|
||||
|
||||
private resolveTable(table: BUN_MARIADB_TableSchemaType) {
|
||||
private resolveTable(
|
||||
table: BUN_MARIADB_TableSchemaType,
|
||||
): BUN_MARIADB_TableSchemaType {
|
||||
if (!table.parentTableName) {
|
||||
return _.cloneDeep(table);
|
||||
}
|
||||
@@ -243,11 +238,24 @@ class MariaDBSchemaManager {
|
||||
);
|
||||
}
|
||||
|
||||
const mergedFieldsMap = new Map<string, BUN_MARIADB_FieldSchemaType>();
|
||||
|
||||
(parentTable.fields || []).forEach((f) => {
|
||||
if (f.fieldName) mergedFieldsMap.set(f.fieldName, f);
|
||||
});
|
||||
|
||||
(table.fields || []).forEach((f) => {
|
||||
if (f.fieldName) {
|
||||
const existing = mergedFieldsMap.get(f.fieldName) || {};
|
||||
mergedFieldsMap.set(f.fieldName, { ...existing, ...f });
|
||||
}
|
||||
});
|
||||
|
||||
return _.merge({}, parentTable, {
|
||||
tableName: table.tableName,
|
||||
tableDescription: table.tableDescription,
|
||||
collation: table.collation,
|
||||
fields: [...(parentTable.fields || []), ...(table.fields || [])],
|
||||
fields: Array.from(mergedFieldsMap.values()),
|
||||
indexes: [...(parentTable.indexes || []), ...(table.indexes || [])],
|
||||
uniqueConstraints: [
|
||||
...(parentTable.uniqueConstraints || []),
|
||||
@@ -317,17 +325,49 @@ class MariaDBSchemaManager {
|
||||
table: BUN_MARIADB_TableSchemaType,
|
||||
): Promise<void> {
|
||||
console.log(`Updating table: ${table.tableName}`);
|
||||
await this.recreateTable(table);
|
||||
|
||||
const resolvedTable = this.resolveTable(table);
|
||||
const existingColumns = await this.getTableColumns(
|
||||
resolvedTable.tableName,
|
||||
);
|
||||
|
||||
const missingFields = (resolvedTable.fields || []).filter(
|
||||
(field) =>
|
||||
!existingColumns.some((col) => col.name === field.fieldName),
|
||||
);
|
||||
|
||||
const hasModifiedFields = (resolvedTable.fields || []).some((field) => {
|
||||
const current = existingColumns.find(
|
||||
(col) => col.name === field.fieldName,
|
||||
);
|
||||
if (!current) return false;
|
||||
|
||||
const resolvedType = this.mapDataType(field).toLowerCase();
|
||||
return !resolvedType.startsWith(current.type.toLowerCase());
|
||||
});
|
||||
|
||||
if (
|
||||
missingFields.length > 0 &&
|
||||
!hasModifiedFields &&
|
||||
!resolvedTable.isVector
|
||||
) {
|
||||
for (const field of missingFields) {
|
||||
await this.addColumn(resolvedTable.tableName, field);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
await this.recreateTable(resolvedTable);
|
||||
}
|
||||
|
||||
private async getTableColumns(tableName: string): Promise<ColumnInfoRow[]> {
|
||||
const tableSchemaWhere = this.tableSchemaWhere(tableName);
|
||||
const schemaCond = this.schemaCondition();
|
||||
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,
|
||||
`SELECT COLUMN_NAME, COLUMN_TYPE FROM information_schema.COLUMNS WHERE ${schemaCond.where} AND TABLE_NAME = ? ORDER BY ORDINAL_POSITION`,
|
||||
[...schemaCond.values, tableName],
|
||||
);
|
||||
|
||||
return rows.map((row) => ({
|
||||
@@ -352,24 +392,18 @@ class MariaDBSchemaManager {
|
||||
`ALTER TABLE ${this.quoteIdentifier(tableName)} ADD COLUMN ${columnDef}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async checkIfTableExists(table: string): Promise<boolean> {
|
||||
const tableSchemaWhere = this.tableSchemaWhere(table);
|
||||
const row = await this.query<{ exists: number }>(
|
||||
`SELECT 1 AS exists FROM information_schema.TABLES WHERE ${tableSchemaWhere.where} AND TABLE_NAME = ? LIMIT 1`,
|
||||
tableSchemaWhere.values,
|
||||
const schemaCond = this.schemaCondition();
|
||||
const row = await this.query<{ table_exists: number }>(
|
||||
`SELECT 1 AS \`table_exists\` FROM information_schema.TABLES WHERE ${schemaCond.where} AND TABLE_NAME = ? LIMIT 1`,
|
||||
[...schemaCond.values, table],
|
||||
);
|
||||
|
||||
return Boolean(row[0]?.exists);
|
||||
return Boolean(row[0]?.table_exists);
|
||||
}
|
||||
|
||||
private async recreateTable(
|
||||
table: BUN_MARIADB_TableSchemaType,
|
||||
): Promise<void> {
|
||||
if (table.isVector && !this.recreate_vector_table) {
|
||||
return;
|
||||
}
|
||||
|
||||
const doesTableExist = await this.checkIfTableExists(table.tableName);
|
||||
|
||||
if (table.isVector) {
|
||||
@@ -394,8 +428,10 @@ class MariaDBSchemaManager {
|
||||
}
|
||||
|
||||
const tempTableName = `${table.tableName}_temp_${Date.now()}`;
|
||||
const backupOldTableName = `${table.tableName}_old_${Date.now()}`;
|
||||
const existingColumns = await this.getTableColumns(table.tableName);
|
||||
const columnsToKeep = table.fields
|
||||
|
||||
const columnsToKeep = (table.fields || [])
|
||||
.filter((field) =>
|
||||
existingColumns.some(
|
||||
(column) => column.name === field.fieldName,
|
||||
@@ -416,10 +452,15 @@ class MariaDBSchemaManager {
|
||||
);
|
||||
}
|
||||
|
||||
await this.run(`DROP TABLE ${this.quoteIdentifier(table.tableName)}`);
|
||||
await this.run(
|
||||
`RENAME TABLE ${this.quoteIdentifier(table.tableName)} TO ${this.quoteIdentifier(backupOldTableName)}`,
|
||||
);
|
||||
await this.run(
|
||||
`RENAME TABLE ${this.quoteIdentifier(tempTableName)} TO ${this.quoteIdentifier(table.tableName)}`,
|
||||
);
|
||||
await this.run(
|
||||
`DROP TABLE ${this.quoteIdentifier(backupOldTableName)}`,
|
||||
);
|
||||
}
|
||||
|
||||
private async insertRows(
|
||||
@@ -495,95 +536,68 @@ class MariaDBSchemaManager {
|
||||
}
|
||||
|
||||
private mapDataType(field: BUN_MARIADB_FieldSchemaType): string {
|
||||
const dataType = field.dataType?.toLowerCase() || "text";
|
||||
const dataType = field.dataType?.toUpperCase() || "TEXT";
|
||||
const vectorSize = field.vectorSize || 1536;
|
||||
|
||||
if (field.isVector) {
|
||||
return `LONGTEXT COMMENT 'vector_size=${vectorSize}'`;
|
||||
}
|
||||
|
||||
if (dataType === "bigint") {
|
||||
if (field.integerLength) {
|
||||
return `BIGINT(${field.integerLength})`;
|
||||
}
|
||||
switch (dataType) {
|
||||
case "VARCHAR":
|
||||
return `VARCHAR(${field.integerLength || 255})`;
|
||||
|
||||
return "BIGINT";
|
||||
case "TEXT":
|
||||
return "TEXT";
|
||||
|
||||
case "LONGTEXT":
|
||||
return "LONGTEXT";
|
||||
|
||||
case "TINYINT":
|
||||
return field.integerLength
|
||||
? `TINYINT(${field.integerLength})`
|
||||
: "TINYINT";
|
||||
|
||||
case "INT":
|
||||
return field.integerLength
|
||||
? `INT(${field.integerLength})`
|
||||
: "INT";
|
||||
|
||||
case "BIGINT":
|
||||
return field.integerLength
|
||||
? `BIGINT(${field.integerLength})`
|
||||
: "BIGINT";
|
||||
|
||||
case "DECIMAL":
|
||||
if (field.integerLength && field.decimals) {
|
||||
return `DECIMAL(${field.integerLength}, ${field.decimals})`;
|
||||
}
|
||||
return "DECIMAL(10,2)";
|
||||
|
||||
case "DOUBLE":
|
||||
return "DOUBLE";
|
||||
|
||||
case "BLOB":
|
||||
return "BLOB";
|
||||
|
||||
case "LONGBLOB":
|
||||
return "LONGBLOB";
|
||||
|
||||
case "BOOLEAN":
|
||||
return "TINYINT(1)";
|
||||
|
||||
case "DATETIME":
|
||||
return "DATETIME";
|
||||
|
||||
case "TIMESTAMP":
|
||||
return "TIMESTAMP";
|
||||
|
||||
case "DATE":
|
||||
return "DATE";
|
||||
|
||||
default:
|
||||
return "TEXT";
|
||||
}
|
||||
|
||||
if (dataType === "smallint") {
|
||||
if (field.integerLength) {
|
||||
return `SMALLINT(${field.integerLength})`;
|
||||
}
|
||||
|
||||
return "SMALLINT";
|
||||
}
|
||||
|
||||
if (dataType === "tinyint") {
|
||||
if (field.integerLength) {
|
||||
return `TINYINT(${field.integerLength})`;
|
||||
}
|
||||
|
||||
return "TINYINT";
|
||||
}
|
||||
|
||||
if (
|
||||
dataType.includes("int") ||
|
||||
dataType === "bigint" ||
|
||||
dataType === "smallint" ||
|
||||
dataType === "tinyint"
|
||||
) {
|
||||
if (field.integerLength) {
|
||||
return `INT(${field.integerLength})`;
|
||||
}
|
||||
|
||||
return "INT";
|
||||
}
|
||||
|
||||
if (dataType.includes("double")) {
|
||||
return "DOUBLE";
|
||||
}
|
||||
|
||||
if (dataType.includes("float")) {
|
||||
return "FLOAT";
|
||||
}
|
||||
|
||||
if (dataType.includes("decimal") || dataType.includes("numeric")) {
|
||||
if (field.integerLength && field.decimals) {
|
||||
return `DECIMAL(${field.integerLength}, ${field.decimals})`;
|
||||
}
|
||||
|
||||
return "DECIMAL";
|
||||
}
|
||||
|
||||
if (dataType.includes("blob") || dataType.includes("binary")) {
|
||||
return "BLOB";
|
||||
}
|
||||
|
||||
if (dataType === "boolean" || dataType === "bool") {
|
||||
return "TINYINT(1)";
|
||||
}
|
||||
|
||||
if (dataType.includes("timestamp")) {
|
||||
return "TIMESTAMP";
|
||||
}
|
||||
|
||||
if (dataType.includes("datetime")) {
|
||||
return "DATETIME";
|
||||
}
|
||||
|
||||
if (dataType.includes("date") || dataType.includes("time")) {
|
||||
return "DATETIME";
|
||||
}
|
||||
|
||||
if (dataType.includes("varchar")) {
|
||||
if (field.integerLength) {
|
||||
return `VARCHAR(${field.integerLength})`;
|
||||
}
|
||||
|
||||
return "VARCHAR(255)";
|
||||
}
|
||||
|
||||
return "TEXT";
|
||||
}
|
||||
|
||||
private buildForeignKeyConstraint(
|
||||
@@ -614,10 +628,10 @@ class MariaDBSchemaManager {
|
||||
return;
|
||||
}
|
||||
|
||||
const tableSchemaWhere = this.tableSchemaWhere(table.tableName);
|
||||
const schemaCond = this.schemaCondition();
|
||||
const rows = await this.query<IndexInfoRow>(
|
||||
`SELECT INDEX_NAME AS name FROM information_schema.STATISTICS WHERE ${tableSchemaWhere.where} AND TABLE_NAME = ? AND INDEX_NAME <> 'PRIMARY' GROUP BY INDEX_NAME ORDER BY INDEX_NAME`,
|
||||
tableSchemaWhere.values,
|
||||
`SELECT INDEX_NAME AS name FROM information_schema.STATISTICS WHERE ${schemaCond.where} AND TABLE_NAME = ? AND INDEX_NAME <> 'PRIMARY' GROUP BY INDEX_NAME ORDER BY INDEX_NAME`,
|
||||
[...schemaCond.values, table.tableName],
|
||||
);
|
||||
const existingIndexes = rows.map((row) => row.name);
|
||||
|
||||
@@ -645,13 +659,27 @@ class MariaDBSchemaManager {
|
||||
|
||||
if (!existingIndexes.includes(index.indexName)) {
|
||||
console.log(`Creating index: ${index.indexName}`);
|
||||
|
||||
const fields = index.indexTableFields
|
||||
.map((field) => this.quoteIdentifier(field))
|
||||
.join(", ");
|
||||
|
||||
await this.run(
|
||||
`CREATE INDEX ${this.quoteIdentifier(index.indexName)} ON ${this.quoteIdentifier(table.tableName)} (${fields})`,
|
||||
);
|
||||
// Determine if we need a specialized modifier prefix like FULLTEXT or SPATIAL
|
||||
const typeUpper = index.indexType?.toUpperCase();
|
||||
const isSpecialType =
|
||||
typeUpper === "FULLTEXT" || typeUpper === "SPATIAL";
|
||||
const indexPrefix = isSpecialType ? `${typeUpper} ` : "";
|
||||
|
||||
// Append USING BTREE/HASH if it's a normal index type option
|
||||
const indexSuffix =
|
||||
!isSpecialType &&
|
||||
(typeUpper === "BTREE" || typeUpper === "HASH")
|
||||
? ` USING ${typeUpper}`
|
||||
: "";
|
||||
|
||||
const sql = `CREATE ${indexPrefix}INDEX ${this.quoteIdentifier(index.indexName)} ON ${this.quoteIdentifier(table.tableName)} (${fields})${indexSuffix}`;
|
||||
|
||||
await this.run(sql);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
import { SQL } from "bun";
|
||||
import init from "../../functions/init";
|
||||
import grabDirNames from "../../data/grab-dir-names";
|
||||
import path from "path";
|
||||
|
||||
await init();
|
||||
|
||||
if (!global.CONFIG) {
|
||||
console.error(`Couldn't grab global Config.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
if (!global.DB_SCHEMA) {
|
||||
console.error(`Couldn't grab Database Schema.`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const config = global.CONFIG;
|
||||
|
||||
const { ROOT_DIR } = grabDirNames();
|
||||
|
||||
const MariaDBClient = new SQL({
|
||||
hostname: process.env.BUN_MARIADB_SERVER_HOST,
|
||||
username: process.env.BUN_MARIADB_SERVER_USERNAME,
|
||||
password: process.env.BUN_MARIADB_SERVER_PASSWORD,
|
||||
database: config.db_name,
|
||||
port: process.env.BUN_MARIADB_SERVER_PORT
|
||||
? Number(process.env.BUN_MARIADB_SERVER_PORT)
|
||||
: undefined,
|
||||
...config.db_config,
|
||||
tls: config.ssl_ca
|
||||
? {
|
||||
ca: Bun.file(path.resolve(ROOT_DIR, config.ssl_ca)),
|
||||
rejectUnauthorized: false,
|
||||
}
|
||||
: {
|
||||
rejectUnauthorized: false,
|
||||
},
|
||||
adapter: "mariadb",
|
||||
});
|
||||
|
||||
const test = await MariaDBClient.unsafe(`SHOW DATABASES`);
|
||||
|
||||
if (!test.count) {
|
||||
console.error(`MariaDBClient Error: Database not ready.`);
|
||||
console.log(test);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
export default MariaDBClient;
|
||||
+51
-7
@@ -132,13 +132,23 @@ export const TextFieldTypesArray = [
|
||||
] as const;
|
||||
|
||||
/**
|
||||
* Core MariaDB column types supported by the schema builder.
|
||||
* Native MariaDB column types supported by the schema builder.
|
||||
*/
|
||||
export const BUN_MARIADB_DATATYPES = [
|
||||
{ value: "VARCHAR" },
|
||||
{ value: "TEXT" },
|
||||
{ value: "INTEGER" },
|
||||
{ value: "LONGTEXT" },
|
||||
{ value: "TINYINT" },
|
||||
{ value: "INT" },
|
||||
{ value: "BIGINT" },
|
||||
{ value: "DECIMAL" },
|
||||
{ value: "DOUBLE" },
|
||||
{ value: "BLOB" },
|
||||
{ value: "REAL" },
|
||||
{ value: "LONGBLOB" },
|
||||
{ value: "BOOLEAN" },
|
||||
{ value: "DATETIME" },
|
||||
{ value: "TIMESTAMP" },
|
||||
{ value: "DATE" },
|
||||
] as const;
|
||||
|
||||
/**
|
||||
@@ -191,6 +201,15 @@ export interface BUN_MARIADB_ForeignKeyType {
|
||||
cascadeUpdate?: boolean;
|
||||
}
|
||||
|
||||
export const MariaDBIndexTypes = [
|
||||
"BTREE",
|
||||
"HASH",
|
||||
"FULLTEXT",
|
||||
"SPATIAL",
|
||||
] as const;
|
||||
|
||||
export type MariaDBIndexType = (typeof MariaDBIndexTypes)[number];
|
||||
|
||||
/**
|
||||
* Describes a table index and the fields it covers.
|
||||
*/
|
||||
@@ -200,7 +219,19 @@ export interface BUN_MARIADB_IndexSchemaType {
|
||||
* `idx_user_id_index`
|
||||
*/
|
||||
indexName?: string;
|
||||
/**
|
||||
* The columns included in the index.
|
||||
*/
|
||||
indexTableFields?: string[];
|
||||
/**
|
||||
* Under the hood index type (BTREE, HASH) or modifier (FULLTEXT, SPATIAL)
|
||||
*/
|
||||
indexType?: MariaDBIndexType;
|
||||
|
||||
/**
|
||||
* Optional documentation or tuning note inside the DB metadata
|
||||
*/
|
||||
comment?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -1511,7 +1542,7 @@ export type BunMariaDBConfig = {
|
||||
/**
|
||||
* Configuration for the MariaDB Connection
|
||||
*/
|
||||
db_config?: ConnectionConfig;
|
||||
db_config?: Bun.SQL.Options;
|
||||
/**
|
||||
* Database charset. Defaults to `utf8mb4`
|
||||
*/
|
||||
@@ -1520,6 +1551,10 @@ export type BunMariaDBConfig = {
|
||||
* Database Connection timeout. Defaults to `10000`
|
||||
*/
|
||||
connection_timeout?: number;
|
||||
/**
|
||||
* File path to the SSL certificate
|
||||
*/
|
||||
ssl_ca?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
@@ -1536,7 +1571,7 @@ export type BunMariaDBConfigReturn = {
|
||||
export const DefaultFields: BUN_MARIADB_FieldSchemaType[] = [
|
||||
{
|
||||
fieldName: "id",
|
||||
dataType: "INTEGER",
|
||||
dataType: "BIGINT",
|
||||
primaryKey: true,
|
||||
autoIncrement: true,
|
||||
notNullValue: true,
|
||||
@@ -1544,13 +1579,13 @@ export const DefaultFields: BUN_MARIADB_FieldSchemaType[] = [
|
||||
},
|
||||
{
|
||||
fieldName: "created_at",
|
||||
dataType: "INTEGER",
|
||||
dataType: "BIGINT",
|
||||
fieldDescription:
|
||||
"The time when the record was created. (Unix Timestamp)",
|
||||
},
|
||||
{
|
||||
fieldName: "updated_at",
|
||||
dataType: "INTEGER",
|
||||
dataType: "BIGINT",
|
||||
fieldDescription:
|
||||
"The time when the record was updated. (Unix Timestamp)",
|
||||
},
|
||||
@@ -1587,6 +1622,15 @@ export type DBResponseObject<
|
||||
success: boolean;
|
||||
payload?: T[];
|
||||
single_res?: T;
|
||||
insert_return?: DBInsertReturn;
|
||||
error?: any;
|
||||
msg?: string;
|
||||
};
|
||||
|
||||
export type DBInsertReturn = {
|
||||
count?: number;
|
||||
last_insert_id?: number;
|
||||
affected_rows?: number;
|
||||
};
|
||||
|
||||
export const RequiredENVs = [
|
||||
|
||||
Reference in New Issue
Block a user