Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions forward_engineering/configs/templates.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ module.exports = {

createSchema: 'CREATE SCHEMA [${name}]${terminator}${comment}',

createProcedure:
'CREATE${orReplace} PROCEDURE ${name}${arguments}${parameters}\nAS\n${body}${terminator}${comment}',

createTable:
'CREATE${external} TABLE ${name} (\n' +
'\t${column_definitions}${temporalTableTime}${keyConstraints}${checkConstraints}${foreignKeyConstraints}${memoryOptimizedIndexes}\n' +
Expand Down Expand Up @@ -102,6 +105,9 @@ module.exports = {
createColumnComment:
"EXEC sp_addextendedproperty 'MS_Description', N'${value}', 'schema', ${schemaName}, 'table', ${tableName}, 'column', ${columnName}${terminator}",

createProcedureComment:
"EXEC sp_addextendedproperty 'MS_Description', N'${value}', 'schema', ${schemaName}, 'procedure', ${procedureName}${terminator}",

createViewComment:
"EXEC sp_addextendedproperty 'MS_Description', N'${value}', 'schema', ${schemaName}, 'view', ${viewName}${terminator}",

Expand Down
119 changes: 83 additions & 36 deletions forward_engineering/ddlProvider.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,12 +55,13 @@ const {
wrapIfNotExistView,
} = require('./helpers/ifNotExistStatementHelper');
const { getPartitionedTables, getCreateViewData } = require('./helpers/viewHelper');
const { getParameters, hydrateProcedures } = require('./helpers/proceduresHelper');

const ddlProvider = (baseProvider, options, app) => {
const terminator = getTerminator(options);

return {
createSchema({ schemaName, databaseName, ifNotExist, comment, isActivated = true }) {
createSchema({ schemaName, databaseName, ifNotExist, comment, procedures = [], isActivated = true }) {
const schemaTerminator = ifNotExist ? ';' : terminator;

const schemaComment = comment
Expand All @@ -71,47 +72,46 @@ const ddlProvider = (baseProvider, options, app) => {
})
: '';

let schemaStatement = commentIfDeactivated(
assignTemplates(templates.createSchema, {
name: schemaName,
terminator: schemaTerminator,
comment: schemaComment ? `\n\n${schemaComment}` : '',
}),
{ isActivated },
);
let databaseStatement = '';
let useDatabaseStatement = '';
let schemaStatement = '';

if (!databaseName) {
return ifNotExist
? wrapIfNotExistSchema({ templates, schemaStatement, schemaName, terminator })
: schemaStatement;
}

const databaseStatement = wrapIfNotExistDatabase({
templates,
databaseName,
terminator,
databaseStatement: assignTemplates(templates.createDatabase, {
name: databaseName,
terminator: schemaTerminator,
}),
});

const useStatement = assignTemplates(templates.useDatabase, {
name: databaseName,
schemaStatement = assignTemplates(templates.createSchema, {
name: schemaName,
terminator: schemaTerminator,
comment: schemaComment ? `\n\n${schemaComment}` : '',
});

schemaStatement = commentIfDeactivated(schemaStatement, { isActivated });

if (ifNotExist) {
return (
databaseStatement +
'\n\n' +
useStatement +
'\n\n' +
wrapIfNotExistSchema({ templates, schemaStatement, schemaName, terminator })
);
schemaStatement = wrapIfNotExistSchema({ templates, schemaStatement, schemaName, terminator });
}

if (databaseName) {
databaseStatement = wrapIfNotExistDatabase({
templates,
databaseName,
terminator,
databaseStatement: assignTemplates(templates.createDatabase, {
name: databaseName,
terminator: schemaTerminator,
}),
});

useDatabaseStatement = assignTemplates(templates.useDatabase, {
name: databaseName,
terminator: schemaTerminator,
});
}

return databaseStatement + '\n\n' + useStatement + '\n\n' + schemaStatement;
const procedureStatements = procedures.map(procedure =>
this.createProcedure({ ...procedure, schemaName, isActivated }),
);

return [databaseStatement, useDatabaseStatement, schemaStatement, ...procedureStatements]
.filter(Boolean)
.join('\n\n');
},

createDefaultConstraint(data, tableName, terminator = ';') {
Expand Down Expand Up @@ -557,13 +557,14 @@ const ddlProvider = (baseProvider, options, app) => {
};
},

hydrateSchema(containerData) {
hydrateSchema(containerData, { procedures } = {}) {
return {
schemaName: containerData.name,
databaseName: containerData.databaseName,
ifNotExist: containerData.ifNotExist,
comment: containerData.role?.description ?? containerData.description,
isActivated: containerData.isActivated,
procedures: hydrateProcedures(procedures),
};
},

Expand Down Expand Up @@ -1094,6 +1095,52 @@ const ddlProvider = (baseProvider, options, app) => {
terminator,
});
},

createProcedureComment({ schemaName, procedureName, comment, customTerminator }) {
if (!schemaName) {
return '';
}

return assignTemplates(templates.createProcedureComment, {
value: escapeSpecialCharacters(comment),
schemaName: wrapInBrackets(schemaName),
procedureName: wrapInBrackets(procedureName),
terminator: customTerminator ?? terminator,
});
},

createProcedure({
schemaName,
isActivated,
name,
description,
orReplace,
inputArgs,
body,
encryption,
recompile,
executeAs,
forReplication,
}) {
const procedureName = getTableName(name, schemaName);
const procedureComment = description
? this.createProcedureComment({ schemaName, procedureName: name, comment: description })
: '';
const parameters = getParameters({ encryption, recompile, executeAs, forReplication });
const args = inputArgs ? `\n${inputArgs.replace(/^\(([\s\S]+)\)$/, '$1')}` : '';

const procedureStatement = assignTemplates(templates.createProcedure, {
orReplace: orReplace ? ' OR ALTER' : '',
name: procedureName,
arguments: args,
body,
parameters,
terminator,
comment: procedureComment ? `\n\n${procedureComment}` : '',
});

return commentIfDeactivated(procedureStatement, { isActivated });
},
};
};

Expand Down
56 changes: 56 additions & 0 deletions forward_engineering/helpers/proceduresHelper.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
const { trim } = require('lodash');
const { clean, tab } = require('../utils/general');

/**
* @typedef {import('../types.d.ts').Procedure} Procedure
*/

/**
*
* @param {Procedure[]} procedures
* @returns {Procedure[]}
*/
const hydrateProcedures = procedures => {
if (!Array.isArray(procedures)) {
return [];
}

return procedures
.map(procedure => {
return clean({
name: procedure.name || undefined,
orReplace: procedure.orReplace || undefined,
inputArgs: procedure.inputArgs ? tab(trim(procedure.inputArgs)) : undefined,
body: procedure.body ? tab(trim(procedure.body)) : undefined,
description: procedure.description || undefined,
encryption: procedure.encryption || undefined,
recompile: procedure.recompile || undefined,
forReplication: procedure.forReplication || undefined,
executeAs: procedure.executeAs || undefined,
});
})
.filter(procedure => procedure.name);
};

/**
*
* @param {object} params
* @param {string} [params.encryption]
* @param {string} [params.recompile]
* @param {string} [params.executeAs]
* @param {string} [params.forReplication]
* @returns {string}
*/
const getParameters = ({ encryption, recompile, executeAs, forReplication }) => {
const executeAsClause = executeAs ? `EXECUTE AS ${executeAs}` : '';
const parametersClause = [encryption, recompile, executeAsClause].filter(Boolean).join(', ');
const withParametersClause = parametersClause ? `\nWITH ${parametersClause}` : '';
const forReplicationClause = forReplication ? `\n${forReplication}` : '';

return `${withParametersClause}${forReplicationClause}`;
};

module.exports = {
getParameters,
hydrateProcedures,
};
50 changes: 31 additions & 19 deletions forward_engineering/types.d.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,45 @@
export type ColumnDefinition = {
name: string;
type: string;
isActivated: boolean;
length?: number;
precision?: number;
primaryKey?: boolean;
scale?: number;
timePrecision?: number;
unique?: boolean;
primaryKeyOptions?: { GUID: string; constraintName: string };
uniqueKeyOptions?: Array<{ GUID: string; constraintName: string }>;
name: string;
type: string;
isActivated: boolean;
length?: number;
precision?: number;
primaryKey?: boolean;
scale?: number;
timePrecision?: number;
unique?: boolean;
primaryKeyOptions?: { GUID: string; constraintName: string };
uniqueKeyOptions?: Array<{ GUID: string; constraintName: string }>;
};

export type AppInstance = {
require: (packageName: string) => unknown;
general: object;
}
require: (packageName: string) => unknown;
general: object;
};

export type ConstraintDtoColumn = {
name: string;
isActivated: boolean;
name: string;
isActivated: boolean;
};

export type KeyType = 'PRIMARY KEY' | 'UNIQUE';

export type ConstraintDto = {
keyType: KeyType;
name: string;
columns?: ConstraintDtoColumn[];
keyType: KeyType;
name: string;
columns?: ConstraintDtoColumn[];
};

export type JsonSchema = Record<string, unknown>;

export type Procedure = {
name: string;
orReplace: boolean;
inputArgs: string;
body: string;
encryption?: string;
recompile?: string;
executeAs?: string;
forReplication?: string;
description?: string;
};
Loading