@naturalcycles/mysql-lib
Version:
MySQL client implementing CommonDB interface
103 lines (102 loc) • 3.2 kB
JavaScript
import * as mysql from 'mysql';
/**
* It currently skips nullability and declares everything as "DEFAULT NULL".
*/
export function jsonSchemaToMySQLDDL(table, schema, opt = {}) {
const { engine = 'InnoDB' } = opt;
const lines = [`CREATE TABLE ${mysql.escapeId(table)} (`];
const innerLines = Object.entries(schema.properties).map(([k, s]) => {
if (k === 'id') {
return `id VARCHAR(255) NOT NULL`;
}
let type;
if (s.type === 'string') {
// can specify isoDate later
type = 'LONGTEXT';
}
else if (s.type === 'integer') {
type = 'INT(11)';
}
else if (s.type === 'number') {
if (['unixTimestamp', 'int32'].includes(s.format)) {
type = 'INT(11)';
}
else {
type = 'FLOAT(11)';
}
}
else if (s.type === 'boolean') {
type = 'TINYINT(1)';
}
else if (s.instanceof === 'Buffer') {
type = 'LONGBLOB';
}
else if (s.type === 'null') {
type = 'VARCHAR(255)';
}
else if (s.type === 'array') {
type = 'LONGTEXT'; // to be JSON.stringified?
}
else if (s.type === 'object') {
type = 'LONGTEXT'; // to be JSON.stringified?
}
else {
// default
type = 'LONGTEXT';
}
const tokens = [mysql.escapeId(mapNameToMySQL(k)), type, `DEFAULT NULL`];
return tokens.join(' ');
});
innerLines.push(`PRIMARY KEY (id)`);
lines.push(innerLines.join(',\n'));
lines.push(`) ENGINE=${engine}`);
return lines.join('\n');
}
export function mysqlTableStatsToJsonSchemaField(table, stats, logger) {
const s = {
$id: `${table}.schema.json`,
type: 'object',
properties: {},
required: [],
additionalProperties: true,
};
stats.forEach(stat => {
const name = stat.Field;
const t = stat.Type.toLowerCase();
const notNull = stat.Null?.toUpperCase() !== 'YES';
if (notNull) {
s.required.push(name);
}
if (t.includes('text') || t.includes('char')) {
s.properties[name] = { type: 'string' };
}
else if (t.includes('lob')) {
s.properties[name] = { instanceof: 'Buffer' };
}
else if (t.startsWith('tinyint') || t.includes('(1)')) {
s.properties[name] = { type: 'boolean' };
}
else if (t === 'int' || t.startsWith('int(')) {
s.properties[name] = { type: 'integer' };
}
else if (t.startsWith('float')) {
s.properties[name] = { type: 'number' };
}
else {
logger.log(s);
throw new Error(`Unknown mysql field type ${name} ${stat.Type}`);
}
});
return s;
}
/**
* Because MySQL doesn't support `.` in field names and escapes them as tableName + fieldName.
*
* @param name
*/
export function mapNameToMySQL(name) {
return name.replaceAll('.', '_dot_');
}
export function mapNameFromMySQL(name) {
return name.replaceAll('_dot_', '.');
}