├── .github ├── issue_template.md └── pull_request_template.md ├── .gitignore ├── CODE_OF_CONDUCT.md ├── CONTRIBUTING.md ├── ChangeLog.md ├── HOW_TO_CONTRIBUTE.md ├── LICENSE ├── README.md ├── SECURITY.md ├── USAGE.md ├── notices.txt └── src ├── DWScripter.sln ├── DWScripter ├── DWScripter.csproj └── Program.cs └── PDWScripter ├── ClusteredDef.cs ├── ColumnDef.cs ├── DBStruct.cs ├── Docs ├── LICENSE └── PDWScripter_How_to.md ├── GenerateScripts.cs ├── GlobalFilterSettings.cs ├── IndexColumnDef.cs ├── NonClusteredIndexDef.cs ├── NonclusteredIndexes.cs ├── ObjectsFilterList.cs ├── PDWScripter.csproj ├── PDWscripter.cs ├── Partition.cs ├── StatColumnDef.cs ├── StatDef.cs ├── Statistics.cs ├── TableDef.cs ├── TableSt.cs └── helper.cs /.github/issue_template.md: -------------------------------------------------------------------------------- 1 | ### Expected Behavior 2 | 3 | ### Actual Behavior 4 | 5 | ### Steps to reproduce the behavior 6 | 7 | ### Project Version 8 | 9 | ### OS details 10 | -------------------------------------------------------------------------------- /.github/pull_request_template.md: -------------------------------------------------------------------------------- 1 | Fixes # 2 | 3 | 4 | Changes proposed in this Pull Request 5 | - 6 | - 7 | - 8 | 9 | @DWScripter Admin 10 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | ##ignore Visual Studio Code 13 | .vscode/ 14 | 15 | # User-specific files (MonoDevelop/Xamarin Studio) 16 | *.userprefs 17 | 18 | # Build results 19 | [Dd]ebug/ 20 | [Dd]ebugPublic/ 21 | [Rr]elease/ 22 | [Rr]eleases/ 23 | x64/ 24 | x86/ 25 | bld/ 26 | [Bb]in/ 27 | [Oo]bj/ 28 | [Ll]og/ 29 | 30 | # Visual Studio 2015 cache/options directory 31 | .vs/ 32 | # Uncomment if you have tasks that create the project's static files in wwwroot 33 | #wwwroot/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # .NET Core 49 | project.lock.json 50 | project.fragment.lock.json 51 | artifacts/ 52 | **/Properties/launchSettings.json 53 | 54 | *_i.c 55 | *_p.c 56 | *_i.h 57 | *.ilk 58 | *.meta 59 | *.obj 60 | *.pch 61 | *.pdb 62 | *.pgc 63 | *.pgd 64 | *.rsp 65 | *.sbr 66 | *.tlb 67 | *.tli 68 | *.tlh 69 | *.tmp 70 | *.tmp_proj 71 | *.log 72 | *.vspscc 73 | *.vssscc 74 | .builds 75 | *.pidb 76 | *.svclog 77 | *.scc 78 | 79 | # Chutzpah Test files 80 | _Chutzpah* 81 | 82 | # Visual C++ cache files 83 | ipch/ 84 | *.aps 85 | *.ncb 86 | *.opendb 87 | *.opensdf 88 | *.sdf 89 | *.cachefile 90 | *.VC.db 91 | *.VC.VC.opendb 92 | 93 | # Visual Studio profiler 94 | *.psess 95 | *.vsp 96 | *.vspx 97 | *.sap 98 | 99 | # TFS 2012 Local Workspace 100 | $tf/ 101 | 102 | # Guidance Automation Toolkit 103 | *.gpState 104 | 105 | # ReSharper is a .NET coding add-in 106 | _ReSharper*/ 107 | *.[Rr]e[Ss]harper 108 | *.DotSettings.user 109 | 110 | # JustCode is a .NET coding add-in 111 | .JustCode 112 | 113 | # TeamCity is a build add-in 114 | _TeamCity* 115 | 116 | # DotCover is a Code Coverage Tool 117 | *.dotCover 118 | 119 | # Visual Studio code coverage results 120 | *.coverage 121 | *.coveragexml 122 | 123 | # NCrunch 124 | _NCrunch_* 125 | .*crunch*.local.xml 126 | nCrunchTemp_* 127 | 128 | # MightyMoose 129 | *.mm.* 130 | AutoTest.Net/ 131 | 132 | # Web workbench (sass) 133 | .sass-cache/ 134 | 135 | # Installshield output folder 136 | [Ee]xpress/ 137 | 138 | # DocProject is a documentation generator add-in 139 | DocProject/buildhelp/ 140 | DocProject/Help/*.HxT 141 | DocProject/Help/*.HxC 142 | DocProject/Help/*.hhc 143 | DocProject/Help/*.hhk 144 | DocProject/Help/*.hhp 145 | DocProject/Help/Html2 146 | DocProject/Help/html 147 | 148 | # Click-Once directory 149 | publish/ 150 | 151 | # Publish Web Output 152 | *.[Pp]ublish.xml 153 | *.azurePubxml 154 | # TODO: Comment the next line if you want to checkin your web deploy settings 155 | # but database connection strings (with potential passwords) will be unencrypted 156 | *.pubxml 157 | *.publishproj 158 | 159 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 160 | # checkin your Azure Web App publish settings, but sensitive information contained 161 | # in these scripts will be unencrypted 162 | PublishScripts/ 163 | 164 | # NuGet Packages 165 | *.nupkg 166 | # The packages folder can be ignored because of Package Restore 167 | **/packages/* 168 | # except build/, which is used as an MSBuild target. 169 | !**/packages/build/ 170 | # Uncomment if necessary however generally it will be regenerated when needed 171 | #!**/packages/repositories.config 172 | # NuGet v3's project.json files produces more ignorable files 173 | *.nuget.props 174 | *.nuget.targets 175 | 176 | # Microsoft Azure Build Output 177 | csx/ 178 | *.build.csdef 179 | 180 | # Microsoft Azure Emulator 181 | ecf/ 182 | rcf/ 183 | 184 | # Windows Store app package directories and files 185 | AppPackages/ 186 | BundleArtifacts/ 187 | Package.StoreAssociation.xml 188 | _pkginfo.txt 189 | 190 | # Visual Studio cache files 191 | # files ending in .cache can be ignored 192 | *.[Cc]ache 193 | # but keep track of directories ending in .cache 194 | !*.[Cc]ache/ 195 | 196 | # Others 197 | ClientBin/ 198 | ~$* 199 | *~ 200 | *.dbmdl 201 | *.dbproj.schemaview 202 | *.jfm 203 | *.pfx 204 | *.publishsettings 205 | orleans.codegen.cs 206 | 207 | # Since there are multiple workflows, uncomment next line to ignore bower_components 208 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 209 | #bower_components/ 210 | 211 | # RIA/Silverlight projects 212 | Generated_Code/ 213 | 214 | # Backup & report files from converting an old project file 215 | # to a newer Visual Studio version. Backup files are not needed, 216 | # because we have git ;-) 217 | _UpgradeReport_Files/ 218 | Backup*/ 219 | UpgradeLog*.XML 220 | UpgradeLog*.htm 221 | 222 | # SQL Server files 223 | *.mdf 224 | *.ldf 225 | *.ndf 226 | 227 | # Business Intelligence projects 228 | *.rdl.data 229 | *.bim.layout 230 | *.bim_*.settings 231 | 232 | # Microsoft Fakes 233 | FakesAssemblies/ 234 | 235 | # GhostDoc plugin setting file 236 | *.GhostDoc.xml 237 | 238 | # Node.js Tools for Visual Studio 239 | .ntvs_analysis.dat 240 | node_modules/ 241 | 242 | # Typescript v1 declaration files 243 | typings/ 244 | 245 | # Visual Studio 6 build log 246 | *.plg 247 | 248 | # Visual Studio 6 workspace options file 249 | *.opt 250 | 251 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 252 | *.vbw 253 | 254 | # Visual Studio LightSwitch build output 255 | **/*.HTMLClient/GeneratedArtifacts 256 | **/*.DesktopClient/GeneratedArtifacts 257 | **/*.DesktopClient/ModelManifest.xml 258 | **/*.Server/GeneratedArtifacts 259 | **/*.Server/ModelManifest.xml 260 | _Pvt_Extensions 261 | 262 | # Paket dependency manager 263 | .paket/paket.exe 264 | paket-files/ 265 | 266 | # FAKE - F# Make 267 | .fake/ 268 | 269 | # JetBrains Rider 270 | .idea/ 271 | *.sln.iml 272 | 273 | # CodeRush 274 | .cr/ 275 | 276 | # Python Tools for Visual Studio (PTVS) 277 | __pycache__/ 278 | *.pyc 279 | 280 | # Cake - Uncomment if you are using it 281 | # tools/** 282 | # !tools/packages.config 283 | 284 | # Telerik's JustMock configuration file 285 | *.jmconfig 286 | 287 | # BizTalk build output 288 | *.btp.cs 289 | *.btm.cs 290 | *.odx.cs 291 | *.xsd.cs 292 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 2 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | This project welcomes contributions and suggestions. Most contributions require you to 4 | agree to a Contributor License Agreement (CLA) declaring that you have the right to, 5 | and actually do, grant us the rights to use your contribution. For details, visit [Contributor License Agreement](https://cla.microsoft.com.) 6 | 7 | When you submit a pull request, a CLA-bot will automatically determine whether you need 8 | to provide a CLA and decorate the PR appropriately (e.g., label, comment). Simply follow the 9 | instructions provided by the bot. You will only need to do this once across all repositories using our CLA. 10 | 11 | This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 12 | For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) 13 | or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments. 14 | 15 | ## Build and Test 16 | 17 | See [How To Contribute](.\HOW_TO_CONTRIBUTE.md) -------------------------------------------------------------------------------- /ChangeLog.md: -------------------------------------------------------------------------------- 1 | # ChangeLog 2 | 3 | ## Version 1.1.0 4 | 5 | 1. Fixed clustered index scripting on non-CCI partitioned tables 6 | 2. Added Command timeout parameter 7 | 3. Added version tag to project files -------------------------------------------------------------------------------- /HOW_TO_CONTRIBUTE.md: -------------------------------------------------------------------------------- 1 | # **Contributing to DWScripter** 2 | 3 | There are many ways of contributing to DWScripter project: logging bugs, submitting pull requests, reporting issues and creating suggestions 4 | 5 | After cloning the repo you can build the solution and start using and testing. 6 | 7 | ## **Build and Run from Source** 8 | 9 | If you want to understand how DWScripter works or want to debug an issue or implement new functionality, you'll want to get the source, build it, and run the tool. 10 | 11 | ### **Prerequisites** 12 | 13 | You will neet [git](https://git-scm.com/) to clone the repo. 14 | 15 | The solution is built on top of .NET Core, thus in order to build DWScripter you will need .NET Core SDK 2.0.2 or above. 16 | 17 | PDWScripter makes use of Newtonsoft.Json (version 10.0.3 or above). You will need to add the package to your system in order to successfully build the solution or PDWScripter library. 18 | 19 | ### **How to Build** 20 | 21 | Once you have obtained the source code and installed the required packages you can build the tool following below steps: 22 | 23 | 1. Open a Command Prompt, Powershell or other shell window to the solution's folder (the folder containing the DWScripter.sln file) 24 | 2. Run `dotnet build -r ` 25 | 26 | > Note: runtime can be configured in DWScripter.csproj file. The `dotnet build` command (without further parameters) will build by default for that runtime. If you wish to build for different runtimes you can use the `-r ` option parameter to build for your specific needs. For a list of available runtimes see [.NET Core RID Catalog](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog) 27 | 28 | If building for Windows you could also target the .NET Framework (4.6.1 or later). In this case you will need to add the target framework to the project files of both the PDWScripter library and the DWScripter console App. 29 | 30 | #### **Building for Windows 10 64 bit** 31 | 32 | > Example: 33 | 34 | ```dotnet build -r win10-x64``` 35 | 36 | #### **Building for Windows 7 64 bit** 37 | 38 | >Example 39 | 40 | ```dotnet build -r win7-x64``` 41 | 42 | #### **Build for Ubuntu 16.10** 43 | 44 | > Example 45 | 46 | ```dotnet build -r ubuntu.16.10-x64``` 47 | 48 | For more reference on building with .NET Core see [dotnet build](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-build?tabs=netcore2x) guide 49 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Introduction 2 | 3 | DWScripter is a scripting tool for Analytics Platform System and Azure SQLDW. This Console app will script the ddl and dml for both APS and SQLDW. 4 | Supports: PDW V2, Azure SQLDW, DDL and DML, schemas (version 2.4) 5 | 6 | The project is intended as a cross platform tool, and as such is built on .NET Core SDK 2.0.2. You can build the solution and run the tool from the OS of your choice. 7 | 8 | ## Requirements 9 | 10 | PDWScripter requires [Newtonsoft.json](https://github.com/JamesNK/Newtonsoft.Json) version 10.0.3 or above. You will need to add the package to your system. For more information and samples on how to add the package see [Newtonsoft.json NuGet page](https://www.nuget.org/packages/Newtonsoft.Json). 11 | 12 | ## Getting Started 13 | 14 | Dowload the sources and build DWScripter tool. For building instructions see [How To Contribute](./HOW_TO_CONTRIBUTE.md) 15 | 16 | The Solution contains two projects: 17 | 18 | 1. PDWScripter 19 | 2. DWScripter 20 | 21 | **PDWScripter** is a class library project, made with the purpose of being reusable in custom projects to provide the Data Warehouse object scripting capability. For more information on using the library in your projects, please refer to [PDWScripter How To](./src/PDWScripter/Docs/PDWScripter_How_to.md) and to [PDWScripter License](./src/PDWScripter/Docs/LICENSE) 22 | **DWScripter** is a console application that makes use of the PDWScripter library 23 | 24 | DWScripter requires some paramters to identify the instance and databases to be scripted, as well as the object (DML, DDL or both). 25 | 26 | You must invoke DWScripter passing all the required parameters. 27 | For more information on the tool usage see [Usage](./USAGE.md) 28 | 29 | ## Current version is 1.1.0 30 | 31 | See [ChangeLog](./ChangeLog.md) 32 | 33 | ### USAGE 34 | 35 | For more information on the tool usage see [Usage](./USAGE.md) 36 | 37 | ## Contributing 38 | 39 | If you are interested in fixing issues and contributing directly to the code base please see [contributing guidelines](./CONTRIBUTING.md) 40 | 41 | Please also review our [Code of Conduct](https://opensource.microsoft.com/codeofconduct/). 42 | 43 | ## Reporting Security Issues 44 | 45 | Security issues and bugs should be reported privately, via email, to the Microsoft Security 46 | Response Center (MSRC) at [secure@microsoft.com](mailto:secure@microsoft.com). You should 47 | receive a response within 24 hours. If for some reason you do not, please follow up via 48 | email to ensure we received your original message. Further information, including the 49 | [MSRC PGP](https://technet.microsoft.com/en-us/security/dn606155) key, can be found in 50 | the [Security TechCenter](https://technet.microsoft.com/en-us/security/default). 51 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | ## Security 4 | 5 | Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin), and [our GitHub organizations](https://opensource.microsoft.com/). 6 | 7 | If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/opensource/security/definition), please report it to us as described below. 8 | 9 | ## Reporting Security Issues 10 | 11 | **Please do not report security vulnerabilities through public GitHub issues.** 12 | 13 | Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/opensource/security/create-report). 14 | 15 | If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/opensource/security/pgpkey). 16 | 17 | You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://aka.ms/opensource/security/msrc). 18 | 19 | Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: 20 | 21 | * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) 22 | * Full paths of source file(s) related to the manifestation of the issue 23 | * The location of the affected source code (tag/branch/commit or direct URL) 24 | * Any special configuration required to reproduce the issue 25 | * Step-by-step instructions to reproduce the issue 26 | * Proof-of-concept or exploit code (if possible) 27 | * Impact of the issue, including how an attacker might exploit the issue 28 | 29 | This information will help us triage your report more quickly. 30 | 31 | If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/opensource/security/bounty) page for more details about our active programs. 32 | 33 | ## Preferred Languages 34 | 35 | We prefer all communications to be in English. 36 | 37 | ## Policy 38 | 39 | Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/opensource/security/cvd). 40 | 41 | 42 | -------------------------------------------------------------------------------- /USAGE.md: -------------------------------------------------------------------------------- 1 | # DWScripter Usage 2 | 3 | The DWScripter Utility lets you script Parallel Data Warehouse or SQL Data Warehouse objects from the command prompt. DWScripter will allow you to produce .dsql files, or JSON structures to be used for comparison against a database. 4 | The utility also allows to compare two databases directly, producing a delta .dsql file to be used to update the target database. 5 | 6 | ## Syntax 7 | 8 | ```` 9 | DWScripter 10 |       -S: Server source 11 |       -D: Database Source 12 |       -E: Trusted connection 13 |       -U: Login source id 14 |       -P: Password source 15 |      -W: work mode [DML|DDL|ALL] 16 |       -O: Work path, no space allowed 17 |       -M: mode [Full|PersistStructure|Compare|CompareFromFile]] 18 |       -St: Server target 19 |       -dt: Database Target 20 |       -Ut: login id target 21 |       -Pt: password target 22 |       -F: filter on feature for scripting 23 |       -Fp: filters file path, no space allowed 24 | -X: Exclusion filter 25 | -t: Command Timoute 26 | -?, -h, --help: (usage) 27 | 28 | ```` 29 | 30 | ## Command Line Options 31 | 32 | **-S** *server[,port]* 33 | Specifies the SQL Data Warehouse or Parallel Data Warehouse instance to which to connect. For SQL Data Warehouse the value is the entire server name, e.g. **myserver.database.windows.net**. For Parallel Data Warehouse the paramter is the Engine IP (CTL node IP) to connect to APS. e.g. -S:192.168.1.100,17001. The connection port is **requireed** for Analytics Platform System, and is defaulted to 1433 for SQL Data Warehouse. 34 | 35 | **-D:** *database* 36 | Source database from which to script out the objects 37 | 38 | **-E:** 39 | Trusted Connection. Uses integrated authentication connectin with credentials of the user currently running the tool Command Prompt or PowerShell session. 40 | 41 | **-U:** *login_id* 42 | User Name to connect to the SQL Data Warehouse instance or APS (when using SQL Authentication) 43 | 44 | **-P:** *password* 45 | Is a User-specified Password (when using SQL Authentication). Passwords are case sensitive. 46 | 47 | **-W:** **[DML|DDL|ALL]** 48 | Defines the work mode the tool will operate with. If DML, the tool will only output DML definitions (Stored Procedures, Functions, Views). If DDL the tool will only output DDL (Schemas and Tables definitions with related indexes and statistics definitions). If ALL, all definitions are processed. 49 | 50 | **-O:** *work path* 51 | Identifies the folder and file (suffix) where the output files will be placed. If mode (-M) is CompareFromFile -O specifies the folder where the source file is read from **AND** where the output files will be saved. 52 | 53 | **-M:** **[FULL|PersistStructure|Compare|CompareFromFile]** 54 | 55 | - **FULL:** Creates .dsql scripts. Output is based on the value of -W:[DDL|DML|ALL]. Output files will be named *DatabaseName*_STRUCT_DDL.dsql and *DatabaseName*_STRUCT_DML.dsql 56 | - **PersistStructure:** Creates output in the form of JSON files. Output is based on the value of -W:[DDL|DML|ALL]. Files will be named *DatabaseName*_STRUCT_DDL.json and *DatabaseName*_STRUCT_DML.json 57 | - **Compare:** Generates two .dsql delta files for DML and DDL and a warning script. Requires access to both source and target database. The warning file will contain information about actions that might issue a potential data loss (DROP Table, DROP Column,...) 58 | - **CompareFromFile:** Generates two .dsql delta files for DML and DDL and a warning script by comparing the target database with the .json files 59 | 60 | **St:** *server[,port]* 61 | Target server against which run the compare. Required if mode is Compare (-M:Compare), unused otherwise 62 | 63 | **-dt:** *database name* 64 | Target database against which run the compare Required if mode is Compare (-M:Compare), unused otherwise 65 | 66 | **-Ut:** *login_id* 67 | User name to connect to target server and database 68 | 69 | **-Pt:** 70 | User defined password to connect to target server and database. Passwords are case sensitive. 71 | 72 | **-F:** 73 | Feature name filter. <> 74 | Requires source database (-D:) 75 | 76 | **-Fp:** 77 | Filters to apply to file path. The Json files are automatically selected based on the database name (-D:). Unused if -F is not used<> 78 | 79 | **-X:** 80 | Regular expression pattern to define object exclusions. Objects which names match the regular expression provided will be excluded from the scripting. 81 | 82 | **-t:** 83 | The time in seconds to wait for the command to execute. 84 | 85 | ## Limitations and Restrictions 86 | 87 | Table, columns and object renaming is not supported. These must be managed with a custom pre-deployment script 88 | 89 | The following features and objects are not supported: 90 | 91 | - External tables 92 | - External File Formats 93 | - External Data Sources 94 | 95 | ## Examples 96 | 97 | ### **Script all objects in .dsql file** 98 | 99 | The following example will generate the files: 100 | C:\Dev\APS\DW_STG_DDL.dsql 101 | C:\Dev\APS\DW_STG_DML.dsql 102 | 103 | ```PowerShell 104 | #Parallel Data Warehouse syntax 105 | .\DWScripter.exe -S:192.168.1.1,17001 -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:Full -W:ALL -F:ALL 106 | 107 | #Azure SQL Data Warehouse Syntax 108 | .\DWScripter.exe -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:Full -W:ALL -F:ALL 109 | ``` 110 | 111 | ### **Script all objects in .json file** 112 | 113 | This syntax can be used to produce a JSON structure of the database objects as a persisted structure for future comparison. The following example will generate the files: 114 | 115 | C:\Dev\APS\DW_STG_DDL.json 116 | C:\Dev\APS\DW_STG_DML.json 117 | 118 | ```PowerShell 119 | #Parallel Data Warehouse syntax 120 | .\DWScripter.exe -S:192.168.1.1,17001 -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:PersistStructure 121 | 122 | #Azure SQL Data Warehouse syntax 123 | .\DWScripter.exe -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:PersistStructure 124 | ``` 125 | 126 | ### **Compare two databases and generate a delta .dsql script** 127 | 128 | This syntax will generate delta .dsql scripts and a warning script. The warning file will contain statements that might lead to data loss (DROP table, DROP column...). Access to both databases is required. The following files will be generated 129 | 130 | C:\Dev\APS\DW_STG_DDL.dsql 131 | C:\Dev\APS\DW_STG_DML.dsql 132 | C:\Dev\APS\DW_STG_DDL.warn 133 | 134 | ```PowerShell 135 | #Parallel Data Warehouse syntax 136 | .\DWScripter.exe -S:192.168.1.1,17001 -D:Fabrikam_DWH_DEV -O:C:\Dev\APS\DW_STG-M:Compare -U: -P: -St:10.192.168.10,17001 -Dt:Fabrikam_DWH_INT -Ut: -Pt: -F:All 137 | 138 | #Azure SQL Data Warehouse syntax 139 | .\DWScripter.exe -S:FabrikamDW.database.windows.net -D:Fabrikam_DWH_DEV -O:C:\Dev\APS\DW_STG -M:Compare -U: -P: -St:pdwQA.database.windows.net -Dt:Fabrikam_DWH_INT -Ut: -Pt: -F:All 140 | ``` 141 | 142 | ### **Compare a database to a persisted structure** 143 | 144 | This syntax will generate delta .dsql scripts and a warning script by comparing a database to a JSON persisted structure. The warning file will contain statements that might lead to data loss (DROP table, DROP column...). Access to both databases is required. 145 | The work path parameter `-O:C:\Dev\APS\DW_STG` indicates the path and file suffix of the .json files to use for comparison. In this case the following files will be used: 146 | 147 | C:\Dev\APS\DW_STG_SSL.json 148 | C:\Dev\APS\DW_STG_DDL.json 149 | 150 | and the following files will be generated 151 | 152 | C:\Dev\APS\DW_STG_DDL.dsql 153 | C:\Dev\APS\DW_STG_DML.dsql 154 | C:\Dev\APS\DW_STG_DDL.warn 155 | 156 | ```PowerShell 157 | #Parallel Data Warehouse syntax 158 | .\DWScripter.exe -Ut: -Pt: -D:Fabrikam_DWH -O:C:\Dev\APS\DW_STG -M:CompareFromFile -St:192.168.1.1,17001 -Dt:Fabrikam_DWH_INT -F:ALL 159 | 160 | #Azure SQL Data Warehouse syntax 161 | .\DWScripter.exe -Ut: -Pt: -D:Fabrikam_DWH -O:C:\Dev\APS\DW_STG -M:CompareFromFile -St:FabrikamDW.database.windows.net -Dt:Fabrikam_DWH_INT -F:ALL 162 | ``` 163 | 164 | ### **Exclude all _dev objects from scripting** 165 | 166 | This syntax will script out only objects which names do not contain ```_dev``` 167 | 168 | ```PowerShell 169 | #Parallel Data Warehouse syntax 170 | .\DWScripter.exe -S:192.168.1.1,17001 -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:Full -X:"_dev" 171 | 172 | #Azure SQL Data Warehouse Syntax 173 | .\DWScripter.exe -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -U: -P: -O:C:\Dev\APS\DW_STG -M:Full -X:"_dev" 174 | ``` 175 | 176 | ### **Only script _dev or _test objects** 177 | 178 | This syntax will only script out objects which names contain ```_dev``` or ```_test``` 179 | 180 | ```PowerShell 181 | #Parallel Data Warehouse syntax 182 | .\DWScripter.exe -S:192.168.1.1,17001 -D:Fabrikam_STG_DEV -E -O:C:\Dev\APS\DW_STG -M:Full -X:"^((?!_dev|_test).)*$" 183 | 184 | #Azure SQL Data Warehouse Syntax 185 | .\DWScripter.exe -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -E -O:C:\Dev\APS\DW_STG -M:Full -X:"^((?!_dev|_test).)*$" 186 | ``` 187 | 188 | ### **Running from Linux** 189 | 190 | After building the tool you can execute it from the build directory using [```dotnet``` syntax](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-run?tabs=netcore2x). 191 | The following example will generate the files: 192 | home/DWAdmin/Documents/DW_STG_DDL.dsql 193 | home/DWAdmin/Documents/DW_STG_DML.dsql 194 | 195 | ```bash 196 | #Running the compiled code 197 | #Open a shell window to the path of the compiled code 198 | dotnet DWScripter.dll -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -U: -P: -O:/home/DWAdmin/Documents/DW_STG -M:Full 199 | ``` 200 | 201 | You can run also the DWScripter from the project folder using the [```dotnet run``` syntax](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-run?tabs=netcore2x). 202 | 203 | ```bash 204 | #Running from the project file 205 | #Open a shell window to the path of the project file (DWScripter.csproj). 206 | #The project parameter (-p) is optional when running from a path taht contains the project file. 207 | dotnet run [-p DWScripter.csproj] -S:FabrikamDW.database.windows.net -D:Fabrikam_STG_DEV -U: -P: -O:/home/DWAdmin/Documents/DW_STG -M:Full 208 | ``` 209 | -------------------------------------------------------------------------------- /notices.txt: -------------------------------------------------------------------------------- 1 | ---------------------- START OF THIRD PARTY NOTICES ---------------------- 2 | This file incorporates material from the projects listed below (Third Party IP). 3 | Newtonsoft.Json 4 | Copyright (c) 2007 James Newton-King 5 | MIT 6 | The MIT License (MIT) 7 | 8 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 9 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 10 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 11 | 12 | ----------------------END OF THIRD PARTY NOTICES---------------------- 13 | -------------------------------------------------------------------------------- /src/DWScripter.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.27004.2008 4 | MinimumVisualStudioVersion = 15.0.26124.0 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "DWScripter", "DWScripter\DWScripter.csproj", "{582583B5-5CFE-47AD-85E1-0493B1F51451}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PDWScripter", "PDWScripter\PDWScripter.csproj", "{6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}" 8 | EndProject 9 | Global 10 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 11 | Debug|Any CPU = Debug|Any CPU 12 | Debug|x64 = Debug|x64 13 | Debug|x86 = Debug|x86 14 | Release|Any CPU = Release|Any CPU 15 | Release|x64 = Release|x64 16 | Release|x86 = Release|x86 17 | EndGlobalSection 18 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 19 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 20 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|Any CPU.Build.0 = Debug|Any CPU 21 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|x64.ActiveCfg = Debug|Any CPU 22 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|x64.Build.0 = Debug|Any CPU 23 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|x86.ActiveCfg = Debug|Any CPU 24 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Debug|x86.Build.0 = Debug|Any CPU 25 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|Any CPU.ActiveCfg = Release|Any CPU 26 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|Any CPU.Build.0 = Release|Any CPU 27 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|x64.ActiveCfg = Release|Any CPU 28 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|x64.Build.0 = Release|Any CPU 29 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|x86.ActiveCfg = Release|Any CPU 30 | {582583B5-5CFE-47AD-85E1-0493B1F51451}.Release|x86.Build.0 = Release|Any CPU 31 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 32 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|Any CPU.Build.0 = Debug|Any CPU 33 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|x64.ActiveCfg = Debug|Any CPU 34 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|x64.Build.0 = Debug|Any CPU 35 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|x86.ActiveCfg = Debug|Any CPU 36 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Debug|x86.Build.0 = Debug|Any CPU 37 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|Any CPU.ActiveCfg = Release|Any CPU 38 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|Any CPU.Build.0 = Release|Any CPU 39 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|x64.ActiveCfg = Release|Any CPU 40 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|x64.Build.0 = Release|Any CPU 41 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|x86.ActiveCfg = Release|Any CPU 42 | {6F3D0A28-0667-4869-9F01-D02B4BA5BBFA}.Release|x86.Build.0 = Release|Any CPU 43 | EndGlobalSection 44 | GlobalSection(SolutionProperties) = preSolution 45 | HideSolutionNode = FALSE 46 | EndGlobalSection 47 | GlobalSection(ExtensibilityGlobals) = postSolution 48 | SolutionGuid = {5A22DECF-E698-4F0F-8CCB-12021CDCC8D0} 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /src/DWScripter/DWScripter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Exe 9 | netcoreapp2.0;net461 10 | 1.1.0 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /src/DWScripter/Program.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | 5 | using System; 6 | using System.Linq; 7 | using System.IO; 8 | using System.Collections.Generic; 9 | using System.Text.RegularExpressions; 10 | 11 | 12 | namespace DWScripter 13 | { 14 | 15 | class Program 16 | { 17 | 18 | static void Main(string[] args) 19 | { 20 | string server = ""; 21 | string sourceDb = ""; 22 | string userName = ""; 23 | string pwd = ""; 24 | string wrkMode = "ALL"; 25 | string filterSpec = "%"; 26 | string outFile = ""; 27 | string system = "PDW"; 28 | string authentication = "SQL"; 29 | string encriptSQLConneciton = "false"; 30 | string trustServerCertificate = "true"; 31 | string mode = ""; 32 | string ExcludeObjectSuffixList = " "; //"_old|_new|_test|_dba"; // used to exclude test or non-user objects; 33 | string serverTarget = ""; 34 | string strportTarget = ""; 35 | string TargetDb = ""; 36 | string userNameTarget = ""; 37 | string pwdTarget = ""; 38 | string featureToScript = ""; 39 | string FiltersFilePath ="" ; 40 | string CommandTimeout =""; 41 | 42 | Dictionary parameters = new Dictionary(); 43 | parameters = GetParametersFromArguments(args); 44 | 45 | foreach (string pKey in parameters.Keys) 46 | { 47 | switch (pKey) 48 | { 49 | case "-S": 50 | server = parameters[pKey]; 51 | break; 52 | case "-D": 53 | sourceDb = parameters[pKey]; 54 | break; 55 | case "-E": 56 | authentication = "WINDOWS"; 57 | break; 58 | case "-U": 59 | userName = parameters[pKey]; 60 | break; 61 | case "-P": 62 | pwd = parameters[pKey]; 63 | break; 64 | case "-W": 65 | wrkMode = parameters[pKey]; 66 | break; 67 | case "-M": 68 | mode = parameters[pKey].ToUpper(); 69 | break; 70 | case "-St": 71 | serverTarget = parameters[pKey]; 72 | break; 73 | case "-Dt": 74 | TargetDb = parameters[pKey]; 75 | break; 76 | case "-Ut": 77 | userNameTarget = parameters[pKey]; 78 | break; 79 | case "-Pt": 80 | pwdTarget = parameters[pKey]; 81 | break; 82 | case "-O": 83 | outFile = parameters[pKey]; 84 | break; 85 | case "-F": 86 | featureToScript = parameters[pKey].ToUpper(); 87 | break; 88 | case "-Fp": 89 | FiltersFilePath = parameters[pKey]; 90 | break; 91 | case "-X": 92 | ExcludeObjectSuffixList = parameters[pKey]; 93 | break; 94 | case "-t": 95 | CommandTimeout = parameters[pKey]; 96 | break; 97 | default: 98 | break; 99 | } 100 | } 101 | 102 | if (wrkMode != "ALL" & wrkMode != "DDL" & wrkMode != "DML") 103 | { 104 | Console.WriteLine("Uknown mode. USE: DML|DDL|ALL"); 105 | return; 106 | } 107 | 108 | if (mode == "COMPARE" & (String.IsNullOrEmpty(serverTarget) || String.IsNullOrEmpty(TargetDb))) 109 | { 110 | Console.WriteLine("Target Database elements must be completed ..."); 111 | return; 112 | } 113 | 114 | PDWscripter c = null; 115 | PDWscripter cTarget = null; 116 | Boolean SourceFromFile = false; 117 | 118 | int? commandTimeout = GetCommandTimeout(CommandTimeout); 119 | 120 | try 121 | { 122 | if (mode == "FULL" || mode == "DELTA" || mode == "COMPARE" || mode == "PERSISTSTRUCTURE") 123 | { 124 | if (mode == "FULL" && featureToScript != "ALL") 125 | { 126 | filterSpec = featureToScript; 127 | } 128 | c = new PDWscripter(system, server, sourceDb, authentication, userName, pwd, wrkMode, ExcludeObjectSuffixList, filterSpec, mode, commandTimeout); 129 | if (mode == "PERSISTSTRUCTURE") 130 | // populate dbstruct class 131 | c.getDbstructure(outFile, wrkMode, true); 132 | if (mode == "COMPARE") 133 | c.getDbstructure(outFile, wrkMode, false); 134 | } 135 | else 136 | c = new PDWscripter(); 137 | 138 | // generate full database script 139 | if (mode == "FULL" || mode == "DELTA") 140 | { 141 | c.getDbTables(false); 142 | c.IterateScriptAllTables(c, outFile); 143 | } 144 | if (mode == "COMPARE" || mode == "COMPAREFROMFILE") 145 | { 146 | SourceFromFile = false; 147 | 148 | if (wrkMode == "ALL" || wrkMode == "DDL") 149 | { 150 | if (mode == "COMPAREFROMFILE") 151 | { 152 | // retrieve database structure from JSON DDL file 153 | SourceFromFile = true; 154 | // intialize from Json file 155 | outFile = outFile.Replace(TargetDb, sourceDb); 156 | string outDBJsonStructureFile = outFile + "_STRUCT_DDL.json"; 157 | c.GetDDLstructureFromJSONfile(outDBJsonStructureFile); 158 | } 159 | else 160 | c.getDbTables(false); 161 | } 162 | 163 | if (mode == "COMPAREFROMFILE") 164 | { 165 | if (wrkMode == "ALL" || wrkMode == "DML") 166 | { 167 | // retrieve database structure from JSON DML file 168 | SourceFromFile = true; 169 | // intialize from Json file 170 | outFile = outFile.Replace(TargetDb, sourceDb); 171 | string outDBJsonStructureFile = outFile + "_STRUCT_DML.json"; 172 | c.GetDMLstructureFromJSONfile(outDBJsonStructureFile); 173 | } 174 | } 175 | 176 | 177 | FilterSettings Filters = new FilterSettings(); 178 | if (featureToScript != "ALL") 179 | { 180 | // retrieve filter settings from file 181 | Console.WriteLine("Retrieving filter settings file : " + FiltersFilePath + "- Feature : " + featureToScript + " - Database : ..."); 182 | GlobalFilterSettings gFilter = new GlobalFilterSettings(); 183 | Filters = gFilter.GetObjectsFromFile(FiltersFilePath, featureToScript, sourceDb); 184 | 185 | if (Filters == null) 186 | { 187 | throw new System.ArgumentException("Filter settings parameter can not be null - initialization from file : " + FiltersFilePath + "- Feature : " + featureToScript + " - Database : ..."); 188 | } 189 | 190 | Console.WriteLine("Filter settings OK"); 191 | } 192 | 193 | cTarget = new PDWscripter(system, serverTarget, TargetDb, authentication, userNameTarget, pwdTarget, wrkMode, "%", filterSpec, mode, commandTimeout); 194 | Console.WriteLine("Target Connection Opened"); 195 | cTarget.getDbstructure(outFile, wrkMode, false); 196 | if (mode != "COMPAREFROMFILE") 197 | cTarget.getDbTables(false); 198 | 199 | cTarget.CompIterateScriptAllTables(c, cTarget, outFile, SourceFromFile, Filters); 200 | } 201 | } 202 | catch (Exception ex) 203 | { 204 | Console.WriteLine(ex); 205 | throw ex; 206 | } 207 | 208 | if (c.conn != null) 209 | { 210 | c.conn.Close(); 211 | } 212 | 213 | if (cTarget != null) 214 | { 215 | cTarget.conn.Close(); 216 | } 217 | 218 | Console.Write("Done !!! "); 219 | Environment.Exit(0); 220 | } 221 | 222 | public static void DisplayHelp() 223 | { 224 | Console.WriteLine("DWScripter Command Line Tool"); 225 | Console.WriteLine("Usage DWScripter "); 226 | Console.WriteLine(" [-S: Server source]"); 227 | Console.WriteLine(" [-D: Database Source]"); 228 | Console.WriteLine(" [-E: Trusted connection]"); 229 | Console.WriteLine(" [-U: Login source id]"); 230 | Console.WriteLine(" [-P: Password source]"); 231 | Console.WriteLine(" [-W: work mode [DML|DDL|ALL]"); 232 | Console.WriteLine(@" [-O: Work folder path \ suffix file ] no space allowed"); 233 | Console.WriteLine(" [-M: mode [Full|PersistStructure|Compare|CompareFromFile]]"); 234 | Console.WriteLine(" [-St: Server target]"); 235 | Console.WriteLine(" [-dt: Database Target]"); 236 | Console.WriteLine(" [-Ut: login id target]"); 237 | Console.WriteLine(" [-Pt: password target]"); 238 | Console.WriteLine(" [-F: filter on feature for scripting]"); 239 | Console.WriteLine(" [-Fp: filters file path] no space allowed"); 240 | Console.WriteLine(" [-X: Exclusion Filter"); 241 | Console.WriteLine(" [-t: Command Timeout]"); 242 | Console.WriteLine(); 243 | Console.WriteLine(@"Sample : DWScripter -S:192.168.1.1,17001 -D:Fabrikam_Dev -E -O:C:\DW_SRC\FabrikamDW_STG -M:PersistStructure"); 244 | Console.WriteLine(@"Sample : DWScripter -St:192.168.1.1,17001 -Dt:Fabrikam_INT -E -O:C:\DW_SRC\FabrikamDW_STG -M:CompareFromFile -F:ALL"); 245 | Console.WriteLine(@"Sample : DWScripter -St:192.168.1.1,17001 -Dt:Fabrikam_INT -E -O:C:\DW_SRC\FabrikamDW_STG -M:CompareFromFile -F:DSN_SPRINT2 -Fp:C:\Data\DW_Databases\GlobalDWFilterSettings.json -d:Fabrikam_STG"); 246 | return; 247 | } 248 | static Dictionary GetParametersFromArguments (string[] args) 249 | { 250 | Dictionary parameters = new Dictionary(); 251 | string ParametersList = "-S|-D|-E|-M|-O|-St|-Dt|-U|-P|-Ut|-Pt|-W|-F|-Fp|-X|-t"; 252 | List ParametersHelp = new List { "-help", "-?", "/?" }; 253 | List ModeList = new List { "FULL", "COMPARE", "COMPAREFROMFILE", "PERSISTSTRUCTURE" }; 254 | Regex Plist = new Regex(ParametersList); 255 | string ParameterSwitch=""; 256 | string value=""; 257 | int SeparatorPosition; 258 | 259 | for (var x = 0; x < args.Count(); x++) 260 | { 261 | SeparatorPosition = args[x].IndexOf(":"); 262 | if (SeparatorPosition != -1) 263 | { 264 | ParameterSwitch = args[x].Substring(0, SeparatorPosition); 265 | value = args[x].Substring(SeparatorPosition + 1, args[x].Length - SeparatorPosition - 1); 266 | } 267 | else 268 | { 269 | ParameterSwitch = args[x]; 270 | value = ""; 271 | } 272 | 273 | if (ParametersHelp.Contains(ParameterSwitch)) 274 | { 275 | DisplayHelp(); 276 | Environment.Exit(0); 277 | } 278 | 279 | if (Plist.IsMatch(ParameterSwitch)) 280 | parameters.Add(ParameterSwitch, value); 281 | } 282 | 283 | if (parameters.ContainsKey("-M")) 284 | { 285 | if (!ModeList.Contains(parameters["-M"].ToUpper())) 286 | { 287 | Console.WriteLine("Value " + parameters["-M"] + "is not allowed. Only values FULL, COMPARE, COMPAREFROMFILE, PERSISTSTRUCTURE for parameter -M"); 288 | Environment.Exit(1); 289 | } 290 | } 291 | else 292 | { 293 | Console.WriteLine("Argument -M mode missing"); 294 | Environment.Exit(1); 295 | } 296 | 297 | // check feature switch existence when work mode different from PERSISTSTRUCTURE or FULL mode 298 | if (parameters["-M"].ToUpper() != "PERSISTSTRUCTURE" && parameters["-M"].ToUpper() != "FULL") 299 | { 300 | if (!parameters.ContainsKey("-F")) 301 | { 302 | Console.WriteLine("Argument -F is missing, fill it to continue"); 303 | Environment.Exit(1); 304 | } 305 | else 306 | { 307 | if (!parameters.ContainsKey("-D") && parameters["-F"].ToUpper() != "ALL") 308 | { 309 | Console.WriteLine("Argument -D is missing [Database Name], fill it to continue"); 310 | Environment.Exit(1); 311 | } 312 | 313 | if (!parameters.ContainsKey("-Fp") && parameters["-F"].ToUpper() != "ALL") 314 | { 315 | Console.WriteLine("Argument -Fp is missing [Filter file], fill it to continue"); 316 | Environment.Exit(1); 317 | } 318 | } 319 | } 320 | 321 | if (parameters.ContainsKey("-t")) 322 | { 323 | int? commandTimeout = GetCommandTimeout(parameters["-t"]); 324 | } 325 | 326 | return parameters; 327 | } 328 | 329 | private static int? GetCommandTimeout(string rawCommandTimeout) 330 | { 331 | int? returnValue = null; 332 | 333 | if (string.IsNullOrWhiteSpace(rawCommandTimeout)) 334 | { 335 | if (int.TryParse(rawCommandTimeout, out int parsedValue) == false) 336 | { 337 | Console.WriteLine("Argument -t must be numeric"); 338 | } 339 | else 340 | { 341 | if (parsedValue < 0) 342 | { 343 | Console.WriteLine("Argument -t must be a positive number"); 344 | } 345 | 346 | returnValue = parsedValue; 347 | } 348 | } 349 | 350 | if (returnValue.HasValue == false) 351 | { 352 | Environment.Exit(1); 353 | } 354 | 355 | return returnValue; 356 | } 357 | } 358 | } -------------------------------------------------------------------------------- /src/PDWScripter/ClusteredDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class ClusteredDef : List 13 | { 14 | public virtual int CompareTo(object obj) 15 | { 16 | if (obj == null) return 1; 17 | ClusteredDef otherclusteredCols = obj as ClusteredDef; 18 | if (otherclusteredCols != null) 19 | { 20 | if (this == null || otherclusteredCols == null) return 1; 21 | if (this.Count == 0 && otherclusteredCols.Count == 0) return 0; 22 | if (this.Count != otherclusteredCols.Count) return 1; 23 | if (this[0].index_type != otherclusteredCols[0].index_type) return 1; 24 | this.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 25 | otherclusteredCols.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 26 | for (int i = 0; i < this.Count; i++) 27 | { 28 | if (this[i].CompareTo(otherclusteredCols[i]) == 1) return 1; 29 | } 30 | } 31 | return 0; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/PDWScripter/ColumnDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public struct ColumnDef 13 | { 14 | public Int32 column_id; 15 | public string name; 16 | public string type; 17 | public Int16 max_length; 18 | public byte precision; 19 | public byte scale; 20 | public bool is_nullable; 21 | public byte distrbution_ordinal; 22 | public string collation_name; 23 | public string defaultconstraint; 24 | public string columnDefinition; 25 | 26 | public ColumnDef(Int32 column_id, string name, string type, Int16 max_length, byte precision, byte scale, bool is_nullable, byte distribution_ordinal, string defaultconstraint, string collation_name) 27 | { 28 | this.column_id = column_id; 29 | this.name = name; 30 | this.type = type; 31 | this.max_length = max_length; 32 | this.precision = precision; 33 | this.scale = scale; 34 | this.is_nullable = is_nullable; 35 | this.distrbution_ordinal = distribution_ordinal; 36 | this.collation_name = collation_name; 37 | this.defaultconstraint = defaultconstraint; 38 | this.columnDefinition = string.Empty; 39 | } 40 | 41 | 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/PDWScripter/DBStruct.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class DBStruct 13 | { 14 | public List schemas = new List(); 15 | public List tables = new List(); 16 | private List _tables = new List(); 17 | public TableSt GetTable(string TableName) 18 | { 19 | return this.tables.Find(delegate (TableSt e) { return e.name == TableName; }); 20 | } 21 | 22 | public List GetTablesBySchema(List ListSchemaName) 23 | { 24 | 25 | foreach (string SchemaName in ListSchemaName) 26 | { 27 | _tables.AddRange(this.tables.FindAll(delegate (TableSt e) { return e.schema == SchemaName; })); 28 | } 29 | 30 | return _tables; 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/PDWScripter/Docs/LICENSE: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | Copyright (c) Microsoft Corporation. All rights reserved. 4 | 5 | MIT License 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. -------------------------------------------------------------------------------- /src/PDWScripter/Docs/PDWScripter_How_to.md: -------------------------------------------------------------------------------- 1 | Including PDWScripter in your projects 2 | 3 | >NOTE: Before including PDWScripter library in your projects please review the [PDWScripter License](./LICENSE.md). 4 | 5 | In order to include PDWScripter in your project you can: 6 | 7 | - Reference the project. In this case you will need to download the library project and add the project code to your project. For more information on how to add a project reference to your .NET Core project see [here](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-reference) 8 | - Reference the library. In this case you will need to build the project and include a reference to the compiled library to your project. See the examples below to add a library reference to your .NET Core Project 9 | 10 | ## Examples 11 | 12 | **Add a library reference in Visual Studio 2017** 13 | If Visual Studio 2017 is your tool of choice, to can add a library reference (or a project reference) to your project follow the documentation at: 14 | [Managing references in a project](https://docs.microsoft.com/en-us/visualstudio/ide/managing-references-in-a-project) 15 | 16 | **Add a library reference to .NET Core projects** 17 | The ```dotnet add reference``` syntax does not currently allow you to add a library reference to your project of a precompiled library. In order to add such a reference you will need to: 18 | 19 | 1. Obtain a copy of the compiled library and place it in a reachable location (e.g. a /libRef subfolder in your project) 20 | 2. Edit the .csproj ItemGroup section to add the include referene and HintPath to the library location. 21 | 22 | In the following example we will add to a default .csproj file a reference to the PDWScripter library. The original .csproj file would look similar to: 23 | 24 | ```XML 25 | 26 | 27 | 28 | Exe 29 | netcoreapp2.0 30 | 31 | 32 | 33 | ``` 34 | 35 | We have placed PDWScripter in the libRef subfolder withing our project folder. 36 | To that project file we will add the following portion to allow the framework to locate our library: 37 | 38 | ```XML 39 | 40 | 41 | 42 | ..\LibRef\PDWScripter.dll 43 | 44 | 45 | 46 | ``` 47 | 48 | The final .csproj file will look similar to: 49 | 50 | ```XML 51 | 52 | 53 | 54 | Exe 55 | netcoreapp2.0 56 | 57 | 58 | 59 | 60 | ..\LibRef\PDWScripter.dll 61 | 62 | 63 | 64 | 65 | ``` 66 | -------------------------------------------------------------------------------- /src/PDWScripter/GenerateScripts.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | 11 | namespace DWScripter 12 | { 13 | class Scripting 14 | { 15 | 16 | public string GenerateColumnDefinition(List cols) 17 | { 18 | string distColumn = ""; 19 | string columnClause = ""; 20 | StringBuilder columnSelect = new StringBuilder(); 21 | StringBuilder columnspec = new StringBuilder(); 22 | 23 | List tempCols = new List(); 24 | 25 | foreach (ColumnDef c in cols) 26 | { 27 | 28 | StringBuilder columnDefinition = new StringBuilder(); 29 | 30 | if (c.distrbution_ordinal == 1) 31 | { 32 | // Save name of Distribution column 33 | distColumn = c.name; 34 | } 35 | if (c.column_id > 1) 36 | { 37 | columnspec.Append("\r\n\t,"); 38 | columnSelect.Append("\r\n\t,"); 39 | } 40 | else 41 | { 42 | columnspec.Append("\t"); 43 | columnSelect.Append("\t"); 44 | } 45 | 46 | columnDefinition.Append("[" + c.name + "]" + "\t" + c.type + "\t"); 47 | columnspec.Append("[" + c.name + "]" + "\t" + c.type + "\t"); 48 | columnSelect.Append(c.name); 49 | if (c.type == "bigint" || 50 | c.type == "bit" || 51 | c.type == "date" || 52 | c.type == "datetime" || 53 | c.type == "int" || 54 | c.type == "smalldatetime" || 55 | c.type == "smallint" || 56 | c.type == "smallmoney" || 57 | c.type == "money" || 58 | c.type == "tinyint" || 59 | c.type == "real") 60 | { 61 | // no size params 62 | } 63 | 64 | else if ( 65 | c.type == "binary" || 66 | c.type == "varbinary") 67 | { 68 | // max_length only 69 | columnspec.Append("("); 70 | columnspec.Append(c.max_length); 71 | columnspec.Append(")\t"); 72 | 73 | 74 | columnDefinition.Append("("); 75 | columnDefinition.Append(c.max_length); 76 | columnDefinition.Append(")\t"); 77 | 78 | } 79 | 80 | else if ( 81 | c.type == "char" || 82 | c.type == "varchar") 83 | { 84 | // max_length only 85 | columnspec.Append("("); 86 | columnspec.Append(c.max_length); 87 | columnspec.Append(")\t"); 88 | columnspec.Append("COLLATE\t"); 89 | columnspec.Append(c.collation_name); 90 | columnspec.Append("\t"); 91 | 92 | 93 | columnDefinition.Append("("); 94 | columnDefinition.Append(c.max_length); 95 | columnDefinition.Append(")\t"); 96 | columnDefinition.Append("COLLATE\t"); 97 | columnDefinition.Append(c.collation_name); 98 | columnDefinition.Append("\t"); 99 | 100 | } 101 | 102 | else if ( 103 | c.type == "nchar" || 104 | c.type == "nvarchar") 105 | { 106 | // max_length only 107 | columnspec.Append("("); 108 | columnspec.Append(c.max_length / 2); 109 | columnspec.Append(")\t"); 110 | columnspec.Append("COLLATE\t"); 111 | columnspec.Append(c.collation_name); 112 | columnspec.Append("\t"); 113 | 114 | columnDefinition.Append("("); 115 | columnDefinition.Append(c.max_length / 2); 116 | columnDefinition.Append(")\t"); 117 | columnDefinition.Append("COLLATE\t"); 118 | columnDefinition.Append(c.collation_name); 119 | columnDefinition.Append("\t"); 120 | 121 | } 122 | 123 | else if ( 124 | c.type == "float") 125 | { 126 | // precision only 127 | columnspec.Append("("); 128 | columnspec.Append(c.precision); 129 | columnspec.Append(")\t"); 130 | 131 | 132 | columnDefinition.Append("("); 133 | columnDefinition.Append(c.precision); 134 | columnDefinition.Append(")\t"); 135 | } 136 | 137 | else if ( 138 | c.type == "datetime2" || 139 | c.type == "datetimeoffset" || 140 | c.type == "time") 141 | { 142 | // Scale only 143 | columnspec.Append("("); 144 | columnspec.Append(c.scale); 145 | columnspec.Append(")\t"); 146 | 147 | columnDefinition.Append("("); 148 | columnDefinition.Append(c.scale); 149 | columnDefinition.Append(")\t"); 150 | 151 | } 152 | 153 | else if ( 154 | c.type == "decimal") 155 | { 156 | // Precision and Scale 157 | columnspec.Append("("); 158 | columnspec.Append(c.precision); 159 | columnspec.Append(","); 160 | columnspec.Append(c.scale); 161 | columnspec.Append(")\t"); 162 | 163 | columnDefinition.Append("("); 164 | columnDefinition.Append(c.precision); 165 | columnDefinition.Append(","); 166 | columnDefinition.Append(c.scale); 167 | columnDefinition.Append(")\t"); 168 | 169 | } 170 | 171 | else 172 | { 173 | Exception e = new Exception("Unsupported Type " + c.type); 174 | throw e; 175 | } 176 | 177 | columnspec.Append(c.is_nullable ? "NULL" : "NOT NULL"); 178 | 179 | columnDefinition.Append(c.is_nullable ? "NULL" : "NOT NULL"); 180 | 181 | columnspec.Append(" " + c.defaultconstraint); 182 | ColumnDef current = cols[cols.IndexOf(c)]; 183 | current.columnDefinition = columnDefinition.ToString(); 184 | tempCols.Add(current); 185 | 186 | } 187 | columnClause = columnspec.ToString(); 188 | return columnClause; 189 | 190 | } 191 | } 192 | } 193 | -------------------------------------------------------------------------------- /src/PDWScripter/GlobalFilterSettings.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | using Newtonsoft.Json; 11 | 12 | namespace DWScripter 13 | { 14 | public class GlobalFilterSettings 15 | { 16 | public ObjectsToFilter DatabaseObjectsToFilter; 17 | 18 | public GlobalFilterSettings() 19 | { 20 | 21 | this.DatabaseObjectsToFilter = new ObjectsToFilter(); 22 | } 23 | 24 | public FilterSettings GetObjects(string featurename, string databasename) 25 | { 26 | return (FilterSettings)this.DatabaseObjectsToFilter.Find(delegate (FilterSettings e) { return e.FeatureName == featurename && e.Database == databasename; }); 27 | } 28 | 29 | public FilterSettings GetObjectsFromFile(string InputFilePath,string featurename, string databasename) 30 | { 31 | using (StreamReader file = File.OpenText(InputFilePath)) 32 | { 33 | JsonSerializer serializer = new JsonSerializer(); 34 | GlobalFilterSettings gfs = (GlobalFilterSettings)serializer.Deserialize(file, typeof(GlobalFilterSettings)); 35 | this.DatabaseObjectsToFilter = gfs.DatabaseObjectsToFilter; 36 | } 37 | 38 | return this.GetObjects(featurename, databasename); 39 | 40 | } 41 | 42 | public void GetFilterSettingsFromFile(string InputFilePath) 43 | { 44 | using (StreamReader file = File.OpenText(InputFilePath)) 45 | { 46 | JsonSerializer serializer = new JsonSerializer(); 47 | GlobalFilterSettings gfs = (GlobalFilterSettings)serializer.Deserialize(file, typeof(GlobalFilterSettings)); 48 | this.DatabaseObjectsToFilter = gfs.DatabaseObjectsToFilter; 49 | } 50 | } 51 | 52 | public void PersistFilterSettings(string OutputFilePath) 53 | { 54 | if (OutputFilePath != "") 55 | { 56 | FileStream fs = new FileStream(OutputFilePath, FileMode.Create); 57 | StreamWriter sw = new StreamWriter(fs); 58 | 59 | sw.Write(JsonConvert.SerializeObject(this)); 60 | sw.Close(); 61 | } 62 | } 63 | 64 | } 65 | 66 | 67 | public class ObjectsToFilter : List 68 | { 69 | 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/PDWScripter/IndexColumnDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class IndexColumnDef : IComparable 13 | { 14 | public byte key_ordinal; 15 | public string name; 16 | public bool is_descending_key; 17 | public byte index_type; 18 | 19 | public IndexColumnDef(byte key_ordinal, string name, bool is_descending_key, byte index_type) 20 | { 21 | this.key_ordinal = key_ordinal; 22 | this.name = name; 23 | this.is_descending_key = is_descending_key; 24 | this.index_type = index_type; 25 | } 26 | 27 | public virtual int CompareTo(object obj) 28 | { 29 | if (obj == null) return 1; 30 | IndexColumnDef otherIndexColumnDef = obj as IndexColumnDef; 31 | if (otherIndexColumnDef != null) 32 | { 33 | if (this == null || otherIndexColumnDef == null) return 1; 34 | if (this.key_ordinal.CompareTo(otherIndexColumnDef.key_ordinal) != 0) return 1; 35 | if (this.name.ToUpper().CompareTo(otherIndexColumnDef.name.ToUpper()) != 0) return 1; 36 | if (this.is_descending_key.CompareTo(otherIndexColumnDef.is_descending_key) != 0) return 1; 37 | if (this.index_type.CompareTo(otherIndexColumnDef.index_type) != 0) return 1; 38 | } 39 | return 0; 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/PDWScripter/NonClusteredIndexDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class NonclusteredIndexDef : IComparable 13 | { 14 | public string name; 15 | public List cols; 16 | 17 | public NonclusteredIndexDef(string name) 18 | { 19 | this.name = name; 20 | cols = new List(); 21 | } 22 | 23 | public virtual int CompareTo(object obj) 24 | { 25 | if (obj == null) return 1; 26 | NonclusteredIndexDef otherNonclusteredIndexDef = obj as NonclusteredIndexDef; 27 | if (otherNonclusteredIndexDef != null) 28 | { 29 | if (this == null || otherNonclusteredIndexDef == null) return 1; 30 | if (this.name.ToUpper().CompareTo(otherNonclusteredIndexDef.name.ToUpper()) != 0) return 1; 31 | this.cols.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 32 | } 33 | return 0; 34 | } 35 | 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /src/PDWScripter/NonclusteredIndexes.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class NonclusteredIndexes : List 13 | { 14 | 15 | public virtual int CompareTo(object obj) 16 | { 17 | if (obj == null) return 1; 18 | NonclusteredIndexes otherNonclusteredIndexes = obj as NonclusteredIndexes; 19 | if (otherNonclusteredIndexes != null) 20 | { 21 | if (this == null || otherNonclusteredIndexes == null) return 1; 22 | this.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 23 | otherNonclusteredIndexes.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 24 | if (this.Count != otherNonclusteredIndexes.Count) return 1; 25 | 26 | for (int i = 0; i < this.Count; i++) 27 | { 28 | if (this[i].CompareTo(otherNonclusteredIndexes[i]) == 1) return 1; 29 | } 30 | } 31 | return 0; 32 | } 33 | 34 | public NonclusteredIndexDef GetIndex(string IndexName) 35 | { 36 | return this.Find(delegate (NonclusteredIndexDef e) { return e.name == IndexName; }); 37 | } 38 | 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /src/PDWScripter/ObjectsFilterList.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using System.IO; 10 | using Newtonsoft.Json; 11 | 12 | 13 | namespace DWScripter 14 | { 15 | public class FilterSettings 16 | { 17 | public string FeatureName; 18 | public string Database; 19 | public string Granularity; 20 | 21 | public ObjectsFilterList ObjectsToFilter; 22 | 23 | public FilterSettings() 24 | { 25 | this.Granularity = "None"; 26 | this.ObjectsToFilter = new ObjectsFilterList(); 27 | } 28 | 29 | public FilterSettings(string FeatureName,string DatabaseName,string Granularity) 30 | { 31 | this.FeatureName = FeatureName; 32 | this.Database = DatabaseName; 33 | this.Granularity = Granularity; 34 | this.ObjectsToFilter = new ObjectsFilterList(); 35 | } 36 | 37 | public List GetSchemas() 38 | { 39 | return this.ObjectsToFilter.Select(o =>o.schemaname).Distinct().ToList(); 40 | } 41 | 42 | public List GetSchemaNameObjects() 43 | { 44 | return this.ObjectsToFilter.Select(o => o.schemaname+"."+o.objectname).ToList(); 45 | } 46 | public void PersistFilterSettings(string OutputFilePath) 47 | { 48 | if (OutputFilePath != "") 49 | { 50 | FileStream fs = new FileStream(OutputFilePath, FileMode.Create); 51 | StreamWriter sw = new StreamWriter(fs); 52 | 53 | sw.Write(JsonConvert.SerializeObject(this)); 54 | sw.Close(); 55 | } 56 | } 57 | 58 | public void GetFilterSettingsFromFile(string InputFilePath) 59 | { 60 | using (StreamReader file = File.OpenText(InputFilePath)) 61 | { 62 | JsonSerializer serializer = new JsonSerializer(); 63 | FilterSettings fs = (FilterSettings)serializer.Deserialize(file, typeof(FilterSettings)); 64 | this.Granularity = fs.Granularity; 65 | this.ObjectsToFilter = fs.ObjectsToFilter; 66 | } 67 | } 68 | 69 | 70 | public void SaveFilterSettingsToFile(string InputFilePath) 71 | { 72 | if (InputFilePath != "") 73 | { 74 | FileStream fs = new FileStream(InputFilePath, FileMode.Create); 75 | StreamWriter sw = new StreamWriter(fs); 76 | sw.Write(JsonConvert.SerializeObject(this)); 77 | sw.Close(); 78 | } 79 | 80 | } 81 | } 82 | 83 | public class ObjectsFilterList : List 84 | { 85 | 86 | } 87 | public class ObjectFiltered 88 | { 89 | public string schemaname; 90 | public string objectname; 91 | public string objecttype; 92 | public Boolean todelete; 93 | 94 | public ObjectFiltered(string schemaname, string objectname, string objecttype, Boolean todelete) 95 | { 96 | this.schemaname = schemaname; 97 | this.objectname = objectname; 98 | this.objecttype = objecttype; 99 | this.todelete = todelete; 100 | } 101 | 102 | } 103 | 104 | 105 | 106 | } 107 | -------------------------------------------------------------------------------- /src/PDWScripter/PDWScripter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2.0;net461 5 | 1.1.0 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/PDWScripter/PDWscripter.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.IO; 6 | using System.Collections.Generic; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Data; 10 | using System.Data.SqlClient; 11 | using Newtonsoft.Json; 12 | using System.Text.RegularExpressions; 13 | 14 | namespace DWScripter 15 | { 16 | public class PDWscripter 17 | { 18 | public SqlConnection conn; 19 | private SqlCommand cmd; 20 | private SqlDataReader rdr; 21 | public List dbTables; 22 | public string DatabaseName; 23 | private List cols; 24 | public DBStruct dbstruct; 25 | private string distColumn; 26 | private List clusteredCols; 27 | private List partitionBoundaries; 28 | private string createTableTxt; 29 | private string alterTableTxt; 30 | private string dropTableTxt; 31 | private string dropDeployTmpTableTxt; 32 | private string dropDeployTableTxt; 33 | private string copyDataToTmpTableTxt; 34 | private string copyDataFromTmpTableTxt; 35 | private string createSchemaTxt; 36 | private string dropSchemaTxt; 37 | private string createDeployTmpSchemaTxt; 38 | private string dropDeployTmpSchemaTxt; 39 | private string sourceDb; 40 | private string destDb; 41 | private string sourceTable; 42 | private string sourceTmpTableName; 43 | private string destTable; 44 | private string destTableFullName; 45 | private Int16 distribution_policy; 46 | private string columnClause; 47 | private string columnSelect; 48 | private string clusteredClause; 49 | private string partitionBoundaryClause; 50 | private string partitionColumn; 51 | private string partitionLeftOrRight; 52 | private string filterSpec; 53 | public string ExcludeObjectSuffixList; 54 | private int? CommandTimeout; 55 | private string wrkMode; 56 | private string scriptMode; 57 | private List nonclusteredIndexes; 58 | private string nonClusteredClause; 59 | private List stats; 60 | private string statsClause; 61 | private string warningFile; 62 | public List> DbObjectDefinitions; 63 | 64 | 65 | public PDWscripter() 66 | { 67 | cols = new List(); 68 | clusteredCols = new List(); 69 | partitionBoundaries = new List(); 70 | nonclusteredIndexes = new List(); 71 | stats = new List(); 72 | dbTables = new List(); 73 | dbstruct = new DBStruct(); 74 | } 75 | public PDWscripter(string system, string server, string sourceDb, string authentication, string userName, string pwd, string wrkMode, string ExcludeObjectSuffixList, string filterSpec, string scriptMode, int? CommandTimeout) 76 | { 77 | DatabaseName = sourceDb; 78 | cols = new List(); 79 | clusteredCols = new List(); 80 | partitionBoundaries = new List(); 81 | nonclusteredIndexes = new List(); 82 | stats = new List(); 83 | dbTables = new List(); 84 | dbstruct = new DBStruct(); 85 | this.filterSpec = filterSpec; 86 | this.ExcludeObjectSuffixList = ExcludeObjectSuffixList; 87 | this.wrkMode = wrkMode; 88 | this.sourceDb = sourceDb; 89 | this.destDb = sourceDb; // For future DB cloning 90 | this.scriptMode = scriptMode; 91 | this.CommandTimeout = CommandTimeout; 92 | 93 | SqlConnectionStringBuilder constrbuilder = new SqlConnectionStringBuilder(); 94 | 95 | conn = new System.Data.SqlClient.SqlConnection(); 96 | if (system == "PDW") 97 | { 98 | constrbuilder.DataSource = server; 99 | constrbuilder.InitialCatalog = sourceDb; 100 | if (authentication == "SQL") 101 | { 102 | constrbuilder.UserID = userName; 103 | constrbuilder.Password = pwd; 104 | constrbuilder.IntegratedSecurity = false; 105 | 106 | } 107 | else 108 | { 109 | constrbuilder.IntegratedSecurity = true; 110 | // constrbuilder.Encrypt = true; 111 | // constrbuilder.TrustServerCertificate = true; 112 | } 113 | conn.ConnectionString = constrbuilder.ConnectionString; 114 | 115 | } 116 | else 117 | { 118 | conn.ConnectionString = "server=" + server + ";database=" + sourceDb + ";User ID=" + userName + ";Password=" + pwd; 119 | } 120 | 121 | 122 | cmd = new System.Data.SqlClient.SqlCommand(); 123 | //sets non default timeout 124 | if(this.CommandTimeout.HasValue) 125 | { 126 | cmd.CommandTimeout = this.CommandTimeout.Value; 127 | 128 | } 129 | Console.WriteLine("Current Command Timeout: " + cmd.CommandTimeout); 130 | 131 | try { 132 | conn.Open(); 133 | cmd.Connection = conn; 134 | } 135 | catch (Exception ex) 136 | { 137 | Console.WriteLine(ex); 138 | throw ex; 139 | } 140 | 141 | } 142 | 143 | private void getSchemas(StreamWriter sw, Boolean GetStructure) 144 | { 145 | cmd.CommandText = 146 | "select name from sys.schemas where name not in ('dbo','sys','INFORMATION_SCHEMA')"; 147 | 148 | rdr = cmd.ExecuteReader(); 149 | 150 | while (rdr.Read()) 151 | { 152 | if (!GetStructure) 153 | { 154 | createSchemaTxt = "CREATE SCHEMA [" + rdr.GetString(rdr.GetOrdinal("name")) + "];\r\nGO\r\n"; 155 | if (sw != null) 156 | sw.WriteLine(createSchemaTxt); 157 | } 158 | else 159 | dbstruct.schemas.Add(rdr.GetString(rdr.GetOrdinal("name"))); 160 | } 161 | 162 | rdr.Close(); 163 | } 164 | 165 | private void CompareSchemas(StreamWriter sw, PDWscripter cSource, PDWscripter cTarget, Boolean SourceFromFile, FilterSettings FilterSet) 166 | { 167 | List TargetSchemas = new List(); 168 | List SourceSchemas = new List(); 169 | StringBuilder dropscript = new StringBuilder(); 170 | String createUseDbTxt = string.Empty; 171 | 172 | String strStartWarningMessage = string.Empty; 173 | String strEndWarningMessage = string.Empty; 174 | 175 | cTarget.cmd.CommandText = "select name from sys.schemas where name not in ('dbo','sys','INFORMATION_SCHEMA')"; 176 | 177 | //==> SOURCE 178 | if (SourceFromFile) 179 | { 180 | SourceSchemas.AddRange(dbstruct.schemas); 181 | } 182 | else 183 | { 184 | cSource.cmd.CommandText = "select name from sys.schemas where name not in ('dbo','sys','INFORMATION_SCHEMA')"; 185 | rdr = cSource.cmd.ExecuteReader(); 186 | while (rdr.Read()) 187 | { 188 | SourceSchemas.Add(rdr.GetString(rdr.GetOrdinal("name"))); 189 | } 190 | rdr.Close(); 191 | } 192 | //==> TARGET 193 | rdr = cTarget.cmd.ExecuteReader(); 194 | while (rdr.Read()) 195 | { 196 | TargetSchemas.Add(rdr.GetString(rdr.GetOrdinal("name"))); 197 | 198 | } 199 | rdr.Close(); 200 | 201 | List ListSchemasToCreate; 202 | List ListSchemasToDelete; 203 | // COMPARE 204 | if (FilterSet.Granularity != "None") 205 | { 206 | ListSchemasToCreate = (SourceSchemas.Intersect(FilterSet.GetSchemas())).Except(TargetSchemas).ToList(); 207 | ListSchemasToDelete = new List(); 208 | 209 | } 210 | else 211 | { 212 | ListSchemasToCreate = SourceSchemas.Except(TargetSchemas).ToList(); 213 | ListSchemasToDelete = TargetSchemas.Except(SourceSchemas).ToList(); 214 | } 215 | 216 | 217 | string description = "/*####################################################################################################################################################*/\r\n"; 218 | description += "--schemas - to create =" + ListSchemasToCreate.Count.ToString() + " - to delete = " + ListSchemasToDelete.Count.ToString() + "\r\n"; 219 | description += "PRINT 'schemas creation'\r\nGO\r\n"; 220 | sw.WriteLine(description); 221 | if (ListSchemasToDelete.Count > 0) 222 | writeWarningtxt(description); 223 | 224 | foreach (string schemaTobeCreated in ListSchemasToCreate) 225 | { 226 | createSchemaTxt = "CREATE SCHEMA [" + schemaTobeCreated + "];\r\nGO\r\n"; 227 | sw.WriteLine(createSchemaTxt); 228 | } 229 | 230 | dropSchemaTxt = string.Empty; 231 | 232 | foreach (string schemaTobeDeleted in ListSchemasToDelete) 233 | { 234 | dropSchemaTxt = "/* DROP SCHEMA " + schemaTobeDeleted + ";\r\nGO */\r\n"; 235 | 236 | dropscript.Append(dropSchemaTxt); 237 | 238 | } 239 | 240 | if (dropscript.Length > 0) 241 | { 242 | createUseDbTxt = "USE " + cTarget.sourceDb + "\r\nGO\r\n"; 243 | strStartWarningMessage = "/* WARNING !!!! ======: SCHEMAS TO DROP.\r\n"; 244 | strEndWarningMessage = "*/\r\n\r\n"; 245 | dropSchemaTxt = strStartWarningMessage + dropscript.ToString() + strEndWarningMessage; 246 | sw.WriteLine(dropSchemaTxt); 247 | 248 | dropSchemaTxt = strStartWarningMessage + createUseDbTxt + dropscript.ToString() + strEndWarningMessage; 249 | writeWarningtxt(dropSchemaTxt); 250 | } 251 | 252 | 253 | } 254 | 255 | private void CompareDbTables(StreamWriter sw, PDWscripter cSource, PDWscripter cTarget, Boolean SourceFromFile, FilterSettings FilterSet) 256 | { 257 | 258 | if (SourceFromFile) 259 | { 260 | cSource.dbTables = new List(); 261 | foreach (TableSt tbl in cSource.dbstruct.tables) 262 | { 263 | cSource.dbTables.Add(new TableDef(tbl.name, tbl.schema, tbl.distribution_policy)); 264 | } 265 | 266 | cTarget.dbTables = new List(); 267 | foreach (TableSt tbl in cTarget.dbstruct.tables) 268 | { 269 | cTarget.dbTables.Add(new TableDef(tbl.name, tbl.schema, tbl.distribution_policy)); 270 | } 271 | 272 | } 273 | 274 | // COMPARE 275 | List ListdbTablesToCreate; 276 | List ListTablesToDelete = new List(); 277 | List ListTablesToBeAlter; 278 | 279 | List ListdbTablesSourceFiltered; 280 | List ListdbTablesTargetFiltered; 281 | 282 | 283 | switch (FilterSet.Granularity.ToUpper()) 284 | { 285 | case "SCHEMA": 286 | 287 | ListdbTablesSourceFiltered = cSource.dbTables.FindAll(delegate (TableDef tdef) { return FilterSet.GetSchemas().Contains(tdef.schema); }); 288 | ListdbTablesTargetFiltered = cTarget.dbTables.FindAll(delegate (TableDef tdef) { return FilterSet.GetSchemas().Contains(tdef.schema); }); 289 | 290 | ListdbTablesToCreate = ListdbTablesSourceFiltered.Except(ListdbTablesTargetFiltered).ToList(); 291 | ListTablesToDelete = ListdbTablesTargetFiltered.Except(ListdbTablesSourceFiltered).ToList(); 292 | ListTablesToBeAlter = ListdbTablesSourceFiltered.Intersect(ListdbTablesTargetFiltered).ToList(); 293 | break; 294 | 295 | case "OBJECTS": 296 | 297 | 298 | ListdbTablesSourceFiltered = cSource.dbTables.FindAll(delegate (TableDef Table) { return FilterSet.GetSchemaNameObjects().Contains(Table.name); }); 299 | ListdbTablesTargetFiltered = cTarget.dbTables.FindAll(delegate (TableDef Table) { return FilterSet.GetSchemaNameObjects().Contains(Table.name); }); 300 | 301 | ListdbTablesToCreate = ListdbTablesSourceFiltered.Except(ListdbTablesTargetFiltered).ToList(); 302 | ListTablesToBeAlter = ListdbTablesSourceFiltered.Intersect(ListdbTablesTargetFiltered).ToList(); 303 | 304 | break; 305 | 306 | default: 307 | ListdbTablesToCreate = cSource.dbTables.Except(cTarget.dbTables).ToList(); 308 | ListTablesToDelete = cTarget.dbTables.Except(cSource.dbTables).ToList(); 309 | ListTablesToBeAlter = cSource.dbTables.Intersect(cTarget.dbTables).ToList(); 310 | break; 311 | } 312 | 313 | 314 | bool bWarning = false; 315 | string description = "/*####################################################################################################################################################*/\r\n"; 316 | description += "--tables - to create = " + ListdbTablesToCreate.Count.ToString() + " - to delete = " + ListTablesToDelete.Count.ToString() + " - to compare = " + ListTablesToBeAlter.Count.ToString() + "\r\n"; 317 | description += "PRINT 'tables creation'\r\n"; 318 | sw.WriteLine(description); 319 | if (ListTablesToDelete.Count > 0) 320 | writeWarningtxt(description); 321 | 322 | // ==> To Delete 323 | foreach (TableDef t in ListTablesToDelete) 324 | { 325 | cTarget.sourceTable = t.name; 326 | cTarget.destTable = t.name; 327 | 328 | // case distribution change then add to alter list 329 | if (cSource.dbTables.Exists(x => x.name == t.name)) 330 | { 331 | ListTablesToBeAlter.Insert(0, t); 332 | } 333 | else 334 | { 335 | 336 | bWarning = true; 337 | cTarget.compBuildDropTableText(sw, bWarning); 338 | } 339 | } 340 | 341 | // ==> To Create 342 | foreach (TableDef t in ListdbTablesToCreate) 343 | { 344 | // case distribution change 345 | if (!cTarget.dbTables.Exists(x => x.name == t.name)) 346 | { 347 | cSource.sourceTable = t.name; 348 | cSource.destTable = t.name; 349 | cSource.distribution_policy = t.distribution_policy; 350 | cSource.getSourceColumns(false, SourceFromFile); 351 | cSource.getClusteredIndex(false, SourceFromFile); 352 | cSource.getPartitioning(false, SourceFromFile); 353 | cSource.getNonclusteredIndexes(false, SourceFromFile); 354 | cSource.getStats(false, SourceFromFile); 355 | 356 | 357 | 358 | cSource.buildCreateTableText(sw, cTarget.destDb, false); 359 | } 360 | } 361 | 362 | 363 | // ==> To ALTER 364 | foreach (TableDef t in ListTablesToBeAlter) 365 | { 366 | 367 | cSource.buildAlterTableText(sw, cSource, cTarget, t, SourceFromFile); 368 | } 369 | 370 | 371 | 372 | } 373 | 374 | 375 | private void writeWarningtxt(string messagewarning) 376 | { 377 | 378 | StreamWriter sw = null; 379 | 380 | sw = new StreamWriter(warningFile, true); 381 | 382 | sw.WriteLine(messagewarning); 383 | 384 | sw.Close(); 385 | 386 | } 387 | 388 | public void getDbstructure(string outFile, string wrkMode, bool generateFile) 389 | { 390 | if (wrkMode == "ALL" || wrkMode == "DDL") 391 | { 392 | Console.Write("Getting " + this.sourceDb + " database DDL structure"); 393 | string outDBJsonDDLStructureFile = outFile + "_STRUCT_DDL.json"; 394 | StreamWriter sw = null; 395 | FileStream fs = null; 396 | 397 | getDbTables(true); 398 | Console.Write("."); 399 | getSchemas(sw, true); 400 | Console.Write("."); 401 | getSourceColumnsGlobal(); 402 | Console.Write("."); 403 | getClusteredIndexGlobal(); 404 | getNonclusteredIndexesGlobal(); 405 | Console.Write("."); 406 | getStatsGlobal(); 407 | Console.Write("."); 408 | getPartitioningGlobal(); 409 | 410 | Console.Write("\r\nDone\r\n"); 411 | if (generateFile) 412 | { 413 | 414 | Console.Write("PersistStructure DDL to JSON file :" + outDBJsonDDLStructureFile + ">"); 415 | if (outDBJsonDDLStructureFile != "") 416 | { 417 | fs = new FileStream(outDBJsonDDLStructureFile, FileMode.Create); 418 | sw = new StreamWriter(fs); 419 | } 420 | sw.Write(JsonConvert.SerializeObject(dbstruct)); 421 | sw.Close(); 422 | } 423 | } 424 | 425 | if (wrkMode == "ALL" || wrkMode == "DML") 426 | { 427 | 428 | Console.Write("Getting " + this.sourceDb + " database DML structure"); 429 | cmd.CommandText = @" SELECT c.definition, b.name + '.' + a.name AS ObjectName 430 | FROM 431 | sys.sql_modules c 432 | INNER JOIN sys.objects a ON a.object_id = c.object_id 433 | INNER JOIN sys.schemas b 434 | ON a.schema_id = b.schema_id"; 435 | 436 | rdr = cmd.ExecuteReader(); 437 | DbObjectDefinitions = new List>(); 438 | Regex r = new Regex(this.ExcludeObjectSuffixList, RegexOptions.IgnoreCase); 439 | string ModuleName; 440 | while (rdr.Read()) 441 | { 442 | IDataRecord record = (IDataRecord)rdr; 443 | ModuleName = record[1].ToString(); 444 | if (!r.IsMatch(ModuleName)) 445 | { 446 | 447 | KeyValuePair kvpObjNameDef = new KeyValuePair(String.Format("{0}", record[1]), String.Format("{0}", record[0]).TrimEnd(new char[] { '\r', '\n', ' ' })); 448 | 449 | if (!DbObjectDefinitions.Exists(objDef => objDef.Key == kvpObjNameDef.Key)) 450 | { 451 | 452 | // Object doesn't exist 453 | if (!DbObjectDefinitions.Any(objDef => objDef.Value.Contains(kvpObjNameDef.Key))) 454 | { 455 | // Object never used by an other object 456 | DbObjectDefinitions.Add(kvpObjNameDef); 457 | } 458 | else 459 | { 460 | // Object already used by an other object, we had it previously to the calling one 461 | int idxCallingObj = DbObjectDefinitions.IndexOf(DbObjectDefinitions.First(objDef => objDef.Value.Contains(kvpObjNameDef.Key))); 462 | DbObjectDefinitions.Insert(idxCallingObj, kvpObjNameDef); 463 | } 464 | } 465 | Console.Write("."); 466 | } 467 | } 468 | rdr.Close(); 469 | Console.Write("\r\nDone\r\n"); 470 | if (generateFile) 471 | { 472 | string outDBJsonDMLStructureFile = outFile + "_STRUCT_DML.json"; 473 | StreamWriter sw = null; 474 | FileStream fs = null; 475 | Console.Write("PersistStructure DML to JSON file :" + outDBJsonDMLStructureFile + ">"); 476 | if (outDBJsonDMLStructureFile != "") 477 | { 478 | fs = new FileStream(outDBJsonDMLStructureFile, FileMode.Create); 479 | sw = new StreamWriter(fs); 480 | } 481 | sw.Write(JsonConvert.SerializeObject(DbObjectDefinitions)); 482 | sw.Close(); 483 | } 484 | } 485 | } 486 | public void getDbTables(Boolean getStructure) 487 | { 488 | dbTables.Clear(); 489 | Regex r = new Regex(this.ExcludeObjectSuffixList, RegexOptions.IgnoreCase); 490 | string TableName; 491 | cmd.CommandText = 492 | "select schema_name(so.schema_id) + '.' + so.name as name, tdp.distribution_policy, schema_name(so.schema_id) as [schema] " + 493 | "from sys.tables so left join sys.external_tables et on so.object_id = et.object_id " + 494 | "JOIN sys.pdw_table_distribution_properties AS tdp ON so.object_id = tdp.object_id " + 495 | "where et.name is NULL and so.type = 'U' " + 496 | "and schema_name(so.schema_id) + '.' + so.name like '" + filterSpec + "' " + 497 | "order by so.name "; 498 | 499 | rdr = cmd.ExecuteReader(); 500 | 501 | if (rdr.HasRows) 502 | { 503 | while (rdr.Read()) 504 | { 505 | TableName = rdr.GetString(rdr.GetOrdinal("name")); 506 | if (!r.IsMatch(TableName)) 507 | { 508 | if (!getStructure) 509 | dbTables.Add(new TableDef( 510 | rdr.GetString(rdr.GetOrdinal("name")), 511 | rdr.GetString(rdr.GetOrdinal("schema")), 512 | rdr.GetByte(rdr.GetOrdinal("distribution_policy")) 513 | )); 514 | else 515 | dbstruct.tables.Add(new TableSt(rdr.GetString(rdr.GetOrdinal("name")), rdr.GetString(rdr.GetOrdinal("schema")), 516 | rdr.GetByte(rdr.GetOrdinal("distribution_policy")) 517 | )); 518 | } 519 | } 520 | } 521 | rdr.Close(); 522 | 523 | } 524 | private void getSourceColumnsGlobal() 525 | { 526 | 527 | List columns = new List(); 528 | string TableKey = ""; 529 | string SchemaName; 530 | string TableName; 531 | string tableKeyPrevious = ""; 532 | TableSt TableStruct = new TableSt(); 533 | cmd.CommandText = 534 | @"select schema_name(tbl.schema_id) as SchemaName,tbl.Name as TableName, c.column_id, c.name, t.name as type, c.max_length, c.precision, 535 | c.scale, c.is_nullable, d.distribution_ordinal, c.collation_name, ISNULL('DEFAULT ' + dc.definition, '') as DefaultConstraint 536 | from sys.columns c 537 | join sys.pdw_column_distribution_properties d 538 | on c.object_id = d.object_id and c.column_id = d.column_id 539 | join sys.types t on t.user_type_id = c.user_type_id 540 | left join sys.default_constraints dc on c.default_object_id = dc.object_id and c.object_id = dc.parent_object_id 541 | inner join sys.tables tbl on tbl.object_id = c.object_id and tbl.type = 'U' 542 | order by schema_name(tbl.schema_id),tbl.name, Column_Id "; 543 | rdr = cmd.ExecuteReader(); 544 | 545 | while (rdr.Read()) 546 | { 547 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 548 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 549 | TableKey = SchemaName + "." + TableName; 550 | if (TableKey != tableKeyPrevious) 551 | { 552 | if (columns.Count != 0 && (TableStruct != null)) 553 | { 554 | TableStruct.Columns = columns; 555 | } 556 | TableStruct = this.dbstruct.GetTable(TableKey); 557 | 558 | 559 | columns = new List(); 560 | tableKeyPrevious = TableKey; 561 | } 562 | 563 | columns.Add(new ColumnDef( 564 | rdr.GetInt32(rdr.GetOrdinal("column_id")), 565 | rdr.GetString(rdr.GetOrdinal("name")), 566 | rdr.GetString(rdr.GetOrdinal("type")), 567 | rdr.GetInt16(rdr.GetOrdinal("max_length")), 568 | rdr.GetByte(rdr.GetOrdinal("precision")), 569 | rdr.GetByte(rdr.GetOrdinal("scale")), 570 | rdr.GetBoolean(rdr.GetOrdinal("is_nullable")), 571 | rdr.GetByte(rdr.GetOrdinal("distribution_ordinal")), 572 | rdr.GetString(rdr.GetOrdinal("DefaultConstraint")), 573 | rdr["collation_name"] == DBNull.Value ? string.Empty : (string)rdr["collation_name"] 574 | )); 575 | 576 | } 577 | if (columns.Count != 0 && (TableStruct != null)) 578 | { 579 | TableStruct.Columns = columns; 580 | } 581 | 582 | rdr.Close(); 583 | 584 | 585 | } 586 | private void getSourceColumns(Boolean getStruture, Boolean SourceFromFile) 587 | { 588 | cols.Clear(); 589 | distColumn = ""; 590 | columnClause = ""; 591 | StringBuilder columnSelect = new StringBuilder(); 592 | StringBuilder columnspec = new StringBuilder(); 593 | 594 | List tempCols = new List(); 595 | 596 | if (!SourceFromFile) 597 | { 598 | cmd.CommandText = 599 | "select c.column_id, c.name, t.name as type, c.max_length, c.precision," + 600 | "c.scale, c.is_nullable, d.distribution_ordinal, c.collation_name, ISNULL('DEFAULT '+dc.definition,'') as DefaultConstraint " + 601 | "from sys.columns c " + 602 | "join sys.pdw_column_distribution_properties d " + 603 | "on c.object_id = d.object_id and c.column_id = d.column_id " + 604 | "join sys.types t on t.user_type_id = c.user_type_id " + 605 | "left join sys.default_constraints dc on c.default_object_id =dc.object_id and c.object_id =dc.parent_object_id " + 606 | "where c.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') " + 607 | "order by Column_Id "; 608 | 609 | rdr = cmd.ExecuteReader(); 610 | 611 | if (rdr.HasRows) 612 | { 613 | while (rdr.Read()) 614 | { 615 | 616 | cols.Add(new ColumnDef( 617 | rdr.GetInt32(rdr.GetOrdinal("column_id")), 618 | rdr.GetString(rdr.GetOrdinal("name")), 619 | rdr.GetString(rdr.GetOrdinal("type")), 620 | rdr.GetInt16(rdr.GetOrdinal("max_length")), 621 | rdr.GetByte(rdr.GetOrdinal("precision")), 622 | rdr.GetByte(rdr.GetOrdinal("scale")), 623 | rdr.GetBoolean(rdr.GetOrdinal("is_nullable")), 624 | rdr.GetByte(rdr.GetOrdinal("distribution_ordinal")), 625 | rdr.GetString(rdr.GetOrdinal("DefaultConstraint")), 626 | rdr["collation_name"] == DBNull.Value ? string.Empty : (string)rdr["collation_name"] 627 | )); 628 | 629 | } 630 | 631 | rdr.Close(); 632 | if (getStruture) 633 | { 634 | dbstruct.GetTable(sourceTable).Columns.AddRange(cols); 635 | return; 636 | } 637 | } 638 | } 639 | else 640 | { 641 | cols = dbstruct.GetTable(sourceTable).Columns; 642 | } 643 | 644 | cols.Sort((a, b) => a.column_id.CompareTo(b.column_id)); 645 | 646 | int minColumnId = this.GetMinColumnId(this.cols); 647 | 648 | foreach (ColumnDef c in cols) 649 | { 650 | 651 | StringBuilder columnDefinition = new StringBuilder(); 652 | 653 | if (c.distrbution_ordinal == 1) 654 | { 655 | // Save name of Distribution column 656 | distColumn = c.name; 657 | } 658 | if (c.column_id > minColumnId) 659 | { 660 | columnspec.Append("\r\n\t,"); 661 | columnSelect.Append("\r\n\t,"); 662 | } 663 | else 664 | { 665 | columnspec.Append("\t"); 666 | columnSelect.Append("\t"); 667 | } 668 | 669 | columnDefinition.Append("[" + c.name + "]" + "\t" + c.type + "\t"); 670 | columnspec.Append("[" + c.name + "]" + "\t" + c.type + "\t"); 671 | columnSelect.Append(c.name); 672 | if (c.type == "bigint" || 673 | c.type == "bit" || 674 | c.type == "date" || 675 | c.type == "datetime" || 676 | c.type == "int" || 677 | c.type == "smalldatetime" || 678 | c.type == "smallint" || 679 | c.type == "smallmoney" || 680 | c.type == "money" || 681 | c.type == "tinyint" || 682 | c.type == "real" || 683 | c.type == "uniqueidentifier") 684 | { 685 | // no size params 686 | } 687 | 688 | else if ( 689 | c.type == "binary" || 690 | c.type == "varbinary") 691 | { 692 | // max_length only 693 | columnspec.Append("("); 694 | columnspec.Append(GetMaxLength(c)); 695 | columnspec.Append(")\t"); 696 | 697 | 698 | columnDefinition.Append("("); 699 | columnDefinition.Append(GetMaxLength(c)); 700 | columnDefinition.Append(")\t"); 701 | 702 | } 703 | 704 | else if ( 705 | c.type == "char" || 706 | c.type == "varchar") 707 | { 708 | // max_length only 709 | columnspec.Append("("); 710 | columnspec.Append(GetMaxLength(c)); 711 | columnspec.Append(")\t"); 712 | columnspec.Append("COLLATE\t"); 713 | columnspec.Append(c.collation_name); 714 | columnspec.Append("\t"); 715 | 716 | 717 | columnDefinition.Append("("); 718 | columnDefinition.Append(GetMaxLength(c)); 719 | columnDefinition.Append(")\t"); 720 | columnDefinition.Append("COLLATE\t"); 721 | columnDefinition.Append(c.collation_name); 722 | columnDefinition.Append("\t"); 723 | 724 | } 725 | 726 | else if ( 727 | c.type == "nchar" || 728 | c.type == "nvarchar") 729 | { 730 | // max_length only 731 | columnspec.Append("("); 732 | columnspec.Append(GetMaxLength(c)); 733 | columnspec.Append(")\t"); 734 | columnspec.Append("COLLATE\t"); 735 | columnspec.Append(c.collation_name); 736 | columnspec.Append("\t"); 737 | 738 | columnDefinition.Append("("); 739 | columnDefinition.Append(GetMaxLength(c)); 740 | columnDefinition.Append(")\t"); 741 | columnDefinition.Append("COLLATE\t"); 742 | columnDefinition.Append(c.collation_name); 743 | columnDefinition.Append("\t"); 744 | 745 | } 746 | 747 | else if ( 748 | c.type == "float") 749 | { 750 | // precision only 751 | columnspec.Append("("); 752 | columnspec.Append(c.precision); 753 | columnspec.Append(")\t"); 754 | 755 | 756 | columnDefinition.Append("("); 757 | columnDefinition.Append(c.precision); 758 | columnDefinition.Append(")\t"); 759 | } 760 | 761 | else if ( 762 | c.type == "datetime2" || 763 | c.type == "datetimeoffset" || 764 | c.type == "time") 765 | { 766 | // Scale only 767 | columnspec.Append("("); 768 | columnspec.Append(c.scale); 769 | columnspec.Append(")\t"); 770 | 771 | columnDefinition.Append("("); 772 | columnDefinition.Append(c.scale); 773 | columnDefinition.Append(")\t"); 774 | 775 | } 776 | 777 | else if ( 778 | c.type == "decimal" || 779 | c.type == "numeric") 780 | { 781 | // Precision and Scale 782 | columnspec.Append("("); 783 | columnspec.Append(c.precision); 784 | columnspec.Append(","); 785 | columnspec.Append(c.scale); 786 | columnspec.Append(")\t"); 787 | 788 | columnDefinition.Append("("); 789 | columnDefinition.Append(c.precision); 790 | columnDefinition.Append(","); 791 | columnDefinition.Append(c.scale); 792 | columnDefinition.Append(")\t"); 793 | 794 | } 795 | 796 | else 797 | { 798 | Exception e = new Exception("Unsupported Type " + c.type + " for column : "+c.name+" - Table : "+ sourceTable); 799 | throw e; 800 | } 801 | 802 | columnspec.Append(c.is_nullable ? "NULL" : "NOT NULL"); 803 | 804 | columnDefinition.Append(c.is_nullable ? "NULL" : "NOT NULL"); 805 | 806 | columnspec.Append(" " + c.defaultconstraint); 807 | ColumnDef current = cols[cols.IndexOf(c)]; 808 | current.columnDefinition = columnDefinition.ToString(); 809 | tempCols.Add(current); 810 | 811 | } 812 | columnClause = columnspec.ToString(); 813 | 814 | 815 | cols = tempCols; 816 | 817 | if (cols.Count == 0) 818 | { 819 | // invalid query 820 | throw new Exception("Unable to retrieve column data for " + sourceTable + " in database " + sourceDb); 821 | } 822 | } 823 | 824 | private int GetMinColumnId(List columnDef) 825 | { 826 | int minColumnId = columnDef.Min(a => a.column_id); 827 | 828 | if (minColumnId != 1) 829 | { 830 | Console.WriteLine(string.Empty); 831 | Console.WriteLine($"MinColumnId: {minColumnId} for Table: {sourceTable}"); 832 | Console.WriteLine(string.Empty); 833 | } 834 | return minColumnId; 835 | } 836 | 837 | private string GetMaxLength(ColumnDef columnDef) 838 | { 839 | var source = new List() 840 | { 841 | "char", 842 | "nchar", 843 | "varchar", 844 | "nvarchar" 845 | }; 846 | 847 | var maxLength = columnDef.max_length.ToString(); 848 | 849 | if (source.Any((Func)(a => a.Equals(columnDef.type, StringComparison.InvariantCultureIgnoreCase)))) 850 | { 851 | if (columnDef.max_length == -1) 852 | { 853 | maxLength = "max"; 854 | } 855 | else if (columnDef.type.StartsWith("n", StringComparison.OrdinalIgnoreCase)) 856 | { 857 | maxLength = (columnDef.max_length / 2).ToString(); 858 | } 859 | } 860 | 861 | return maxLength; 862 | } 863 | 864 | private void getClusteredIndexGlobal() 865 | { 866 | clusteredCols.Clear(); 867 | string TableKey = ""; 868 | string SchemaName; 869 | string TableName; 870 | string tableKeyPrevious = ""; 871 | TableSt TableStruct = new TableSt(); 872 | string clusterindexname = string.Empty; 873 | string clusterindexnamePrevious = string.Empty; 874 | 875 | cmd.CommandText = 876 | "select schema_name(tbl.schema_id) as SchemaName,tbl.Name as TableName,i.key_ordinal, c.name, i.is_descending_key, si.[type] as index_type ,si.name as indexname " + 877 | "from sys.indexes si " + 878 | "inner join sys.tables tbl on tbl.object_id=Si.object_id and tbl.type = 'U' " + 879 | "left join sys.index_columns i on i.object_id = si.object_id " + 880 | "left join sys.columns c on c.column_id = i.column_id and c.object_id = i.object_id " + 881 | "where i.index_id = 1 and si.[type] <> 2 " + 882 | "order by schema_name(tbl.schema_id),tbl.name,key_ordinal "; 883 | 884 | rdr = cmd.ExecuteReader(); 885 | 886 | 887 | 888 | while (rdr.Read()) 889 | { 890 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 891 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 892 | clusterindexname = rdr.GetString(rdr.GetOrdinal("indexname")); 893 | TableKey = SchemaName + "." + TableName; 894 | if (TableKey != tableKeyPrevious) 895 | { 896 | if (clusteredCols.Count != 0 && (TableStruct != null)) 897 | { 898 | TableStruct.clusteredcols.AddRange(clusteredCols); 899 | TableStruct.ClusteredIndexName = clusterindexnamePrevious; 900 | } 901 | TableStruct = this.dbstruct.GetTable(TableKey); 902 | clusteredCols.Clear(); 903 | tableKeyPrevious = TableKey; 904 | clusterindexnamePrevious = clusterindexname; 905 | } 906 | 907 | 908 | clusteredCols.Add(new IndexColumnDef( 909 | rdr.GetByte(rdr.GetOrdinal("key_ordinal")), 910 | rdr.GetString(rdr.GetOrdinal("name")), 911 | rdr.GetBoolean(rdr.GetOrdinal("is_descending_key")), 912 | rdr.GetByte(rdr.GetOrdinal("index_type")) 913 | )); 914 | } 915 | if (TableKey != "" && TableStruct != null) 916 | { 917 | TableStruct.clusteredcols.AddRange(clusteredCols); 918 | TableStruct.ClusteredIndexName = clusterindexname; 919 | } 920 | rdr.Close(); 921 | 922 | } 923 | private void getClusteredIndex(Boolean GetStructure, Boolean SourceFromFile) 924 | { 925 | clusteredCols.Clear(); 926 | clusteredClause = ""; 927 | StringBuilder clusteredspec = new StringBuilder(); 928 | Boolean isCCI = false; 929 | 930 | if (!SourceFromFile) 931 | { 932 | 933 | cmd.CommandText = 934 | "select i.key_ordinal, c.name, i.is_descending_key, si.[type] as index_type ,si.name as indexname " + 935 | "from sys.indexes si " + 936 | "left join sys.index_columns i on i.object_id = si.object_id " + 937 | "left join sys.columns c on c.column_id = i.column_id and c.object_id = i.object_id " + 938 | "where i.index_id = 1 and si.[type] <> 2 and " + 939 | "i.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') " + 940 | "and i.partition_ordinal = 0" + 941 | "order by key_ordinal "; 942 | 943 | rdr = cmd.ExecuteReader(); 944 | string clusterindexname = string.Empty; 945 | if (rdr.HasRows) 946 | { 947 | while (rdr.Read()) 948 | { 949 | if (clusterindexname == string.Empty) 950 | clusterindexname = rdr.GetString(rdr.GetOrdinal("indexname")); 951 | 952 | clusteredCols.Add(new IndexColumnDef( 953 | rdr.GetByte(rdr.GetOrdinal("key_ordinal")), 954 | rdr.GetString(rdr.GetOrdinal("name")), 955 | rdr.GetBoolean(rdr.GetOrdinal("is_descending_key")), 956 | rdr.GetByte(rdr.GetOrdinal("index_type")) 957 | )); 958 | } 959 | } 960 | rdr.Close(); 961 | 962 | if (GetStructure) 963 | { 964 | dbstruct.GetTable(sourceTable).ClusteredIndexName = clusterindexname; 965 | dbstruct.GetTable(sourceTable).clusteredcols.AddRange(clusteredCols); 966 | return; 967 | } 968 | } 969 | else 970 | { 971 | // get definition from file structure 972 | clusteredCols = dbstruct.GetTable(sourceTable).clusteredcols; 973 | } 974 | 975 | 976 | foreach (IndexColumnDef i in clusteredCols.OrderBy(o => o.key_ordinal)) 977 | { 978 | if (i.index_type == 5) // CCI 979 | { 980 | isCCI = true; 981 | clusteredspec.Append("CLUSTERED COLUMNSTORE INDEX"); 982 | break; 983 | } 984 | if (i.key_ordinal > 1) 985 | { 986 | clusteredspec.Append("\r\n,"); 987 | } 988 | else 989 | { 990 | clusteredspec.Append("CLUSTERED INDEX ("); 991 | } 992 | clusteredspec.Append("[" + i.name + "]" + (i.is_descending_key ? " DESC " : "")); 993 | } 994 | if (clusteredspec.Length > 0 && !isCCI) 995 | { 996 | clusteredspec.Append(")"); 997 | } 998 | else if (clusteredClause.Length == 0 && !isCCI) 999 | { clusteredspec.Append("HEAP"); } 1000 | 1001 | clusteredClause = clusteredspec.ToString(); 1002 | } 1003 | private void getNonclusteredIndexesGlobal() 1004 | { 1005 | nonclusteredIndexes.Clear(); 1006 | string TableKey = ""; 1007 | string SchemaName; 1008 | string TableName; 1009 | string tableKeyPrevious = ""; 1010 | TableSt TableStruct = new TableSt(); 1011 | string idxnamePrevious = ""; 1012 | string idxname = ""; 1013 | NonclusteredIndexDef ncidef = null; 1014 | 1015 | cmd.CommandText = @"select tbl.Name as TableName,schema_name(schema_id) as SchemaName,ix.name as index_name, i.key_ordinal, c.name, i.is_descending_key 1016 | from sys.index_columns i 1017 | inner join sys.tables tbl on tbl.object_id = i.object_id and tbl.type = 'U' 1018 | join sys.indexes ix on ix.index_id = i.index_id and ix.object_id = i.object_id 1019 | join sys.columns c on c.column_id = i.column_id and c.object_id = ix.object_id 1020 | where i.key_ordinal > 0 1021 | and i.index_id > 1-- NonClustered Indexes 1022 | order by schema_name(tbl.schema_id),tbl.name,ix.name, key_ordinal"; 1023 | rdr = cmd.ExecuteReader(); 1024 | 1025 | while (rdr.Read()) 1026 | { 1027 | 1028 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 1029 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 1030 | idxname = rdr.GetString(rdr.GetOrdinal("index_name")); 1031 | TableKey = SchemaName + "." + TableName; 1032 | if (TableKey != tableKeyPrevious) 1033 | { 1034 | if (nonclusteredIndexes.Count != 0 && TableStruct != null) 1035 | { 1036 | TableStruct.nonclusteredIndexes.AddRange(nonclusteredIndexes); 1037 | } 1038 | TableStruct = this.dbstruct.GetTable(TableKey); 1039 | nonclusteredIndexes.Clear(); 1040 | tableKeyPrevious = TableKey; 1041 | } 1042 | 1043 | if (TableStruct != null) 1044 | { 1045 | if (idxname != idxnamePrevious) 1046 | { 1047 | ncidef = new NonclusteredIndexDef(idxname); 1048 | nonclusteredIndexes.Add(ncidef); 1049 | idxnamePrevious = idxname; 1050 | TableStruct.nonclusteredIndexes.Add(new NonclusteredIndexDef(idxname)); 1051 | } 1052 | 1053 | TableStruct.GetIndex(idxname).cols.Add(new IndexColumnDef( 1054 | rdr.GetByte(rdr.GetOrdinal("key_ordinal")), 1055 | rdr.GetString(rdr.GetOrdinal("name")), 1056 | rdr.GetBoolean(rdr.GetOrdinal("is_descending_key")), 1057 | 0 1058 | )); 1059 | } 1060 | } 1061 | 1062 | 1063 | rdr.Close(); 1064 | 1065 | 1066 | } 1067 | private void getNonclusteredIndexes(Boolean GetStructure, Boolean SourceFromFile) 1068 | { 1069 | nonclusteredIndexes.Clear(); 1070 | nonClusteredClause = ""; 1071 | StringBuilder nonclusteredspec = new StringBuilder(); 1072 | if (!SourceFromFile) 1073 | { 1074 | cmd.CommandText = 1075 | "select ix.name as index_name, i.key_ordinal, c.name, i.is_descending_key " + 1076 | "from sys.index_columns i " + 1077 | "join sys.indexes ix on ix.index_id = i.index_id and ix.object_id = i.object_id " + 1078 | "join sys.columns c on c.column_id = i.column_id and c.object_id = ix.object_id " + 1079 | "where i.key_ordinal > 0 and " + 1080 | "i.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') and " + 1081 | "i.index_id > 1 " + // NonClustered Indexes 1082 | "order by ix.name, key_ordinal "; 1083 | 1084 | rdr = cmd.ExecuteReader(); 1085 | 1086 | if (rdr.HasRows) 1087 | { 1088 | string idxname = ""; 1089 | NonclusteredIndexDef ncidef = null; 1090 | while (rdr.Read()) 1091 | { 1092 | if (idxname != rdr.GetString(rdr.GetOrdinal("index_name"))) 1093 | { 1094 | idxname = rdr.GetString(rdr.GetOrdinal("index_name")); 1095 | ncidef = new NonclusteredIndexDef(idxname); 1096 | nonclusteredIndexes.Add(ncidef); 1097 | 1098 | if (GetStructure) 1099 | { 1100 | dbstruct.GetTable(sourceTable).nonclusteredIndexes.Add(ncidef); 1101 | } 1102 | 1103 | } 1104 | 1105 | if (!GetStructure) 1106 | { 1107 | ncidef.cols.Add(new IndexColumnDef( 1108 | rdr.GetByte(rdr.GetOrdinal("key_ordinal")), 1109 | rdr.GetString(rdr.GetOrdinal("name")), 1110 | rdr.GetBoolean(rdr.GetOrdinal("is_descending_key")), 1111 | 0 1112 | )); 1113 | } 1114 | else 1115 | { 1116 | dbstruct.GetTable(sourceTable).GetIndex(idxname).cols.Add(new IndexColumnDef( 1117 | rdr.GetByte(rdr.GetOrdinal("key_ordinal")), 1118 | rdr.GetString(rdr.GetOrdinal("name")), 1119 | rdr.GetBoolean(rdr.GetOrdinal("is_descending_key")), 1120 | 0 1121 | )); 1122 | } 1123 | 1124 | } 1125 | } 1126 | 1127 | rdr.Close(); 1128 | if (GetStructure) 1129 | { 1130 | return; 1131 | } 1132 | } 1133 | else 1134 | { 1135 | nonclusteredIndexes = dbstruct.GetTable(sourceTable).nonclusteredIndexes; 1136 | } 1137 | foreach (NonclusteredIndexDef ncidef in nonclusteredIndexes) 1138 | { 1139 | nonclusteredspec.Append("CREATE INDEX [" + ncidef.name + "] ON [" + destDb + "].[" + destTable.Replace(".", "].[") + "] "); 1140 | foreach (IndexColumnDef i in ncidef.cols) 1141 | { 1142 | if (i.key_ordinal > 1) 1143 | { 1144 | nonclusteredspec.Append("\r\n,"); 1145 | } 1146 | else 1147 | { 1148 | nonclusteredspec.Append("\r\n("); 1149 | } 1150 | nonclusteredspec.Append("[" + i.name + "]" + (i.is_descending_key ? " DESC " : "")); 1151 | } 1152 | nonclusteredspec.Append(");\r\n"); 1153 | } 1154 | nonClusteredClause = nonclusteredspec.ToString(); 1155 | } 1156 | 1157 | private void getStatsGlobal() 1158 | { 1159 | stats.Clear(); 1160 | string statname = ""; 1161 | StatDef statdef = null; 1162 | string TableKey = ""; 1163 | string SchemaName; 1164 | string TableName; 1165 | string tableKeyPrevious = ""; 1166 | TableSt TableStruct = new TableSt(); 1167 | string statnamePrevious = ""; 1168 | 1169 | 1170 | cmd.CommandText = 1171 | "select schema_name(tbl.schema_id) as SchemaName,tbl.name as TableName,s.name as stat_name, sc.stats_column_id, c.name " + 1172 | "from sys.stats s " + 1173 | "inner join sys.tables tbl on tbl.object_id=s.object_id and tbl.type = 'U' " + 1174 | "join sys.stats_columns sc on s.stats_id = sc.stats_id and s.object_id = sc.object_id " + 1175 | "join sys.columns c on c.column_id = sc.column_id and c.object_id = sc.object_id " + 1176 | "and user_created=1 " + 1177 | "order by schema_name(tbl.schema_id),tbl.name,s.name, sc.stats_column_id "; 1178 | 1179 | rdr = cmd.ExecuteReader(); 1180 | 1181 | while (rdr.Read()) 1182 | { 1183 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 1184 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 1185 | statname = rdr.GetString(rdr.GetOrdinal("stat_name")); 1186 | TableKey = SchemaName + "." + TableName; 1187 | if (TableKey != tableKeyPrevious) 1188 | { 1189 | if (stats.Count != 0 && TableStruct != null) 1190 | { 1191 | TableStruct.statistics.AddRange(stats); 1192 | } 1193 | TableStruct = this.dbstruct.GetTable(TableKey); 1194 | stats.Clear(); 1195 | tableKeyPrevious = TableKey; 1196 | 1197 | } 1198 | 1199 | if (TableStruct != null) 1200 | { 1201 | if (statname != statnamePrevious) 1202 | { 1203 | statname = rdr.GetString(rdr.GetOrdinal("stat_name")); 1204 | statdef = new StatDef(statname); 1205 | TableStruct.statistics.Add(statdef); 1206 | statnamePrevious = statname; 1207 | } 1208 | 1209 | 1210 | statdef.cols.Add(new StatColumnDef( 1211 | rdr.GetInt32(rdr.GetOrdinal("stats_column_id")), 1212 | rdr.GetString(rdr.GetOrdinal("name")) 1213 | )); 1214 | } 1215 | 1216 | } 1217 | 1218 | rdr.Close(); 1219 | } 1220 | private void getStats(Boolean GetStructure, Boolean SourceFromFile) 1221 | { 1222 | stats.Clear(); 1223 | statsClause = ""; 1224 | StringBuilder statspec = new StringBuilder(); 1225 | 1226 | if (!SourceFromFile) 1227 | { 1228 | cmd.CommandText = 1229 | "select s.name as stat_name, sc.stats_column_id, c.name, has_filter, filter_definition " + 1230 | "from sys.stats s " + 1231 | "join sys.stats_columns sc on s.stats_id = sc.stats_id and s.object_id = sc.object_id " + 1232 | "join sys.columns c on c.column_id = sc.column_id and c.object_id = sc.object_id " + 1233 | "where s.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') " + 1234 | "and user_created=1 " + 1235 | "order by s.name, sc.stats_column_id "; 1236 | 1237 | rdr = cmd.ExecuteReader(); 1238 | 1239 | if (rdr.HasRows) 1240 | { 1241 | string statname = ""; 1242 | StatDef statdef = null; 1243 | while (rdr.Read()) 1244 | { 1245 | 1246 | 1247 | if (statname != rdr.GetString(rdr.GetOrdinal("stat_name"))) 1248 | { 1249 | statname = rdr.GetString(rdr.GetOrdinal("stat_name")); 1250 | statdef = new StatDef(statname); 1251 | if (!(rdr.IsDBNull(rdr.GetOrdinal("filter_definition")))) 1252 | statdef.filter = rdr.GetString(rdr.GetOrdinal("filter_definition")); 1253 | stats.Add(statdef); 1254 | if (!GetStructure) 1255 | { 1256 | //no further steps needed 1257 | } 1258 | else 1259 | { 1260 | //adding stat definition to database structure 1261 | dbstruct.GetTable(sourceTable).statistics.Add(statdef); 1262 | } 1263 | 1264 | 1265 | } 1266 | statdef.cols.Add(new StatColumnDef( 1267 | rdr.GetInt32(rdr.GetOrdinal("stats_column_id")), 1268 | rdr.GetString(rdr.GetOrdinal("name")) 1269 | )); 1270 | 1271 | 1272 | } 1273 | } 1274 | 1275 | rdr.Close(); 1276 | if (GetStructure) 1277 | { 1278 | return; 1279 | } 1280 | } 1281 | else 1282 | { 1283 | // retrieve stats from dbstruct from json file 1284 | stats.AddRange(dbstruct.GetTable(sourceTable).statistics); 1285 | } 1286 | foreach (StatDef statdef in stats) 1287 | { 1288 | statspec.Append(buildCreateStatisticsText(statdef,destTable)); 1289 | 1290 | } 1291 | statsClause = statspec.ToString(); 1292 | } 1293 | 1294 | private void getPartitioningGlobal () 1295 | { 1296 | string TableKey = ""; 1297 | string SchemaName; 1298 | string TableName; 1299 | string tableKeyPrevious = ""; 1300 | TableSt TableStruct = new TableSt(); 1301 | partitionColumn = null; 1302 | partitionLeftOrRight = null; 1303 | partitionBoundaryClause = ""; 1304 | StringBuilder partitionspec = new StringBuilder(); 1305 | partitionBoundaries.Clear(); 1306 | 1307 | cmd.CommandText =@"select schema_name(tbl.schema_id) as SchemaName,tbl.name as TableName,c.name,pf.boundary_value_on_right 1308 | from sys.tables tbl 1309 | join sys.indexes i on (i.object_id = tbl.object_id and i.index_id < 2) 1310 | join sys.index_columns ic on(ic.partition_ordinal > 0 and ic.index_id = i.index_id and ic.object_id = tbl.object_id) 1311 | join sys.columns c on(c.object_id = ic.object_id and c.column_id = ic.column_id) 1312 | JOIN sys.data_spaces ds on i.data_space_id = ds.data_space_id 1313 | JOIN sys.partition_schemes ps on ps.data_space_id = ds.data_space_id 1314 | JOIN sys.partition_functions pf on pf.function_id = ps.function_id 1315 | order by schema_name(tbl.schema_id),tbl.name"; 1316 | 1317 | rdr = cmd.ExecuteReader(); 1318 | 1319 | while (rdr.Read()) 1320 | { 1321 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 1322 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 1323 | partitionColumn = rdr.GetString(rdr.GetOrdinal("name")); 1324 | partitionLeftOrRight = ((bool)rdr.GetBoolean(rdr.GetOrdinal("boundary_value_on_right")) ? "RIGHT" : "LEFT"); 1325 | TableKey = SchemaName + "." + TableName; 1326 | 1327 | TableStruct = this.dbstruct.GetTable(TableKey); 1328 | TableStruct.DBPartition.partitionColumn = partitionColumn; 1329 | TableStruct.DBPartition.partitionLeftOrRight = partitionLeftOrRight; 1330 | 1331 | } 1332 | rdr.Close(); 1333 | // get partitions boundaries 1334 | TableStruct = new TableSt(); 1335 | cmd.CommandText = @"select schema_name(tbl.schema_id) as SchemaName,tbl.name as TableName,cast(sp.partition_number as int) as partition_number , prv.value as boundary_value, lower(sty.name) as boundary_value_type 1336 | from sys.tables st join sys.indexes si on st.object_id = si.object_id and si.index_id <2 1337 | join sys.partitions sp on sp.object_id = st.object_id 1338 | and sp.index_id = si.index_id 1339 | join sys.partition_schemes ps on ps.data_space_id = si.data_space_id 1340 | join sys.partition_range_values prv on prv.function_id = ps.function_id 1341 | join sys.partition_parameters pp on pp.function_id = ps.function_id 1342 | join sys.types sty on sty.user_type_id = pp.user_type_id 1343 | and prv.boundary_id = sp.partition_number 1344 | JOIN sys.tables Tbl on si.object_id = Tbl.object_id 1345 | order by schema_name(tbl.schema_id),tbl.name,sp.partition_number"; 1346 | 1347 | rdr = cmd.ExecuteReader(); 1348 | 1349 | 1350 | while (rdr.Read()) 1351 | { 1352 | SchemaName = rdr.GetString(rdr.GetOrdinal("SchemaName")); 1353 | TableName = rdr.GetString(rdr.GetOrdinal("TableName")); 1354 | TableKey = SchemaName + "." + TableName; 1355 | 1356 | if (TableKey != tableKeyPrevious) 1357 | { 1358 | if ( TableStruct.DBPartition.partitionColumn !="") 1359 | { 1360 | TableStruct.DBPartition.partitionBoundaries.AddRange(partitionBoundaries); 1361 | } 1362 | TableStruct = this.dbstruct.GetTable(TableKey); 1363 | partitionBoundaries.Clear(); 1364 | tableKeyPrevious = TableKey; 1365 | 1366 | } 1367 | 1368 | if (TableStruct != null) 1369 | { 1370 | partitionBoundaries.Add(new PartitionBoundary( 1371 | rdr.GetInt32(rdr.GetOrdinal("partition_number")), 1372 | rdr.GetValue(rdr.GetOrdinal("boundary_value")).ToString(), 1373 | rdr.GetString(rdr.GetOrdinal("boundary_value_type")) 1374 | )); 1375 | } 1376 | 1377 | } 1378 | 1379 | if (TableKey != "" && TableStruct != null) 1380 | { 1381 | TableStruct.DBPartition.partitionBoundaries.AddRange(partitionBoundaries); 1382 | } 1383 | 1384 | 1385 | rdr.Close(); 1386 | } 1387 | private void getPartitioning(Boolean GetStructure, Boolean SourceFromFile) 1388 | { 1389 | partitionColumn = null; 1390 | partitionLeftOrRight = null; 1391 | partitionBoundaryClause = ""; 1392 | StringBuilder partitionspec = new StringBuilder(); 1393 | partitionBoundaries.Clear(); 1394 | if (!SourceFromFile) 1395 | { 1396 | cmd.CommandText = 1397 | "select c.name " + 1398 | "from sys.tables t " + 1399 | "join sys.indexes i on(i.object_id = t.object_id and i.index_id < 2) " + 1400 | "join sys.index_columns ic on(ic.partition_ordinal > 0 " + 1401 | "and ic.index_id = i.index_id and ic.object_id = t.object_id) " + 1402 | "join sys.columns c on(c.object_id = ic.object_id " + 1403 | "and c.column_id = ic.column_id) " + 1404 | "where t.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') "; 1405 | partitionColumn = (string)cmd.ExecuteScalar(); 1406 | 1407 | if (partitionColumn != null) 1408 | { 1409 | 1410 | cmd.CommandText = 1411 | "select pf.boundary_value_on_right from sys.partition_functions pf " + 1412 | "JOIN sys.partition_schemes ps on pf.function_id=ps.function_id " + 1413 | "JOIN sys.data_spaces ds on ps.data_space_id = ds.data_space_id " + 1414 | "JOIN sys.indexes si on si.data_space_id = ds.data_space_id " + 1415 | "WHERE si.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') "; 1416 | partitionLeftOrRight = ((bool)cmd.ExecuteScalar() ? "RIGHT" : "LEFT"); 1417 | 1418 | cmd.CommandText = 1419 | "select cast(sp.partition_number as int) as partition_number , prv.value as boundary_value, lower(sty.name) as boundary_value_type " + 1420 | "from sys.tables st join sys.indexes si on st.object_id = si.object_id and si.index_id <2" + 1421 | "join sys.partitions sp on sp.object_id = st.object_id " + 1422 | "and sp.index_id = si.index_id " + 1423 | "join sys.partition_schemes ps on ps.data_space_id = si.data_space_id " + 1424 | "join sys.partition_range_values prv on prv.function_id = ps.function_id " + 1425 | "join sys.partition_parameters pp on pp.function_id = ps.function_id " + 1426 | "join sys.types sty on sty.user_type_id = pp.user_type_id " + 1427 | "and prv.boundary_id = sp.partition_number " + 1428 | "where st.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + sourceTable + "') " + 1429 | "order by sp.partition_number "; 1430 | 1431 | rdr = cmd.ExecuteReader(); 1432 | 1433 | if (rdr.HasRows) 1434 | { 1435 | while (rdr.Read()) 1436 | { 1437 | partitionBoundaries.Add(new PartitionBoundary( 1438 | rdr.GetInt32(rdr.GetOrdinal("partition_number")), 1439 | rdr.GetValue(rdr.GetOrdinal("boundary_value")).ToString(), 1440 | rdr.GetString(rdr.GetOrdinal("boundary_value_type")) 1441 | )); 1442 | } 1443 | } 1444 | 1445 | rdr.Close(); 1446 | if (GetStructure) 1447 | { 1448 | dbstruct.GetTable(sourceTable).DBPartition.partitionColumn = partitionColumn; 1449 | dbstruct.GetTable(sourceTable).DBPartition.partitionLeftOrRight = partitionLeftOrRight; 1450 | dbstruct.GetTable(sourceTable).DBPartition.partitionBoundaries = partitionBoundaries; 1451 | 1452 | } 1453 | } 1454 | } 1455 | else 1456 | { 1457 | partitionBoundaries = dbstruct.GetTable(sourceTable).DBPartition.partitionBoundaries; 1458 | partitionColumn = dbstruct.GetTable(sourceTable).DBPartition.partitionColumn; 1459 | partitionLeftOrRight = dbstruct.GetTable(sourceTable).DBPartition.partitionLeftOrRight; 1460 | } 1461 | foreach (PartitionBoundary b in partitionBoundaries) 1462 | { 1463 | if (b.partition_number > 1) 1464 | { 1465 | partitionspec.Append("\r\n,"); 1466 | } 1467 | partitionspec.Append("N'" + b.boundary_value + "'"); 1468 | } 1469 | if (partitionspec.Length > 0) 1470 | { 1471 | partitionspec.Append(")"); 1472 | } 1473 | 1474 | 1475 | partitionBoundaryClause = partitionspec.ToString(); 1476 | } 1477 | 1478 | 1479 | private void getDML(PDWscripter c, string outFile) 1480 | { 1481 | 1482 | 1483 | StreamWriter sw = null; 1484 | FileStream fs = null; 1485 | Regex r = new Regex(this.ExcludeObjectSuffixList, RegexOptions.IgnoreCase); 1486 | string ModuleName; 1487 | 1488 | if (outFile != "") 1489 | { 1490 | fs = new FileStream(outFile, FileMode.Create); 1491 | sw = new StreamWriter(fs); 1492 | } 1493 | 1494 | Console.Write("DML>"); 1495 | 1496 | 1497 | // get generated statistics 1498 | String description = "-- script generated the : " + String.Format("{0:d/M/yyyy HH:mm:ss}", DateTime.Now); 1499 | cmd.CommandText = "select count(1) from sys.sql_modules"; 1500 | int objectcount = (int)cmd.ExecuteScalar(); 1501 | description += "\r\n-- objects scripted - objects = " + objectcount.ToString(); 1502 | Console.Write("objects scripted - objects = " + objectcount.ToString()); 1503 | sw.WriteLine(description); 1504 | 1505 | 1506 | 1507 | // Adaptation to sort by dependency 1508 | cmd.CommandText = "SELECT definition, object_name(M.object_id) from sys.sql_modules m JOIN SYS.OBJECTS o ON M.OBJECT_ID = o.OBJECT_ID " + 1509 | "WHERE SCHEMA_NAME(SCHEMA_ID) +'.' + object_name(M.object_id) LIKE '" + filterSpec + "' order by definition;"; 1510 | //cmd.CommandText = "select definition, object_name(object_id) from sys.sql_modules order by definition;"; 1511 | 1512 | rdr = cmd.ExecuteReader(); 1513 | 1514 | 1515 | 1516 | 1517 | String createUseDbTxt = "USE " + c.sourceDb + "\r\nGO\r\n"; 1518 | sw.WriteLine(createUseDbTxt); 1519 | 1520 | List> lstDbObjectDefinitions = new List>(); 1521 | 1522 | 1523 | 1524 | while (rdr.Read()) 1525 | { 1526 | 1527 | ModuleName = rdr.GetString(1); 1528 | if (!r.IsMatch(ModuleName)) 1529 | { 1530 | IDataRecord record = (IDataRecord)rdr; 1531 | KeyValuePair kvpObjNameDef = new KeyValuePair(String.Format("{0}", record[1]), String.Format("{0}", record[0])); 1532 | 1533 | 1534 | if (!lstDbObjectDefinitions.Exists(objDef => objDef.Key == kvpObjNameDef.Key)) 1535 | { 1536 | 1537 | // Object doesn't exist 1538 | if (!lstDbObjectDefinitions.Any(objDef => objDef.Value.Contains(kvpObjNameDef.Key))) 1539 | { 1540 | // Object never used by an other object 1541 | 1542 | lstDbObjectDefinitions.Add(kvpObjNameDef); 1543 | } 1544 | else 1545 | { 1546 | // Object already used by an other object, we had it previously to the calling one 1547 | int idxCallingObj = lstDbObjectDefinitions.IndexOf(lstDbObjectDefinitions.First(objDef => objDef.Value.Contains(kvpObjNameDef.Key))); 1548 | 1549 | lstDbObjectDefinitions.Insert(idxCallingObj, kvpObjNameDef); 1550 | } 1551 | } 1552 | } 1553 | } 1554 | int nbObjectDefinitions = lstDbObjectDefinitions.Count(); 1555 | int index = 0; 1556 | string prevDbObjectDefinition = String.Empty; 1557 | foreach (KeyValuePair dbObjectDefinition in lstDbObjectDefinitions) 1558 | { 1559 | if (prevDbObjectDefinition != dbObjectDefinition.Value) 1560 | { 1561 | prevDbObjectDefinition = dbObjectDefinition.Value; 1562 | 1563 | if (scriptMode == "Delta") 1564 | { 1565 | // Add object Drop 1566 | string strObjectFullName = dbObjectDefinition.Value.Substring(12, dbObjectDefinition.Value.IndexOf(' ', 13) - 12); 1567 | 1568 | string strObjectType = dbObjectDefinition.Value.Substring(7, 4); 1569 | 1570 | string strDropObjectQuery = "DROP " + strObjectType + " " + strObjectFullName + ";"; 1571 | sw.WriteLine(strDropObjectQuery); 1572 | sw.WriteLine(""); 1573 | if (index != nbObjectDefinitions - 1) 1574 | { 1575 | sw.WriteLine("GO"); 1576 | } 1577 | 1578 | sw.WriteLine(""); 1579 | sw.WriteLine(""); 1580 | } 1581 | 1582 | // Add object Create 1583 | sw.WriteLine(dbObjectDefinition.Value.ToString()); 1584 | sw.WriteLine(""); 1585 | 1586 | sw.WriteLine("GO"); 1587 | 1588 | sw.WriteLine(""); 1589 | sw.WriteLine(""); 1590 | Console.Write("."); 1591 | } 1592 | index++; 1593 | } 1594 | sw.WriteLine("PRINT 'END'"); 1595 | if (outFile != "") 1596 | { 1597 | sw.Close(); 1598 | } 1599 | rdr.Close(); 1600 | Console.WriteLine("done"); 1601 | 1602 | } 1603 | 1604 | 1605 | private string buildAlterTableForDistributionPolicyChangeText (TableSt SourceTbl) 1606 | { 1607 | StringBuilder ScriptAtlterTableDistribution = new StringBuilder(); 1608 | string[] substringsTableName = SourceTbl.name.Split('.'); 1609 | string sourcetableSchemaOnly = substringsTableName[0]; 1610 | string sourcetablenameOnly = substringsTableName[1]; 1611 | string copyTmpTableNameOnly = substringsTableName[1] + "_tmp"; 1612 | 1613 | string copyTmpTableName = "[" + substringsTableName[0] + "].[" + copyTmpTableNameOnly + "]"; 1614 | 1615 | copyDataToTmpTableTxt = "CREATE TABLE " + copyTmpTableName + "\r\n" + 1616 | "WITH ( DISTRIBUTION = " + (SourceTbl.distribution_policy == 2 ? ("HASH ([" + SourceTbl.GetDistributionColumn() + "])") : (SourceTbl.distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1617 | ")\r\n" + 1618 | "AS SELECT * FROM [" + SourceTbl.name.Replace(".", "].[") + "]" + 1619 | "\r\n;\r\n"; 1620 | ScriptAtlterTableDistribution.Append(copyDataToTmpTableTxt); 1621 | 1622 | 1623 | // Drop Table 1624 | dropDeployTableTxt = "DROP TABLE [" + SourceTbl.name.Replace(".", "].[") + "]" + 1625 | ";\r\n"; 1626 | ScriptAtlterTableDistribution.Append(dropDeployTableTxt); 1627 | 1628 | 1629 | //Rename table 1630 | string RenameTable = "RENAME OBJECT::" + sourcetableSchemaOnly + "." + copyTmpTableNameOnly + " TO " + sourcetablenameOnly + ";\r\n"; 1631 | 1632 | ScriptAtlterTableDistribution.Append(RenameTable); 1633 | 1634 | return ScriptAtlterTableDistribution.ToString(); 1635 | 1636 | 1637 | } 1638 | 1639 | private void buildAlterTableForDistributionPolicyChange(StreamWriter sw, TableSt SourceTbl) 1640 | { 1641 | 1642 | string[] substringsTableName = SourceTbl.name.Split('.'); 1643 | string sourcetableSchemaOnly = substringsTableName[0]; 1644 | string sourcetablenameOnly = substringsTableName[1]; 1645 | string copyTmpTableNameOnly = substringsTableName[1] + "_tmp"; 1646 | 1647 | string copyTmpTableName = "[" + substringsTableName[0] + "].[" + copyTmpTableNameOnly + "]"; 1648 | 1649 | copyDataToTmpTableTxt = "CREATE TABLE " + copyTmpTableName + "\r\n" + 1650 | "WITH ( DISTRIBUTION = " + (SourceTbl.distribution_policy == 2 ? ("HASH ([" + SourceTbl.GetDistributionColumn() + "])") : (SourceTbl.distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1651 | ")\r\n" + 1652 | "AS SELECT * FROM [" + SourceTbl.name.Replace(".", "].[") + "]" + 1653 | "\r\n;\r\n"; 1654 | 1655 | sw.WriteLine(copyDataToTmpTableTxt); 1656 | 1657 | // Drop Table 1658 | dropDeployTableTxt = "DROP TABLE [" + SourceTbl.name.Replace(".", "].[") + "]" + 1659 | ";\r\n"; 1660 | 1661 | sw.WriteLine(dropDeployTableTxt); 1662 | 1663 | //Rename table 1664 | string RenameTable = "RENAME OBJECT::" + sourcetableSchemaOnly + "." + copyTmpTableNameOnly + " TO " + sourcetablenameOnly + ";\r\n"; 1665 | 1666 | sw.WriteLine(RenameTable); 1667 | 1668 | 1669 | } 1670 | private void buildCreateTableText(StreamWriter sw) 1671 | { 1672 | 1673 | destTableFullName = destTable.Replace(".", "].["); 1674 | 1675 | sourceTmpTableName = "[" + destDb + "].[" + destTableFullName.Replace(destTableFullName.Substring(0, destTableFullName.IndexOf(']')), "DEP") + "]"; 1676 | if (scriptMode == "Delta") 1677 | { 1678 | // Copy Data table to Temporary Table 1679 | copyDataToTmpTableTxt = "CREATE TABLE " + sourceTmpTableName + "\r\n" + 1680 | "WITH ( DISTRIBUTION = " + (distribution_policy == 2 ? ("HASH ([" + distColumn + "])") : (distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1681 | ")\r\n" + 1682 | "AS SELECT * FROM [" + destDb + "].[" + destTable.Replace(".", "].[") + "]" + 1683 | "\r\n;\r\n"; 1684 | 1685 | sw.WriteLine(copyDataToTmpTableTxt); 1686 | 1687 | // Drop Table 1688 | dropDeployTableTxt = "DROP TABLE [" + destDb + "].[" + destTable.Replace(".", "].[") + "]" + 1689 | ";\r\n"; 1690 | sw.WriteLine(dropDeployTableTxt); 1691 | } 1692 | 1693 | createTableTxt = "CREATE TABLE [" + destTable.Replace(".", "].[") + "]\r\n(\r\n" + 1694 | columnClause + "\r\n)\r\n" + 1695 | "WITH ( DISTRIBUTION = " + (distribution_policy == 2 ? ("HASH ([" + distColumn + "])") : (distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1696 | 1697 | (clusteredClause != "" ? "\r\n, " + clusteredClause : "") + 1698 | (partitionBoundaryClause != "" ? ("\r\n, PARTITION ([" + partitionColumn + "] RANGE " + partitionLeftOrRight + " FOR VALUES \r\n(" + 1699 | partitionBoundaryClause + ")") : "") + 1700 | ");\r\n" + nonClusteredClause + statsClause; 1701 | sw.WriteLine(createTableTxt); 1702 | 1703 | if (scriptMode == "Delta") 1704 | { 1705 | // Copy data from Temporary Table 1706 | 1707 | columnSelect = "SET @COLUMNSNAME = NULL \r\n" + 1708 | "select @COLUMNSNAME = COALESCE(@COLUMNSNAME,'') + c.name + ', ' " + 1709 | "from sys.columns c " + 1710 | "where c.object_id = (select object_id from sys.tables where schema_name(schema_id) + '.' + name = '" + destTable + "') \r\n"; 1711 | sw.WriteLine(columnSelect); 1712 | 1713 | copyDataFromTmpTableTxt = "INSERT INTO [" + destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n(\r\n" + 1714 | "@COLUMNSNAME)\r\n " + 1715 | "FROM (SELECT @COLUMNSNAME FROM " + sourceTmpTableName + " \r\n " + 1716 | ";\r\n"; 1717 | sw.WriteLine(copyDataFromTmpTableTxt); 1718 | 1719 | // Drop Temporary Table 1720 | dropDeployTmpTableTxt = "DROP TABLE " + sourceTmpTableName + "\r\n" + 1721 | ";\r\n"; 1722 | sw.WriteLine(dropDeployTmpTableTxt); 1723 | } 1724 | 1725 | Console.Write("."); 1726 | } 1727 | 1728 | 1729 | 1730 | private string buildScriptCreateTable() 1731 | { 1732 | 1733 | createTableTxt = "CREATE TABLE [" + destTable.Replace(".", "].[") + "]\r\n(\r\n" + 1734 | columnClause + "\r\n)\r\n" + 1735 | "WITH ( DISTRIBUTION = " + (distribution_policy == 2 ? ("HASH ([" + distColumn + "])") : (distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1736 | 1737 | (clusteredClause != "" ? "\r\n, " + clusteredClause : "") + 1738 | (partitionBoundaryClause != "" ? ("\r\n, PARTITION ([" + partitionColumn + "] RANGE " + partitionLeftOrRight + " FOR VALUES \r\n(" + 1739 | partitionBoundaryClause + ")") : "") + 1740 | ");\r\n" + nonClusteredClause + statsClause; 1741 | 1742 | return createTableTxt; 1743 | } 1744 | 1745 | private void buildCreateTableText(StreamWriter sw, string dbTarget, bool bWarn) 1746 | { 1747 | 1748 | String strStartWarningMessage = string.Empty; 1749 | String strEndWarningMessage = string.Empty; 1750 | 1751 | strStartWarningMessage = "/* WARNING !!!! ======: Possible Distribution changed .\r\n"; 1752 | strEndWarningMessage = "*/\r\n\r\n"; 1753 | 1754 | 1755 | createTableTxt = "CREATE TABLE [" + dbTarget + "].[" + destTable.Replace(".", "].[") + "]\r\n(\r\n" + 1756 | columnClause + "\r\n)\r\n" + 1757 | "WITH ( DISTRIBUTION = " + (distribution_policy == 2 ? ("HASH ([" + distColumn + "])") : (distribution_policy == 3 ? "REPLICATE" : "ROUND_ROBIN")) + 1758 | 1759 | (clusteredClause != "" ? "\r\n, " + clusteredClause : "") + 1760 | (partitionBoundaryClause != "" ? ("\r\n, PARTITION ([" + partitionColumn + "] RANGE " + partitionLeftOrRight + " FOR VALUES \r\n(" + 1761 | partitionBoundaryClause + ")") : "") + 1762 | ");\r\n\r\n" + nonClusteredClause + statsClause; 1763 | 1764 | if (bWarn) 1765 | { 1766 | createTableTxt = strStartWarningMessage + createTableTxt + strEndWarningMessage; 1767 | writeWarningtxt(createTableTxt); 1768 | } 1769 | 1770 | sw.WriteLine(createTableTxt); 1771 | 1772 | Console.Write("."); 1773 | } 1774 | 1775 | public string buildAlterTableScript(PDWscripter cSource, PDWscripter cTarget, TableDef t, Boolean SourceFromFile) 1776 | { 1777 | 1778 | StringBuilder columnspecAdd = new StringBuilder(); 1779 | StringBuilder columnspecAlter = new StringBuilder(); 1780 | StringBuilder columnsAlterDefaultCnstr = new StringBuilder(); 1781 | StringBuilder columnspecDrop = new StringBuilder(); 1782 | StringBuilder columnspecDropStat = new StringBuilder(); 1783 | StringBuilder alterScript = new StringBuilder(); 1784 | StringBuilder alterclusteredindexscript = new StringBuilder(); 1785 | string strStartWarningMessage = string.Empty; 1786 | string strWarningAlterMessage = string.Empty; 1787 | string targetdistColumn = string.Empty; 1788 | string strDopStats = string.Empty; 1789 | string strDopStatsTXT = string.Empty; 1790 | string strDistributionChangedTXT = string.Empty; 1791 | 1792 | string strAlterScript; 1793 | TableSt SourceTbl; 1794 | TableSt TargetTbl; 1795 | 1796 | 1797 | // Init 1798 | cSource.sourceTable = t.name; 1799 | cSource.destTable = t.name; 1800 | 1801 | cTarget.sourceTable = t.name; 1802 | cTarget.destTable = t.name; 1803 | 1804 | SourceTbl = cSource.dbstruct.GetTable(t.name); 1805 | TargetTbl = cTarget.dbstruct.GetTable(t.name); 1806 | 1807 | // GetColumns 1808 | cSource.getSourceColumns(false, SourceFromFile); 1809 | cTarget.getSourceColumns(false, SourceFromFile); 1810 | targetdistColumn = cTarget.distColumn; 1811 | 1812 | // If Distribution Changed, not the same object , so not Alter !!! ==> TODO 1813 | if (SourceTbl.distribution_policy != TargetTbl.distribution_policy || cTarget.distColumn != cSource.distColumn) 1814 | { 1815 | 1816 | if (SourceTbl.distribution_policy != TargetTbl.distribution_policy) 1817 | { 1818 | string strWarningDistibutionTypeChange = "/* Distribution type changed for table " + SourceTbl.name + " FROM " + TargetTbl.distribution_policy.ToString() + " to " + SourceTbl.distribution_policy + " */\r\n"; 1819 | alterScript.Append(strWarningDistibutionTypeChange); 1820 | } 1821 | if (cTarget.distColumn != cSource.distColumn) 1822 | { 1823 | string strWarningDistibutionTypeChange = "/* Distribution Column changed for table " + SourceTbl.name + " From " + cTarget.distColumn + " to " + cSource.distColumn + " */\r\n"; 1824 | alterScript.Append(strWarningDistibutionTypeChange); 1825 | 1826 | } 1827 | 1828 | alterScript.Append(cSource.buildAlterTableForDistributionPolicyChangeText(SourceTbl)); 1829 | 1830 | } 1831 | 1832 | 1833 | //Compare columns 1834 | var ListColumnsToCreateOrAlter = cSource.cols.Except(cTarget.cols).ToList(); 1835 | var ListolumnsToDelete = cTarget.cols.Except(cSource.cols).ToList(); 1836 | 1837 | 1838 | #region CompareColumnsTo ALTER 1839 | int iCountAdd = 0; 1840 | bool isToAdd = false; 1841 | bool isToAddDefaultContraint = false; 1842 | bool isToAlter = false; 1843 | 1844 | 1845 | foreach (ColumnDef c in ListColumnsToCreateOrAlter) 1846 | { 1847 | isToAdd = true; 1848 | strStartWarningMessage = ""; 1849 | 1850 | 1851 | foreach (ColumnDef cTemp in cTarget.cols) 1852 | { 1853 | if (cTemp.name == c.name) 1854 | { 1855 | isToAdd = false; 1856 | isToAlter = (cTemp.columnDefinition != c.columnDefinition); 1857 | isToAddDefaultContraint = (cTemp.defaultconstraint == "" && c.defaultconstraint != ""); 1858 | 1859 | break; 1860 | } 1861 | } 1862 | 1863 | 1864 | if (isToAdd) 1865 | { 1866 | iCountAdd += 1; 1867 | } 1868 | else 1869 | { 1870 | if (isToAlter) 1871 | { 1872 | if (cTarget.stats.Exists(x => x.name == c.name)) 1873 | { 1874 | // Stats not clusturedIndex ==> DROP 1875 | strDopStats = "DROP STATISTICS [" + destTable.Replace(".", "].[") + "].[" + c.name + "];\r\n"; 1876 | columnspecDropStat.Append(strDopStats); 1877 | } 1878 | } 1879 | else 1880 | { 1881 | if (isToAddDefaultContraint) 1882 | { 1883 | alterTableTxt = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 1884 | "ADD " + c.defaultconstraint + " FOR [" + c.name + "];\r\n"; 1885 | 1886 | columnsAlterDefaultCnstr.Append(alterTableTxt); 1887 | columnsAlterDefaultCnstr.Append("\r\n"); 1888 | } 1889 | continue; 1890 | } 1891 | } 1892 | if (c.distrbution_ordinal == 1) 1893 | { 1894 | // Changing a distribution column is not supported in Parallel Data Warehouse. 1895 | distColumn = c.name; 1896 | 1897 | } 1898 | 1899 | if (isToAdd) 1900 | { 1901 | if (iCountAdd > 1) 1902 | { 1903 | columnspecAdd.Append("\r\n\t," + c.columnDefinition); 1904 | } 1905 | else 1906 | { 1907 | columnspecAdd.Append("\t" + c.columnDefinition); 1908 | } 1909 | columnspecAdd.Append(strStartWarningMessage); 1910 | } 1911 | else 1912 | { 1913 | alterTableTxt = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 1914 | "ALTER COLUMN " + c.columnDefinition + ";\r\n"; 1915 | 1916 | columnspecAlter.Append(alterTableTxt); 1917 | columnspecAlter.Append(strStartWarningMessage); 1918 | columnspecAlter.Append("\r\n"); 1919 | } 1920 | 1921 | 1922 | 1923 | 1924 | } 1925 | // ====>>> SCRIPTS 1926 | // script : ADD 1927 | if (columnspecAdd.Length > 0) 1928 | { 1929 | strAlterScript = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 1930 | "ADD " + columnspecAdd.ToString() + ";\r\n"; 1931 | 1932 | alterScript.Append(strAlterScript); 1933 | // sw.WriteLine(alterTableTxt); 1934 | } 1935 | 1936 | // script : DROP STATISTICS 1937 | // Drop Statistics before Alter Table 1938 | if (columnspecDropStat.Length > 0) 1939 | { 1940 | strDopStatsTXT = "-- === DROP STATISTICS before ALTER tables\r\n"; 1941 | alterScript.Append(strDopStatsTXT); 1942 | 1943 | alterScript.Append(columnspecDropStat.ToString()); 1944 | } 1945 | 1946 | // script : ALTER TABLE / ALTER COLUMNS 1947 | if (columnspecAlter.Length > 0) 1948 | { 1949 | alterScript.Append(columnspecAlter.ToString()); 1950 | 1951 | } 1952 | #endregion 1953 | 1954 | #region DROP COLUMNS 1955 | 1956 | int iCountDrop = 0; 1957 | 1958 | 1959 | foreach (ColumnDef c in ListolumnsToDelete) 1960 | { 1961 | strStartWarningMessage = string.Empty; 1962 | // If Drop or Modify 1963 | if (cSource.cols.Exists(x => x.name == c.name)) 1964 | { 1965 | continue; 1966 | } 1967 | else 1968 | { 1969 | 1970 | iCountDrop += 1; 1971 | } 1972 | 1973 | 1974 | if (c.distrbution_ordinal == 1) 1975 | { 1976 | // Cannot drop distribute Column 1977 | distColumn = c.name; 1978 | 1979 | } 1980 | 1981 | if (iCountDrop > 1) 1982 | { 1983 | columnspecDrop.Append("\r\n\t," + "[" + c.name + "]"); 1984 | } 1985 | else 1986 | { 1987 | columnspecDrop.Append("\t" + "[" + c.name + "]"); 1988 | } 1989 | columnspecDrop.Append(strStartWarningMessage); 1990 | 1991 | } 1992 | 1993 | 1994 | if (columnspecDrop.Length > 0) 1995 | { 1996 | 1997 | dropTableTxt = " ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 1998 | "DROP COLUMN " + columnspecDrop.ToString() + "; \r\n"; 1999 | 2000 | alterScript.Append(dropTableTxt); 2001 | string description = "/* WARNING !!!! ======: DROP COLUMN : " + columnspecDrop.ToString() + " ON TABLE : " + destTable + " \r\n"; 2002 | description += dropTableTxt; 2003 | 2004 | } 2005 | 2006 | #endregion 2007 | 2008 | 2009 | 2010 | //Compare clustered index 2011 | if (SourceTbl.clusteredcols.CompareTo(TargetTbl.clusteredcols) == 1) 2012 | { 2013 | alterScript.Append(buildAlterTableClusteredIndexText(SourceTbl, TargetTbl)); 2014 | } 2015 | 2016 | //Compare NonClustered index 2017 | if (SourceTbl.nonclusteredIndexes.CompareTo(TargetTbl.nonclusteredIndexes) == 1) 2018 | { 2019 | alterScript.Append(buildAlterTableNonClusteredIndexText(SourceTbl, TargetTbl)); 2020 | } 2021 | //Compare Statistics 2022 | if (SourceTbl.statistics.CompareTo(TargetTbl.statistics) == 1) 2023 | { 2024 | alterScript.Append(buildAlterTableStatisticsText(SourceTbl, TargetTbl)); 2025 | } 2026 | 2027 | // add default constraints 2028 | alterScript.Append(columnsAlterDefaultCnstr); 2029 | 2030 | return alterScript.ToString(); 2031 | } 2032 | 2033 | 2034 | private void buildAlterTableText(StreamWriter sw, PDWscripter cSource, PDWscripter cTarget, TableDef t, Boolean SourceFromFile) 2035 | { 2036 | StringBuilder columnspecAdd = new StringBuilder(); 2037 | StringBuilder columnspecAlter = new StringBuilder(); 2038 | StringBuilder columnsAlterDefaultCnstr = new StringBuilder(); 2039 | StringBuilder columnspecDrop = new StringBuilder(); 2040 | StringBuilder columnspecDropStat = new StringBuilder(); 2041 | StringBuilder alterScript = new StringBuilder(); 2042 | StringBuilder alterclusteredindexscript = new StringBuilder(); 2043 | string strStartWarningMessage = string.Empty; 2044 | string strWarningAlterMessage = string.Empty; 2045 | string targetdistColumn = string.Empty; 2046 | string strDopStats = string.Empty; 2047 | string strDopStatsTXT = string.Empty; 2048 | string strDistributionChangedTXT = string.Empty; 2049 | bool bIsWarning; 2050 | string strAlterScript; 2051 | TableSt SourceTbl; 2052 | TableSt TargetTbl; 2053 | 2054 | 2055 | // Init 2056 | cSource.sourceTable = t.name; 2057 | cSource.destTable = t.name; 2058 | 2059 | cTarget.sourceTable = t.name; 2060 | cTarget.destTable = t.name; 2061 | 2062 | SourceTbl = cSource.dbstruct.GetTable(t.name); 2063 | TargetTbl = cTarget.dbstruct.GetTable(t.name); 2064 | 2065 | // GetColumns 2066 | cSource.getSourceColumns(false, SourceFromFile); 2067 | cTarget.getSourceColumns(false, SourceFromFile); 2068 | targetdistColumn = cTarget.distColumn; 2069 | 2070 | if (SourceTbl.distribution_policy != TargetTbl.distribution_policy || cTarget.distColumn != cSource.distColumn) 2071 | { 2072 | 2073 | cSource.buildAlterTableForDistributionPolicyChange(sw, SourceTbl); 2074 | string strWarningDistibutionTypeChange = "/* Distribution type or column change changed for table " + SourceTbl.name + "\r\n*/\r\n\r\n"; 2075 | writeWarningtxt(strWarningDistibutionTypeChange); 2076 | 2077 | } 2078 | 2079 | 2080 | 2081 | 2082 | 2083 | 2084 | 2085 | //Compare columns 2086 | var ListColumnsToCreateOrAlter = cSource.cols.Except(cTarget.cols).ToList(); 2087 | var ListolumnsToDelete = cTarget.cols.Except(cSource.cols).ToList(); 2088 | 2089 | 2090 | 2091 | #region CompareColumnsTo ALTER 2092 | int iCountAdd = 0; 2093 | bool isToAdd = false; 2094 | bool isToAddDefaultContraint = false; 2095 | bool isToAlter = false; 2096 | 2097 | // Init 2098 | bIsWarning = false; 2099 | 2100 | 2101 | foreach (ColumnDef c in ListColumnsToCreateOrAlter) 2102 | { 2103 | isToAdd = true; 2104 | strStartWarningMessage = ""; 2105 | 2106 | // If Create Or MODIFY ==> AMD à améliorer !!!! 2107 | foreach (ColumnDef cTemp in cTarget.cols) 2108 | { 2109 | if (cTemp.name == c.name) 2110 | { 2111 | isToAdd = false; 2112 | isToAlter = (cTemp.columnDefinition != c.columnDefinition); 2113 | isToAddDefaultContraint = (cTemp.defaultconstraint == "" && c.defaultconstraint != ""); 2114 | 2115 | break; 2116 | } 2117 | } 2118 | 2119 | //if (cTarget.cols.Exists(x => x.name == c.name)) 2120 | if (isToAdd) 2121 | { 2122 | iCountAdd += 1; 2123 | } 2124 | else 2125 | { 2126 | if (isToAlter) 2127 | { 2128 | if (cTarget.stats.Exists(x => x.name == c.name)) 2129 | { 2130 | // Stats not clusturedIndex ==> DROP 2131 | strDopStats = "DROP STATISTICS [" + destTable.Replace(".", "].[") + "].[" + c.name + "];\r\n"; 2132 | columnspecDropStat.Append(strDopStats); 2133 | } 2134 | } 2135 | else 2136 | { 2137 | if (isToAddDefaultContraint) 2138 | { 2139 | alterTableTxt = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 2140 | "ADD " + c.defaultconstraint + " FOR [" + c.name + "];\r\n"; 2141 | 2142 | columnsAlterDefaultCnstr.Append(alterTableTxt); 2143 | columnsAlterDefaultCnstr.Append("\r\n"); 2144 | } 2145 | continue; 2146 | } 2147 | } 2148 | if (c.distrbution_ordinal == 1) 2149 | { 2150 | // Changing a distribution column is not supported in Parallel Data Warehouse. 2151 | distColumn = c.name; 2152 | strStartWarningMessage = " -- WARNING !!!! ======: Changing a distribution column is not supported in Parallel Data Warehouse.\r\n"; 2153 | bIsWarning = true; 2154 | writeWarningtxt(strStartWarningMessage + alterTableTxt); 2155 | 2156 | } 2157 | 2158 | if (isToAdd) 2159 | { 2160 | if (iCountAdd > 1) 2161 | { 2162 | columnspecAdd.Append("\r\n\t," + c.columnDefinition); 2163 | } 2164 | else 2165 | { 2166 | columnspecAdd.Append("\t" + c.columnDefinition); 2167 | } 2168 | columnspecAdd.Append(strStartWarningMessage); 2169 | } 2170 | else 2171 | { 2172 | alterTableTxt = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 2173 | "ALTER COLUMN " + c.columnDefinition + ";\r\n"; 2174 | 2175 | columnspecAlter.Append(alterTableTxt); 2176 | columnspecAlter.Append(strStartWarningMessage); 2177 | columnspecAlter.Append("\r\n"); 2178 | } 2179 | 2180 | 2181 | 2182 | 2183 | } 2184 | // ====>>> SCRIPTS 2185 | // script : ADD 2186 | if (columnspecAdd.Length > 0) 2187 | { 2188 | strAlterScript = "ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 2189 | "ADD " + columnspecAdd.ToString() + ";\r\n"; 2190 | 2191 | alterScript.Append(strAlterScript); 2192 | // sw.WriteLine(alterTableTxt); 2193 | } 2194 | 2195 | // script : DROP STATISTICS 2196 | // Drop Statistics before Alter Table 2197 | if (columnspecDropStat.Length > 0) 2198 | { 2199 | strDopStatsTXT = "-- === DROP STATISTICS before ALTER tables\r\n"; 2200 | alterScript.Append(strDopStatsTXT); 2201 | 2202 | alterScript.Append(columnspecDropStat.ToString()); 2203 | } 2204 | 2205 | // script : ALTER TABLE / ALTER COLUMNS 2206 | if (columnspecAlter.Length > 0) 2207 | { 2208 | alterScript.Append(columnspecAlter.ToString()); 2209 | 2210 | } 2211 | #endregion 2212 | 2213 | #region DROP COLUMNS 2214 | 2215 | int iCountDrop = 0; 2216 | 2217 | 2218 | foreach (ColumnDef c in ListolumnsToDelete) 2219 | { 2220 | strStartWarningMessage = string.Empty; 2221 | // If Drop or Modify 2222 | if (cSource.cols.Exists(x => x.name == c.name)) 2223 | { 2224 | continue; 2225 | } 2226 | else 2227 | { 2228 | 2229 | iCountDrop += 1; 2230 | } 2231 | 2232 | 2233 | if (c.distrbution_ordinal == 1) 2234 | { 2235 | // Cannot drop distribute Column 2236 | distColumn = c.name; 2237 | 2238 | strStartWarningMessage = "-- WARNING !!!! ======: Cannot drop distribute Column.\r\n"; 2239 | bIsWarning = true; 2240 | 2241 | 2242 | } 2243 | 2244 | if (iCountDrop > 1) 2245 | { 2246 | columnspecDrop.Append("\r\n\t," + "[" + c.name + "]"); 2247 | } 2248 | else 2249 | { 2250 | columnspecDrop.Append("\t" + "[" + c.name + "]"); 2251 | } 2252 | columnspecDrop.Append(strStartWarningMessage); 2253 | 2254 | } 2255 | 2256 | 2257 | if (columnspecDrop.Length > 0) 2258 | { 2259 | 2260 | dropTableTxt = "/* ALTER TABLE [" + cTarget.destDb + "].[" + destTable.Replace(".", "].[") + "]\r\n" + 2261 | "DROP COLUMN " + columnspecDrop.ToString() + "; */ \r\n"; 2262 | 2263 | alterScript.Append(dropTableTxt); 2264 | string description = "/* WARNING !!!! ======: DROP COLUMN : " + columnspecDrop.ToString() + " ON TABLE : " + destTable + " \r\n"; 2265 | description += dropTableTxt; 2266 | writeWarningtxt(description); 2267 | } 2268 | 2269 | #endregion 2270 | 2271 | //Compare clustered index 2272 | if (SourceTbl.clusteredcols.CompareTo(TargetTbl.clusteredcols) == 1) 2273 | { 2274 | alterScript.Append(buildAlterTableClusteredIndexText(SourceTbl, TargetTbl)); 2275 | } 2276 | 2277 | //Compare NonClustered index 2278 | if (SourceTbl.nonclusteredIndexes.CompareTo(TargetTbl.nonclusteredIndexes) == 1) 2279 | { 2280 | alterScript.Append(buildAlterTableNonClusteredIndexText(SourceTbl, TargetTbl)); 2281 | } 2282 | //Compare Statistics 2283 | if (SourceTbl.statistics.CompareTo(TargetTbl.statistics) == 1) 2284 | { 2285 | alterScript.Append(buildAlterTableStatisticsText(SourceTbl, TargetTbl)); 2286 | } 2287 | 2288 | // add default constraints 2289 | alterScript.Append(columnsAlterDefaultCnstr); 2290 | 2291 | if (alterScript.Length > 0) 2292 | sw.WriteLine(alterScript); 2293 | 2294 | //Console.Write("."); 2295 | Console.Write("."); 2296 | } 2297 | 2298 | private string buildCreateStatisticsText(StatDef stat, string TargetTbl) 2299 | { 2300 | StringBuilder CreateStatsText = new StringBuilder(); 2301 | CreateStatsText.Append("CREATE STATISTICS [" + stat.name + "] ON [" + TargetTbl.Replace(".", "].[") + "] "); 2302 | foreach (StatColumnDef i in stat.cols) 2303 | { 2304 | if (i.key_ordinal > 1) 2305 | { 2306 | CreateStatsText.Append("\r\n,"); 2307 | } 2308 | else 2309 | { 2310 | CreateStatsText.Append("\r\n("); 2311 | } 2312 | CreateStatsText.Append("[" + i.name + "] "); 2313 | } 2314 | //managing filters 2315 | CreateStatsText.Append(")"); 2316 | if (stat.IsFilteredStat()) 2317 | { 2318 | CreateStatsText.Append("\r\nWHERE "+stat.filter); 2319 | } 2320 | CreateStatsText.Append("\r\n;\r\n"); 2321 | 2322 | return CreateStatsText.ToString(); 2323 | } 2324 | 2325 | private string buildAlterTableStatisticsText(TableSt SourceTbl, TableSt TargetTbl) 2326 | { 2327 | StringBuilder alterstatisticsscript = new StringBuilder(); 2328 | foreach (StatDef stat in SourceTbl.statistics) 2329 | { 2330 | StatDef targetstat = TargetTbl.statistics.GetStat(stat.name); 2331 | if (targetstat != null) 2332 | { 2333 | // index name exists on target table 2334 | if (stat.CompareTo(targetstat) == 1) 2335 | { 2336 | alterstatisticsscript.Append("DROP STATISTICS [" + stat.name + "] ON [" + TargetTbl.name.Replace(".", "].[") + "];\r\n"); 2337 | alterstatisticsscript.Append(buildCreateStatisticsText(stat,TargetTbl.name)); 2338 | 2339 | } 2340 | } 2341 | else 2342 | { 2343 | alterstatisticsscript.Append(buildCreateStatisticsText(stat,TargetTbl.name)); 2344 | } 2345 | 2346 | } 2347 | 2348 | var ListStatisticsToDelete = TargetTbl.statistics.Except(SourceTbl.statistics).ToList(); 2349 | 2350 | foreach (var stattodalete in ListStatisticsToDelete) 2351 | { 2352 | alterstatisticsscript.Append("DROP STATISTICS [" + TargetTbl.name.Replace(".", "].[") + "].[" + stattodalete.name + "];\r\n"); 2353 | } 2354 | 2355 | return alterstatisticsscript.ToString(); 2356 | 2357 | } 2358 | private string buildAlterTableClusteredIndexText(TableSt SourceTbl, TableSt TargetTbl) 2359 | { 2360 | StringBuilder alterclusteredindexscript = new StringBuilder(); 2361 | // alterclusteredindexscript.Append("\r\n"); 2362 | 2363 | if (TargetTbl.clusteredcols.Count != 0) 2364 | { 2365 | alterclusteredindexscript.Append("DROP INDEX [" + TargetTbl.ClusteredIndexName + "] ON " + TargetTbl.name + ";\r\n"); 2366 | 2367 | } 2368 | // create clustered index script create 2369 | Boolean isCCI = false; 2370 | foreach (IndexColumnDef i in SourceTbl.clusteredcols.OrderBy(o => o.key_ordinal)) 2371 | { 2372 | if (i.index_type == 5) // CCI 2373 | { 2374 | isCCI = true; 2375 | foreach (NonclusteredIndexDef ncidx in TargetTbl.nonclusteredIndexes) 2376 | { 2377 | alterclusteredindexscript.Append("DROP INDEX [" + ncidx.name + "] ON [" + TargetTbl.name.Replace(".", "].[") + "];\r\n"); 2378 | } 2379 | TargetTbl.nonclusteredIndexes.Clear(); 2380 | alterclusteredindexscript.Append("CREATE CLUSTERED COLUMNSTORE INDEX [" + SourceTbl.ClusteredIndexName + "] ON " + SourceTbl.name + ";\r\n"); 2381 | break; 2382 | } 2383 | if (i.key_ordinal > 1) 2384 | { 2385 | alterclusteredindexscript.Append("\r\n,"); 2386 | } 2387 | else 2388 | { 2389 | alterclusteredindexscript.Append("CREATE CLUSTERED INDEX [" + SourceTbl.ClusteredIndexName + "] ON " + SourceTbl.name + "("); 2390 | } 2391 | alterclusteredindexscript.Append("[" + i.name + "]" + (i.is_descending_key ? " DESC " : "")); 2392 | } 2393 | if (alterclusteredindexscript.Length > 0 && !isCCI) 2394 | { 2395 | alterclusteredindexscript.Append(");\r\n"); 2396 | } 2397 | return alterclusteredindexscript.ToString(); 2398 | } 2399 | private string buildAlterTableNonClusteredIndexText(TableSt SourceTbl, TableSt TargetTbl) 2400 | { 2401 | StringBuilder alternonclusteredindex = new StringBuilder(); 2402 | foreach (NonclusteredIndexDef ncidx in SourceTbl.nonclusteredIndexes) 2403 | { 2404 | NonclusteredIndexDef targetncidx = TargetTbl.nonclusteredIndexes.GetIndex(ncidx.name); 2405 | if (targetncidx != null) 2406 | { 2407 | // index name exists on target table 2408 | if (ncidx.CompareTo(targetncidx) == 1) 2409 | { 2410 | alternonclusteredindex.Append("DROP INDEX [" + ncidx.name + "] ON [" + TargetTbl.name.Replace(".", "].[") + "];\r\n"); 2411 | alternonclusteredindex.Append("CREATE INDEX [" + ncidx.name + "] ON [" + TargetTbl.name.Replace(".", "].[") + "] "); 2412 | foreach (IndexColumnDef i in ncidx.cols) 2413 | { 2414 | if (i.key_ordinal > 1) 2415 | { 2416 | alternonclusteredindex.Append("\r\n,"); 2417 | } 2418 | else 2419 | { 2420 | alternonclusteredindex.Append("\r\n("); 2421 | } 2422 | alternonclusteredindex.Append("[" + i.name + "]" + (i.is_descending_key ? " DESC " : "")); 2423 | } 2424 | alternonclusteredindex.Append(");\r\n"); 2425 | } 2426 | } 2427 | else 2428 | { 2429 | alternonclusteredindex.Append("CREATE INDEX [" + ncidx.name + "] ON [" + TargetTbl.name.Replace(".", "].[") + "] "); 2430 | foreach (IndexColumnDef i in ncidx.cols) 2431 | { 2432 | if (i.key_ordinal > 1) 2433 | { 2434 | alternonclusteredindex.Append("\r\n,"); 2435 | } 2436 | else 2437 | { 2438 | alternonclusteredindex.Append("\r\n("); 2439 | } 2440 | alternonclusteredindex.Append("[" + i.name + "]" + (i.is_descending_key ? " DESC " : "")); 2441 | } 2442 | alternonclusteredindex.Append(");\r\n"); 2443 | } 2444 | 2445 | } 2446 | 2447 | var ListNonClusteredIndexToDelete = TargetTbl.nonclusteredIndexes.Except(SourceTbl.nonclusteredIndexes).ToList(); 2448 | 2449 | foreach (var idxtodalete in ListNonClusteredIndexToDelete) 2450 | { 2451 | alternonclusteredindex.Append("DROP INDEX [" + TargetTbl.name.Replace(".", "].[") + "].[" + idxtodalete.name + "];\r\n"); 2452 | } 2453 | 2454 | return alternonclusteredindex.ToString(); 2455 | 2456 | } 2457 | private void compBuildDropTableText(StreamWriter sw, bool bWarn) 2458 | { 2459 | 2460 | String strStartWarningMessage = string.Empty; 2461 | String strEndWarningMessage = string.Empty; 2462 | 2463 | strStartWarningMessage = "/* WARNING !!!! ======: Potential data loss .\r\n"; 2464 | strEndWarningMessage = "*/\r\n\r\n"; 2465 | 2466 | destTableFullName = destTable.Replace(".", "].["); 2467 | 2468 | 2469 | dropTableTxt = "/* DROP TABLE [" + destDb + "].[" + destTable.Replace(".", "].[") + "] */\r\n"; 2470 | 2471 | if (bWarn) 2472 | { 2473 | dropTableTxt = strStartWarningMessage + dropTableTxt + strEndWarningMessage; 2474 | writeWarningtxt(dropTableTxt); 2475 | } 2476 | 2477 | sw.WriteLine(dropTableTxt); 2478 | // writeWarningtxt(dropTableTxt); 2479 | 2480 | 2481 | Console.Write("."); 2482 | } 2483 | 2484 | private void compareDML(PDWscripter cSource, PDWscripter cTarget, string outFile, Boolean SourceFromFile, FilterSettings FilterSet) 2485 | { 2486 | StreamWriter outfile = null; 2487 | FileStream fs = null; 2488 | Console.Write("CompareDML>"); 2489 | 2490 | cTarget.cmd.CommandText = @" SELECT c.definition, b.name + '.' + a.name AS ObjectName 2491 | FROM 2492 | sys.sql_modules c 2493 | INNER JOIN sys.objects a ON a.object_id = c.object_id 2494 | INNER JOIN sys.schemas b 2495 | ON a.schema_id = b.schema_id"; 2496 | 2497 | if (outFile != "") 2498 | { 2499 | fs = new FileStream(outFile, FileMode.Create); 2500 | outfile = new StreamWriter(fs); 2501 | } 2502 | String description = "-- script generated the : " + String.Format("{0:d/M/yyyy HH:mm:ss}", DateTime.Now) + "\r\n"; 2503 | outfile.WriteLine(description); 2504 | 2505 | String createUseDbTxt = "USE " + cTarget.sourceDb + "\r\nGO\r\n"; 2506 | outfile.WriteLine(createUseDbTxt); 2507 | 2508 | List> lstSourceDbObjectDefinitions = new List>(); 2509 | List> lstTargetDbObjectDefinitions = new List>(); 2510 | 2511 | List> lstCreateOrAlterDbObjectDefinitions; 2512 | List> lstDropDbObjectDefinitions; 2513 | 2514 | if (!SourceFromFile) 2515 | { 2516 | // ===>>>< source 2517 | cSource.cmd.CommandText = @" SELECT c.definition, b.name + '.' + a.name AS ObjectName 2518 | FROM 2519 | sys.sql_modules c 2520 | INNER JOIN sys.objects a ON a.object_id = c.object_id 2521 | INNER JOIN sys.schemas b 2522 | ON a.schema_id = b.schema_id"; 2523 | rdr = cSource.cmd.ExecuteReader(); 2524 | 2525 | while (rdr.Read()) 2526 | { 2527 | IDataRecord record = (IDataRecord)rdr; 2528 | KeyValuePair sourceKvpObjNameDef = new KeyValuePair(String.Format("{0}", record[1]), String.Format("{0}", record[0]).TrimEnd(new char[] { '\r', '\n', ' ' })); 2529 | 2530 | if (!lstSourceDbObjectDefinitions.Exists(objDef => objDef.Key == sourceKvpObjNameDef.Key)) 2531 | { 2532 | 2533 | // Object doesn't exist 2534 | if (!lstSourceDbObjectDefinitions.Any(objDef => objDef.Value.Contains(sourceKvpObjNameDef.Key))) 2535 | { 2536 | // Object never used by an other object 2537 | lstSourceDbObjectDefinitions.Add(sourceKvpObjNameDef); 2538 | } 2539 | else 2540 | { 2541 | // Object already used by an other object, we had it previously to the calling one 2542 | int idxCallingObj = lstSourceDbObjectDefinitions.IndexOf(lstSourceDbObjectDefinitions.First(objDef => objDef.Value.Contains(sourceKvpObjNameDef.Key))); 2543 | lstSourceDbObjectDefinitions.Insert(idxCallingObj, sourceKvpObjNameDef); 2544 | } 2545 | } 2546 | } 2547 | } 2548 | else 2549 | lstSourceDbObjectDefinitions = DbObjectDefinitions; 2550 | 2551 | // ===>>>< Target 2552 | rdr = cTarget.cmd.ExecuteReader(); 2553 | 2554 | while (rdr.Read()) 2555 | { 2556 | IDataRecord record = (IDataRecord)rdr; 2557 | KeyValuePair targetKvpObjNameDef = new KeyValuePair(String.Format("{0}", record[1]), String.Format("{0}", record[0]).TrimEnd(new char[] { '\r', '\n', ' ' })); 2558 | 2559 | if (!lstTargetDbObjectDefinitions.Exists(objDef => objDef.Key == targetKvpObjNameDef.Key)) 2560 | { 2561 | 2562 | // Object doesn't exist 2563 | if (!lstTargetDbObjectDefinitions.Any(objDef => objDef.Value.Contains(targetKvpObjNameDef.Key))) 2564 | { 2565 | // Object never used by an other object 2566 | lstTargetDbObjectDefinitions.Add(targetKvpObjNameDef); 2567 | } 2568 | else 2569 | { 2570 | // Object already used by an other object, we had it previously to the calling one 2571 | int idxCallingObj = lstTargetDbObjectDefinitions.IndexOf(lstTargetDbObjectDefinitions.First(objDef => objDef.Value.Contains(targetKvpObjNameDef.Key))); 2572 | lstTargetDbObjectDefinitions.Insert(idxCallingObj, targetKvpObjNameDef); 2573 | } 2574 | } 2575 | } 2576 | 2577 | // ==>> Compare 2578 | int toDelete = 0; 2579 | lstCreateOrAlterDbObjectDefinitions = new List>(); 2580 | lstDropDbObjectDefinitions = new List>(); 2581 | List> lstSourceDbObjectDefinitionsSource; 2582 | List> lstSourceDbObjectDefinitionsTarget; 2583 | 2584 | // apply the filter 2585 | switch (FilterSet.Granularity.ToUpper()) 2586 | { 2587 | case "SCHEMA": 2588 | // split to retrieve the schema name moduledef.Key.Split((char)'.')[0] -- schema.name 2589 | lstSourceDbObjectDefinitionsSource = lstSourceDbObjectDefinitions.FindAll(delegate (KeyValuePair moduledef) { return FilterSet.GetSchemas().Contains(moduledef.Key.Split((char)'.')[0]); }); 2590 | lstSourceDbObjectDefinitionsTarget = lstTargetDbObjectDefinitions.FindAll(delegate (KeyValuePair moduledef) { return FilterSet.GetSchemas().Contains(moduledef.Key.Split((char)'.')[0]); }); 2591 | 2592 | lstCreateOrAlterDbObjectDefinitions = lstSourceDbObjectDefinitionsSource.Except(lstSourceDbObjectDefinitionsTarget).ToList(); 2593 | lstDropDbObjectDefinitions = lstSourceDbObjectDefinitionsTarget.Except(lstSourceDbObjectDefinitionsSource).ToList(); 2594 | break; 2595 | case "OBJECTS": 2596 | lstSourceDbObjectDefinitionsSource = lstSourceDbObjectDefinitions.FindAll(delegate (KeyValuePair moduledef) { return FilterSet.GetSchemaNameObjects().Contains(moduledef.Key); }); 2597 | lstSourceDbObjectDefinitionsTarget = lstTargetDbObjectDefinitions.FindAll(delegate (KeyValuePair moduledef) { return FilterSet.GetSchemaNameObjects().Contains(moduledef.Key); }); 2598 | 2599 | lstCreateOrAlterDbObjectDefinitions = lstSourceDbObjectDefinitionsSource.Except(lstSourceDbObjectDefinitionsTarget).ToList(); 2600 | 2601 | break; 2602 | 2603 | 2604 | default: 2605 | lstCreateOrAlterDbObjectDefinitions = lstSourceDbObjectDefinitions.Except(lstTargetDbObjectDefinitions).ToList(); 2606 | lstDropDbObjectDefinitions = lstTargetDbObjectDefinitions.Except(lstSourceDbObjectDefinitions).ToList(); 2607 | toDelete = lstCreateOrAlterDbObjectDefinitions.Except(lstDropDbObjectDefinitions).ToList().Count(); 2608 | break; 2609 | } 2610 | 2611 | 2612 | description = "\r\n-- objects scripted - ALTERorCREATE = " + lstCreateOrAlterDbObjectDefinitions.Count().ToString() + " - DROP = " + toDelete.ToString(); 2613 | Console.Write("\r\n objects scripted - ALTERorCREATE = " + lstCreateOrAlterDbObjectDefinitions.Count().ToString() + " - DROP = " + toDelete.ToString()); 2614 | outfile.WriteLine(description); 2615 | //==> EnumReferencedObjects ToolBar create OrderedParallelQuery ALTER 2616 | int nbObjectDefinitions = lstCreateOrAlterDbObjectDefinitions.Count(); 2617 | int index = 0; 2618 | string prevDbObjectDefinition = String.Empty; 2619 | 2620 | if (nbObjectDefinitions > 0) 2621 | { 2622 | outfile.WriteLine("/* =========== DROP AND/OR CREATE ###==> Begin =================*/\r\n"); 2623 | } 2624 | 2625 | // we order the create script based on dependencies 2626 | List> lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered = new List>(); 2627 | 2628 | List> lstCreateOrAlterDbObjectDefinitionsSanitized = new List>(); 2629 | foreach (KeyValuePair CreateOrAlterDbObjectDefinition in lstCreateOrAlterDbObjectDefinitions) 2630 | { 2631 | KeyValuePair CreateOrAlterDbObjectDefinitionSanitized = new KeyValuePair(CreateOrAlterDbObjectDefinition.Key, CreateOrAlterDbObjectDefinition.Value.Replace("[", "").Replace("]", "")); 2632 | 2633 | lstCreateOrAlterDbObjectDefinitionsSanitized.Add(CreateOrAlterDbObjectDefinitionSanitized); 2634 | } 2635 | 2636 | 2637 | foreach (KeyValuePair CreateOrAlterDbObjectDefinition in lstCreateOrAlterDbObjectDefinitionsSanitized) 2638 | { 2639 | if (!lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.Exists(objDef => objDef.Key == CreateOrAlterDbObjectDefinition.Key)) 2640 | { 2641 | 2642 | // Object doesn't exist 2643 | if (!lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.Any(objDef => objDef.Value.Contains(CreateOrAlterDbObjectDefinition.Key))) 2644 | { 2645 | // Object never used by an other object 2646 | lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.Add(CreateOrAlterDbObjectDefinition); 2647 | } 2648 | else 2649 | { 2650 | // Object already used by an other object, we had it previously to the calling one 2651 | int idxCallingObj = lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.IndexOf(lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.First(objDef => objDef.Value.Contains(CreateOrAlterDbObjectDefinition.Key))); 2652 | lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered.Insert(idxCallingObj, CreateOrAlterDbObjectDefinition); 2653 | } 2654 | } 2655 | } 2656 | 2657 | 2658 | 2659 | foreach (KeyValuePair CreateOrAlterDbObjectDefinitionsSanitized in lstCreateOrAlterDbObjectDefinitionsSanitizedOrdered) 2660 | 2661 | { 2662 | 2663 | // we retrieve the original value 2664 | KeyValuePair dbObjectDefinition = lstCreateOrAlterDbObjectDefinitions.Find(objdef => objdef.Key == CreateOrAlterDbObjectDefinitionsSanitized.Key); 2665 | 2666 | 2667 | 2668 | if (lstTargetDbObjectDefinitions.Exists(objDef => objDef.Key == dbObjectDefinition.Key)) 2669 | { 2670 | index++; 2671 | 2672 | // Add object Drop 2673 | string strObjectFullName = dbObjectDefinition.Value.Substring(12, dbObjectDefinition.Value.IndexOf(' ', 13) - 12); 2674 | string strObjectType = dbObjectDefinition.Value.Substring(7, 4); 2675 | string strDropObjectQuery = "DROP " + strObjectType + " " + strObjectFullName + ";"; 2676 | if (index != 1) 2677 | { 2678 | strDropObjectQuery = "GO\r\n" + strDropObjectQuery; 2679 | } 2680 | outfile.WriteLine(strDropObjectQuery); 2681 | } 2682 | 2683 | index++; 2684 | // Add object Create 2685 | 2686 | if (index != 1) 2687 | { 2688 | outfile.WriteLine("GO"); 2689 | } 2690 | 2691 | outfile.WriteLine(dbObjectDefinition.Value.ToString()); 2692 | outfile.WriteLine("GO"); 2693 | 2694 | Console.Write("."); 2695 | } 2696 | 2697 | if (nbObjectDefinitions > 0) 2698 | { 2699 | outfile.WriteLine("/* =========== DROP AND/OR CREATE ###==> End ===================*/"); 2700 | outfile.WriteLine("PRINT 'DROP AND/OR CREATE End'\r\n"); 2701 | } 2702 | 2703 | // Drop 2704 | nbObjectDefinitions = lstDropDbObjectDefinitions.Count(); 2705 | 2706 | if (nbObjectDefinitions > 0) 2707 | { 2708 | outfile.WriteLine("/* =========== DROP ###==> Begin =================*/"); 2709 | outfile.WriteLine("PRINT 'DROP Begin'\r\n"); 2710 | } 2711 | 2712 | prevDbObjectDefinition = String.Empty; 2713 | foreach (KeyValuePair dbObjectDefinition in lstDropDbObjectDefinitions) 2714 | { 2715 | 2716 | if (!lstSourceDbObjectDefinitions.Exists(objDef => objDef.Key == dbObjectDefinition.Key)) 2717 | { 2718 | index++; 2719 | // Add object Drop 2720 | string strObjectFullName = dbObjectDefinition.Value.Substring(12, dbObjectDefinition.Value.IndexOf(' ', 13) - 12); 2721 | string strObjectType = dbObjectDefinition.Value.Substring(7, 4); 2722 | if (index != 1) 2723 | { 2724 | outfile.WriteLine("GO"); 2725 | } 2726 | string strDropObjectQuery = "DROP " + strObjectType + " " + strObjectFullName + ";"; 2727 | outfile.WriteLine(strDropObjectQuery); 2728 | outfile.WriteLine(""); 2729 | } 2730 | } 2731 | 2732 | if (nbObjectDefinitions > 0) 2733 | { 2734 | outfile.WriteLine("/* =========== DROP ###==> End ===============*/\r\n"); 2735 | } 2736 | outfile.WriteLine("PRINT 'END'"); 2737 | if (outFile != "") 2738 | { 2739 | outfile.Close(); 2740 | } 2741 | rdr.Close(); 2742 | Console.WriteLine("done"); 2743 | 2744 | } 2745 | 2746 | private void getDDL(PDWscripter c, string outFile) 2747 | { 2748 | StreamWriter sw = null; 2749 | FileStream fs = null; 2750 | 2751 | 2752 | if (outFile != "") 2753 | { 2754 | fs = new FileStream(outFile, FileMode.Create); 2755 | sw = new StreamWriter(fs); 2756 | } 2757 | 2758 | 2759 | // get generated statistics 2760 | String description = "-- script generated the : " + String.Format("{0:d/M/yyyy HH:mm:ss}", DateTime.Now); 2761 | 2762 | //get objects count 2763 | cmd.CommandText = "select count(1) from sys.schemas where name not in ('dbo','sys','INFORMATION_SCHEMA')"; 2764 | int schemacount = (int)cmd.ExecuteScalar(); 2765 | 2766 | description += "\r\n-- objects scripted - tables = " + c.dbTables.Count.ToString() + " - schemas = " + schemacount.ToString(); 2767 | sw.WriteLine(description); 2768 | 2769 | String createUseDbTxt = "USE " + c.sourceDb + "\r\nGO\r\n"; 2770 | sw.WriteLine(createUseDbTxt); 2771 | Console.Write("DDL>"); 2772 | Console.Write("Objects to script - tables = " + c.dbTables.Count.ToString() + " - schemas = " + schemacount.ToString()); 2773 | 2774 | 2775 | getSchemas(sw, false); 2776 | 2777 | if (scriptMode == "Delta") 2778 | { 2779 | // Create temporary DEP schema 2780 | createDeployTmpSchemaTxt = "CREATE SCHEMA DEP;\r\nGO\r\n;\r\n"; 2781 | sw.WriteLine(createDeployTmpSchemaTxt); 2782 | sw.WriteLine("DECLARE @COLUMNSNAME VARCHAR(8000) \r\n"); 2783 | } 2784 | 2785 | foreach (TableDef t in dbTables) 2786 | { 2787 | c.sourceTable = t.name; 2788 | c.destTable = t.name; 2789 | c.distribution_policy = t.distribution_policy; 2790 | c.getSourceColumns(false, false); 2791 | c.getClusteredIndex(false, false); 2792 | c.getPartitioning(false, false); 2793 | c.getNonclusteredIndexes(false, false); 2794 | c.getStats(false, false); 2795 | c.buildCreateTableText(sw); 2796 | } 2797 | 2798 | if (scriptMode == "Delta") 2799 | { 2800 | // DROP temporary DEP schema 2801 | dropDeployTmpSchemaTxt = "DROP SCHEMA DEP;\r\nGO\r\n;\r\n"; 2802 | sw.WriteLine(dropDeployTmpSchemaTxt); 2803 | } 2804 | 2805 | if (outFile != "") 2806 | { 2807 | sw.Close(); 2808 | } 2809 | 2810 | Console.WriteLine("done"); 2811 | } 2812 | 2813 | public void compareDDL(PDWscripter cSource, PDWscripter cTarget, string outFile, Boolean SourceFromFile, FilterSettings Filters) 2814 | { 2815 | StreamWriter sw = null; 2816 | FileStream fs = null; 2817 | 2818 | 2819 | if (outFile != "") 2820 | { 2821 | fs = new FileStream(outFile, FileMode.Create); 2822 | sw = new StreamWriter(fs); 2823 | } 2824 | String description = "-- script generated the : " + String.Format("{0:d/M/yyyy HH:mm:ss}", DateTime.Now) + "\r\n"; 2825 | sw.WriteLine(description); 2826 | 2827 | Console.Write("CompareDDL>"); 2828 | 2829 | String createUseDbTxt = "USE " + cTarget.sourceDb + "\r\nGO\r\n"; 2830 | sw.WriteLine(createUseDbTxt); 2831 | 2832 | CompareSchemas(sw, cSource, cTarget, SourceFromFile,Filters); 2833 | 2834 | CompareDbTables(sw, cSource, cTarget, SourceFromFile,Filters); 2835 | 2836 | 2837 | if (outFile != "") 2838 | { 2839 | sw.Close(); 2840 | } 2841 | 2842 | Console.WriteLine("done"); 2843 | } 2844 | 2845 | private void getDB(PDWscripter c, string outFile) 2846 | { 2847 | StreamWriter sw = null; 2848 | FileStream fs = null; 2849 | 2850 | Console.Write("DB>"); 2851 | if (outFile != "") 2852 | { 2853 | fs = new FileStream(outFile, FileMode.Create); 2854 | sw = new StreamWriter(fs); 2855 | } 2856 | 2857 | String createDbTxt = "CREATE DATABASE " + c.sourceDb + "\r\nWITH\r\n(\r\n"; 2858 | sw.WriteLine(createDbTxt); 2859 | 2860 | if (scriptMode == "Delta") 2861 | { 2862 | // Create temporary DEP schema 2863 | createDeployTmpSchemaTxt = "CREATE SCHEMA DEP;\r\nGO\r\n;\r\n"; 2864 | sw.WriteLine(createDeployTmpSchemaTxt); 2865 | } 2866 | 2867 | foreach (TableDef t in dbTables) 2868 | { 2869 | c.sourceTable = t.name; 2870 | c.destTable = t.name; 2871 | c.distribution_policy = t.distribution_policy; 2872 | c.getSourceColumns(false, false); 2873 | c.getClusteredIndex(false, false); 2874 | c.getPartitioning(false, false); 2875 | c.getNonclusteredIndexes(false, false); 2876 | c.buildCreateTableText(sw); 2877 | } 2878 | 2879 | if (outFile != "") 2880 | { 2881 | sw.Close(); 2882 | } 2883 | Console.WriteLine("done"); 2884 | } 2885 | 2886 | public void CompIterateScriptAllTables(PDWscripter cSource, PDWscripter cTarget, string outFile, Boolean SourceFromFile, FilterSettings Filters) 2887 | { 2888 | string outCompDDLFile = outFile + "_COMP_DDL.dsql"; 2889 | string outCompDMLFile = outFile + "_COMP_DML.dsql"; 2890 | string strWarningFile = outFile + "_COMP_DDL.warn"; 2891 | 2892 | StreamWriter swWarn = null; 2893 | FileStream fs = null; 2894 | 2895 | cTarget.warningFile = strWarningFile; 2896 | cSource.warningFile = strWarningFile; 2897 | 2898 | fs = new FileStream(strWarningFile, FileMode.Create); 2899 | swWarn = new StreamWriter(fs); 2900 | swWarn.Close(); 2901 | 2902 | Console.WriteLine("==== Start Comparison ===="); 2903 | 2904 | if ("ALL" == wrkMode || "DDL" == wrkMode) 2905 | { 2906 | cSource.compareDDL(cSource, cTarget, outCompDDLFile, SourceFromFile, Filters); 2907 | } 2908 | if ("ALL" == wrkMode || "DML" == wrkMode) 2909 | { 2910 | cSource.compareDML(cSource, cTarget, outCompDMLFile, SourceFromFile,Filters); 2911 | } 2912 | Console.WriteLine("==== End Comparison ===="); 2913 | 2914 | } 2915 | 2916 | public void IterateScriptAllTables(PDWscripter c, string outFile) 2917 | { 2918 | 2919 | string outDDLFile = outFile + "_DDL.dsql"; 2920 | string outDMLFile = outFile + "_DML.dsql"; 2921 | 2922 | if ("ALL" == wrkMode || "DDL" == wrkMode) 2923 | { 2924 | c.getDDL(c, outDDLFile); 2925 | } 2926 | if ("ALL" == wrkMode || "DML" == wrkMode) 2927 | { 2928 | c.getDML(c, outDMLFile); 2929 | } 2930 | 2931 | 2932 | } 2933 | 2934 | public void GetDDLstructureFromJSONfile(string DDLJsonStructureFile) 2935 | { 2936 | using (StreamReader file = File.OpenText(DDLJsonStructureFile)) 2937 | { 2938 | JsonSerializer serializer = new JsonSerializer(); 2939 | this.dbstruct = (DBStruct)serializer.Deserialize(file, typeof(DBStruct)); 2940 | } 2941 | 2942 | foreach (TableSt tbl in this.dbstruct.tables) 2943 | { 2944 | this.dbTables.Add(new TableDef(tbl.name, tbl.schema, tbl.distribution_policy)); 2945 | } 2946 | } 2947 | 2948 | public void GetDMLstructureFromJSONfile(string DMLJsonStructureFile) 2949 | { 2950 | using (StreamReader file = File.OpenText(DMLJsonStructureFile)) 2951 | { 2952 | JsonSerializer serializer = new JsonSerializer(); 2953 | this.DbObjectDefinitions = new List>(); 2954 | this.DbObjectDefinitions.AddRange((List>)serializer.Deserialize(file, typeof(List>))); 2955 | } 2956 | } 2957 | 2958 | public string GenerateTableCreateScript (TableSt tbl) 2959 | { 2960 | this.sourceTable = tbl.name; 2961 | this.destTable = tbl.name; 2962 | this.distribution_policy = tbl.distribution_policy; 2963 | this.getSourceColumns(false, true); 2964 | this.getClusteredIndex(false, true); 2965 | this.getPartitioning(false, true); 2966 | this.getNonclusteredIndexes(false, true); 2967 | this.getStats(false, true); 2968 | return this.buildScriptCreateTable(); 2969 | } 2970 | 2971 | } 2972 | 2973 | } 2974 | -------------------------------------------------------------------------------- /src/PDWScripter/Partition.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class Partition 13 | { 14 | public string partitionColumn; 15 | public string partitionLeftOrRight; 16 | public List partitionBoundaries; 17 | public Partition() 18 | { 19 | this.partitionColumn = ""; 20 | this.partitionLeftOrRight = ""; 21 | this.partitionBoundaries = new List(); 22 | } 23 | 24 | } 25 | public struct PartitionBoundary 26 | { 27 | public Int32 partition_number; 28 | public string boundary_value; 29 | public string boundary_value_type; 30 | 31 | public PartitionBoundary(Int32 partition_number, string boundary_value, string boundary_value_type) 32 | { 33 | this.partition_number = partition_number; 34 | this.boundary_value = boundary_value; 35 | this.boundary_value_type = boundary_value_type; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/PDWScripter/StatColumnDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class StatColumnDef : IComparable 13 | { 14 | public int key_ordinal; 15 | public string name; 16 | 17 | public StatColumnDef(int key_ordinal, string name) 18 | { 19 | this.key_ordinal = key_ordinal; 20 | this.name = name; 21 | } 22 | 23 | public virtual int CompareTo(object obj) 24 | { 25 | if (obj == null) return 1; 26 | StatColumnDef otherStatColumnDef = obj as StatColumnDef; 27 | if (otherStatColumnDef != null) 28 | { 29 | if (this == null || otherStatColumnDef == null) return 1; 30 | if (this.key_ordinal.CompareTo(otherStatColumnDef.key_ordinal) != 0) return 1; 31 | if (this.name.ToUpper().CompareTo(otherStatColumnDef.name.ToUpper()) != 0) return 1; 32 | } 33 | return 0; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/PDWScripter/StatDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class StatDef 13 | { 14 | public string name; 15 | public List cols; 16 | 17 | public string filter; 18 | 19 | public StatDef(string name) 20 | { 21 | this.name = name; 22 | cols = new List(); 23 | } 24 | public virtual int CompareTo(object obj) 25 | { 26 | if (obj == null) return 1; 27 | StatDef otherStatDef = obj as StatDef; 28 | if (otherStatDef != null) 29 | { 30 | if (this == null || otherStatDef == null) return 1; 31 | if (this.name.ToUpper().CompareTo(otherStatDef.name.ToUpper()) != 0) return 1; 32 | this.cols.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 33 | } 34 | return 0; 35 | } 36 | public Boolean ContainsColumn(string ColumnName) 37 | { 38 | return this.cols.Exists(delegate (StatColumnDef e) { return e.name == ColumnName; }); 39 | } 40 | 41 | public Boolean IsFilteredStat() 42 | { 43 | return (this.filter != null); 44 | } 45 | 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/PDWScripter/Statistics.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class Statistics : List 13 | { 14 | 15 | public virtual int CompareTo(object obj) 16 | { 17 | if (obj == null) return 1; 18 | Statistics otherStatistics = obj as Statistics; 19 | if (otherStatistics != null) 20 | { 21 | if (this == null || otherStatistics == null) return 1; 22 | this.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 23 | otherStatistics.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 24 | if (this.Count != otherStatistics.Count) return 1; 25 | 26 | for (int i = 0; i < this.Count; i++) 27 | { 28 | if (this[i].CompareTo(otherStatistics[i]) == 1) return 1; 29 | } 30 | } 31 | return 0; 32 | } 33 | 34 | public StatDef GetStat(string StatName) 35 | { 36 | return this.Find(delegate (StatDef e) { return e.name == StatName; }); 37 | } 38 | 39 | public Statistics GetStatsWithColumn(string ColumnName) 40 | { 41 | Statistics StatsWithColumn = new Statistics(); 42 | foreach (StatDef stat in this) 43 | { 44 | if (stat.ContainsColumn(ColumnName)) 45 | StatsWithColumn.Add(stat); 46 | } 47 | return StatsWithColumn; 48 | } 49 | 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/PDWScripter/TableDef.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public struct TableDef 13 | { 14 | public string name; 15 | public string schema; 16 | public byte distribution_policy; 17 | 18 | 19 | 20 | public TableDef(string name, string schema, byte distribution_policy) 21 | { 22 | this.name = name; 23 | this.schema = schema; 24 | this.distribution_policy = distribution_policy; 25 | } 26 | 27 | 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/PDWScripter/TableSt.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | 10 | namespace DWScripter 11 | { 12 | public class TableSt : IComparable 13 | { 14 | public string name; 15 | public string schema; 16 | public byte distribution_policy; 17 | public List Columns; 18 | public string ClusteredIndexName; 19 | public ClusteredDef clusteredcols; 20 | public NonclusteredIndexes nonclusteredIndexes; 21 | public Statistics statistics; 22 | public Partition DBPartition; 23 | 24 | public TableSt () 25 | { 26 | this.name = ""; 27 | this.schema = ""; 28 | this.distribution_policy = 1; 29 | this.Columns = new List(); 30 | this.clusteredcols = new ClusteredDef(); 31 | this.nonclusteredIndexes = new NonclusteredIndexes(); 32 | this.statistics = new Statistics(); 33 | this.DBPartition = new Partition(); 34 | this.ClusteredIndexName = string.Empty; 35 | } 36 | 37 | public TableSt(string name, string schema, byte distribution_policy) 38 | { 39 | this.name = name; 40 | this.schema = schema; 41 | this.distribution_policy = distribution_policy; 42 | this.Columns = new List(); 43 | this.clusteredcols = new ClusteredDef(); 44 | this.nonclusteredIndexes = new NonclusteredIndexes(); 45 | this.statistics = new Statistics(); 46 | this.DBPartition = new Partition(); 47 | this.ClusteredIndexName = string.Empty; 48 | } 49 | 50 | 51 | 52 | public virtual Boolean ColumnEquals (object obj) 53 | { 54 | if (obj == null) return false; 55 | List otherColumns = obj as List; 56 | if (otherColumns != null) 57 | { 58 | if (this == null || otherColumns == null) return false; 59 | if (this.Columns.Count != otherColumns.Count) return false; 60 | this.Columns.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 61 | otherColumns.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 62 | 63 | for (int i = 0; i < this.Columns.Count; i++) 64 | { 65 | if (this.Columns[i].column_id != otherColumns[i].column_id) return false; 66 | if (this.Columns[i].name != otherColumns[i].name) return false; 67 | if (this.Columns[i].type != otherColumns[i].type) return false; 68 | if (this.Columns[i].max_length != otherColumns[i].max_length) return false; 69 | if (this.Columns[i].precision != otherColumns[i].precision) return false; 70 | if (this.Columns[i].scale != otherColumns[i].scale) return false; 71 | if (this.Columns[i].is_nullable != otherColumns[i].is_nullable) return false; 72 | if (this.Columns[i].distrbution_ordinal != otherColumns[i].distrbution_ordinal) return false; 73 | if (this.Columns[i].collation_name != otherColumns[i].collation_name) return false; 74 | if (this.Columns[i].defaultconstraint != otherColumns[i].defaultconstraint) return false; 75 | } 76 | } 77 | return true; 78 | } 79 | public virtual int CompareTo(object obj) 80 | { 81 | if (obj == null) return 1; 82 | TableSt otherTable = obj as TableSt; 83 | if (otherTable != null) 84 | { 85 | if (this == null || otherTable == null) return 1; 86 | if (this.distribution_policy != otherTable.distribution_policy) return 1; 87 | if (this.nonclusteredIndexes.CompareTo(otherTable.nonclusteredIndexes) == 1) return 1; 88 | if (this.clusteredcols.CompareTo(otherTable.clusteredcols) == 1) return 1; 89 | if (this.statistics.Count != otherTable.statistics.Count) return 1; 90 | if (this.statistics.CompareTo(otherTable.statistics) == 1) return 1; 91 | if (this.Columns.Count != otherTable.Columns.Count) return 1; 92 | this.Columns.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 93 | otherTable.Columns.Sort((a, b) => a.name.ToUpper().CompareTo(b.name)); 94 | for (int i = 0; i < this.Columns.Count; i++) 95 | { 96 | if (this.Columns[i].column_id != otherTable.Columns[i].column_id) return 1; 97 | if (this.Columns[i].name != otherTable.Columns[i].name) return 1; 98 | if (this.Columns[i].type != otherTable.Columns[i].type) return 1; 99 | if (this.Columns[i].max_length != otherTable.Columns[i].max_length) return 1; 100 | if (this.Columns[i].precision != otherTable.Columns[i].precision) return 1; 101 | if (this.Columns[i].scale != otherTable.Columns[i].scale) return 1; 102 | if (this.Columns[i].is_nullable != otherTable.Columns[i].is_nullable) return 1; 103 | if (this.Columns[i].distrbution_ordinal != otherTable.Columns[i].distrbution_ordinal) return 1; 104 | if (this.Columns[i].collation_name != otherTable.Columns[i].collation_name) return 1; 105 | if (this.Columns[i].defaultconstraint != otherTable.Columns[i].defaultconstraint) return 1; 106 | } 107 | 108 | } 109 | 110 | return 0; 111 | } 112 | 113 | public NonclusteredIndexDef GetIndex(string IndexName) 114 | { 115 | return this.nonclusteredIndexes.Find(delegate (NonclusteredIndexDef e) { return e.name == IndexName; }); 116 | } 117 | public string GetDistributionColumn() 118 | { 119 | return (this.Columns.Find(delegate (ColumnDef e) { return e.distrbution_ordinal == 1; })).name; 120 | } 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/PDWScripter/helper.cs: -------------------------------------------------------------------------------- 1 | // Copyright (c) Microsoft Corporation. All rights reserved. 2 | // Licensed under the MIT License. 3 | 4 | using System; 5 | using System.Collections.Generic; 6 | using System.Linq; 7 | using System.Text; 8 | using System.Threading.Tasks; 9 | using Newtonsoft.Json; 10 | using System.Data; 11 | 12 | namespace DWScripter 13 | { 14 | public class Helper 15 | { 16 | public static string ConvertToJson(TableSt obj) 17 | { 18 | return JsonConvert.SerializeObject(obj, Formatting.Indented); 19 | } 20 | } 21 | } 22 | --------------------------------------------------------------------------------