Skip to content
This repository has been archived by the owner on Sep 30, 2024. It is now read-only.

Commit

Permalink
feat: add support for table-valued parameters
Browse files Browse the repository at this point in the history
  • Loading branch information
justinlettau authored Feb 27, 2018
1 parent 34cfbbd commit 25d4c0d
Show file tree
Hide file tree
Showing 8 changed files with 137 additions and 17 deletions.
8 changes: 7 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ Example output:
...
./views
dbo.super-cool-view.sql
./user-defined-types/table-valued-parameters
dbo.insert-people-params.sql
...
```

Expand Down Expand Up @@ -152,6 +154,9 @@ Configuration options are stored in a `ssc.json` file.

// directory to script views (relative to root)
"views": "./views"

// directory to script table-valued parameters (relative to root)
"table-valued-parameters": "./user-defined-types/table-valued-parameters"
},

"idempotency": {
Expand All @@ -162,7 +167,8 @@ Configuration options are stored in a `ssc.json` file.
"table-valued": "if-exists-drop",
"tables": "if-not-exists",
"triggers": "if-exists-drop",
"views": "if-exists-drop"
"views": "if-exists-drop",
"table-valued-parameters": "if-not-exists";

}
}
Expand Down
34 changes: 31 additions & 3 deletions src/commands/pull.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,19 @@ import {
ObjectRecordSet,
PrimaryKeyRecordSet,
SchemaRecordSet,
TableRecordSet
TableRecordSet,
TvpRecordSet
} from '../sql/record-set';
import * as script from '../sql/script';
import { columnRead, foreignKeyRead, indexRead, objectRead, primaryKeyRead, tableRead } from '../sql/sys';
import {
columnRead,
foreignKeyRead,
indexRead,
objectRead,
primaryKeyRead,
tableRead,
tvpRead
} from '../sql/sys';

/**
* Generate SQL files for all tables, stored procedures, functions, etc.
Expand All @@ -44,7 +53,8 @@ export function pull(name: string): void {
pool.request().query(columnRead),
pool.request().query(primaryKeyRead),
pool.request().query(foreignKeyRead),
pool.request().query(indexRead)
pool.request().query(indexRead),
pool.request().query(tvpRead)
]).then(results => {
pool.close();
return results;
Expand Down Expand Up @@ -74,6 +84,7 @@ function scriptFiles(config: Config, results: sql.IResult<any>[]): void {
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
Expand Down Expand Up @@ -119,6 +130,19 @@ function scriptFiles(config: Config, results: sql.IResult<any>[]): void {
exclude(config, existing, dir);
}

// write files for user-defined table-valued parameter
for (const item of tvps) {
const file: string = util.safeFile(`${item.schema}.${item.name}.sql`);

if (!include(config, file)) {
continue;
}

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);
}
Expand Down Expand Up @@ -166,6 +190,10 @@ function createFile(config: Config, item: any, file: string, content: string): s
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';
}
Expand Down
1 change: 1 addition & 0 deletions src/common/idempotency.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ export interface IdempotencyConfig {
'tables'?: IdempotencyOption;
'triggers'?: IdempotencyOption;
'views'?: IdempotencyOption;
'table-valued-parameters'?: IdempotencyOption;
}

/**
Expand Down
1 change: 1 addition & 0 deletions src/common/output.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ export interface OutputConfig {
'tables'?: string;
'triggers'?: string;
'views'?: string;
'table-valued-parameters'?: string;
}
9 changes: 6 additions & 3 deletions src/common/utility.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,17 @@ export const configDefaults: Config = {
'table-valued': './functions/table-valued',
'tables': './tables',
'triggers': './triggers',
'views': './views'
'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'
'views': 'if-exists-drop',
'table-valued-parameters': 'if-not-exists'
}
};

Expand Down Expand Up @@ -192,7 +194,8 @@ export function getFilesOrdered(config: Config): string[] {
config.output['scalar-valued'],
config.output['table-valued'],
config.output.procs,
config.output.triggers
config.output.triggers,
config.output['table-valued-parameters']
];

for (const dir of directories) {
Expand Down
6 changes: 6 additions & 0 deletions src/sql/record-set.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,12 @@ export interface SchemaRecordSet {
// tslint:disable-next-line:no-empty-interface
export interface TableRecordSet extends AbstractRecordSet { }

/**
* Dataset returned from user-defined table-valued parameter query.
*/
// tslint:disable-next-line:no-empty-interface
export interface TvpRecordSet extends AbstractRecordSet { }

/**
* Dataset returned from column query.
*/
Expand Down
75 changes: 65 additions & 10 deletions src/sql/script.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,9 @@ export function idempotency(item: AbstractRecordSet, type: IdempotencyOption): s
case 'U':
obj = 'table';
break;
case 'TT':
obj = 'table';
break;
case 'P':
obj = 'procedure';
break;
Expand All @@ -47,18 +50,42 @@ export function idempotency(item: AbstractRecordSet, type: IdempotencyOption): s

if (type === 'if-exists-drop') {
// if exists drop
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 (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
return [
`if not exists (select * from sys.objects where object_id = object_id('[${objectId}]') and type = '${item.type}')`,
''
].join(EOL);
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);
}
}

// none
Expand Down Expand Up @@ -133,6 +160,34 @@ export function table(
return output;
}

/**
* Get script for user-defined table-valued parameter's column.
*
* @param item Row from `sys.columns` query.
* @param columns Array of records from `sys.columns` query.
*/
export function tvp(
item: TableRecordSet,
columns: ColumnRecordSet[]
): string {
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;
}

output += ')';
output += EOL;
output += EOL;

return output;
}

/**
* Get script for table's column.
*
Expand Down
20 changes: 20 additions & 0 deletions src/sql/sys.ts
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,26 @@ export const indexRead: string = `
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
`;

/**
* Get SQL information for procs, triggers, functions, etc.
*/
Expand Down

0 comments on commit 25d4c0d

Please sign in to comment.