Updates
This commit is contained in:
@@ -0,0 +1,46 @@
|
||||
import type { BUN_MARIADB_FieldSchemaType } from "../../types";
|
||||
import mapDataType from "./map-data-types";
|
||||
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
||||
|
||||
export default function buildColumnDefinition(
|
||||
field: BUN_MARIADB_FieldSchemaType,
|
||||
): string {
|
||||
if (!field.fieldName) {
|
||||
throw new Error("Field name is required");
|
||||
}
|
||||
|
||||
const parts: string[] = [MariaDBQuoteGen(field.fieldName)];
|
||||
parts.push(mapDataType(field));
|
||||
|
||||
if (field.autoIncrement) {
|
||||
parts.push("AUTO_INCREMENT");
|
||||
}
|
||||
|
||||
if (field.notNullValue || field.primaryKey || field.isVector) {
|
||||
if (!field.primaryKey) {
|
||||
parts.push("NOT NULL");
|
||||
}
|
||||
}
|
||||
|
||||
if (field.unique && !field.primaryKey) {
|
||||
parts.push("UNIQUE");
|
||||
}
|
||||
|
||||
if (field.defaultValue !== undefined) {
|
||||
if (typeof field.defaultValue === "string") {
|
||||
parts.push(`DEFAULT '${field.defaultValue.replace(/'/g, "''")}'`);
|
||||
} else {
|
||||
parts.push(`DEFAULT ${field.defaultValue}`);
|
||||
}
|
||||
} else if (field.defaultValueLiteral) {
|
||||
parts.push(`DEFAULT ${field.defaultValueLiteral}`);
|
||||
}
|
||||
|
||||
if (field.onUpdate) {
|
||||
parts.push(`ON UPDATE ${field.onUpdate}`);
|
||||
} else if (field.onUpdateLiteral) {
|
||||
parts.push(`ON UPDATE ${field.onUpdateLiteral}`);
|
||||
}
|
||||
|
||||
return parts.join(" ");
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
import { AppData } from "../../data/app-data";
|
||||
import type { CreateDBSchemaParams } from "../../types";
|
||||
import dbHandler from "../db-handler";
|
||||
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
||||
|
||||
export default async function createDBManagerTable({
|
||||
db_schema,
|
||||
config,
|
||||
}: CreateDBSchemaParams) {
|
||||
let sql = ``;
|
||||
|
||||
sql += `CREATE TABLE IF NOT EXISTS ${MariaDBQuoteGen(AppData["DbSchemaManagerTableName"])} (\n`;
|
||||
sql += ` table_name VARCHAR(255) NOT NULL PRIMARY KEY,\n`;
|
||||
sql += ` created_at BIGINT NOT NULL,\n`;
|
||||
sql += ` updated_at BIGINT NOT NULL\n`;
|
||||
sql += `) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci\n`;
|
||||
|
||||
await dbHandler({
|
||||
query: sql,
|
||||
config,
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
import type { CreateDBSchemaParams } from "../../types";
|
||||
import createDBManagerTable from "./create-db-manager-table";
|
||||
import handleDBSchemaTables from "./handle-db-schema-tables";
|
||||
import orderDBSchema from "./order-db-schema";
|
||||
|
||||
export default async function createDBSchema(params: CreateDBSchemaParams) {
|
||||
/**
|
||||
* Create Schema Manager Table
|
||||
*/
|
||||
await createDBManagerTable(params);
|
||||
|
||||
/**
|
||||
* Reorder Tables
|
||||
*/
|
||||
const ordered_db_schema = await orderDBSchema(params);
|
||||
|
||||
/**
|
||||
* Handle Tables
|
||||
*/
|
||||
await handleDBSchemaTables({ ...params, db_schema: ordered_db_schema });
|
||||
|
||||
process.exit();
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import { AppData } from "../../data/app-data";
|
||||
import type {
|
||||
BUN_MARIADB_DB_TABLE_MANAGER_TABLE,
|
||||
CreateDBSchemaParams,
|
||||
} from "../../types";
|
||||
import dbHandler from "../db-handler";
|
||||
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
||||
|
||||
export default async function getExistingTablesFromTablesManagerTable({
|
||||
config,
|
||||
}: CreateDBSchemaParams) {
|
||||
const rows = await dbHandler<BUN_MARIADB_DB_TABLE_MANAGER_TABLE>({
|
||||
query: `SELECT table_name FROM ${MariaDBQuoteGen(AppData["DbSchemaManagerTableName"])}`,
|
||||
config,
|
||||
});
|
||||
|
||||
return rows.payload?.map((row) => row.table_name) || [];
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
import { AppData } from "../../data/app-data";
|
||||
import type {
|
||||
BUN_MARIADB_DB_TABLE_MANAGER_TABLE,
|
||||
BUN_MARIADB_INFORMATION_SCHEMA_TABLES,
|
||||
CreateDBSchemaTableHandlerParams,
|
||||
} from "../../types";
|
||||
import dbHandler from "../db-handler";
|
||||
import DbInsert from "../mariadb/db-insert";
|
||||
import buildColumnDefinition from "./build-column-definition";
|
||||
import MariaDBQuoteGen from "./mariadb-quote-gen";
|
||||
|
||||
export default async function handleDBSchemaTable({
|
||||
db_schema,
|
||||
config,
|
||||
table,
|
||||
db_manager_table_name,
|
||||
existing_live_table,
|
||||
}: CreateDBSchemaTableHandlerParams) {
|
||||
if (!db_manager_table_name || !existing_live_table?.TABLE_NAME) {
|
||||
const insert_table = await DbInsert<BUN_MARIADB_DB_TABLE_MANAGER_TABLE>(
|
||||
{
|
||||
data: [
|
||||
{
|
||||
table_name: table.tableName,
|
||||
},
|
||||
],
|
||||
table: AppData["DbSchemaManagerTableName"],
|
||||
config,
|
||||
},
|
||||
);
|
||||
|
||||
let sql = ``;
|
||||
|
||||
sql += `CREATE TABLE IF NOT EXISTS ${MariaDBQuoteGen(table.tableName)}`;
|
||||
sql += ` (`;
|
||||
|
||||
for (let i = 0; i < table.fields.length; i++) {
|
||||
const field = table.fields[i];
|
||||
if (!field) continue;
|
||||
const col_def = buildColumnDefinition(field);
|
||||
sql += ` ${col_def}`;
|
||||
}
|
||||
|
||||
sql += ` )`;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
import { AppData } from "../../data/app-data";
|
||||
import type {
|
||||
BUN_MARIADB_INFORMATION_SCHEMA_TABLES,
|
||||
CreateDBSchemaParams,
|
||||
} from "../../types";
|
||||
import dbHandler from "../db-handler";
|
||||
import getExistingTablesFromTablesManagerTable from "./get-existing-tables-from-tables-manager-table";
|
||||
import handleDBSchemaTable from "./handle-db-schema-table";
|
||||
|
||||
export default async function handleDBSchemaTables(
|
||||
params: CreateDBSchemaParams,
|
||||
) {
|
||||
const { db_schema, config } = params;
|
||||
|
||||
/**
|
||||
* Grab Tables that have been recorded in the Schema
|
||||
* Manager Table
|
||||
*/
|
||||
const existing_schema_tables =
|
||||
await getExistingTablesFromTablesManagerTable(params);
|
||||
|
||||
const existing_live_tables =
|
||||
await dbHandler<BUN_MARIADB_INFORMATION_SCHEMA_TABLES>({
|
||||
query: `SELECT TABLE_NAME FROM information_schema.TABLES WHERE TABLE_SCHEMA = DATABASE() AND TABLE_TYPE = 'BASE TABLE' AND TABLE_NAME != ?`,
|
||||
config: params.config,
|
||||
values: [AppData["DbSchemaManagerTableName"]],
|
||||
});
|
||||
|
||||
for (let i = 0; i < db_schema.tables.length; i++) {
|
||||
const table = db_schema.tables[i];
|
||||
|
||||
const existing_table = existing_schema_tables.find(
|
||||
(t) => t == table?.tableName,
|
||||
);
|
||||
|
||||
const existing_live_table = existing_live_tables.payload?.find(
|
||||
(t) => t.TABLE_NAME == table?.tableName,
|
||||
);
|
||||
|
||||
if (!table) {
|
||||
if (existing_live_table?.TABLE_NAME) {
|
||||
await dbHandler<BUN_MARIADB_INFORMATION_SCHEMA_TABLES>({
|
||||
query: `DROP TABLE ${existing_live_table.TABLE_NAME}`,
|
||||
config: params.config,
|
||||
});
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
await handleDBSchemaTable({
|
||||
...params,
|
||||
table,
|
||||
db_manager_table_name: existing_table,
|
||||
existing_live_table,
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
import type { BUN_MARIADB_FieldSchemaType } from "../../types";
|
||||
|
||||
export default function mapDataType(
|
||||
field: BUN_MARIADB_FieldSchemaType,
|
||||
): string {
|
||||
const dataType = field.dataType?.toUpperCase() || "TEXT";
|
||||
const vectorSize = field.vectorSize || 1536;
|
||||
|
||||
if (field.isVector) {
|
||||
return `LONGTEXT COMMENT 'vector_size=${vectorSize}'`;
|
||||
}
|
||||
|
||||
switch (dataType) {
|
||||
case "CHAR":
|
||||
return `CHAR(${field.integerLength || 255})`;
|
||||
case "VARCHAR":
|
||||
return `VARCHAR(${field.integerLength || 255})`;
|
||||
case "TEXT":
|
||||
return "TEXT";
|
||||
case "TINYTEXT":
|
||||
return "TINYTEXT";
|
||||
case "MEDIUMTEXT":
|
||||
return "MEDIUMTEXT";
|
||||
case "LONGTEXT":
|
||||
return "LONGTEXT";
|
||||
case "TINYINT":
|
||||
return field.integerLength
|
||||
? `TINYINT(${field.integerLength})`
|
||||
: "TINYINT";
|
||||
case "SMALLINT":
|
||||
return field.integerLength
|
||||
? `SMALLINT(${field.integerLength})`
|
||||
: "SMALLINT";
|
||||
case "MEDIUMINT":
|
||||
return field.integerLength
|
||||
? `MEDIUMINT(${field.integerLength})`
|
||||
: "MEDIUMINT";
|
||||
case "INT":
|
||||
return field.integerLength ? `INT(${field.integerLength})` : "INT";
|
||||
case "BIGINT":
|
||||
return field.integerLength
|
||||
? `BIGINT(${field.integerLength})`
|
||||
: "BIGINT";
|
||||
case "FLOAT":
|
||||
return "FLOAT";
|
||||
case "DOUBLE":
|
||||
return "DOUBLE";
|
||||
case "DECIMAL":
|
||||
if (field.integerLength && field.decimals) {
|
||||
return `DECIMAL(${field.integerLength}, ${field.decimals})`;
|
||||
}
|
||||
return "DECIMAL(10,2)";
|
||||
case "BINARY":
|
||||
return `BINARY(${field.integerLength || 1})`;
|
||||
case "VARBINARY":
|
||||
return `VARBINARY(${field.integerLength || 255})`;
|
||||
case "BLOB":
|
||||
return "BLOB";
|
||||
case "TINYBLOB":
|
||||
return "TINYBLOB";
|
||||
case "MEDIUMBLOB":
|
||||
return "MEDIUMBLOB";
|
||||
case "LONGBLOB":
|
||||
return "LONGBLOB";
|
||||
case "DATE":
|
||||
return "DATE";
|
||||
case "TIME":
|
||||
return "TIME";
|
||||
case "DATETIME":
|
||||
return "DATETIME";
|
||||
case "TIMESTAMP":
|
||||
return "TIMESTAMP";
|
||||
case "YEAR":
|
||||
return "YEAR";
|
||||
case "UUID":
|
||||
return "CHAR(36)"; // MariaDB does not have a native UUID type
|
||||
case "JSON":
|
||||
return "JSON";
|
||||
case "INET6":
|
||||
return "INET6";
|
||||
case "VECTOR": {
|
||||
const dimensions = field.vectorSize || 1536;
|
||||
return `VECTOR(${dimensions})`;
|
||||
}
|
||||
case "BOOLEAN":
|
||||
return "TINYINT(1)";
|
||||
case "ENUM": {
|
||||
const enumVals = (field.options || [])
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.join(", ");
|
||||
return `ENUM(${enumVals || "''"})`;
|
||||
}
|
||||
case "SET": {
|
||||
const setVals = (field.options || [])
|
||||
.map((v) => `'${String(v).replace(/'/g, "''")}'`)
|
||||
.join(", ");
|
||||
return `SET(${setVals || "''"})`;
|
||||
}
|
||||
default:
|
||||
return "TEXT";
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
export default function MariaDBQuoteGen(str: string) {
|
||||
return `\`${str.replace(/`/g, "``")}\``;
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
import _ from "lodash";
|
||||
import type {
|
||||
BUN_MARIADB_DatabaseSchemaType,
|
||||
BUN_MARIADB_TableSchemaType,
|
||||
CreateDBSchemaParams,
|
||||
} from "../../types";
|
||||
|
||||
export default async function orderDBSchema({
|
||||
db_schema,
|
||||
}: CreateDBSchemaParams): Promise<BUN_MARIADB_DatabaseSchemaType> {
|
||||
let new_db_schema = _.cloneDeep(db_schema);
|
||||
|
||||
const tables = new_db_schema.tables;
|
||||
|
||||
let new_tables_set = new Set<BUN_MARIADB_TableSchemaType>();
|
||||
let new_tables_start_set = new Set<BUN_MARIADB_TableSchemaType>();
|
||||
let new_tables_end_set = new Set<BUN_MARIADB_TableSchemaType>();
|
||||
|
||||
function setParentTable(table: BUN_MARIADB_TableSchemaType) {
|
||||
const fields = table.fields;
|
||||
let does_table_have_foreign_keys = false;
|
||||
|
||||
for (let f = 0; f < fields.length; f++) {
|
||||
const field = fields[f];
|
||||
if (!field) continue;
|
||||
|
||||
const dst_table_name = field.foreignKey?.destinationTableName;
|
||||
|
||||
if (dst_table_name) {
|
||||
const fk_table = tables.find(
|
||||
(tb) => tb.tableName == dst_table_name,
|
||||
);
|
||||
|
||||
if (
|
||||
fk_table &&
|
||||
!new_tables_start_set.has(fk_table) &&
|
||||
!new_tables_end_set.has(fk_table)
|
||||
) {
|
||||
setParentTable(fk_table);
|
||||
}
|
||||
|
||||
new_tables_end_set.add(table);
|
||||
|
||||
if (fk_table) {
|
||||
new_tables_start_set.add(fk_table);
|
||||
}
|
||||
|
||||
does_table_have_foreign_keys = true;
|
||||
}
|
||||
}
|
||||
|
||||
return { does_table_have_foreign_keys };
|
||||
}
|
||||
|
||||
for (let i = 0; i < tables.length; i++) {
|
||||
const table = tables[i];
|
||||
|
||||
if (!table) {
|
||||
continue;
|
||||
}
|
||||
|
||||
let { does_table_have_foreign_keys } = setParentTable(table);
|
||||
|
||||
if (does_table_have_foreign_keys) {
|
||||
new_tables_end_set.add(table);
|
||||
} else {
|
||||
new_tables_start_set.add(table);
|
||||
}
|
||||
}
|
||||
|
||||
const parsed_tables = [
|
||||
...Array.from(new_tables_start_set),
|
||||
...Array.from(new_tables_end_set),
|
||||
];
|
||||
|
||||
for (let nt = 0; nt < parsed_tables.length; nt++) {
|
||||
const new_table = parsed_tables[nt];
|
||||
if (new_table) {
|
||||
new_tables_set.add(new_table);
|
||||
}
|
||||
}
|
||||
|
||||
new_db_schema.tables = Array.from(new_tables_set);
|
||||
|
||||
return new_db_schema;
|
||||
}
|
||||
Reference in New Issue
Block a user