quick-erd
Version:
quick and easy text-based ERD + code generator for migration, query, typescript types and orm entity
144 lines (143 loc) • 4.7 kB
JavaScript
Object.defineProperty(exports, "__esModule", { value: true });
exports.scanMssqlTableSchema = scanMssqlTableSchema;
async function scanMssqlTableSchema(knex) {
const table_list = [];
const table_rows = await knex
.select('table_name')
.from('information_schema.tables')
.where({ table_type: 'BASE TABLE' });
for (const table_row of table_rows) {
const table = {
name: table_row.table_name,
field_list: [],
};
table_list.push(table);
let result = await knex.raw(
/* sql */ `
select
column_name
, ordinal_position
, column_default
, is_nullable
, data_type
, character_maximum_length
, numeric_precision
, numeric_scale
from information_schema.columns
where table_name = ?
`, [table.name]);
for (const column of result) {
let type = column.data_type;
/* check foreign key */
result = await knex.raw(
/* sql */ `
select
object_name (fkeys.referenced_object_id) foreign_table_name
, col_name(fkeys.referenced_object_id, fkeys.referenced_column_id) foreign_column_name
from sys.foreign_key_columns as fkeys
where object_name(fkeys.parent_object_id) = ?
and col_name(fkeys.parent_object_id, fkeys.parent_column_id) = ?
`, [table.name, column.column_name]);
const fk_row = result[0];
/* check primary key */
result = await knex.raw(
/* sql */ `
select
k.constraint_name
from information_schema.table_constraints as c
join information_schema.key_column_usage as k
on c.table_name = k.table_name
and c.constraint_catalog = k.constraint_catalog
and c.constraint_schema = k.constraint_schema
and c.constraint_name = k.constraint_name
where c.constraint_type = 'PRIMARY KEY'
and k.table_name = ?
and k.column_name = ?
`, [table.name, column.column_name]);
const pk_row = result[0];
/* check unique */
result = await knex.raw(
/* sql */ `
select
i.name as index_name
from sys.objects t
inner join sys.indexes i on t.object_id = i.object_id
cross apply (
select col.[name] + ', '
from sys.index_columns ic
inner join sys.columns col
on ic.object_id = col.object_id
and ic.column_id = col.column_id
where ic.object_id = t.object_id
and ic.index_id = i.index_id
order by key_ordinal
for xml path ('')
) D (column_name_)
where t.is_ms_shipped <> 1
and t.type = 'U' -- U for table, V for view
and i.is_unique = 1
and t.name = ?
and substring(column_name_, 1, len(column_name_)-1) = ?
order by i.[name]
`, [table.name, column.column_name]);
const unique_row = result[0];
// TODO
/* check enum */
result = await knex.raw(
/* sql */ `
select
con.[name] as constraint_name
-- e.g. "([status]='pending' OR [status]='active')"
, con.definition as constraint_definition
from sys.check_constraints con
left outer join sys.objects t
on con.parent_object_id = t.object_id
left outer join sys.all_columns col
on con.parent_column_id = col.column_id
and con.parent_object_id = col.object_id
where con.is_disabled = 0
and t.name = ?
and col.name = ?
`, [table.name, column.column_name]);
for (const row of result) {
const text = row.constraint_definition;
const match = text.match(/=([\w'"]+)/g);
if (!match)
continue;
const values = match.map(text => text.slice(1));
type = `enum(${values.join(',')})`;
}
table.field_list.push({
name: column.column_name,
type: toDataType(type, column),
is_primary_key: !!pk_row,
is_null: column.is_nullable == 'YES',
is_unsigned: false,
is_unique: !pk_row && !!unique_row,
references: fk_row
? {
type: '>0-',
table: fk_row.foreign_table_name,
field: fk_row.foreign_column_name,
}
: undefined,
default_value: column.column_default || undefined,
});
}
}
return table_list;
}
function toDataType(type, column) {
if (type.includes('varchar')) {
return column.character_maximum_length == -1
? 'text'
: type.includes('nvarchar')
? `nvarchar(${column.character_maximum_length})`
: `varchar(${column.character_maximum_length})`;
}
if (type.includes('datetime2')) {
return 'timestamp';
}
return type;
}
;