From 807b93930f1e86a2a6df89bd95eb29e5ab357cd7 Mon Sep 17 00:00:00 2001 From: Justin Lettau Date: Sat, 10 Mar 2018 16:52:42 -0500 Subject: [PATCH] chore: update indent size --- .travis.yml | 6 +- src/commands/cat.ts | 32 +-- src/commands/conns.ts | 34 +-- src/commands/init.ts | 150 ++++++------- src/commands/pull.ts | 344 ++++++++++++++--------------- src/commands/push.ts | 44 ++-- src/common/config.ts | 40 ++-- src/common/connection.ts | 20 +- src/common/idempotency.ts | 14 +- src/common/output.ts | 18 +- src/common/utility.ts | 297 +++++++++++++------------ src/index.ts | 40 ++-- src/sql/record-set.ts | 86 ++++---- src/sql/script.ts | 448 +++++++++++++++++++------------------- src/sql/sys.ts | 272 +++++++++++------------ src/typings.d.ts | 4 +- tsconfig.json | 28 +-- tslint.json | 8 +- 18 files changed, 943 insertions(+), 942 deletions(-) diff --git a/.travis.yml b/.travis.yml index f69d6a4..a2f0352 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,8 +3,8 @@ node_js: - "node" before_install: - - npm install + - npm install script: - - npm run lint - - npm run build + - npm run lint + - npm run build diff --git a/src/commands/cat.ts b/src/commands/cat.ts index 6a935db..1b0a57e 100644 --- a/src/commands/cat.ts +++ b/src/commands/cat.ts @@ -10,25 +10,25 @@ import * as util from '../common/utility'; * Concatenate all SQL files into a single file. */ export function cat(): void { - const start: [number, number] = process.hrtime(); - const config: Config = util.getConfig(); - let output: string = ''; + const start: [number, number] = process.hrtime(); + const config: Config = util.getConfig(); + let output: string = ''; - // order is important - const files: string[] = util.getFilesOrdered(config); + // order is important + const files: string[] = util.getFilesOrdered(config); - for (const file of files) { - const content: string = fs.readFileSync(file).toString(); - const end: string = content.substr(-2).toLowerCase(); + for (const file of files) { + const content: string = fs.readFileSync(file).toString(); + const end: string = content.substr(-2).toLowerCase(); - output += content; - output += EOL; - output += (end !== 'go' ? 'go' : ''); - output += EOL + EOL; - } + output += content; + output += EOL; + output += (end !== 'go' ? 'go' : ''); + output += EOL + EOL; + } - fs.outputFileSync(`${config.output.root}/cat.sql`, output); + fs.outputFileSync(`${config.output.root}/cat.sql`, output); - const time: [number, number] = process.hrtime(start); - console.log(chalk.green(`Finished after ${time[0]}s!`)); + const time: [number, number] = process.hrtime(start); + console.log(chalk.green(`Finished after ${time[0]}s!`)); } diff --git a/src/commands/conns.ts b/src/commands/conns.ts index 4b2b50d..d85caf1 100644 --- a/src/commands/conns.ts +++ b/src/commands/conns.ts @@ -8,24 +8,24 @@ import * as util from '../common/utility'; * List all available connections. */ export function conns(): void { - const config: Config = util.getConfig(); - const connections: Connection[] = util.getConns(config); - const placeholder: string = 'n/a'; + const config: Config = util.getConfig(); + const connections: Connection[] = util.getConns(config); + const placeholder: string = 'n/a'; - const table: Table = new Table({ - head: ['Name', 'Server', 'Port', 'Database', 'User', 'Password'] - }); + const table: Table = new Table({ + head: ['Name', 'Server', 'Port', 'Database', 'User', 'Password'] + }); - for (const conn of connections) { - table.push([ - conn.name || placeholder, - conn.server || placeholder, - conn.port || placeholder, - conn.database || placeholder, - conn.user || placeholder, - conn.password || placeholder - ]); - } + for (const conn of connections) { + table.push([ + conn.name || placeholder, + conn.server || placeholder, + conn.port || placeholder, + conn.database || placeholder, + conn.user || placeholder, + conn.password || placeholder + ]); + } - console.log(table.toString()); + console.log(table.toString()); } diff --git a/src/commands/init.ts b/src/commands/init.ts index 77e059f..603235f 100644 --- a/src/commands/init.ts +++ b/src/commands/init.ts @@ -8,9 +8,9 @@ import * as util from '../common/utility'; * CLI arguments for `init` command. */ interface InitOptions { - webconfig?: string; - force?: boolean; - skip?: boolean; + webconfig?: string; + force?: boolean; + skip?: boolean; } /** @@ -19,81 +19,81 @@ interface InitOptions { * @param options CommanderJS options. */ export function init(options: InitOptions): void { - const webConfigFile: string = options.webconfig || util.webConfigFile; - const webConfigConns: Connection[] = util.getWebConfigConns(webConfigFile); - const conn: Connection = new Connection(); + const webConfigFile: string = options.webconfig || util.webConfigFile; + const webConfigConns: Connection[] = util.getWebConfigConns(webConfigFile); + const conn: Connection = new Connection(); - if (webConfigConns) { - // use options from web config - Object.assign(conn, webConfigConns[0]); - } + if (webConfigConns) { + // use options from web config + Object.assign(conn, webConfigConns[0]); + } - if (fs.existsSync(util.configFile) && !options.force) { - // don't overwrite existing config file - return console.error('Config file already exists!'); - } + if (fs.existsSync(util.configFile) && !options.force) { + // don't overwrite existing config file + return console.error('Config file already exists!'); + } - if (options.skip) { - // skip prompts and create with defaults - util.setConfig({ connections: options.webconfig || [conn] }); - return; - } + if (options.skip) { + // skip prompts and create with defaults + util.setConfig({ connections: options.webconfig || [conn] }); + return; + } - const questions: inquirer.Questions = [ - { - name: 'path', - message: 'Use connections from Web.config file?', - type: 'confirm', - when: (): boolean => !!webConfigConns - }, { - name: 'server', - message: 'Server URL.', - default: (conn.server || undefined), - when: (answers): boolean => (!webConfigConns || !answers.path) - }, { - name: 'port', - message: 'Server port.', - default: (conn.port || undefined), - when: (answers): boolean => (!webConfigConns || !answers.path) - }, { - name: 'database', - message: 'Database name.', - default: (conn.database || undefined), - when: (answers): boolean => (!webConfigConns || !answers.path) - }, { - name: 'user', - message: 'Login username.', - default: (conn.user || undefined), - when: (answers): boolean => (!webConfigConns || !answers.path) - }, { - name: 'password', - message: 'Login password.', - default: (conn.password || undefined), - when: (answers): boolean => (!webConfigConns || !answers.path) - }, { - name: 'name', - message: 'Connection name.', - default: 'dev', - when: (answers): boolean => (!webConfigConns || !answers.path) - } - ]; + const questions: inquirer.Questions = [ + { + name: 'path', + message: 'Use connections from Web.config file?', + type: 'confirm', + when: (): boolean => !!webConfigConns + }, { + name: 'server', + message: 'Server URL.', + default: (conn.server || undefined), + when: (answers): boolean => (!webConfigConns || !answers.path) + }, { + name: 'port', + message: 'Server port.', + default: (conn.port || undefined), + when: (answers): boolean => (!webConfigConns || !answers.path) + }, { + name: 'database', + message: 'Database name.', + default: (conn.database || undefined), + when: (answers): boolean => (!webConfigConns || !answers.path) + }, { + name: 'user', + message: 'Login username.', + default: (conn.user || undefined), + when: (answers): boolean => (!webConfigConns || !answers.path) + }, { + name: 'password', + message: 'Login password.', + default: (conn.password || undefined), + when: (answers): boolean => (!webConfigConns || !answers.path) + }, { + name: 'name', + message: 'Connection name.', + default: 'dev', + when: (answers): boolean => (!webConfigConns || !answers.path) + } + ]; - // prompt user for config options - inquirer.prompt(questions).then((answers: inquirer.Answers): void => { - if (answers.path) { - // use Web.config path - util.setConfig({ connections: webConfigFile }); - } else { - util.setConfig({ - connections: [new Connection({ - name: answers.name, - server: answers.server, - port: answers.port, - database: answers.database, - user: answers.user, - password: answers.password - })] - }); - } - }); + // prompt user for config options + inquirer.prompt(questions).then((answers: inquirer.Answers): void => { + if (answers.path) { + // use Web.config path + util.setConfig({ connections: webConfigFile }); + } else { + util.setConfig({ + connections: [new Connection({ + name: answers.name, + server: answers.server, + port: answers.port, + database: answers.database, + user: answers.user, + password: answers.password + })] + }); + } + }); } diff --git a/src/commands/pull.ts b/src/commands/pull.ts index ab396a1..68603d8 100644 --- a/src/commands/pull.ts +++ b/src/commands/pull.ts @@ -11,24 +11,24 @@ import { Connection } from '../common/connection'; import { IdempotencyOption } from '../common/idempotency'; import * as util from '../common/utility'; import { - ColumnRecordSet, - ForeignKeyRecordSet, - IndexRecordSet, - ObjectRecordSet, - PrimaryKeyRecordSet, - SchemaRecordSet, - TableRecordSet, - TvpRecordSet + ColumnRecordSet, + ForeignKeyRecordSet, + IndexRecordSet, + ObjectRecordSet, + PrimaryKeyRecordSet, + SchemaRecordSet, + TableRecordSet, + TvpRecordSet } from '../sql/record-set'; import * as script from '../sql/script'; import { - columnRead, - foreignKeyRead, - indexRead, - objectRead, - primaryKeyRead, - tableRead, - tvpRead + columnRead, + foreignKeyRead, + indexRead, + objectRead, + primaryKeyRead, + tableRead, + tvpRead } from '../sql/sys'; /** @@ -37,35 +37,35 @@ import { * @param name Connection name to use. */ export function pull(name: string): void { - const start: [number, number] = process.hrtime(); - const config: Config = util.getConfig(); - const conn: Connection = util.getConn(config, name); - - console.log(`Pulling ${chalk.magenta(conn.database)} from ${chalk.magenta(conn.server)} ...`); - - // connect to db - new sql.ConnectionPool(conn) - .connect() - .then((pool: sql.ConnectionPool): Promise[]> => { - return Promise.all([ - pool.request().query(objectRead), - pool.request().query(tableRead), - pool.request().query(columnRead), - pool.request().query(primaryKeyRead), - pool.request().query(foreignKeyRead), - pool.request().query(indexRead), - pool.request().query(tvpRead) - ]).then(results => { - pool.close(); - return results; - }); - }) - .then(results => scriptFiles(config, results)) - .then(() => { - const time: [number, number] = process.hrtime(start); - console.log(chalk.green(`Finished after ${time[0]}s!`)); - }) - .catch(err => console.error(err)); + const start: [number, number] = process.hrtime(); + const config: Config = util.getConfig(); + const conn: Connection = util.getConn(config, name); + + console.log(`Pulling ${chalk.magenta(conn.database)} from ${chalk.magenta(conn.server)} ...`); + + // connect to db + new sql.ConnectionPool(conn) + .connect() + .then((pool: sql.ConnectionPool): Promise[]> => { + return Promise.all([ + pool.request().query(objectRead), + pool.request().query(tableRead), + pool.request().query(columnRead), + pool.request().query(primaryKeyRead), + pool.request().query(foreignKeyRead), + pool.request().query(indexRead), + pool.request().query(tvpRead) + ]).then(results => { + pool.close(); + return results; + }); + }) + .then(results => scriptFiles(config, results)) + .then(() => { + const time: [number, number] = process.hrtime(start); + console.log(chalk.green(`Finished after ${time[0]}s!`)); + }) + .catch(err => console.error(err)); } /** @@ -75,76 +75,76 @@ export function pull(name: string): void { * @param results Array of data sets from SQL queries. */ function scriptFiles(config: Config, results: sql.IResult[]): void { - const existing: string[] = glob.sync(`${config.output.root}/**/*.sql`); - - // note: array order MUST match query promise array - const objects: ObjectRecordSet[] = results[0].recordset; - const tables: TableRecordSet[] = results[1].recordset; - const columns: ColumnRecordSet[] = results[2].recordset; - const primaryKeys: PrimaryKeyRecordSet[] = results[3].recordset; - const foreignKeys: ForeignKeyRecordSet[] = results[4].recordset; - const indexes: IndexRecordSet[] = results[5].recordset; - const tvps: TvpRecordSet[] = results[6].recordset; - - // get unique schema names - const schemas: SchemaRecordSet[] = tables - .map(item => item.schema) - .filter((value, index, array) => array.indexOf(value) === index) - .map(value => ({ name: value, type: 'SCHEMA' })); - - // write files for schemas - for (const item of schemas) { - const file: string = util.safeFile(`${item.name}.sql`); - - if (!include(config, file)) { - continue; - } - - const content: string = script.schema(item); - const dir: string = createFile(config, item, file, content); - exclude(config, existing, dir); + const existing: string[] = glob.sync(`${config.output.root}/**/*.sql`); + + // note: array order MUST match query promise array + const objects: ObjectRecordSet[] = results[0].recordset; + const tables: TableRecordSet[] = results[1].recordset; + const columns: ColumnRecordSet[] = results[2].recordset; + const primaryKeys: PrimaryKeyRecordSet[] = results[3].recordset; + const foreignKeys: ForeignKeyRecordSet[] = results[4].recordset; + const indexes: IndexRecordSet[] = results[5].recordset; + const tvps: TvpRecordSet[] = results[6].recordset; + + // get unique schema names + const schemas: SchemaRecordSet[] = tables + .map(item => item.schema) + .filter((value, index, array) => array.indexOf(value) === index) + .map(value => ({ name: value, type: 'SCHEMA' })); + + // write files for schemas + for (const item of schemas) { + const file: string = util.safeFile(`${item.name}.sql`); + + if (!include(config, file)) { + continue; } - // write files for stored procedures, functions, ect. - for (const item of objects) { - const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); + const content: string = script.schema(item); + const dir: string = createFile(config, item, file, content); + exclude(config, existing, dir); + } - if (!include(config, file)) { - continue; - } + // write files for stored procedures, functions, ect. + for (const item of objects) { + const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); - const dir: string = createFile(config, item, file, item.text); - exclude(config, existing, dir); + if (!include(config, file)) { + continue; } - // write files for tables - for (const item of tables) { - const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); + const dir: string = createFile(config, item, file, item.text); + exclude(config, existing, dir); + } - if (!include(config, file)) { - continue; - } + // write files for tables + for (const item of tables) { + const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); - const content: string = script.table(item, columns, primaryKeys, foreignKeys, indexes); - const dir: string = createFile(config, item, file, content); - exclude(config, existing, dir); + if (!include(config, file)) { + continue; } - // write files for user-defined table-valued parameter - for (const item of tvps) { - const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); + const content: string = script.table(item, columns, primaryKeys, foreignKeys, indexes); + const dir: string = createFile(config, item, file, content); + exclude(config, existing, dir); + } - if (!include(config, file)) { - continue; - } + // write files for user-defined table-valued parameter + for (const item of tvps) { + const file: string = util.safeFile(`${item.schema}.${item.name}.sql`); - const content: string = script.tvp(item, columns); - const dir: string = createFile(config, item, file, content); - exclude(config, existing, dir); + if (!include(config, file)) { + continue; } - // all remaining files in `existing` need deleted - removeFiles(existing); + const content: string = script.tvp(item, columns); + const dir: string = createFile(config, item, file, content); + exclude(config, existing, dir); + } + + // all remaining files in `existing` need deleted + removeFiles(existing); } /** @@ -156,59 +156,59 @@ function scriptFiles(config: Config, results: sql.IResult[]): void { * @param content Script file contents. */ function createFile(config: Config, item: any, file: string, content: string): string { - let dir: string; - let output: string; - let type: IdempotencyOption; - - switch (item.type.trim()) { - case 'SCHEMA': // not a real object type - output = config.output.schemas; - type = null; - break; - case 'U': - output = config.output.tables; - type = config.idempotency.tables; - break; - case 'P': - output = config.output.procs; - type = config.idempotency.procs; - break; - case 'V': - output = config.output.views; - type = config.idempotency.views; - break; - case 'TF': - case 'IF': - output = config.output['table-valued']; - type = config.idempotency['table-valued']; - break; - case 'FN': - output = config.output['scalar-valued']; - type = config.idempotency['scalar-valued']; - break; - case 'TR': - output = config.output.triggers; - type = config.idempotency.triggers; - break; - case 'TT': - output = config.output['table-valued-parameters']; - type = config.idempotency['table-valued-parameters']; - break; - default: - output = 'unknown'; - } - - // get full output path - dir = path.join(config.output.root, output, file); - - // idempotent prefix - content = script.idempotency(item, type) + content; - - // create file - console.log(`Creating ${chalk.cyan(dir)} ...`); - fs.outputFileSync(dir, content.trim()); - - return dir; + let dir: string; + let output: string; + let type: IdempotencyOption; + + switch (item.type.trim()) { + case 'SCHEMA': // not a real object type + output = config.output.schemas; + type = null; + break; + case 'U': + output = config.output.tables; + type = config.idempotency.tables; + break; + case 'P': + output = config.output.procs; + type = config.idempotency.procs; + break; + case 'V': + output = config.output.views; + type = config.idempotency.views; + break; + case 'TF': + case 'IF': + output = config.output['table-valued']; + type = config.idempotency['table-valued']; + break; + case 'FN': + output = config.output['scalar-valued']; + type = config.idempotency['scalar-valued']; + break; + case 'TR': + output = config.output.triggers; + type = config.idempotency.triggers; + break; + case 'TT': + output = config.output['table-valued-parameters']; + type = config.idempotency['table-valued-parameters']; + break; + default: + output = 'unknown'; + } + + // get full output path + dir = path.join(config.output.root, output, file); + + // idempotent prefix + content = script.idempotency(item, type) + content; + + // create file + console.log(`Creating ${chalk.cyan(dir)} ...`); + fs.outputFileSync(dir, content.trim()); + + return dir; } /** @@ -218,16 +218,16 @@ function createFile(config: Config, item: any, file: string, content: string): s * @param file File path to check. */ function include(config: Config, file: string | string[]): boolean { - if (!config.files || !config.files.length) { - return true; - } + if (!config.files || !config.files.length) { + return true; + } - if (!isArray(file)) { - file = [file]; - } + if (!isArray(file)) { + file = [file]; + } - const results: string[] = multimatch(file, config.files); - return !!results.length; + const results: string[] = multimatch(file, config.files); + return !!results.length; } /** @@ -238,15 +238,15 @@ function include(config: Config, file: string | string[]): boolean { * @param dir File path to check. */ function exclude(config: Config, existing: string[], dir: string): void { - if (config.output.root.startsWith('./') && !dir.startsWith('./')) { - dir = `./${dir}`; - } + if (config.output.root.startsWith('./') && !dir.startsWith('./')) { + dir = `./${dir}`; + } - const index: number = existing.indexOf(dir.replace(/\\/g, '/')); + const index: number = existing.indexOf(dir.replace(/\\/g, '/')); - if (index !== -1) { - existing.splice(index, 1); - } + if (index !== -1) { + existing.splice(index, 1); + } } /** @@ -255,8 +255,8 @@ function exclude(config: Config, existing: string[], dir: string): void { * @param files Array of file paths to delete. */ function removeFiles(files: string[]): void { - for (const file of files) { - console.log(`Removing ${chalk.cyan(file)} ...`); - fs.removeSync(file); - } + for (const file of files) { + console.log(`Removing ${chalk.cyan(file)} ...`); + fs.removeSync(file); + } } diff --git a/src/commands/push.ts b/src/commands/push.ts index 73113e5..0339a57 100644 --- a/src/commands/push.ts +++ b/src/commands/push.ts @@ -14,32 +14,32 @@ import * as util from '../common/utility'; * @param name Connection name to use. */ export function push(name?: string): void { - const start: [number, number] = process.hrtime(); - const config: Config = util.getConfig(); - const conn: Connection = util.getConn(config, name); + const start: [number, number] = process.hrtime(); + const config: Config = util.getConfig(); + const conn: Connection = util.getConn(config, name); - console.log(`Pushing to ${chalk.magenta(conn.database)} on ${chalk.magenta(conn.server)} ...`); + console.log(`Pushing to ${chalk.magenta(conn.database)} on ${chalk.magenta(conn.server)} ...`); - const files: string[] = util.getFilesOrdered(config); - let promise: Promise = new sql.ConnectionPool(conn).connect(); + const files: string[] = util.getFilesOrdered(config); + let promise: Promise = new sql.ConnectionPool(conn).connect(); - for (const file of files) { - console.log(`Executing ${chalk.cyan(file)} ...`); + for (const file of files) { + console.log(`Executing ${chalk.cyan(file)} ...`); - const content: string = fs.readFileSync(file, 'utf8'); - const statements: string[] = content.split('go' + EOL); + const content: string = fs.readFileSync(file, 'utf8'); + const statements: string[] = content.split('go' + EOL); - for (const statement of statements) { - promise = promise.then(pool => { - return pool.request().batch(statement).then(result => pool); - }); - } + for (const statement of statements) { + promise = promise.then(pool => { + return pool.request().batch(statement).then(result => pool); + }); } - - promise - .then(() => { - const time: [number, number] = process.hrtime(start); - console.log(chalk.green(`Finished after ${time[0]}s!`)); - }) - .catch(err => console.error(err)); + } + + promise + .then(() => { + const time: [number, number] = process.hrtime(start); + console.log(chalk.green(`Finished after ${time[0]}s!`)); + }) + .catch(err => console.error(err)); } diff --git a/src/common/config.ts b/src/common/config.ts index e9c73d1..dceff87 100644 --- a/src/common/config.ts +++ b/src/common/config.ts @@ -7,28 +7,28 @@ import { OutputConfig } from './output'; */ export interface Config { - /** - * Path to `Web.config` file or collection of connection objects. - */ - connections: string | Connection[]; + /** + * Path to `Web.config` file or collection of connection objects. + */ + connections: string | Connection[]; - /** - * Deprecated (v1.1.0). - */ - connection?: string | Connection; + /** + * Deprecated (v1.1.0). + */ + connection?: string | Connection; - /** - * Glob of files to include / exclude. - */ - files?: string[]; + /** + * Glob of files to include / exclude. + */ + files?: string[]; - /** - * Describes output folder structure. - */ - output?: OutputConfig; + /** + * Describes output folder structure. + */ + output?: OutputConfig; - /** - * Describes idempotent script to be used. - */ - idempotency?: IdempotencyConfig; + /** + * Describes idempotent script to be used. + */ + idempotency?: IdempotencyConfig; } diff --git a/src/common/connection.ts b/src/common/connection.ts index 111eb28..fbaae5e 100644 --- a/src/common/connection.ts +++ b/src/common/connection.ts @@ -2,16 +2,16 @@ * SQL server connection configuration. */ export class Connection { - constructor(conn?: Connection) { - if (conn) { - Object.assign(this, conn); - } + constructor(conn?: Connection) { + if (conn) { + Object.assign(this, conn); } + } - public name: string = ''; - public server: string = ''; - public database: string = ''; - public port?: number = 1433; - public user: string = ''; - public password: string = ''; + public name: string = ''; + public server: string = ''; + public database: string = ''; + public port?: number = 1433; + public user: string = ''; + public password: string = ''; } diff --git a/src/common/idempotency.ts b/src/common/idempotency.ts index d4afa9f..3f26721 100644 --- a/src/common/idempotency.ts +++ b/src/common/idempotency.ts @@ -2,13 +2,13 @@ * Supported idempotency configuration. */ export interface IdempotencyConfig { - 'procs'?: IdempotencyOption; - 'scalar-valued'?: IdempotencyOption; - 'table-valued'?: IdempotencyOption; - 'tables'?: IdempotencyOption; - 'triggers'?: IdempotencyOption; - 'views'?: IdempotencyOption; - 'table-valued-parameters'?: IdempotencyOption; + 'procs'?: IdempotencyOption; + 'scalar-valued'?: IdempotencyOption; + 'table-valued'?: IdempotencyOption; + 'tables'?: IdempotencyOption; + 'triggers'?: IdempotencyOption; + 'views'?: IdempotencyOption; + 'table-valued-parameters'?: IdempotencyOption; } /** diff --git a/src/common/output.ts b/src/common/output.ts index 4a7a142..27e5d11 100644 --- a/src/common/output.ts +++ b/src/common/output.ts @@ -2,13 +2,13 @@ * Supported output configuration. */ export interface OutputConfig { - 'root'?: string; - 'procs'?: string; - 'schemas'?: string; - 'scalar-valued'?: string; - 'table-valued'?: string; - 'tables'?: string; - 'triggers'?: string; - 'views'?: string; - 'table-valued-parameters'?: string; + 'root'?: string; + 'procs'?: string; + 'schemas'?: string; + 'scalar-valued'?: string; + 'table-valued'?: string; + 'tables'?: string; + 'triggers'?: string; + 'views'?: string; + 'table-valued-parameters'?: string; } diff --git a/src/common/utility.ts b/src/common/utility.ts index e9c25c1..561a287 100644 --- a/src/common/utility.ts +++ b/src/common/utility.ts @@ -23,28 +23,28 @@ export const webConfigFile: string = './Web.config'; * Default values for config file. */ export const configDefaults: Config = { - connections: [], - files: [], - output: { - 'root': './_sql-database', - 'procs': './stored-procedures', - 'schemas': './schemas', - 'scalar-valued': './functions/scalar-valued', - 'table-valued': './functions/table-valued', - 'tables': './tables', - 'triggers': './triggers', - 'views': './views', - 'table-valued-parameters': './user-defined-types/table-valued-parameters' - }, - idempotency: { - 'procs': 'if-exists-drop', - 'scalar-valued': 'if-exists-drop', - 'table-valued': 'if-exists-drop', - 'tables': 'if-not-exists', - 'triggers': 'if-exists-drop', - 'views': 'if-exists-drop', - 'table-valued-parameters': 'if-not-exists' - } + connections: [], + files: [], + output: { + 'root': './_sql-database', + 'procs': './stored-procedures', + 'schemas': './schemas', + 'scalar-valued': './functions/scalar-valued', + 'table-valued': './functions/table-valued', + 'tables': './tables', + 'triggers': './triggers', + 'views': './views', + 'table-valued-parameters': './user-defined-types/table-valued-parameters' + }, + idempotency: { + 'procs': 'if-exists-drop', + 'scalar-valued': 'if-exists-drop', + 'table-valued': 'if-exists-drop', + 'tables': 'if-not-exists', + 'triggers': 'if-exists-drop', + 'views': 'if-exists-drop', + 'table-valued-parameters': 'if-not-exists' + } }; /** @@ -53,23 +53,23 @@ export const configDefaults: Config = { * @param file Path and file name. */ export function safeFile(file: string): string { - return file.replace(/\//g, '_'); + return file.replace(/\//g, '_'); } /** * Get and parse config file. */ export function getConfig(): Config { - let config: Config; + let config: Config; - try { - config = fs.readJsonSync(configFile); - } catch (error) { - console.error('Could not find or parse config file. You can use the `init` command to create one!'); - process.exit(); - } + try { + config = fs.readJsonSync(configFile); + } catch (error) { + console.error('Could not find or parse config file. You can use the `init` command to create one!'); + process.exit(); + } - return deepmerge(configDefaults, config); + return deepmerge(configDefaults, config); } /** @@ -78,16 +78,16 @@ export function getConfig(): Config { * @param config Object to save for config file. */ export function setConfig(config: Config): void { - const content: string = JSON.stringify(config, null, 2); + const content: string = JSON.stringify(config, null, 2); - // save file - fs.outputFile(configFile, content, (error: Error) => { - if (error) { - return console.error(error); - } + // save file + fs.outputFile(configFile, content, (error: Error) => { + if (error) { + return console.error(error); + } - console.log('Config file created!'); - }); + console.log('Config file created!'); + }); } /** @@ -96,20 +96,20 @@ export function setConfig(config: Config): void { * @param config Config object used to search for connections. */ export function getConns(config: Config): Connection[] { - if (config.connection) { - // deprecated (v1.1.0) - console.warn(chalk.yellow('Warning! The config `connection` object is deprecated. Use `connections` instead.')); - - const legacyConn: string | Connection = config.connection; - config.connections = (isString(legacyConn) ? legacyConn : [legacyConn]); - } - - if (isString(config.connections)) { - // get form web config - return getWebConfigConns(config.connections); - } else { - return config.connections; - } + if (config.connection) { + // deprecated (v1.1.0) + console.warn(chalk.yellow('Warning! The config `connection` object is deprecated. Use `connections` instead.')); + + const legacyConn: string | Connection = config.connection; + config.connections = (isString(legacyConn) ? legacyConn : [legacyConn]); + } + + if (isString(config.connections)) { + // get form web config + return getWebConfigConns(config.connections); + } else { + return config.connections; + } } /** @@ -119,23 +119,22 @@ export function getConns(config: Config): Connection[] { * @param name Optional connection `name` to get. */ export function getConn(config: Config, name?: string): Connection { - const conns: Connection[] = getConns(config); - let conn: Connection; - - if (name) { - conn = conns.find(item => item.name.toLocaleLowerCase() === name.toLowerCase()); - } else { - // default to first - conn = conns[0]; - } - - if (!conn) { - const message: string = (name ? `Could not find connection by name '${name}'!` : 'Could not find default connection!'); - console.error(message); - process.exit(); - } - - return conn; + const conns: Connection[] = getConns(config); + let conn: Connection; + + if (name) { + conn = conns.find(item => item.name.toLocaleLowerCase() === name.toLowerCase()); + } else { + // default to first + conn = conns[0]; + } + + if (!conn) { + console.error(name ? `Could not find connection by name '${name}'!` : 'Could not find default connection!'); + process.exit(); + } + + return conn; } /** @@ -144,40 +143,40 @@ export function getConn(config: Config, name?: string): Connection { * @param file Optional relative path to web config. */ export function getWebConfigConns(file?: string): Connection[] { - const parser: xml2js.Parser = new xml2js.Parser(); - const webConfig: string = file || webConfigFile; - let content: string; - const conns: Connection[] = []; - - if (!fs.existsSync(webConfig)) { - // web config not found, use defaults - return; + const parser: xml2js.Parser = new xml2js.Parser(); + const webConfig: string = file || webConfigFile; + let content: string; + const conns: Connection[] = []; + + if (!fs.existsSync(webConfig)) { + // web config not found, use defaults + return; + } + + // read config file + content = fs.readFileSync(webConfig, 'utf-8'); + + // parse config file + parser.parseString(content, (err, result): void => { + if (err) { + console.error(err); + process.exit(); } - // read config file - content = fs.readFileSync(webConfig, 'utf-8'); - - // parse config file - parser.parseString(content, (err, result): void => { - if (err) { - console.error(err); - process.exit(); - } - - try { - const connStrings: any[] = result.configuration.connectionStrings[0].add; + try { + const connStrings: any[] = result.configuration.connectionStrings[0].add; - for (const item of connStrings) { - conns.push(parseConnString(item.$.name, item.$.connectionString)); - } + for (const item of connStrings) { + conns.push(parseConnString(item.$.name, item.$.connectionString)); + } - } catch (err) { - console.error('Could not parse connection strings from Web.config file!'); - process.exit(); - } - }); + } catch (err) { + console.error('Could not parse connection strings from Web.config file!'); + process.exit(); + } + }); - return (conns.length ? conns : undefined); + return (conns.length ? conns : undefined); } /** @@ -186,24 +185,24 @@ export function getWebConfigConns(file?: string): Connection[] { * @param config Config object used to search for connection. */ export function getFilesOrdered(config: Config): string[] { - const output: string[] = []; - const directories: string[] = [ - config.output.schemas, - config.output.tables, - config.output.views, - config.output['scalar-valued'], - config.output['table-valued'], - config.output.procs, - config.output.triggers, - config.output['table-valued-parameters'] - ]; - - for (const dir of directories) { - const files: string[] = glob.sync(`${config.output.root}/${dir}/**/*.sql`); - output.push(...files); - } - - return output; + const output: string[] = []; + const directories: string[] = [ + config.output.schemas, + config.output.tables, + config.output.views, + config.output['scalar-valued'], + config.output['table-valued'], + config.output.procs, + config.output.triggers, + config.output['table-valued-parameters'] + ]; + + for (const dir of directories) { + const files: string[] = glob.sync(`${config.output.root}/${dir}/**/*.sql`); + output.push(...files); + } + + return output; } /** @@ -213,36 +212,36 @@ export function getFilesOrdered(config: Config): string[] { * @param connString Connection string to parse. */ function parseConnString(name: string, connString: string): Connection { - const parts: string[] = connString.split(';'); - - // match connection parts - let server: string = parts.find(x => /^(server)/ig.test(x)); - let database: string = parts.find(x => /^(database)/ig.test(x)); - let user: string = parts.find(x => /^(uid)/ig.test(x)); - let password: string = parts.find(x => /^(password|pwd)/ig.test(x)); - let port: number; - - // get values - server = (server && server.split('=')[1]); - database = (database && database.split('=')[1]); - user = (user && user.split('=')[1]); - password = (password && password.split('=')[1]); - - // separate server and port - if (server) { - // format: `dev.example.com\instance,1435` - const sub: string[] = server.split(','); - - server = sub[0]; - port = parseInt(sub[1], 10) || undefined; - } - - return new Connection({ - name, - server, - database, - port, - user, - password - }); + const parts: string[] = connString.split(';'); + + // match connection parts + let server: string = parts.find(x => /^(server)/ig.test(x)); + let database: string = parts.find(x => /^(database)/ig.test(x)); + let user: string = parts.find(x => /^(uid)/ig.test(x)); + let password: string = parts.find(x => /^(password|pwd)/ig.test(x)); + let port: number; + + // get values + server = (server && server.split('=')[1]); + database = (database && database.split('=')[1]); + user = (user && user.split('=')[1]); + password = (password && password.split('=')[1]); + + // separate server and port + if (server) { + // format: `dev.example.com\instance,1435` + const sub: string[] = server.split(','); + + server = sub[0]; + port = parseInt(sub[1], 10) || undefined; + } + + return new Connection({ + name, + server, + database, + port, + user, + password + }); } diff --git a/src/index.ts b/src/index.ts index c6e2c4e..90c6e5a 100644 --- a/src/index.ts +++ b/src/index.ts @@ -12,33 +12,33 @@ import { push } from './commands/push'; updateNotifier({ pkg }).notify(); program - .command('init') - .description('Create default config file.') - .option('-f, --force', 'Overwrite existing config file, if present.') - .option('-s, --skip', 'Use defaults only and skip the option prompts.') - .option('-w, --webconfig [value]', 'Relative path to Web.config file.') - .action(init); + .command('init') + .description('Create default config file.') + .option('-f, --force', 'Overwrite existing config file, if present.') + .option('-s, --skip', 'Use defaults only and skip the option prompts.') + .option('-w, --webconfig [value]', 'Relative path to Web.config file.') + .action(init); program - .command('conns') - .description('List all available connections.') - .action(conns); + .command('conns') + .description('List all available connections.') + .action(conns); program - .command('pull [name]') - .description('Generate SQL files for all tables, stored procedures, functions, etc.') - .action(pull); + .command('pull [name]') + .description('Generate SQL files for all tables, stored procedures, functions, etc.') + .action(pull); program - .command('push [name]') - .description('Execute all scripts against the requested database.') - .action(push); + .command('push [name]') + .description('Execute all scripts against the requested database.') + .action(push); program - .command('cat') - .description('Concatenate all SQL files into a single file.') - .action(cat); + .command('cat') + .description('Concatenate all SQL files into a single file.') + .action(cat); program - .version((pkg as any).version) - .parse(process.argv); + .version((pkg as any).version) + .parse(process.argv); diff --git a/src/sql/record-set.ts b/src/sql/record-set.ts index b9c1d7d..68c2fde 100644 --- a/src/sql/record-set.ts +++ b/src/sql/record-set.ts @@ -2,18 +2,18 @@ * Base record set for record sets. */ export interface AbstractRecordSet { - object_id: number; - type: string; - schema: string; - name: string; + object_id: number; + type: string; + schema: string; + name: string; } /** * Mock dataset, properties from table query. */ export interface SchemaRecordSet { - name: string; - type: string; + name: string; + type: string; } /** @@ -32,65 +32,65 @@ export interface TvpRecordSet extends AbstractRecordSet { } * Dataset returned from column query. */ export interface ColumnRecordSet { - object_id: number; - name: string; - datatype: string; - max_length: number; - is_computed: boolean; - precision: number; - scale: string; - collation_name: string; - is_nullable: boolean; - definition: string; - is_identity: boolean; - seed_value: number; - increment_value: number; + object_id: number; + name: string; + datatype: string; + max_length: number; + is_computed: boolean; + precision: number; + scale: string; + collation_name: string; + is_nullable: boolean; + definition: string; + is_identity: boolean; + seed_value: number; + increment_value: number; } /** * Dataset returned from primary key query. */ export interface PrimaryKeyRecordSet { - object_id: number; - is_descending_key: boolean; - name: string; - column: string; + object_id: number; + is_descending_key: boolean; + name: string; + column: string; } /** * Dataset returned from foreign query. */ export interface ForeignKeyRecordSet { - object_id: number; - constraint_object_id: number; - is_not_trusted: boolean; - column: string; - reference: string; - name: string; - schema: string; - table: string; - delete_referential_action: number; - update_referential_action: number; + object_id: number; + constraint_object_id: number; + is_not_trusted: boolean; + column: string; + reference: string; + name: string; + schema: string; + table: string; + delete_referential_action: number; + update_referential_action: number; } /** * Dataset returned from index query. */ export interface IndexRecordSet { - object_id: number; - index_id: number; - is_descending_key: boolean; - is_included_column: boolean; - is_unique: boolean; - name: string; - column: string; - schema: string; - table: string; + object_id: number; + index_id: number; + is_descending_key: boolean; + is_included_column: boolean; + is_unique: boolean; + name: string; + column: string; + schema: string; + table: string; } /** * Dataset returned from object query. */ export interface ObjectRecordSet extends AbstractRecordSet { - text: string; + text: string; } diff --git a/src/sql/script.ts b/src/sql/script.ts index 578511d..9bbd67d 100644 --- a/src/sql/script.ts +++ b/src/sql/script.ts @@ -2,14 +2,14 @@ import { EOL } from 'os'; import { IdempotencyOption } from '../common/idempotency'; import { - AbstractRecordSet, - ColumnRecordSet, - ForeignKeyRecordSet, - IndexRecordSet, - ObjectRecordSet, - PrimaryKeyRecordSet, - SchemaRecordSet, - TableRecordSet + AbstractRecordSet, + ColumnRecordSet, + ForeignKeyRecordSet, + IndexRecordSet, + ObjectRecordSet, + PrimaryKeyRecordSet, + SchemaRecordSet, + TableRecordSet } from '../sql/record-set'; /** @@ -19,77 +19,78 @@ import { * @param type Idempotency prefix type. */ export function idempotency(item: AbstractRecordSet, type: IdempotencyOption): string { - let obj: string; - const objectId: string = `${item.schema}].[${item.name}`; - - item.type = item.type.trim(); - - // get proper object type for `drop` statement - switch (item.type) { - case 'U': - obj = 'table'; - break; - case 'TT': - obj = 'table'; - break; - case 'P': - obj = 'procedure'; - break; - case 'V': - obj = 'view'; - break; - case 'TF': - case 'IF': - case 'FN': - obj = 'function'; - break; - case 'TR': - obj = 'trigger'; - break; + let obj: string; + const objectId: string = `${item.schema}].[${item.name}`; + + item.type = item.type.trim(); + + // get proper object type for `drop` statement + switch (item.type) { + case 'U': + obj = 'table'; + break; + case 'TT': + obj = 'table'; + break; + case 'P': + obj = 'procedure'; + break; + case 'V': + obj = 'view'; + break; + case 'TF': + case 'IF': + case 'FN': + obj = 'function'; + break; + case 'TR': + obj = 'trigger'; + break; + } + + if (type === 'if-exists-drop') { + // if exists drop + if (item.type === 'TT') { + return [ + 'if exists (', + ' select * from sys.table_types as t', + ' join sys.schemas s on t.schema_id = s.schema_id', + ` where t.name = '${item.name}' and s.name = '${item.schema}'`, + ')', + `drop type [${objectId}]`, + 'go', + EOL + ].join(EOL); + } else { + return [ + `if exists (select * from sys.objects where object_id = object_id('[${objectId}]') and type = '${item.type}')`, + `drop ${obj} [${objectId}]`, + 'go', + EOL + ].join(EOL); } - - if (type === 'if-exists-drop') { - // if exists drop - if (item.type === 'TT') { - return [ - 'if exists (', - ' select * from sys.table_types as t', - ' join sys.schemas s on t.schema_id = s.schema_id', - ` where t.name = '${item.name}' and s.name = '${item.schema}'`, - ')', - `drop type [${objectId}]`, - 'go', - EOL - ].join(EOL); - } else { - return [ - `if exists (select * from sys.objects where object_id = object_id('[${objectId}]') and type = '${item.type}')`, - `drop ${obj} [${objectId}]`, - 'go', - EOL - ].join(EOL); - } - } else if (type === 'if-not-exists') { - // if not exists - if (item.type === 'TT') { - return [ - 'if exists (', - ' select * from sys.table_types as t', - ' join sys.schemas s on t.schema_id = s.schema_id', - ` where t.name = '${item.name}' and s.name = '${item.schema}'`, - ')', - '' - ].join(EOL); - } else { - return [ - `if not exists (select * from sys.objects where object_id = object_id('[${objectId}]') and type = '${item.type}')`, - '' - ].join(EOL); - } + } else if (type === 'if-not-exists') { + // if not exists + if (item.type === 'TT') { + return [ + 'if exists (', + ' select * from sys.table_types as t', + ' join sys.schemas s on t.schema_id = s.schema_id', + ` where t.name = '${item.name}' and s.name = '${item.schema}'`, + ')', + '' + ].join(EOL); + } else { + return [ + // tslint:disable-next-line:max-line-length + `if not exists (select * from sys.objects where object_id = object_id('[${objectId}]') and type = '${item.type}')`, + '' + ].join(EOL); } + } - // none - return ''; + // none + return ''; } /** @@ -98,14 +99,14 @@ export function idempotency(item: AbstractRecordSet, type: IdempotencyOption): s * @param item Object containing schema info. */ export function schema(item: SchemaRecordSet): string { - let output: string = ''; + let output: string = ''; - // idempotency - output += `if not exists (select * from sys.schemas where name = '${item.name}')`; - output += EOL; + // idempotency + output += `if not exists (select * from sys.schemas where name = '${item.name}')`; + output += EOL; - output += `exec('create schema ${item.name}')`; - return output; + output += `exec('create schema ${item.name}')`; + return output; } /** @@ -118,46 +119,46 @@ export function schema(item: SchemaRecordSet): string { * @param indexes Array of records from `sys.indexes` query. */ export function table( - item: TableRecordSet, - columns: ColumnRecordSet[], - primaryKeys: PrimaryKeyRecordSet[], - foreignKeys: ForeignKeyRecordSet[], - indexes: IndexRecordSet[] + item: TableRecordSet, + columns: ColumnRecordSet[], + primaryKeys: PrimaryKeyRecordSet[], + foreignKeys: ForeignKeyRecordSet[], + indexes: IndexRecordSet[] ): string { - let output: string = `create table [${item.schema}].[${item.name}]`; - output += EOL; - output += '('; + let output: string = `create table [${item.schema}].[${item.name}]`; + output += EOL; + output += '('; + output += EOL; + + // columns + for (const col of columns.filter(x => x.object_id === item.object_id)) { + output += ' ' + column(col); output += EOL; + } - // columns - for (const col of columns.filter(x => x.object_id === item.object_id)) { - output += ' ' + column(col); - output += EOL; - } + // primary keys + for (const pk of primaryKeys.filter(x => x.object_id === item.object_id)) { + output += ' ' + primaryKey(pk); + output += EOL; + } - // primary keys - for (const pk of primaryKeys.filter(x => x.object_id === item.object_id)) { - output += ' ' + primaryKey(pk); - output += EOL; - } + // foreign keys + for (const fk of foreignKeys.filter(x => x.object_id === item.object_id)) { + output += ' ' + foreignKey(fk); + output += EOL; + } - // foreign keys - for (const fk of foreignKeys.filter(x => x.object_id === item.object_id)) { - output += ' ' + foreignKey(fk); - output += EOL; - } + output += ')'; + output += EOL; + output += EOL; - output += ')'; - output += EOL; + // indexes + for (const ix of indexes.filter(x => x.object_id === item.object_id)) { + output += index(ix); output += EOL; + } - // indexes - for (const ix of indexes.filter(x => x.object_id === item.object_id)) { - output += index(ix); - output += EOL; - } - - return output; + return output; } /** @@ -167,25 +168,25 @@ export function table( * @param columns Array of records from `sys.columns` query. */ export function tvp( - item: TableRecordSet, - columns: ColumnRecordSet[] + item: TableRecordSet, + columns: ColumnRecordSet[] ): string { - let output: string = `create type [${item.schema}].[${item.name}] as table`; - output += EOL; - output += '('; + let output: string = `create type [${item.schema}].[${item.name}] as table`; + output += EOL; + output += '('; + output += EOL; + + // columns + for (const col of columns.filter(x => x.object_id === item.object_id)) { + output += ' ' + column(col); output += EOL; + } - // columns - for (const col of columns.filter(x => x.object_id === item.object_id)) { - output += ' ' + column(col); - output += EOL; - } + output += ')'; + output += EOL; + output += EOL; - output += ')'; - output += EOL; - output += EOL; - - return output; + return output; } /** @@ -194,53 +195,53 @@ export function tvp( * @param item Row from `sys.columns` query. */ function column(item: ColumnRecordSet): string { - let output: string = `[${item.name}]`; - - if (item.is_computed) { - output += ` as ${item.definition}`; - } - - output += ` ${item.datatype}`; - - switch (item.datatype) { - case 'varchar': - case 'char': - case 'varbinary': - case 'binary': - case 'text': - output += '(' + (item.max_length === -1 ? 'max' : item.max_length) + ')'; - break; - case 'nvarchar': - case 'nchar': - case 'ntext': - output += '(' + (item.max_length === -1 ? 'max' : item.max_length / 2) + ')'; - break; - case 'datetime2': - case 'time2': - case 'datetimeoffset': - output += '(' + item.scale + ')'; - break; - case 'decimal': - output += '(' + item.precision + ', ' + item.scale + ')'; - break; - } - - if (item.collation_name) { - output += ` collate ${item.collation_name}`; - } - - output += item.is_nullable ? ' null' : ' not null'; - - if (item.definition) { - output += ` default${item.definition}`; - } - - if (item.is_identity) { - output += ` identity(${item.seed_value || 0}, ${item.increment_value || 1})`; - } - - output += ','; - return output; + let output: string = `[${item.name}]`; + + if (item.is_computed) { + output += ` as ${item.definition}`; + } + + output += ` ${item.datatype}`; + + switch (item.datatype) { + case 'varchar': + case 'char': + case 'varbinary': + case 'binary': + case 'text': + output += '(' + (item.max_length === -1 ? 'max' : item.max_length) + ')'; + break; + case 'nvarchar': + case 'nchar': + case 'ntext': + output += '(' + (item.max_length === -1 ? 'max' : item.max_length / 2) + ')'; + break; + case 'datetime2': + case 'time2': + case 'datetimeoffset': + output += '(' + item.scale + ')'; + break; + case 'decimal': + output += '(' + item.precision + ', ' + item.scale + ')'; + break; + } + + if (item.collation_name) { + output += ` collate ${item.collation_name}`; + } + + output += item.is_nullable ? ' null' : ' not null'; + + if (item.definition) { + output += ` default${item.definition}`; + } + + if (item.is_identity) { + output += ` identity(${item.seed_value || 0}, ${item.increment_value || 1})`; + } + + output += ','; + return output; } /** @@ -249,7 +250,7 @@ function column(item: ColumnRecordSet): string { * @param item Row from `sys.primaryKeys` query. */ function primaryKey(item: PrimaryKeyRecordSet): string { - return `constraint [${item.name}] primary key ([${item.column}] ${item.is_descending_key ? 'desc' : 'asc'})`; + return `constraint [${item.name}] primary key ([${item.column}] ${item.is_descending_key ? 'desc' : 'asc'})`; } /** @@ -258,38 +259,38 @@ function primaryKey(item: PrimaryKeyRecordSet): string { * @param item Row from `sys.foreignKeys` query. */ function foreignKey(item: ForeignKeyRecordSet): string { - const objectId: string = `${item.schema}].[${item.table}`; - - let output: string = `alter table [${objectId}] with ${item.is_not_trusted ? 'nocheck' : 'check'}`; - output += ` add constraint [${item.name}] foreign key([${item.column}])`; - output += ` references [${objectId}] ([${item.reference}])`; - - switch (item.delete_referential_action) { - case 1: - output += ' on delete cascade'; - break; - case 2: - output += ' on delete set null'; - break; - case 3: - output += ' on delete set default'; - break; - } - - switch (item.update_referential_action) { - case 1: - output += ' on update cascade'; - break; - case 2: - output += ' on update set null'; - break; - case 3: - output += ' on update set default'; - break; - } - - output += ` alter table [${objectId}] check constraint [${item.name}]`; - return output; + const objectId: string = `${item.schema}].[${item.table}`; + + let output: string = `alter table [${objectId}] with ${item.is_not_trusted ? 'nocheck' : 'check'}`; + output += ` add constraint [${item.name}] foreign key([${item.column}])`; + output += ` references [${objectId}] ([${item.reference}])`; + + switch (item.delete_referential_action) { + case 1: + output += ' on delete cascade'; + break; + case 2: + output += ' on delete set null'; + break; + case 3: + output += ' on delete set default'; + break; + } + + switch (item.update_referential_action) { + case 1: + output += ' on update cascade'; + break; + case 2: + output += ' on update set null'; + break; + case 3: + output += ' on update set default'; + break; + } + + output += ` alter table [${objectId}] check constraint [${item.name}]`; + return output; } /** @@ -298,24 +299,25 @@ function foreignKey(item: ForeignKeyRecordSet): string { * @param item Row from `sys.indexes` query. */ function index(item: IndexRecordSet): string { - const objectId: string = `${item.schema}].[${item.table}`; - let output: string = ''; + const objectId: string = `${item.schema}].[${item.table}`; + let output: string = ''; - // idempotency - output += `if not exists (select * from sys.indexes where object_id = object_id('[${objectId}]') and name = '${item.name}')`; - output += EOL; + // idempotency + // tslint:disable-next-line:max-line-length + output += `if not exists (select * from sys.indexes where object_id = object_id('[${objectId}]') and name = '${item.name}')`; + output += EOL; - output += 'create'; + output += 'create'; - if (item.is_unique) { - output += ' unique'; - } + if (item.is_unique) { + output += ' unique'; + } - output += ` nonclustered index [${item.name}] on [${objectId}]`; - output += `([${item.column}] ${item.is_descending_key ? 'desc' : 'asc'})`; + output += ` nonclustered index [${item.name}] on [${objectId}]`; + output += `([${item.column}] ${item.is_descending_key ? 'desc' : 'asc'})`; - // todo (jbl): includes + // todo (jbl): includes - output += EOL; - return output; + output += EOL; + return output; } diff --git a/src/sql/sys.ts b/src/sql/sys.ts index 7018e82..6007ef3 100644 --- a/src/sql/sys.ts +++ b/src/sql/sys.ts @@ -2,176 +2,176 @@ * Get SQL table information. */ export const tableRead: string = ` - select - o.object_id, - o.type, - s.name as [schema], - o.name - from - sys.objects o - join sys.schemas s on o.schema_id = s.schema_id - where - o.type = 'U' - and o.is_ms_shipped = 0 - order by - s.name, - o.name + select + o.object_id, + o.type, + s.name as [schema], + o.name + from + sys.objects o + join sys.schemas s on o.schema_id = s.schema_id + where + o.type = 'U' + and o.is_ms_shipped = 0 + order by + s.name, + o.name `; /** * Get SQL column information. */ export const columnRead: string = ` - select - c.object_id, - c.name, - tp.name as [datatype], - c.max_length, - c.is_computed, - c.precision, - c.scale as scale, - c.collation_name, - c.is_nullable, - dc.definition, - ic.is_identity, - ic.seed_value, - ic.increment_value - from - sys.columns c - join sys.types tp on c.user_type_id = tp.user_type_id - left join sys.computed_columns cc on c.object_id = cc.object_id and c.column_id = cc.column_id - left join sys.default_constraints dc on - c.default_object_id != 0 - and c.object_id = dc.parent_object_id - and c.column_id = dc.parent_column_id - left join sys.identity_columns ic on c.is_identity = 1 and c.object_id = ic.object_id and c.column_id = ic.column_id - order by - ic.is_identity desc, - c.name + select + c.object_id, + c.name, + tp.name as [datatype], + c.max_length, + c.is_computed, + c.precision, + c.scale as scale, + c.collation_name, + c.is_nullable, + dc.definition, + ic.is_identity, + ic.seed_value, + ic.increment_value + from + sys.columns c + join sys.types tp on c.user_type_id = tp.user_type_id + left join sys.computed_columns cc on c.object_id = cc.object_id and c.column_id = cc.column_id + left join sys.default_constraints dc on + c.default_object_id != 0 + and c.object_id = dc.parent_object_id + and c.column_id = dc.parent_column_id + left join sys.identity_columns ic on c.is_identity = 1 and c.object_id = ic.object_id and c.column_id = ic.column_id + order by + ic.is_identity desc, + c.name `; /** * Get SQL primary key information. */ export const primaryKeyRead: string = ` - select - c.object_id, - ic.is_descending_key, - k.name, - c.name as [column] - from - sys.index_columns ic - join sys.columns c on c.object_id = ic.object_id and c.column_id = ic.column_id - left join sys.key_constraints k on k.parent_object_id = ic.object_id - where - ic.is_included_column = 0 - and ic.index_id = k.unique_index_id - and k.type = 'PK' + select + c.object_id, + ic.is_descending_key, + k.name, + c.name as [column] + from + sys.index_columns ic + join sys.columns c on c.object_id = ic.object_id and c.column_id = ic.column_id + left join sys.key_constraints k on k.parent_object_id = ic.object_id + where + ic.is_included_column = 0 + and ic.index_id = k.unique_index_id + and k.type = 'PK' `; /** * Get SQL foreign key information. */ export const foreignKeyRead: string = ` - select - ro.object_id, - k.constraint_object_id, - fk.is_not_trusted, - c.name as [column], - rc.name as [reference], - fk.name, - schema_name(ro.schema_id) as [schema], - ro.name as [table], - fk.delete_referential_action, - fk.update_referential_action - from - sys.foreign_key_columns k - join sys.columns rc on rc.object_id = k.referenced_object_id and rc.column_id = k.referenced_column_id - join sys.columns c on c.object_id = k.parent_object_id and c.column_id = k.parent_column_id - join sys.foreign_keys fk on fk.object_id = k.constraint_object_id - join sys.objects ro on ro.object_id = fk.referenced_object_id + select + ro.object_id, + k.constraint_object_id, + fk.is_not_trusted, + c.name as [column], + rc.name as [reference], + fk.name, + schema_name(ro.schema_id) as [schema], + ro.name as [table], + fk.delete_referential_action, + fk.update_referential_action + from + sys.foreign_key_columns k + join sys.columns rc on rc.object_id = k.referenced_object_id and rc.column_id = k.referenced_column_id + join sys.columns c on c.object_id = k.parent_object_id and c.column_id = k.parent_column_id + join sys.foreign_keys fk on fk.object_id = k.constraint_object_id + join sys.objects ro on ro.object_id = fk.referenced_object_id `; /** * Get SQL index information. */ export const indexRead: string = ` - select - ic.object_id, - ic.index_id, - ic.is_descending_key, - ic.is_included_column, - i.is_unique, - i.name, - c.name as [column], - schema_name(ro.schema_id) as [schema], - ro.name as [table] - from - sys.index_columns ic - join sys.columns c on ic.object_id = c.object_id and ic.column_id = c.column_id - join sys.indexes i on i.object_id = c.object_id and i.index_id = ic.index_id and i.is_primary_key = 0 and i.type = 2 - inner join sys.objects ro on ro.object_id = c.object_id - where - ro.is_ms_shipped = 0 - and ic.is_included_column = 0 - order by - ro.schema_id, - ro.name, - c.object_id + select + ic.object_id, + ic.index_id, + ic.is_descending_key, + ic.is_included_column, + i.is_unique, + i.name, + c.name as [column], + schema_name(ro.schema_id) as [schema], + ro.name as [table] + from + sys.index_columns ic + join sys.columns c on ic.object_id = c.object_id and ic.column_id = c.column_id + join sys.indexes i on i.object_id = c.object_id and i.index_id = ic.index_id and i.is_primary_key = 0 and i.type = 2 + inner join sys.objects ro on ro.object_id = c.object_id + where + ro.is_ms_shipped = 0 + and ic.is_included_column = 0 + order by + ro.schema_id, + ro.name, + c.object_id `; /** * Get SQL information for table-valued parameters. */ export const tvpRead: string = ` - select - o.object_id, - o.type, - s.name as [schema], - t.name - from sys.table_types t - inner join sys.objects o on o.object_id = t.type_table_object_id - join sys.schemas s on t.schema_id = s.schema_id - where - o.type = 'TT' - and t.is_user_defined = 1 - order by - s.name, - o.name + select + o.object_id, + o.type, + s.name as [schema], + t.name + from sys.table_types t + inner join sys.objects o on o.object_id = t.type_table_object_id + join sys.schemas s on t.schema_id = s.schema_id + where + o.type = 'TT' + and t.is_user_defined = 1 + order by + s.name, + o.name `; /** * Get SQL information for procs, triggers, functions, etc. */ export const objectRead: string = ` - select - so.name, - s.name as [schema], - so.type as [type], - stuff - ( - ( - select - cast(sc_inner.text as varchar(max)) - from - sys.objects so_inner - inner join syscomments sc_inner on sc_inner.id = so_inner.object_id - inner join sys.schemas s_inner on s_inner.schema_id = so_inner.schema_id - where - so_inner.name = so.name - and s_inner.name = s.name - for xml path(''), type - ).value('(./text())[1]', 'varchar(max)') - ,1 - ,0 - ,'' - ) as [text] - from - sys.objects so - inner join syscomments sc on sc.id = so.object_id and so.type in ('P', 'V', 'TF', 'IF', 'FN', 'TR') - inner join sys.schemas s on s.schema_id = so.schema_id - group by - so.name - ,s.name - ,so.type + select + so.name, + s.name as [schema], + so.type as [type], + stuff + ( + ( + select + cast(sc_inner.text as varchar(max)) + from + sys.objects so_inner + inner join syscomments sc_inner on sc_inner.id = so_inner.object_id + inner join sys.schemas s_inner on s_inner.schema_id = so_inner.schema_id + where + so_inner.name = so.name + and s_inner.name = s.name + for xml path(''), type + ).value('(./text())[1]', 'varchar(max)') + ,1 + ,0 + ,'' + ) as [text] + from + sys.objects so + inner join syscomments sc on sc.id = so.object_id and so.type in ('P', 'V', 'TF', 'IF', 'FN', 'TR') + inner join sys.schemas s on s.schema_id = so.schema_id + group by + so.name + ,s.name + ,so.type `; diff --git a/src/typings.d.ts b/src/typings.d.ts index 4b3471f..f3f5966 100644 --- a/src/typings.d.ts +++ b/src/typings.d.ts @@ -1,4 +1,4 @@ declare module "*.json" { - const value: any; - export default value; + const value: any; + export default value; } diff --git a/tsconfig.json b/tsconfig.json index 3fda767..e9b7b15 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -1,16 +1,16 @@ { - "compilerOptions": { - "lib": [ - "es6", - "dom" - ], - "module": "commonjs", - "moduleResolution": "node", - "outDir": "dist", - "sourceMap": true, - "target": "es5" - }, - "include": [ - "src/**/*.ts" - ] + "compilerOptions": { + "lib": [ + "es6", + "dom" + ], + "module": "commonjs", + "moduleResolution": "node", + "outDir": "dist", + "sourceMap": true, + "target": "es5" + }, + "include": [ + "src/**/*.ts" + ] } \ No newline at end of file diff --git a/tslint.json b/tslint.json index 6739dab..9cafb91 100644 --- a/tslint.json +++ b/tslint.json @@ -1,6 +1,6 @@ { - "extends": "tslint-config-unicorn-science", - "rules": { - "no-console": false - } + "extends": "tslint-config-unicorn-science", + "rules": { + "no-console": false + } } \ No newline at end of file