From a54bf6ff39a107a5c27b23f0b040c59dfe8dd38b Mon Sep 17 00:00:00 2001 From: Justin Lettau Date: Sun, 4 Feb 2018 15:01:25 -0500 Subject: [PATCH] feat: add push command --- README.md | 11 +++++++++++ src/commands/cat.ts | 29 ++++++++-------------------- src/commands/push.ts | 45 +++++++++++++++++++++++++++++++++++++++++++ src/common/utility.ts | 26 +++++++++++++++++++++++++ src/index.ts | 6 ++++++ src/sql/script.ts | 2 +- 6 files changed, 97 insertions(+), 22 deletions(-) create mode 100644 src/commands/push.ts diff --git a/README.md b/README.md index e11c073..9da2b76 100644 --- a/README.md +++ b/README.md @@ -83,6 +83,17 @@ Example output: ... ``` +## Push +Execute all local scripts against the requested database. + +```bash +ssc push prod +``` + +WARNING: +All scripts are directly executed against the requested connetion. This can not be undone! Be sure +to backup your database before running the `push` command. + ## Cat Concatenate all SQL files into a single file. Outputs to `./_sql-database/cat.sql`. diff --git a/src/commands/cat.ts b/src/commands/cat.ts index b0fb227..6a935db 100644 --- a/src/commands/cat.ts +++ b/src/commands/cat.ts @@ -15,29 +15,16 @@ export function cat(): void { let output: string = ''; // order is important - const directories: string[] = [ - config.output.schemas, - config.output.tables, - config.output.views, - config.output['scalar-valued'], - config.output['table-valued'], - config.output.views, - config.output.procs, - config.output.triggers - ]; + const files: string[] = util.getFilesOrdered(config); - for (const dir of directories) { - const files: string[] = glob.sync(`${config.output.root}/${dir}/**/*.sql`); + 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); diff --git a/src/commands/push.ts b/src/commands/push.ts new file mode 100644 index 0000000..73113e5 --- /dev/null +++ b/src/commands/push.ts @@ -0,0 +1,45 @@ +import chalk from 'chalk'; +import * as fs from 'fs-extra'; +import * as glob from 'glob'; +import * as sql from 'mssql'; +import { EOL } from 'os'; + +import { Config } from '../common/config'; +import { Connection } from '../common/connection'; +import * as util from '../common/utility'; + +/** + * Execute all scripts against the requested database. + * + * @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); + + 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(); + + 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); + + 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)); +} diff --git a/src/common/utility.ts b/src/common/utility.ts index 69f20c1..83bb524 100644 --- a/src/common/utility.ts +++ b/src/common/utility.ts @@ -1,6 +1,7 @@ import chalk from 'chalk'; import * as deepmerge from 'deepmerge'; import * as fs from 'fs-extra'; +import * as glob from 'glob'; import * as path from 'path'; import { isString } from 'ts-util-is'; import * as xml2js from 'xml2js'; @@ -177,6 +178,31 @@ export function getWebConfigConns(file?: string): Connection[] { return (conns.length ? conns : undefined); } +/** + * Get all SQL files in correct execution order. + * + * @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 + ]; + + for (const dir of directories) { + const files: string[] = glob.sync(`${config.output.root}/${dir}/**/*.sql`); + output.push(...files); + } + + return output; +} + /** * Parse connection string into object. * diff --git a/src/index.ts b/src/index.ts index 2da09bc..c6e2c4e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -6,6 +6,7 @@ import { cat } from './commands/cat'; import { conns } from './commands/conns'; import { init } from './commands/init'; import { pull } from './commands/pull'; +import { push } from './commands/push'; // check for updates updateNotifier({ pkg }).notify(); @@ -28,6 +29,11 @@ program .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); + program .command('cat') .description('Concatenate all SQL files into a single file.') diff --git a/src/sql/script.ts b/src/sql/script.ts index e7d5377..513ddc4 100644 --- a/src/sql/script.ts +++ b/src/sql/script.ts @@ -77,7 +77,7 @@ export function schema(item: SchemaRecordSet): string { output += `if not exists (select * from sys.schemas where name = '${item.name}')`; output += EOL; - output += `create schema ${item.name}`; + output += `exec('create schema ${item.name}')`; return output; }