diff --git a/.gitignore b/.gitignore index e2a25d0..5b4363e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ -docs -node_modules/**/* -.idea* -*.log -.vscode \ No newline at end of file +docs +node_modules/**/* +.idea* +*.log +.vscode +package-lock.json \ No newline at end of file diff --git a/crud/create.js b/crud/create.js new file mode 100644 index 0000000..a98b99e --- /dev/null +++ b/crud/create.js @@ -0,0 +1,45 @@ +module.exports = (binding) => { + let columns = binding.fields.filter(f => !f.identity).map(f => `[${f.column}]`).join(', '); + return ` + CREATE PROCEDURE ${binding.spName} + @data ${binding.tt} READONLY + AS + SET NOCOUNT ON + DECLARE @result ${binding.tt} + BEGIN TRY + INSERT INTO ${binding.name} (${columns}) + OUTPUT INSERTED.* INTO @result + SELECT ${columns} + FROM @data + + SELECT 'data' AS resultSetName + SELECT * from @result + END TRY + BEGIN CATCH + IF @@TRANCOUNT != 0 + ROLLBACK TRANSACTION + DECLARE + @errmsg NVARCHAR(2048), + @severity TINYINT, + @state TINYINT, + @errno INT, + @proc sysname, + @lineno INT + SELECT + @errmsg = error_message(), + @severity = error_severity(), + @state = error_state(), + @errno = error_number(), + @proc = error_procedure(), + @lineno = error_line() + IF @errmsg NOT LIKE '***%' + BEGIN + SELECT @errmsg = '*** ' + COALESCE(QUOTENAME(@proc), '') + + ', Line ' + LTRIM(STR(@lineno)) + '. Errno ' + + LTRIM(STR(@errno)) + ': ' + @errmsg + END + RAISERROR('%s', @severity, @state, @errmsg) + RETURN 55555 + END CATCH + `; +}; diff --git a/crud/delete.js b/crud/delete.js new file mode 100644 index 0000000..f38a4a1 --- /dev/null +++ b/crud/delete.js @@ -0,0 +1,49 @@ +module.exports = (binding) => { + const pkField = binding.fields.find(f => !!f.identity); + if (!pkField) { + return ''; + } + const pk = `[${pkField.column}]`; + return ` + CREATE PROCEDURE ${binding.spName} + @data ${binding.tt} READONLY + AS + SET NOCOUNT ON + DECLARE @result ${binding.tt} + BEGIN TRY + DELETE target + OUTPUT DELETED.* INTO @result + FROM ${binding.name} target + JOIN @data source ON target.${pk} = source.${pk} + + SELECT 'data' AS resultSetName + SELECT * from @result + END TRY + BEGIN CATCH + IF @@TRANCOUNT != 0 + ROLLBACK TRANSACTION + DECLARE + @errmsg NVARCHAR(2048), + @severity TINYINT, + @state TINYINT, + @errno INT, + @proc sysname, + @lineno INT + SELECT + @errmsg = error_message(), + @severity = error_severity(), + @state = error_state(), + @errno = error_number(), + @proc = error_procedure(), + @lineno = error_line() + IF @errmsg NOT LIKE '***%' + BEGIN + SELECT @errmsg = '*** ' + COALESCE(QUOTENAME(@proc), '') + + ', Line ' + LTRIM(STR(@lineno)) + '. Errno ' + + LTRIM(STR(@errno)) + ': ' + @errmsg + END + RAISERROR('%s', @severity, @state, @errmsg) + RETURN 55555 + END CATCH + `; +}; diff --git a/crud/index.js b/crud/index.js new file mode 100644 index 0000000..7b564c6 --- /dev/null +++ b/crud/index.js @@ -0,0 +1,25 @@ +const crud = { + create: require('./create'), + read: require('./read'), + update: require('./update'), + delete: require('./delete') +}; + +module.exports = { + actions: Object.keys(crud), + generate: (binding, action) => { + let name; + let suffix; + if (binding.name.match(/]$/)) { + name = binding.name.slice(0, -1); + suffix = ']'; + } else { + name = binding.name; + suffix = ''; + } + binding.spName = `${name}.${action}${suffix}`; + binding.tt = `${name}TT${suffix}`; + binding.ttu = `${name}TTU${suffix}`; + return crud[action](binding); + } +}; diff --git a/crud/read.js b/crud/read.js new file mode 100644 index 0000000..5f4ca3f --- /dev/null +++ b/crud/read.js @@ -0,0 +1,3 @@ +module.exports = () => { + return ``; +}; diff --git a/crud/update.js b/crud/update.js new file mode 100644 index 0000000..5f4ca3f --- /dev/null +++ b/crud/update.js @@ -0,0 +1,3 @@ +module.exports = () => { + return ``; +}; diff --git a/index.js b/index.js index b363808..ad8a6a3 100644 --- a/index.js +++ b/index.js @@ -7,6 +7,9 @@ const fs = require('fs'); const fsplus = require('fs-plus'); const crypto = require('./crypto'); const mssqlQueries = require('./sql'); +const crud = require('./crud'); +const parserSP = require('./parsers/mssqlSP'); +const parserDefault = require('./parsers/mssqlDefault'); const xml2js = require('xml2js'); const uuid = require('uuid'); const path = require('path'); @@ -78,10 +81,13 @@ module.exports = function({parent}) { type: 'sql', cache: false, createTT: false, - allowQuery: false, - retry: 10000, tableToType: {}, skipTableType: [], + createCRUD: false, + tableToCRUD: {}, + skipCRUD: {}, + allowQuery: false, + retry: 10000, paramsOutName: 'out', doc: false, db: { @@ -165,6 +171,18 @@ module.exports = function({parent}) { return obj; }, {})); } + + if (typeof this.config.createCRUD === 'object') { + // e.g {'namespace.entity': ['create', 'read', 'update', 'delete']} + // or {'namespace.entity': true} + let crudTables = Object.keys(this.config.createCRUD); + if (crudTables.length) { + Object.assign(this.config.tableToCRUD, crudTables.reduce((obj, tableName) => { + obj[tableName.toLowerCase()] = this.config.createCRUD[tableName]; + return obj; + }, {})); + } + } return Promise.resolve() .then(() => parent && parent.prototype.start.apply(this, Array.prototype.slice.call(arguments))) .then(this.connect.bind(this)) @@ -381,13 +399,11 @@ module.exports = function({parent}) { let busConfig = flatten(this.bus.config); function replaceAuditLog(statement) { - let parserSP = require('./parsers/mssqlSP'); let binding = parserSP.parse(statement); return statement.trim().replace(AUDIT_LOG, mssqlQueries.auditLog(binding)); } function replaceCallParams(statement) { - let parserSP = require('./parsers/mssqlSP'); let binding = parserSP.parse(statement); return statement.trim().replace(CALL_PARAMS, mssqlQueries.callParams(binding)); } @@ -430,7 +446,6 @@ module.exports = function({parent}) { function tableToType(statement) { if (statement.match(/^CREATE\s+TABLE/i)) { - let parserSP = require('./parsers/mssqlSP'); let binding = parserSP.parse(statement); if (binding.type === 'table') { let name = binding.name.match(/]$/) ? binding.name.slice(0, -1) + 'TT]' : binding.name + 'TT'; @@ -448,10 +463,19 @@ module.exports = function({parent}) { return ''; } + function tableToCRUD(statement, action) { + if (statement.match(/^CREATE\s+TABLE/i)) { + let binding = parserSP.parse(statement); + if (binding.type === 'table') { + return crud.generate(binding, action); + } + } + return ''; + } + function tableToTTU(statement) { let result = ''; if (statement.match(/^CREATE\s+TABLE/i)) { - let parserSP = require('./parsers/mssqlSP'); let binding = parserSP.parse(statement); if (binding.type === 'table') { let name = binding.name.match(/]$/) ? binding.name.slice(0, -1) + 'TTU]' : binding.name + 'TTU'; @@ -477,7 +501,6 @@ module.exports = function({parent}) { function getSource(statement, fileName, objectName) { statement = preProcess(statement, fileName, objectName); if (statement.trim().match(/^CREATE\s+TYPE/i)) { - let parserSP = require('./parsers/mssqlSP'); let binding = parserSP.parse(statement); if (binding && binding.type === 'table type') { return binding.fields.map(fieldSource).join('\r\n'); @@ -522,6 +545,20 @@ module.exports = function({parent}) { return (self.config.createTT === true || self.includesConfig('tableToType', tableName, false)) && !self.includesConfig('skipTableType', tableName, false); } + function shouldCreateCRUD(tableName, action) { + let includesConfig = (name) => { + let config = self.config[name] && self.config[name][tableName]; + if (typeof config === 'boolean') { + return config; + } + if (Array.isArray(config)) { + return config.indexOf(action) !== -1; + } + return false; + }; + return (self.config.createCRUD === true || includesConfig('tableToCRUD')) && !includesConfig('skipCRUD'); + } + function retrySchemaUpdate(failedQueue) { let newFailedQueue = []; let request = self.getRequest(); @@ -630,6 +667,25 @@ module.exports = function({parent}) { }); } } + crud.actions.forEach((action) => { + if (!objectIds[`${objectId}.${action}`] && shouldCreateCRUD(objectId, action)) { + let sp = tableToCRUD(fileContent.trim().replace(/^ALTER /i, 'CREATE '), action); + if (sp) { + let context = { + objectName: `${objectName}.${action}`, + objectId: `${objectId}.${action}` + }; + schemaConfig.linkSP && (objectList[context.objectId] = fileName); + addQuery(queries, { + fileName: fileName, + objectName: context.objectName, + objectId: context.objectId, + fileContent: sp, + createStatement: sp + }); + } + } + }); }); let request = self.getRequest(); @@ -1067,7 +1123,6 @@ module.exports = function({parent}) { SqlPort.prototype.linkSP = function(schema) { if (schema.parseList.length) { - let parserSP = require('./parsers/mssqlSP'); schema.parseList.forEach(function(procedure) { let binding = parserSP.parse(procedure.source); let flatName = binding.name.replace(/[[\]]/g, ''); @@ -1186,7 +1241,6 @@ module.exports = function({parent}) { return prev; }, schema); result.recordsets[1].reduce(function(prev, cur) { // extract columns of user defined table types - let parserDefault = require('./parsers/mssqlDefault'); changeRowVersionType(cur); if (!(mssql[cur.type.toUpperCase()] instanceof Function)) { throw errors.unexpectedColumnType({ @@ -1267,7 +1321,6 @@ module.exports = function({parent}) { this.checkConnection(); let self = this; let schemas = this.getSchema(); - let parserSP = require('./parsers/mssqlSP'); return new Promise(function(resolve, reject) { let docList = []; let promise = Promise.resolve(); diff --git a/package.json b/package.json index 36eaa8f..4037818 100644 --- a/package.json +++ b/package.json @@ -6,11 +6,14 @@ "fs-plus": "3.0.2", "json-stringify-deterministic": "1.0.1", "lodash.merge": "4.6.0", - "through2": "2.0.3", "mssql": "4.1.0", + "through2": "2.0.3", "uuid": "3.2.1", "xml2js": "0.4.19" }, + "peerDependencies": { + "tedious": "^2.0.0" + }, "devDependencies": { "ut-tools": "^5.32.5", "pegjs": "0.10.0" diff --git a/sql.js b/sql.js deleted file mode 100644 index acb6c07..0000000 --- a/sql.js +++ /dev/null @@ -1,803 +0,0 @@ -'use strict'; -module.exports = { - getHash: function() { - return `IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL SELECT dbo.utSchemaHash() hash`; - }, - setHash: function(hash) { - return `CREATE FUNCTION dbo.utSchemaHash() RETURNS VARCHAR(64) AS BEGIN RETURN '${hash}' END`; - }, - dropHash: function() { - return `IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL DROP FUNCTION dbo.utSchemaHash`; - }, - loadSchema: function(partial) { - return ` - SELECT - o.create_date, - c.id, - c.colid, - RTRIM(o.[type]) [type], - SCHEMA_NAME(o.schema_id) [namespace], - o.Name AS [name], - SCHEMA_NAME(o.schema_id) + '.' + o.Name AS [full], - CASE o.[type] - WHEN 'SN' THEN 'DROP SYNONYM [' + SCHEMA_NAME(o.schema_id) + '].[' + o.Name + - '] CREATE SYNONYM [' + SCHEMA_NAME(o.schema_id) + '].[' + o.Name + '] FOR ' + s.base_object_name - ELSE ${partial ? `LEFT(c.text, CASE CHARINDEX(CHAR(10)+'AS'+CHAR(13), c.text) WHEN 0 THEN 2500 ELSE CHARINDEX(CHAR(10)+'AS'+CHAR(13), c.text) + 10 END)` : 'c.text'} - END AS [source] - - FROM - sys.objects o - LEFT JOIN - dbo.syscomments c on o.object_id = c.id - LEFT JOIN - sys.synonyms s on s.object_id = o.object_id - WHERE - o.type IN (${partial ? `'P'` : `'V', 'P', 'FN','F','IF','SN','TF','TR','U'`}) AND - user_name(objectproperty(o.object_id, 'OwnerId')) in (USER_NAME(),'dbo') AND - objectproperty(o.object_id, 'IsMSShipped') = 0 AND - SCHEMA_NAME(o.schema_id) != 'dbo' - ${partial ? 'AND ISNULL(c.colid, 1)=1' : ''} - UNION ALL - SELECT 0,0,0,'S',name,NULL,NULL,NULL FROM sys.schemas WHERE principal_id = USER_ID() - UNION ALL - SELECT - 0,0,0,'T',SCHEMA_NAME(t.schema_id)+'.'+t.name,NULL,NULL,NULL - FROM - sys.types t - JOIN - sys.schemas s ON s.principal_id = USER_ID() AND s.schema_id=t.schema_id - WHERE - t.is_user_defined=1 - ORDER BY - 1, 2, 3 - - SELECT - SCHEMA_NAME(types.schema_id) + '.' + types.name name, - c.name [column], - st.name type, - CASE - WHEN st.name in ('decimal','numeric') then CAST(c.[precision] AS VARCHAR) - WHEN st.name in ('datetime2','time','datetimeoffset') then CAST(c.[scale] AS VARCHAR) - WHEN st.name in ('varchar','varbinary','char','binary') AND c.max_length>=0 THEN CAST(c.max_length as VARCHAR) - WHEN st.name in ('nvarchar','nchar') AND c.max_length>=0 THEN CAST(c.max_length/2 as VARCHAR) - WHEN st.name in ('varchar','varbinary','char','binary','nvarchar','nchar') AND c.max_length<0 THEN 'max' - END [length], - CASE - WHEN st.name in ('decimal','numeric') then c.scale - END scale, - object_definition(c.default_object_id) [default] - FROM - sys.table_types types - JOIN - sys.columns c ON types.type_table_object_id = c.object_id - JOIN - sys.systypes AS st ON st.xtype = c.system_type_id - WHERE - types.is_user_defined = 1 AND st.name <> 'sysname' - ORDER BY - 1,c.column_id - - SELECT - 1 sort, - s.name + '.' + o.name [name], - 'IF (OBJECT_ID(''[' + s.name + '].[' + o.name + ']'') IS NOT NULL) DROP ' + CASE o.type WHEN 'FN' THEN 'FUNCTION' ELSE 'PROCEDURE' END + ' [' + s.name + '].[' + o.name + ']' [drop], - p.name [param], - SCHEMA_NAME(t.schema_id) + '.' + t.name [type] - FROM - sys.schemas s - JOIN - sys.objects o ON o.schema_id = s.schema_id - JOIN - sys.parameters p ON p.object_id = o.object_id - JOIN - sys.types t ON p.user_type_id = t.user_type_id AND t.is_user_defined=1 - WHERE - user_name(objectproperty(o.object_id, 'OwnerId')) in (USER_NAME(),'dbo') - UNION - SELECT - 2, - s.name + '.' + t.name [name], - 'DROP TYPE [' + s.name + '].[' + t.name + ']' [drop], - NULL [param], - SCHEMA_NAME(t.schema_id) + '.' + t.name [type] - FROM - sys.schemas s - JOIN - sys.types t ON t.schema_id=s.schema_id and t.is_user_defined=1 - WHERE - user_name(s.principal_id) in (USER_NAME(),'dbo') - ORDER BY 1`; - }, - refreshView: function(drop) { - return ` - SET NOCOUNT ON; - - DECLARE @ViewName VARCHAR(255); - DECLARE @error_table TABLE - ( - view_name VARCHAR(255) , - error_msg VARCHAR(MAX) - ); - - DECLARE view_cursor CURSOR FAST_FORWARD - FOR - --- Get all the user defined views with no schema binding on them - SELECT DISTINCT - '[' + ss.name + '].[' + av.name +']' AS ViewName - FROM sys.all_views av - JOIN sys.schemas ss ON av.schema_id = ss.schema_id - WHERE OBJECTPROPERTY(av.[object_id], 'IsSchemaBound') <> 1 - AND av.Is_Ms_Shipped = 0 - - OPEN view_cursor - - FETCH NEXT FROM view_cursor - INTO @ViewName - - WHILE @@FETCH_STATUS = 0 - BEGIN - - BEGIN TRY - -- Refresh the view - EXEC sp_refreshview @ViewName; - - -- RAISERROR('%s', 10, 1, @ViewName) WITH NOWAIT; - - END TRY - BEGIN CATCH - IF @@trancount > 0 ROLLBACK TRANSACTION - --- Insert all the errors - IF (1=${drop ? 1 : 0}) - BEGIN - EXEC ('DROP VIEW ' + @ViewName) - END ELSE - BEGIN - INSERT INTO - @error_table(view_name, error_msg) - SELECT @ViewName, ERROR_MESSAGE(); - END - - END CATCH - - FETCH NEXT FROM view_cursor INTO @ViewName; - - END - - --- Check if there was an error - IF EXISTS (SELECT TOP 1 1 FROM @error_table) - BEGIN - SELECT view_name , - error_msg - FROM @error_table; - END - - CLOSE view_cursor - DEALLOCATE view_cursor - - IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL SELECT dbo.utSchemaHash() hash - - SET NOCOUNT OFF;`; - }, - auditLog: function(statement) { - if (!statement.params) { - return; - } - var sql = ' DECLARE @callParams XML = ( SELECT '; - statement.params.map(function(param) { - if (param.def.type === 'table') { - sql += `(SELECT * from @${param.name} rows FOR XML AUTO, TYPE) [${param.name}], `; - } else { - sql += `@${param.name} [${param.name}], `; - } - }); - sql = sql.replace(/,\s$/, ' '); - sql += 'FOR XML RAW(\'params\'),TYPE) EXEC core.auditCall @procid = @@PROCID, @params=@callParams'; - return sql; - }, - callParams: function(statement) { - if (!statement.params) { - return; - } - var sql = 'DECLARE @callParams XML = ( SELECT '; - statement.params.map(function(param) { - if (param.def.type === 'table') { - sql += `(SELECT * from @${param.name} rows FOR XML AUTO, TYPE) [${param.name}], `; - } else { - sql += `@${param.name} [${param.name}], `; - } - }); - sql = sql.replace(/,\s$/, ' '); - sql += 'FOR XML RAW(\'params\'),TYPE)'; - return sql; - }, - createDatabase: function(name, user) { - return ` - IF NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = '${name}') - BEGIN - CREATE DATABASE [${name}] - ALTER DATABASE [${name}] SET READ_COMMITTED_SNAPSHOT ON - ALTER DATABASE [${name}] SET AUTO_SHRINK OFF - END`; - }, - createUser: function(name, user, password) { - return ` - IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '${user}') - BEGIN - CREATE LOGIN [${user}] WITH PASSWORD = N'${password}', CHECK_POLICY = OFF - END - USE [${name}] - IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = '${user}') - BEGIN - CREATE USER [${user}] FOR LOGIN [${user}] - END - IF (is_rolemember('db_owner', '${user}') IS NULL OR is_rolemember('db_owner', '${user}') = 0) - BEGIN - EXEC sp_addrolemember 'db_owner', '${user}' - END - IF NOT EXISTS (SELECT 1 FROM sys.server_principals AS pr - INNER JOIN sys.server_permissions AS pe ON pe.grantee_principal_id = pr.principal_id - WHERE permission_name = N'VIEW SERVER STATE' AND state = N'G' AND pr.name = N'${user}') - BEGIN - USE [master] - GRANT VIEW SERVER STATE to [${user}] - END - `; - }, - enableDatabaseDiagrams: function(name) { - return ` - USE [${name}] - - IF OBJECT_ID(N'dbo.sp_upgraddiagrams') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_upgraddiagrams - AS - BEGIN - IF OBJECT_ID(N''dbo.sysdiagrams'') IS NOT NULL - return 0; - - CREATE TABLE dbo.sysdiagrams - ( - name sysname NOT NULL, - principal_id int NOT NULL, -- we may change it to varbinary(85) - diagram_id int PRIMARY KEY IDENTITY, - version int, - - definition varbinary(max) - CONSTRAINT UK_principal_name UNIQUE - ( - principal_id, - name - ) - ); - - /* Add this if we need to have some form of extended properties for diagrams */ - IF OBJECT_ID(N''dbo.sysdiagram_properties'') IS NULL - BEGIN - CREATE TABLE dbo.sysdiagram_properties - ( - diagram_id int, - name sysname, - value varbinary(max) NOT NULL - ) - END - - IF OBJECT_ID(N''dbo.dtproperties'') IS NOT NULL - begin - insert into dbo.sysdiagrams - ( - [name], - [principal_id], - [version], - [definition] - ) - select - convert(sysname, dgnm.[uvalue]), - DATABASE_PRINCIPAL_ID(N''dbo''), -- will change to the sid of sa - 0, -- zero for old format, dgdef.[version], - dgdef.[lvalue] - from dbo.[dtproperties] dgnm - inner join dbo.[dtproperties] dggd on dggd.[property] = ''DtgSchemaGUID'' and dggd.[objectid] = dgnm.[objectid] - inner join dbo.[dtproperties] dgdef on dgdef.[property] = ''DtgSchemaDATA'' and dgdef.[objectid] = dgnm.[objectid] - - where dgnm.[property] = ''DtgSchemaNAME'' and dggd.[uvalue] like N''_EA3E6268-D998-11CE-9454-00AA00A3F36E_'' - return 2; - end - return 1; - END - ' - END - - -- This sproc could be executed by any other users than dbo - IF IS_MEMBER('db_owner') = 1 - EXEC dbo.sp_upgraddiagrams; - - IF OBJECT_ID(N'dbo.sp_helpdiagrams') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_helpdiagrams - ( - @diagramname sysname = NULL, - @owner_id int = NULL - ) - WITH EXECUTE AS N''dbo'' - AS - BEGIN - DECLARE @user sysname - DECLARE @dboLogin bit - EXECUTE AS CALLER; - SET @user = USER_NAME(); - SET @dboLogin = CONVERT(bit,IS_MEMBER(''db_owner'')); - REVERT; - SELECT - [Database] = DB_NAME(), - [Name] = name, - [ID] = diagram_id, - [Owner] = USER_NAME(principal_id), - [OwnerID] = principal_id - FROM - sysdiagrams - WHERE - (@dboLogin = 1 OR USER_NAME(principal_id) = @user) AND - (@diagramname IS NULL OR name = @diagramname) AND - (@owner_id IS NULL OR principal_id = @owner_id) - ORDER BY - 4, 5, 1 - END - ' - - GRANT EXECUTE ON dbo.sp_helpdiagrams TO public - DENY EXECUTE ON dbo.sp_helpdiagrams TO guest - END - - IF OBJECT_ID(N'dbo.sp_helpdiagramdefinition') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_helpdiagramdefinition - ( - @diagramname sysname, - @owner_id int = null - ) - WITH EXECUTE AS N''dbo'' - AS - BEGIN - set nocount on - - declare @theId int - declare @IsDbo int - declare @DiagId int - declare @UIDFound int - - if(@diagramname is null) - begin - RAISERROR (N''E_INVALIDARG'', 16, 1); - return -1 - end - - execute as caller; - select @theId = DATABASE_PRINCIPAL_ID(); - select @IsDbo = IS_MEMBER(N''db_owner''); - if(@owner_id is null) - select @owner_id = @theId; - revert; - - select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname; - if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId )) - begin - RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1); - return -3 - end - - select version, definition FROM dbo.sysdiagrams where diagram_id = @DiagId ; - return 0 - END - ' - GRANT EXECUTE ON dbo.sp_helpdiagramdefinition TO public - DENY EXECUTE ON dbo.sp_helpdiagramdefinition TO guest - END - - IF OBJECT_ID(N'dbo.sp_creatediagram') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_creatediagram - ( - @diagramname sysname, - @owner_id int = null, - @version int, - @definition varbinary(max) - ) - WITH EXECUTE AS ''dbo'' - AS - BEGIN - set nocount on - - declare @theId int - declare @retval int - declare @IsDbo int - declare @userName sysname - if(@version is null or @diagramname is null) - begin - RAISERROR (N''E_INVALIDARG'', 16, 1); - return -1 - end - - execute as caller; - select @theId = DATABASE_PRINCIPAL_ID(); - select @IsDbo = IS_MEMBER(N''db_owner''); - revert; - - if @owner_id is null - begin - select @owner_id = @theId; - end - else - begin - if @theId <> @owner_id - begin - if @IsDbo = 0 - begin - RAISERROR (N''E_INVALIDARG'', 16, 1); - return -1 - end - select @theId = @owner_id - end - end - -- next 2 line only for test, will be removed after define name unique - if EXISTS(select diagram_id from dbo.sysdiagrams where principal_id = @theId and name = @diagramname) - begin - RAISERROR (''The name is already used.'', 16, 1); - return -2 - end - - insert into dbo.sysdiagrams(name, principal_id , version, definition) - VALUES(@diagramname, @theId, @version, @definition) ; - - select @retval = @@IDENTITY - return @retval - END - ' - GRANT EXECUTE ON dbo.sp_creatediagram TO public - DENY EXECUTE ON dbo.sp_creatediagram TO guest - END - - IF OBJECT_ID(N'dbo.sp_renamediagram') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_renamediagram - ( - @diagramname sysname, - @owner_id int = null, - @new_diagramname sysname - - ) - WITH EXECUTE AS ''dbo'' - AS - BEGIN - set nocount on - declare @theId int - declare @IsDbo int - - declare @UIDFound int - declare @DiagId int - declare @DiagIdTarg int - declare @u_name sysname - if((@diagramname is null) or (@new_diagramname is null)) - begin - RAISERROR (''Invalid value'', 16, 1); - return -1 - end - - EXECUTE AS CALLER; - select @theId = DATABASE_PRINCIPAL_ID(); - select @IsDbo = IS_MEMBER(N''db_owner''); - if(@owner_id is null) - select @owner_id = @theId; - REVERT; - - select @u_name = USER_NAME(@owner_id) - - select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname - if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId)) - begin - RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1) - return -3 - end - - -- if((@u_name is not null) and (@new_diagramname = @diagramname)) -- nothing will change - -- return 0; - - if(@u_name is null) - select @DiagIdTarg = diagram_id from dbo.sysdiagrams where principal_id = @theId and name = @new_diagramname - else - select @DiagIdTarg = diagram_id from dbo.sysdiagrams where principal_id = @owner_id and name = @new_diagramname - - if((@DiagIdTarg is not null) and @DiagId <> @DiagIdTarg) - begin - RAISERROR (''The name is already used.'', 16, 1); - return -2 - end - - if(@u_name is null) - update dbo.sysdiagrams set [name] = @new_diagramname, principal_id = @theId where diagram_id = @DiagId - else - update dbo.sysdiagrams set [name] = @new_diagramname where diagram_id = @DiagId - return 0 - END - ' - GRANT EXECUTE ON dbo.sp_renamediagram TO public - DENY EXECUTE ON dbo.sp_renamediagram TO guest - END - - IF OBJECT_ID(N'dbo.sp_alterdiagram') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_alterdiagram - ( - @diagramname sysname, - @owner_id int = null, - @version int, - @definition varbinary(max) - ) - WITH EXECUTE AS ''dbo'' - AS - BEGIN - set nocount on - - declare @theId int - declare @retval int - declare @IsDbo int - declare @UIDFound int - declare @DiagId int - declare @ShouldChangeUID int - - if(@diagramname is null) - begin - RAISERROR (''Invalid ARG'', 16, 1) - return -1 - end - - execute as caller; - select @theId = DATABASE_PRINCIPAL_ID(); - select @IsDbo = IS_MEMBER(N''db_owner''); - if(@owner_id is null) - select @owner_id = @theId; - revert; - - select @ShouldChangeUID = 0 - select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname - - if(@DiagId IS NULL or (@IsDbo = 0 and @theId <> @UIDFound)) - begin - RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1); - return -3 - end - - if(@IsDbo <> 0) - begin - if(@UIDFound is null or USER_NAME(@UIDFound) is null) -- invalid principal_id - begin - select @ShouldChangeUID = 1 ; - end - end - - -- update dds data - update dbo.sysdiagrams set definition = @definition where diagram_id = @DiagId ; - - -- change owner - if(@ShouldChangeUID = 1) - update dbo.sysdiagrams set principal_id = @theId where diagram_id = @DiagId ; - - -- update dds version - if(@version is not null) - update dbo.sysdiagrams set version = @version where diagram_id = @DiagId ; - - return 0 - END - ' - - GRANT EXECUTE ON dbo.sp_alterdiagram TO public - DENY EXECUTE ON dbo.sp_alterdiagram TO guest - END - - IF OBJECT_ID(N'dbo.sp_dropdiagram') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE PROCEDURE dbo.sp_dropdiagram - ( - @diagramname sysname, - @owner_id int = null - ) - WITH EXECUTE AS ''dbo'' - AS - BEGIN - set nocount on - declare @theId int - declare @IsDbo int - - declare @UIDFound int - declare @DiagId int - - if(@diagramname is null) - begin - RAISERROR (''Invalid value'', 16, 1); - return -1 - end - - EXECUTE AS CALLER; - select @theId = DATABASE_PRINCIPAL_ID(); - select @IsDbo = IS_MEMBER(N''db_owner''); - if(@owner_id is null) - select @owner_id = @theId; - REVERT; - - select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname - if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId)) - begin - RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1) - return -3 - end - - delete from dbo.sysdiagrams where diagram_id = @DiagId; - - return 0; - END - ' - GRANT EXECUTE ON dbo.sp_dropdiagram TO public - DENY EXECUTE ON dbo.sp_dropdiagram TO guest - END - - IF OBJECT_ID(N'dbo.fn_diagramobjects') IS NULL and IS_MEMBER('db_owner') = 1 - BEGIN - EXEC sp_executesql N' - CREATE FUNCTION dbo.fn_diagramobjects() - RETURNS int - WITH EXECUTE AS N''dbo'' - AS - BEGIN - declare @id_upgraddiagrams int - declare @id_sysdiagrams int - declare @id_helpdiagrams int - declare @id_helpdiagramdefinition int - declare @id_creatediagram int - declare @id_renamediagram int - declare @id_alterdiagram int - declare @id_dropdiagram int - declare @InstalledObjects int - - select @InstalledObjects = 0 - - select @id_upgraddiagrams = object_id(N''dbo.sp_upgraddiagrams''), - @id_sysdiagrams = object_id(N''dbo.sysdiagrams''), - @id_helpdiagrams = object_id(N''dbo.sp_helpdiagrams''), - @id_helpdiagramdefinition = object_id(N''dbo.sp_helpdiagramdefinition''), - @id_creatediagram = object_id(N''dbo.sp_creatediagram''), - @id_renamediagram = object_id(N''dbo.sp_renamediagram''), - @id_alterdiagram = object_id(N''dbo.sp_alterdiagram''), - @id_dropdiagram = object_id(N''dbo.sp_dropdiagram'') - - - if @id_upgraddiagrams is not null - select @InstalledObjects = @InstalledObjects + 1 - if @id_sysdiagrams is not null - select @InstalledObjects = @InstalledObjects + 2 - if @id_helpdiagrams is not null - select @InstalledObjects = @InstalledObjects + 4 - if @id_helpdiagramdefinition is not null - select @InstalledObjects = @InstalledObjects + 8 - if @id_creatediagram is not null - select @InstalledObjects = @InstalledObjects + 16 - if @id_renamediagram is not null - select @InstalledObjects = @InstalledObjects + 32 - if @id_alterdiagram is not null - select @InstalledObjects = @InstalledObjects + 64 - if @id_dropdiagram is not null - select @InstalledObjects = @InstalledObjects + 128 - - return @InstalledObjects - END - ' - - GRANT EXECUTE ON dbo.fn_diagramobjects TO public - DENY EXECUTE ON dbo.fn_diagramobjects TO guest - END - - if IS_MEMBER('db_owner') = 1 - BEGIN - declare @val int - select @val = 1 - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sysdiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'TABLE', N'sysdiagrams', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_upgraddiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_upgraddiagrams', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_helpdiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_helpdiagrams', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_helpdiagramdefinition') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_helpdiagramdefinition', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_creatediagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_creatediagram', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_renamediagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_renamediagram', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_alterdiagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_alterdiagram', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.sp_dropdiagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_dropdiagram', NULL, NULL - end - - if NOT EXISTS( select major_id - from sys.extended_properties - where major_id = object_id(N'dbo.fn_diagramobjects') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') - begin - exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'FUNCTION', N'fn_diagramobjects', NULL, NULL - end - END`; - }, - disableDatabaseDiagrams: function(name) { - return ` - USE [${name}] - - IF OBJECT_ID(N'dbo.fn_diagramobjects') IS NULL and IS_MEMBER('db_owner') = 1 - DROP FUNCTION dbo.fn_diagramobjects - - IF OBJECT_ID(N'dbo.sp_dropdiagram') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_dropdiagram - - IF OBJECT_ID(N'dbo.sp_alterdiagram') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_alterdiagram - - IF OBJECT_ID(N'dbo.sp_renamediagram') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_renamediagram - - IF OBJECT_ID(N'dbo.sp_creatediagram') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_creatediagram - - IF OBJECT_ID(N'dbo.sp_helpdiagramdefinition') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_helpdiagramdefinition - - IF OBJECT_ID(N'dbo.sp_helpdiagrams') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_helpdiagrams - - IF OBJECT_ID(N'dbo.sysdiagrams') IS NOT NULL and IS_MEMBER('db_owner') = 1 - DROP TABLE dbo.sysdiagrams - - IF OBJECT_ID(N'dbo.sp_upgraddiagrams') IS NULL and IS_MEMBER('db_owner') = 1 - DROP PROCEDURE dbo.sp_upgraddiagrams`; - } -}; diff --git a/sql/auditLog.js b/sql/auditLog.js new file mode 100644 index 0000000..be6cb90 --- /dev/null +++ b/sql/auditLog.js @@ -0,0 +1,16 @@ +module.exports = function(statement) { + if (!statement.params) { + return; + } + var sql = ' DECLARE @callParams XML = ( SELECT '; + statement.params.map(function(param) { + if (param.def.type === 'table') { + sql += `(SELECT * from @${param.name} rows FOR XML AUTO, TYPE) [${param.name}], `; + } else { + sql += `@${param.name} [${param.name}], `; + } + }); + sql = sql.replace(/,\s$/, ' '); + sql += 'FOR XML RAW(\'params\'),TYPE) EXEC core.auditCall @procid = @@PROCID, @params=@callParams'; + return sql; +}; diff --git a/sql/callParams.js b/sql/callParams.js new file mode 100644 index 0000000..e7227a8 --- /dev/null +++ b/sql/callParams.js @@ -0,0 +1,16 @@ +module.exports = function(statement) { + if (!statement.params) { + return; + } + var sql = 'DECLARE @callParams XML = ( SELECT '; + statement.params.map(function(param) { + if (param.def.type === 'table') { + sql += `(SELECT * from @${param.name} rows FOR XML AUTO, TYPE) [${param.name}], `; + } else { + sql += `@${param.name} [${param.name}], `; + } + }); + sql = sql.replace(/,\s$/, ' '); + sql += 'FOR XML RAW(\'params\'),TYPE)'; + return sql; +}; diff --git a/sql/createDatabase.js b/sql/createDatabase.js new file mode 100644 index 0000000..7b9d465 --- /dev/null +++ b/sql/createDatabase.js @@ -0,0 +1,9 @@ +module.exports = function(name, user) { + return ` + IF NOT EXISTS (SELECT name FROM master.dbo.sysdatabases WHERE name = '${name}') + BEGIN + CREATE DATABASE [${name}] + ALTER DATABASE [${name}] SET READ_COMMITTED_SNAPSHOT ON + ALTER DATABASE [${name}] SET AUTO_SHRINK OFF + END`; +}; diff --git a/sql/createUser.js b/sql/createUser.js new file mode 100644 index 0000000..c3305ad --- /dev/null +++ b/sql/createUser.js @@ -0,0 +1,24 @@ +module.exports = function(name, user, password) { + return ` + IF NOT EXISTS (SELECT name FROM master.sys.server_principals WHERE name = '${user}') + BEGIN + CREATE LOGIN [${user}] WITH PASSWORD = N'${password}', CHECK_POLICY = OFF + END + USE [${name}] + IF NOT EXISTS (SELECT name FROM sys.database_principals WHERE name = '${user}') + BEGIN + CREATE USER [${user}] FOR LOGIN [${user}] + END + IF (is_rolemember('db_owner', '${user}') IS NULL OR is_rolemember('db_owner', '${user}') = 0) + BEGIN + EXEC sp_addrolemember 'db_owner', '${user}' + END + IF NOT EXISTS (SELECT 1 FROM sys.server_principals AS pr + INNER JOIN sys.server_permissions AS pe ON pe.grantee_principal_id = pr.principal_id + WHERE permission_name = N'VIEW SERVER STATE' AND state = N'G' AND pr.name = N'${user}') + BEGIN + USE [master] + GRANT VIEW SERVER STATE to [${user}] + END + `; +}; diff --git a/sql/disableDatabaseDiagrams.js b/sql/disableDatabaseDiagrams.js new file mode 100644 index 0000000..2463ee9 --- /dev/null +++ b/sql/disableDatabaseDiagrams.js @@ -0,0 +1,31 @@ +module.exports = function(name) { + return ` + USE [${name}] + + IF OBJECT_ID(N'dbo.fn_diagramobjects') IS NULL and IS_MEMBER('db_owner') = 1 + DROP FUNCTION dbo.fn_diagramobjects + + IF OBJECT_ID(N'dbo.sp_dropdiagram') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_dropdiagram + + IF OBJECT_ID(N'dbo.sp_alterdiagram') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_alterdiagram + + IF OBJECT_ID(N'dbo.sp_renamediagram') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_renamediagram + + IF OBJECT_ID(N'dbo.sp_creatediagram') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_creatediagram + + IF OBJECT_ID(N'dbo.sp_helpdiagramdefinition') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_helpdiagramdefinition + + IF OBJECT_ID(N'dbo.sp_helpdiagrams') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_helpdiagrams + + IF OBJECT_ID(N'dbo.sysdiagrams') IS NOT NULL and IS_MEMBER('db_owner') = 1 + DROP TABLE dbo.sysdiagrams + + IF OBJECT_ID(N'dbo.sp_upgraddiagrams') IS NULL and IS_MEMBER('db_owner') = 1 + DROP PROCEDURE dbo.sp_upgraddiagrams`; +}; diff --git a/sql/dropHash.js b/sql/dropHash.js new file mode 100644 index 0000000..b86ef80 --- /dev/null +++ b/sql/dropHash.js @@ -0,0 +1,3 @@ +module.exports = function() { + return `IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL DROP FUNCTION dbo.utSchemaHash`; +}; diff --git a/sql/enableDatabaseDiagrams.js b/sql/enableDatabaseDiagrams.js new file mode 100644 index 0000000..7521b33 --- /dev/null +++ b/sql/enableDatabaseDiagrams.js @@ -0,0 +1,526 @@ +module.exports = function(name) { + return ` + USE [${name}] + + IF OBJECT_ID(N'dbo.sp_upgraddiagrams') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_upgraddiagrams + AS + BEGIN + IF OBJECT_ID(N''dbo.sysdiagrams'') IS NOT NULL + return 0; + + CREATE TABLE dbo.sysdiagrams + ( + name sysname NOT NULL, + principal_id int NOT NULL, -- we may change it to varbinary(85) + diagram_id int PRIMARY KEY IDENTITY, + version int, + + definition varbinary(max) + CONSTRAINT UK_principal_name UNIQUE + ( + principal_id, + name + ) + ); + + /* Add this if we need to have some form of extended properties for diagrams */ + IF OBJECT_ID(N''dbo.sysdiagram_properties'') IS NULL + BEGIN + CREATE TABLE dbo.sysdiagram_properties + ( + diagram_id int, + name sysname, + value varbinary(max) NOT NULL + ) + END + + IF OBJECT_ID(N''dbo.dtproperties'') IS NOT NULL + begin + insert into dbo.sysdiagrams + ( + [name], + [principal_id], + [version], + [definition] + ) + select + convert(sysname, dgnm.[uvalue]), + DATABASE_PRINCIPAL_ID(N''dbo''), -- will change to the sid of sa + 0, -- zero for old format, dgdef.[version], + dgdef.[lvalue] + from dbo.[dtproperties] dgnm + inner join dbo.[dtproperties] dggd on dggd.[property] = ''DtgSchemaGUID'' and dggd.[objectid] = dgnm.[objectid] + inner join dbo.[dtproperties] dgdef on dgdef.[property] = ''DtgSchemaDATA'' and dgdef.[objectid] = dgnm.[objectid] + + where dgnm.[property] = ''DtgSchemaNAME'' and dggd.[uvalue] like N''_EA3E6268-D998-11CE-9454-00AA00A3F36E_'' + return 2; + end + return 1; + END + ' + END + + -- This sproc could be executed by any other users than dbo + IF IS_MEMBER('db_owner') = 1 + EXEC dbo.sp_upgraddiagrams; + + IF OBJECT_ID(N'dbo.sp_helpdiagrams') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_helpdiagrams + ( + @diagramname sysname = NULL, + @owner_id int = NULL + ) + WITH EXECUTE AS N''dbo'' + AS + BEGIN + DECLARE @user sysname + DECLARE @dboLogin bit + EXECUTE AS CALLER; + SET @user = USER_NAME(); + SET @dboLogin = CONVERT(bit,IS_MEMBER(''db_owner'')); + REVERT; + SELECT + [Database] = DB_NAME(), + [Name] = name, + [ID] = diagram_id, + [Owner] = USER_NAME(principal_id), + [OwnerID] = principal_id + FROM + sysdiagrams + WHERE + (@dboLogin = 1 OR USER_NAME(principal_id) = @user) AND + (@diagramname IS NULL OR name = @diagramname) AND + (@owner_id IS NULL OR principal_id = @owner_id) + ORDER BY + 4, 5, 1 + END + ' + + GRANT EXECUTE ON dbo.sp_helpdiagrams TO public + DENY EXECUTE ON dbo.sp_helpdiagrams TO guest + END + + IF OBJECT_ID(N'dbo.sp_helpdiagramdefinition') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_helpdiagramdefinition + ( + @diagramname sysname, + @owner_id int = null + ) + WITH EXECUTE AS N''dbo'' + AS + BEGIN + set nocount on + + declare @theId int + declare @IsDbo int + declare @DiagId int + declare @UIDFound int + + if(@diagramname is null) + begin + RAISERROR (N''E_INVALIDARG'', 16, 1); + return -1 + end + + execute as caller; + select @theId = DATABASE_PRINCIPAL_ID(); + select @IsDbo = IS_MEMBER(N''db_owner''); + if(@owner_id is null) + select @owner_id = @theId; + revert; + + select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname; + if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId )) + begin + RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1); + return -3 + end + + select version, definition FROM dbo.sysdiagrams where diagram_id = @DiagId ; + return 0 + END + ' + GRANT EXECUTE ON dbo.sp_helpdiagramdefinition TO public + DENY EXECUTE ON dbo.sp_helpdiagramdefinition TO guest + END + + IF OBJECT_ID(N'dbo.sp_creatediagram') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_creatediagram + ( + @diagramname sysname, + @owner_id int = null, + @version int, + @definition varbinary(max) + ) + WITH EXECUTE AS ''dbo'' + AS + BEGIN + set nocount on + + declare @theId int + declare @retval int + declare @IsDbo int + declare @userName sysname + if(@version is null or @diagramname is null) + begin + RAISERROR (N''E_INVALIDARG'', 16, 1); + return -1 + end + + execute as caller; + select @theId = DATABASE_PRINCIPAL_ID(); + select @IsDbo = IS_MEMBER(N''db_owner''); + revert; + + if @owner_id is null + begin + select @owner_id = @theId; + end + else + begin + if @theId <> @owner_id + begin + if @IsDbo = 0 + begin + RAISERROR (N''E_INVALIDARG'', 16, 1); + return -1 + end + select @theId = @owner_id + end + end + -- next 2 line only for test, will be removed after define name unique + if EXISTS(select diagram_id from dbo.sysdiagrams where principal_id = @theId and name = @diagramname) + begin + RAISERROR (''The name is already used.'', 16, 1); + return -2 + end + + insert into dbo.sysdiagrams(name, principal_id , version, definition) + VALUES(@diagramname, @theId, @version, @definition) ; + + select @retval = @@IDENTITY + return @retval + END + ' + GRANT EXECUTE ON dbo.sp_creatediagram TO public + DENY EXECUTE ON dbo.sp_creatediagram TO guest + END + + IF OBJECT_ID(N'dbo.sp_renamediagram') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_renamediagram + ( + @diagramname sysname, + @owner_id int = null, + @new_diagramname sysname + + ) + WITH EXECUTE AS ''dbo'' + AS + BEGIN + set nocount on + declare @theId int + declare @IsDbo int + + declare @UIDFound int + declare @DiagId int + declare @DiagIdTarg int + declare @u_name sysname + if((@diagramname is null) or (@new_diagramname is null)) + begin + RAISERROR (''Invalid value'', 16, 1); + return -1 + end + + EXECUTE AS CALLER; + select @theId = DATABASE_PRINCIPAL_ID(); + select @IsDbo = IS_MEMBER(N''db_owner''); + if(@owner_id is null) + select @owner_id = @theId; + REVERT; + + select @u_name = USER_NAME(@owner_id) + + select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname + if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId)) + begin + RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1) + return -3 + end + + -- if((@u_name is not null) and (@new_diagramname = @diagramname)) -- nothing will change + -- return 0; + + if(@u_name is null) + select @DiagIdTarg = diagram_id from dbo.sysdiagrams where principal_id = @theId and name = @new_diagramname + else + select @DiagIdTarg = diagram_id from dbo.sysdiagrams where principal_id = @owner_id and name = @new_diagramname + + if((@DiagIdTarg is not null) and @DiagId <> @DiagIdTarg) + begin + RAISERROR (''The name is already used.'', 16, 1); + return -2 + end + + if(@u_name is null) + update dbo.sysdiagrams set [name] = @new_diagramname, principal_id = @theId where diagram_id = @DiagId + else + update dbo.sysdiagrams set [name] = @new_diagramname where diagram_id = @DiagId + return 0 + END + ' + GRANT EXECUTE ON dbo.sp_renamediagram TO public + DENY EXECUTE ON dbo.sp_renamediagram TO guest + END + + IF OBJECT_ID(N'dbo.sp_alterdiagram') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_alterdiagram + ( + @diagramname sysname, + @owner_id int = null, + @version int, + @definition varbinary(max) + ) + WITH EXECUTE AS ''dbo'' + AS + BEGIN + set nocount on + + declare @theId int + declare @retval int + declare @IsDbo int + declare @UIDFound int + declare @DiagId int + declare @ShouldChangeUID int + + if(@diagramname is null) + begin + RAISERROR (''Invalid ARG'', 16, 1) + return -1 + end + + execute as caller; + select @theId = DATABASE_PRINCIPAL_ID(); + select @IsDbo = IS_MEMBER(N''db_owner''); + if(@owner_id is null) + select @owner_id = @theId; + revert; + + select @ShouldChangeUID = 0 + select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname + + if(@DiagId IS NULL or (@IsDbo = 0 and @theId <> @UIDFound)) + begin + RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1); + return -3 + end + + if(@IsDbo <> 0) + begin + if(@UIDFound is null or USER_NAME(@UIDFound) is null) -- invalid principal_id + begin + select @ShouldChangeUID = 1 ; + end + end + + -- update dds data + update dbo.sysdiagrams set definition = @definition where diagram_id = @DiagId ; + + -- change owner + if(@ShouldChangeUID = 1) + update dbo.sysdiagrams set principal_id = @theId where diagram_id = @DiagId ; + + -- update dds version + if(@version is not null) + update dbo.sysdiagrams set version = @version where diagram_id = @DiagId ; + + return 0 + END + ' + + GRANT EXECUTE ON dbo.sp_alterdiagram TO public + DENY EXECUTE ON dbo.sp_alterdiagram TO guest + END + + IF OBJECT_ID(N'dbo.sp_dropdiagram') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE PROCEDURE dbo.sp_dropdiagram + ( + @diagramname sysname, + @owner_id int = null + ) + WITH EXECUTE AS ''dbo'' + AS + BEGIN + set nocount on + declare @theId int + declare @IsDbo int + + declare @UIDFound int + declare @DiagId int + + if(@diagramname is null) + begin + RAISERROR (''Invalid value'', 16, 1); + return -1 + end + + EXECUTE AS CALLER; + select @theId = DATABASE_PRINCIPAL_ID(); + select @IsDbo = IS_MEMBER(N''db_owner''); + if(@owner_id is null) + select @owner_id = @theId; + REVERT; + + select @DiagId = diagram_id, @UIDFound = principal_id from dbo.sysdiagrams where principal_id = @owner_id and name = @diagramname + if(@DiagId IS NULL or (@IsDbo = 0 and @UIDFound <> @theId)) + begin + RAISERROR (''Diagram does not exist or you do not have permission.'', 16, 1) + return -3 + end + + delete from dbo.sysdiagrams where diagram_id = @DiagId; + + return 0; + END + ' + GRANT EXECUTE ON dbo.sp_dropdiagram TO public + DENY EXECUTE ON dbo.sp_dropdiagram TO guest + END + + IF OBJECT_ID(N'dbo.fn_diagramobjects') IS NULL and IS_MEMBER('db_owner') = 1 + BEGIN + EXEC sp_executesql N' + CREATE FUNCTION dbo.fn_diagramobjects() + RETURNS int + WITH EXECUTE AS N''dbo'' + AS + BEGIN + declare @id_upgraddiagrams int + declare @id_sysdiagrams int + declare @id_helpdiagrams int + declare @id_helpdiagramdefinition int + declare @id_creatediagram int + declare @id_renamediagram int + declare @id_alterdiagram int + declare @id_dropdiagram int + declare @InstalledObjects int + + select @InstalledObjects = 0 + + select @id_upgraddiagrams = object_id(N''dbo.sp_upgraddiagrams''), + @id_sysdiagrams = object_id(N''dbo.sysdiagrams''), + @id_helpdiagrams = object_id(N''dbo.sp_helpdiagrams''), + @id_helpdiagramdefinition = object_id(N''dbo.sp_helpdiagramdefinition''), + @id_creatediagram = object_id(N''dbo.sp_creatediagram''), + @id_renamediagram = object_id(N''dbo.sp_renamediagram''), + @id_alterdiagram = object_id(N''dbo.sp_alterdiagram''), + @id_dropdiagram = object_id(N''dbo.sp_dropdiagram'') + + + if @id_upgraddiagrams is not null + select @InstalledObjects = @InstalledObjects + 1 + if @id_sysdiagrams is not null + select @InstalledObjects = @InstalledObjects + 2 + if @id_helpdiagrams is not null + select @InstalledObjects = @InstalledObjects + 4 + if @id_helpdiagramdefinition is not null + select @InstalledObjects = @InstalledObjects + 8 + if @id_creatediagram is not null + select @InstalledObjects = @InstalledObjects + 16 + if @id_renamediagram is not null + select @InstalledObjects = @InstalledObjects + 32 + if @id_alterdiagram is not null + select @InstalledObjects = @InstalledObjects + 64 + if @id_dropdiagram is not null + select @InstalledObjects = @InstalledObjects + 128 + + return @InstalledObjects + END + ' + + GRANT EXECUTE ON dbo.fn_diagramobjects TO public + DENY EXECUTE ON dbo.fn_diagramobjects TO guest + END + + if IS_MEMBER('db_owner') = 1 + BEGIN + declare @val int + select @val = 1 + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sysdiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'TABLE', N'sysdiagrams', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_upgraddiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_upgraddiagrams', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_helpdiagrams') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_helpdiagrams', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_helpdiagramdefinition') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_helpdiagramdefinition', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_creatediagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_creatediagram', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_renamediagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_renamediagram', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_alterdiagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_alterdiagram', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.sp_dropdiagram') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'PROCEDURE', N'sp_dropdiagram', NULL, NULL + end + + if NOT EXISTS( select major_id + from sys.extended_properties + where major_id = object_id(N'dbo.fn_diagramobjects') and class = 1 and minor_id = 0 and name = N'microsoft_database_tools_support') + begin + exec sp_addextendedproperty N'microsoft_database_tools_support', @val, 'SCHEMA', N'dbo', 'FUNCTION', N'fn_diagramobjects', NULL, NULL + end + END`; +}; diff --git a/sql/getHash.js b/sql/getHash.js new file mode 100644 index 0000000..4e19bb4 --- /dev/null +++ b/sql/getHash.js @@ -0,0 +1,3 @@ +module.exports = function() { + return `IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL SELECT dbo.utSchemaHash() hash`; +}; diff --git a/sql/index.js b/sql/index.js new file mode 100644 index 0000000..686ca9b --- /dev/null +++ b/sql/index.js @@ -0,0 +1,13 @@ +module.exports = { + auditLog: require('./auditLog'), + callParams: require('./callParams'), + createDatabase: require('./createDatabase'), + createUser: require('./createUser'), + disableDatabaseDiagrams: require('./disableDatabaseDiagrams'), + dropHash: require('./dropHash'), + enableDatabaseDiagrams: require('./enableDatabaseDiagrams'), + getHash: require('./getHash'), + loadSchema: require('./loadSchema'), + refreshView: require('./refreshView'), + setHash: require('./setHash') +}; diff --git a/sql/loadSchema.js b/sql/loadSchema.js new file mode 100644 index 0000000..4ff642b --- /dev/null +++ b/sql/loadSchema.js @@ -0,0 +1,99 @@ +module.exports = function(partial) { + return ` + SELECT + o.create_date, + c.id, + c.colid, + RTRIM(o.[type]) [type], + SCHEMA_NAME(o.schema_id) [namespace], + o.Name AS [name], + SCHEMA_NAME(o.schema_id) + '.' + o.Name AS [full], + CASE o.[type] + WHEN 'SN' THEN 'DROP SYNONYM [' + SCHEMA_NAME(o.schema_id) + '].[' + o.Name + + '] CREATE SYNONYM [' + SCHEMA_NAME(o.schema_id) + '].[' + o.Name + '] FOR ' + s.base_object_name + ELSE ${partial ? `LEFT(c.text, CASE CHARINDEX(CHAR(10)+'AS'+CHAR(13), c.text) WHEN 0 THEN 2500 ELSE CHARINDEX(CHAR(10)+'AS'+CHAR(13), c.text) + 10 END)` : 'c.text'} + END AS [source] + + FROM + sys.objects o + LEFT JOIN + dbo.syscomments c on o.object_id = c.id + LEFT JOIN + sys.synonyms s on s.object_id = o.object_id + WHERE + o.type IN (${partial ? `'P'` : `'V', 'P', 'FN','F','IF','SN','TF','TR','U'`}) AND + user_name(objectproperty(o.object_id, 'OwnerId')) in (USER_NAME(),'dbo') AND + objectproperty(o.object_id, 'IsMSShipped') = 0 AND + SCHEMA_NAME(o.schema_id) != 'dbo' + ${partial ? 'AND ISNULL(c.colid, 1)=1' : ''} + UNION ALL + SELECT 0,0,0,'S',name,NULL,NULL,NULL FROM sys.schemas WHERE principal_id = USER_ID() + UNION ALL + SELECT + 0,0,0,'T',SCHEMA_NAME(t.schema_id)+'.'+t.name,NULL,NULL,NULL + FROM + sys.types t + JOIN + sys.schemas s ON s.principal_id = USER_ID() AND s.schema_id=t.schema_id + WHERE + t.is_user_defined=1 + ORDER BY + 1, 2, 3 + + SELECT + SCHEMA_NAME(types.schema_id) + '.' + types.name name, + c.name [column], + st.name type, + CASE + WHEN st.name in ('decimal','numeric') then CAST(c.[precision] AS VARCHAR) + WHEN st.name in ('datetime2','time','datetimeoffset') then CAST(c.[scale] AS VARCHAR) + WHEN st.name in ('varchar','varbinary','char','binary') AND c.max_length>=0 THEN CAST(c.max_length as VARCHAR) + WHEN st.name in ('nvarchar','nchar') AND c.max_length>=0 THEN CAST(c.max_length/2 as VARCHAR) + WHEN st.name in ('varchar','varbinary','char','binary','nvarchar','nchar') AND c.max_length<0 THEN 'max' + END [length], + CASE + WHEN st.name in ('decimal','numeric') then c.scale + END scale, + object_definition(c.default_object_id) [default] + FROM + sys.table_types types + JOIN + sys.columns c ON types.type_table_object_id = c.object_id + JOIN + sys.systypes AS st ON st.xtype = c.system_type_id + WHERE + types.is_user_defined = 1 AND st.name <> 'sysname' + ORDER BY + 1,c.column_id + + SELECT + 1 sort, + s.name + '.' + o.name [name], + 'IF (OBJECT_ID(''[' + s.name + '].[' + o.name + ']'') IS NOT NULL) DROP ' + CASE o.type WHEN 'FN' THEN 'FUNCTION' ELSE 'PROCEDURE' END + ' [' + s.name + '].[' + o.name + ']' [drop], + p.name [param], + SCHEMA_NAME(t.schema_id) + '.' + t.name [type] + FROM + sys.schemas s + JOIN + sys.objects o ON o.schema_id = s.schema_id + JOIN + sys.parameters p ON p.object_id = o.object_id + JOIN + sys.types t ON p.user_type_id = t.user_type_id AND t.is_user_defined=1 + WHERE + user_name(objectproperty(o.object_id, 'OwnerId')) in (USER_NAME(),'dbo') + UNION + SELECT + 2, + s.name + '.' + t.name [name], + 'DROP TYPE [' + s.name + '].[' + t.name + ']' [drop], + NULL [param], + SCHEMA_NAME(t.schema_id) + '.' + t.name [type] + FROM + sys.schemas s + JOIN + sys.types t ON t.schema_id=s.schema_id and t.is_user_defined=1 + WHERE + user_name(s.principal_id) in (USER_NAME(),'dbo') + ORDER BY 1`; +}; diff --git a/sql/refreshView.js b/sql/refreshView.js new file mode 100644 index 0000000..3a14fc7 --- /dev/null +++ b/sql/refreshView.js @@ -0,0 +1,70 @@ +module.exports = function(drop) { + return ` + SET NOCOUNT ON; + + DECLARE @ViewName VARCHAR(255); + DECLARE @error_table TABLE + ( + view_name VARCHAR(255) , + error_msg VARCHAR(MAX) + ); + + DECLARE view_cursor CURSOR FAST_FORWARD + FOR + --- Get all the user defined views with no schema binding on them + SELECT DISTINCT + '[' + ss.name + '].[' + av.name +']' AS ViewName + FROM sys.all_views av + JOIN sys.schemas ss ON av.schema_id = ss.schema_id + WHERE OBJECTPROPERTY(av.[object_id], 'IsSchemaBound') <> 1 + AND av.Is_Ms_Shipped = 0 + + OPEN view_cursor + + FETCH NEXT FROM view_cursor + INTO @ViewName + + WHILE @@FETCH_STATUS = 0 + BEGIN + + BEGIN TRY + -- Refresh the view + EXEC sp_refreshview @ViewName; + + -- RAISERROR('%s', 10, 1, @ViewName) WITH NOWAIT; + + END TRY + BEGIN CATCH + IF @@trancount > 0 ROLLBACK TRANSACTION + --- Insert all the errors + IF (1=${drop ? 1 : 0}) + BEGIN + EXEC ('DROP VIEW ' + @ViewName) + END ELSE + BEGIN + INSERT INTO + @error_table(view_name, error_msg) + SELECT @ViewName, ERROR_MESSAGE(); + END + + END CATCH + + FETCH NEXT FROM view_cursor INTO @ViewName; + + END + + --- Check if there was an error + IF EXISTS (SELECT TOP 1 1 FROM @error_table) + BEGIN + SELECT view_name , + error_msg + FROM @error_table; + END + + CLOSE view_cursor + DEALLOCATE view_cursor + + IF OBJECT_ID('dbo.utSchemaHash') IS NOT NULL SELECT dbo.utSchemaHash() hash + + SET NOCOUNT OFF;`; +}; diff --git a/sql/setHash.js b/sql/setHash.js new file mode 100644 index 0000000..e37644b --- /dev/null +++ b/sql/setHash.js @@ -0,0 +1,3 @@ +module.exports = function(hash) { + return `CREATE FUNCTION dbo.utSchemaHash() RETURNS VARCHAR(64) AS BEGIN RETURN '${hash}' END`; +};