@jsindos/sequelize-typescript-generator
Version:
Automatically generates typescript models compatible with sequelize-typescript library (https://www.npmjs.com/package/sequelize-typescript) directly from your source database.
291 lines (290 loc) • 11.5 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.DialectPostgres = void 0;
const sequelize_1 = require("sequelize");
const sequelize_typescript_1 = require("sequelize-typescript");
const Dialect_1 = require("./Dialect");
const utils_1 = require("./utils");
const sequelizeDataTypesMap = {
int2: sequelize_typescript_1.DataType.INTEGER,
int4: sequelize_typescript_1.DataType.INTEGER,
int8: sequelize_typescript_1.DataType.BIGINT,
numeric: sequelize_typescript_1.DataType.DECIMAL,
float4: sequelize_typescript_1.DataType.FLOAT,
float8: sequelize_typescript_1.DataType.DOUBLE,
money: sequelize_typescript_1.DataType.NUMBER,
varchar: sequelize_typescript_1.DataType.STRING,
bpchar: sequelize_typescript_1.DataType.STRING,
text: sequelize_typescript_1.DataType.STRING,
bytea: sequelize_typescript_1.DataType.BLOB,
timestamp: sequelize_typescript_1.DataType.DATE,
timestamptz: sequelize_typescript_1.DataType.DATE,
date: sequelize_typescript_1.DataType.STRING,
time: sequelize_typescript_1.DataType.STRING,
timetz: sequelize_typescript_1.DataType.STRING,
// interval: DataType.STRING,
bool: sequelize_typescript_1.DataType.BOOLEAN,
point: sequelize_typescript_1.DataType.GEOMETRY,
line: sequelize_typescript_1.DataType.GEOMETRY,
lseg: sequelize_typescript_1.DataType.GEOMETRY,
box: sequelize_typescript_1.DataType.GEOMETRY,
path: sequelize_typescript_1.DataType.GEOMETRY,
polygon: sequelize_typescript_1.DataType.GEOMETRY,
circle: sequelize_typescript_1.DataType.GEOMETRY,
geometry: sequelize_typescript_1.DataType.GEOMETRY,
cidr: sequelize_typescript_1.DataType.STRING,
inet: sequelize_typescript_1.DataType.STRING,
macaddr: sequelize_typescript_1.DataType.STRING,
macaddr8: sequelize_typescript_1.DataType.STRING,
bit: sequelize_typescript_1.DataType.STRING,
varbit: sequelize_typescript_1.DataType.STRING,
uuid: sequelize_typescript_1.DataType.UUID,
xml: sequelize_typescript_1.DataType.STRING,
json: sequelize_typescript_1.DataType.JSON,
jsonb: sequelize_typescript_1.DataType.JSONB,
jsonpath: sequelize_typescript_1.DataType.JSON,
};
const jsDataTypesMap = {
int2: 'number',
int4: 'number',
int8: 'string',
numeric: 'string',
float4: 'number',
float8: 'number',
money: 'string',
varchar: 'string',
bpchar: 'string',
text: 'string',
bytea: 'Uint8Array',
timestamp: 'Date',
timestamptz: 'Date',
date: 'string',
time: 'string',
timetz: 'string',
interval: 'object',
bool: 'boolean',
point: 'object',
line: 'object',
lseg: 'object',
box: 'object',
path: 'object',
polygon: 'object',
circle: 'object',
geometry: 'object',
cidr: 'string',
inet: 'string',
macaddr: 'string',
macaddr8: 'string',
bit: 'string',
varbit: 'string',
uuid: 'string',
xml: 'string',
json: 'object',
jsonb: 'object',
jsonpath: 'object',
};
/**
* Dialect for Postgres
* @class DialectPostgres
*/
class DialectPostgres extends Dialect_1.Dialect {
constructor() {
super('postgres');
}
/**
* Map database data type to sequelize data type
* @param {string} dbType
* @returns {string}
*/
mapDbTypeToSequelize(dbType) {
return sequelizeDataTypesMap[dbType];
}
/**
* Map database data type to javascript data type
* @param {string} dbType
* @returns {string
*/
mapDbTypeToJs(dbType) {
return jsDataTypesMap[dbType];
}
/**
* Map database default values to Sequelize type (e.g. uuid() => DataType.UUIDV4).
* @param {string} v
* @returns {string}
*/
mapDefaultValueToSequelize(v) {
return v;
}
/**
* Fetch table names for the provided database/schema
* @param {Sequelize} connection
* @param {IConfig} config
* @returns {Promise<ITable[]>}
*/
async fetchTables(connection, config) {
const query = `
SELECT
t.table_name AS table_name,
obj_description(pc.oid) AS table_comment
FROM information_schema.tables t
JOIN pg_class pc
ON t.table_name = pc.relname
WHERE t.table_schema='${config.metadata.schema}' AND pc.relkind = 'r';
`;
const tables = (await connection.query(query, {
type: sequelize_1.QueryTypes.SELECT,
raw: true,
})).map(({ table_name, table_comment }) => {
const t = {
name: table_name,
comment: table_comment !== null && table_comment !== void 0 ? table_comment : undefined,
};
return t;
});
return tables;
}
/**
* Fetch columns metadata for the provided schema and table
* @param {Sequelize} connection
* @param {IConfig} config
* @param {string} table
* @returns {Promise<IColumnMetadata[]>}
*/
async fetchColumnsMetadata(connection, config, table) {
var _a;
const columnsMetadata = [];
const query = `
SELECT
CASE WHEN (seq.sequence_name IS NOT NULL) THEN TRUE ELSE FALSE END AS is_sequence,
EXISTS( -- primary key
SELECT
x.indisprimary
FROM pg_attribute a
LEFT OUTER JOIN pg_index x
ON a.attnum = ANY (x.indkey) AND a.attrelid = x.indrelid
WHERE a.attrelid = '${config.metadata.schema}.\"${table}\"'::regclass AND a.attnum > 0
AND c.ordinal_position = a.attnum AND x.indisprimary IS TRUE
) AS is_primary,
c.*,
pgd.description
FROM information_schema.columns c
INNER JOIN pg_catalog.pg_statio_all_tables as st
ON c.table_schema = st.schemaname AND c.table_name = st.relname
LEFT OUTER JOIN pg_catalog.pg_description pgd
ON pgd.objoid = st.relid AND pgd.objsubid = c.ordinal_position
LEFT OUTER JOIN ( -- Sequences (auto increment) metadata
SELECT seqclass.relname AS sequence_name,
pn.nspname AS schema_name,
depclass.relname AS table_name,
attrib.attname AS column_name
FROM pg_class AS seqclass
JOIN pg_sequence AS seq
ON (seq.seqrelid = seqclass.relfilenode)
JOIN pg_depend AS dep
ON (seq.seqrelid = dep.objid)
JOIN pg_class AS depclass
ON (dep.refobjid = depclass.relfilenode)
JOIN pg_attribute AS attrib
ON (attrib.attnum = dep.refobjsubid AND attrib.attrelid = dep.refobjid)
JOIN pg_namespace pn
ON seqclass.relnamespace = pn.oid
WHERE pn.nspname = '${config.metadata.schema}' AND depclass.relname = '${table}'
) seq
ON c.table_schema = seq.schema_name AND c.table_name = seq.table_name AND
c.column_name = seq.column_name
WHERE c.table_schema = '${config.metadata.schema}' AND c.table_name = '${table}'
ORDER BY c.ordinal_position;
`;
const columns = await connection.query(query, {
type: sequelize_1.QueryTypes.SELECT,
raw: true,
});
for (const column of columns) {
// Unknown data type
if (!this.mapDbTypeToSequelize(column.udt_name)) {
(0, utils_1.warnUnknownMappingForDataType)(column.udt_name);
}
const columnMetadata = {
name: column.column_name,
originName: column.column_name,
type: column.udt_name,
typeExt: column.data_type,
...this.mapDbTypeToSequelize(column.udt_name) && {
dataType: 'DataType.' +
this.mapDbTypeToSequelize(column.udt_name).key
.split(' ')[0], // avoids 'DOUBLE PRECISION' key to include PRECISION in the mapping
},
allowNull: column.is_nullable === 'YES' && !column.is_primary,
primaryKey: column.is_primary,
autoIncrement: column.is_sequence,
indices: [],
comment: (_a = column.description) !== null && _a !== void 0 ? _a : undefined,
};
if (column.column_default) {
columnMetadata.defaultValue = `Sequelize.literal("${column.column_default.replace(/\"/g, '\\\"')}")`;
}
// Additional data type information
switch (column.udt_name) {
case 'decimal':
case 'numeric':
case 'float':
case 'double':
columnMetadata.dataType +=
(0, utils_1.generatePrecisionSignature)(column.numeric_precision, column.numeric_scale);
break;
case 'timestamp':
case 'timestampz':
columnMetadata.dataType += (0, utils_1.generatePrecisionSignature)(column.datetime_precision);
break;
case 'bpchar':
case 'varchar':
columnMetadata.dataType += (0, utils_1.generatePrecisionSignature)(column.character_maximum_length);
break;
}
columnsMetadata.push(columnMetadata);
}
return columnsMetadata;
}
/**
* Fetch index metadata for the provided table and column
* @param {Sequelize} connection
* @param {IConfig} config
* @param {string} table
* @param {string} column
* @returns {Promise<IIndexMetadata[]>}
*/
async fetchColumnIndexMetadata(connection, config, table, column) {
const indicesMetadata = [];
const query = `
SELECT pc.relname AS index_name,
am.amname AS index_type,
a.attname AS column_name,
a.attnum AS ordinal_position,
x.indisprimary AS is_primary,
x.indisunique AS is_unique,
x.indisclustered AS is_clustered
FROM pg_attribute a
INNER JOIN pg_index x
ON a.attnum = ANY (x.indkey) AND a.attrelid = x.indrelid
INNER JOIN pg_class pc
ON x.indexrelid = pc.oid
INNER JOIN pg_am am
ON pc.relam = am.oid
WHERE a.attrelid = '${config.metadata.schema}.\"${table}\"'::regclass AND a.attnum > 0
AND a.attname = '${column}';
`;
const indices = await connection.query(query, {
type: sequelize_1.QueryTypes.SELECT,
raw: true,
});
for (const index of indices) {
indicesMetadata.push({
name: index.index_name,
using: index.index_type,
unique: index.is_unique,
});
}
return indicesMetadata;
}
}
exports.DialectPostgres = DialectPostgres;