bin/SnsMsSqlPsModule.xml
<?xml version="1.0"?>
<doc> <assembly> <name>SnsMsSqlPsModule</name> </assembly> <members> <member name="T:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert"> <summary> <para type="synopsis"> This Cmdlet Imports Collection Of Objects Into Specified Table Within Specified DataBase Into Specified MS SQL Server And Instance. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> This Cmdlet Imports Collection Of Objects Into Specified Table Within Specified DataBase Into Specified MS SQL Server And Instance. </para> <para type="description"> This CmdLet Does Allow Users Without Or With Limited Knowledge About SQL Query Language To Work With DataBases. The CmdLet Creates The SQL Query And The SQL Parameters On Its Own. The User Has No Need Even To Know What SQL Injection Is And How To Avoid It. </para> <para type="description"> There Are Cases When We Have Large Collections Of Similar Objects That Corresponds Exactly To Specific Table In A DataBase And We Need To Insert Them At Once Taking The Advantage Of The SQL Transactions. This CmdLet Is Made Exactly For Those Cases. Like For Example We Need To Insert The Information About All Users In The Environment Into A DataBase Table Named Users. Then We Simply Query The Active Directory About All Users And Pipeline The Output To This CmdLet. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> The CmdLet Have Three Parameter Sets Which Represent The Allowed Combination Of Input Parameters Based On The Way That The CmdLet Connects To The Destination DataBase. </para> <para type="description"> The CmdLet Support Only Authenticated Connection The MS SQL Server Instance. There Are Three Possible Ways This To Be Achieved: </para> <para type="description"> - Using The Windows Authentication - The CmdLet Uses The Windows Single Sign On To Connect To The DataBase Server Instance As The Currently Logged On User. It Is Preferable For Scripts Executed In Interactive Mode Which Eliminates The Need OF Additional Logon Steps. Whenever The Script Or The Automation Are Executed Unattended On Schedule As A Service This Might Lead To Unexpected Behavior. In Those Cases Is Preferable To Be Used Some Of The Next Authentication Options. Using This Option Can Be Authenticated Only Domain Or Windows Local Accounts. </para> <para type="description"> </para> <para type="description"> - Via Specifying UserName And Password In Clear Text - In This Way, The UserName And The Password Are Included In The SQL Connection String, That Way Send Over The Network. In This Way, The User Relies Only On The Encryption Of The SQL Connection. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Either Domain Or Windows Local Accounts Or SQL Users. </para> <para type="description"> - Via Specifying [System.Management.Automation.PSCredential] Object. In This Way, The Password Is Not Kept In Clear Text. This Is Considered As More Secure. The Authentication Happens In The Same Way As The Windows Authenticates The Users. Therefore The Password Cannot Be Intercepted During The Authentication Even If The SQL Connection Encryption Is Already Breached. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Only SQL Users. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> In The Begin Method The CmdLet Initializes A Connection To The Destination MS SQL DataBase And Queries It About The System Time. Thus Way Verify The Connection. </para> <para type="description"> Then CmdLet Queries The Destination Database About The Schema Of The Destination Table. Using The Schema And The Value Specified In "ConflictClause" Parameter, The CmdLet Creates Automatically The SQL Query Which Will Be Used For Inserting Of The Specified Objects. The SQL Query And The SQL Parameters Which Will Be Used In The SQL Query Are Enumerated Solley On The Table Schema And The Input Objects Are Not Evaluated At All. This Would Means That If The Input Objects Have Property With Name That Corresponds To A Column With The Same Name In The Destination Table, Then The Value Of That Property Will Be Inserted In The Corresponding Cell On The Corresponding Row Of The Table. If The Object Have No Property That Corresponds To Certain Column Will Be Inserted DB NULL Value. If The Object Contains Properties Which Have No Corresponding Columns The Values Of Those Properties Will Be Ignored. In Human Language Using The Example Above About The Users Table, Would Mean That We Can Pipeline All The AD Accounts To The CmdLet And Only The AD Attributes With Columns In The Destination Table Schema Will Be Inserted The Rest AD Attributes Will Be Ignored. The Columns That Do Not Correspond To Any AD Attribute Will Have Value DB NULL. </para> <para type="description"> Whenever The Destination Table Have An Autoincrement Identity Column, The Value Taken From The Input Objects Will Be Not Included In The INSERT And / Or UPDATE SQL Queries. </para> <para type="description"> The CmdLet Enumerates The Table Primary Key From The Table Schema. Whenever The Primary Key Column Is Enumerated Properly The CmdLet Uses Different SQL Queries Depending On The Value Specified In ConflictClause CmdLet Parameter. This Does Allow Any Possible Conflicts And Primary Key Uniqueness Violations To Be Managed By The CmdLet And Insert The Specified Objects Without Errors. Please Keep In Mind That The CmdLet Is Intended To Be Used For Bulk Uploads As Fast As Possible. Therefore It Uses A Single Transaction To Insert All Of The Objects. Therefore In Case An Error Is Thrown All The Inserted Within The Transaction Data Will Be Reverted. It Is Not Possible To Preserve The Data Inserted Before The Error. If That Is Needed Please Consider Either Splitting The Data On Batches, Or Use Invoke-SnsMsSqlQuery In A Way So Every Insert To Be Different Transaction. The ConflictClause Parameter Accepts Only Predefined Values. For More Details Please Check The Parameter Section Bellow. It Is Important To Avoid Using The Options For Error Handling Whenever The Destination Table Have Autoincrement Column. This Increases The Complexity And You Might Receive Unexpected Results. Avoiding The Usage Of This Parameter When The Destination Table Have Autoincrement Columns Is Only A Recommendation, Programmatically The Usage Of That Parameter Is Not Restricted So The Users That Can Manage The Additional Complexity To Be Able To Take Advantage Of That. In Case The Destination Table Have No Primary Key Or The CmdLet Fail To Enumerate It, The Values Specified Within The ConflictClause Parameter Is Ignored And The CmdLet Process The Data With "Fail" Option. If That Happens The User Is Informed With A Warning. </para> <para type="description"> Using The Enumerated Table Schema And The ConflictClause, The CmdLet Generates The SQL Query That Will Be Used For Insert / Update The Data And The SQL Parameters. In Order To Eliminate The Possibility About SQL Injection, The CmdLet Manages The Data Using SQL Parameters. Having The Table Schema Already Enumerated, The CmdLet Uses Parameters Add Method To Initialize The Parameters With Explicitly Specified Type And Length. This Improves The CmdLet Performance, Since There Is No Need OBS To Evaluate The Specified Data. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> During The Processing Of The Objects Coming From The Pipeline, Each Object Is Converted To SQL Parameters And Inserted Using The Already Enumerated SQL Query. For The Conversion Are Used The Column Names From The Destination Table Schema. In Case The Input Object Have No Property That Corresponds To The Table Column The Enumerated Parameter Have Value DBNULL. The Input Object Properties That Have No Corresponding Columns In The Destination Table Are Ignored. Column Name Matching Against The Input Object Property Name Is Case Sensitive Exact Matching. Whenever The Value Of A Property Have Type That Does Not Match The Destination Column DataType, The SQL Server Will Try To Cast The Value. If Cast Is Not Possible SQL Error Is Thrown And The Whole Transaction Is Reverted. </para> <para type="description"> During The Insert The Values In Properties From The Input Objects That Correspond To Columns In The Destination Table Will Be Inserted In The Corresponding Columns. The Values In Input Object Properties That Do Not Correspond To A Column In The Destination Table Will Be Ignored. The Columns In The Destination Table With No Matching Properties In The Input Objects Will Have Value NULL. </para> <para type="description"> When All Input Objects Are Processed And There Are No Errors The Transaction Is Committed. During The Processing, The Destination Table Is Locked. At The End The CmdLet Reverts In The Output PowerShell Stream An Integer That Indicates The Number Of Affected Rows.</para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <list type="alertSet"> <item> <term> </term> <description> <para>AUTHOR: Svetoslav Nedyalkov Savov</para> <para>THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK</para> <para>OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.</para> <para></para> <para></para> <para></para> </description> </item> </list> <example> <code> [System.Int32]$intRows = Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" ` -Table "AdAccounts" -InputObject $arrAccounts -ConflictClause "Update" -UserName "John.Smith" -Password "Pa$$w0rd"; </code> <para> </para> <para> Converts The Specified InputObjects Collection To SQL Parameters And Insert The Data Into "AdAccounts" Table. In Case The Table Already Contains Data The Existing Entries Will Be Updated. </para> <para> </para> <para> The CmdLet Connects To The Specified DataBase And Instance Using UserName And Password Specified In Clear Text. Of Course The Example Values Above Are Fake. </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> $objCred = Get-Credential; [System.Int32]$intRows = $arrAccounts | Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" ` -DatabaseName "TestDB" -Table "AdAccounts" -ConflictClause "Ignore" -Credentials $objCred; </code> <para> </para> <para> Converts The Pipelined InputObjects Collection To SQL Parameters And Insert The Data Into AdAccounts Table. In Case There Are Entries That Conflicts With Existing Table Entries The CmdLet Will Ignore The Specific Objects Coming From The Pipeline And Will Complete The Transaction If There Are No Other SQL Errors. </para> <para> </para> <para> The CmdLet Connects To The Specified DataBase And Instance Using The Credential Object Generated On The First Line. </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> [System.Int32]$intRows = $arrAccounts | Invoke-SnsMsSqlObjectInsert -Computer "DB01.contoso.com\Test" ` -DatabaseName "TestDB" -Table "AdAccounts" -ConflictClause "Ignore" -UseCurrentLogOnSession; </code> <para> </para> <para> In This Example The CmdLet Connects To The Specified DataBase And Instance Using The Currently Logged On User Security Context. From DataBase Perspective On This Example Is Done Exactly The Same As On The Previous Example. </para> <para> </para> <para> </para> <para> </para> </example> </summary> <para type="link" uri="https://github.com/svesavov/SnsMsSqlPsModule"> svesavov / SnsMsSqlPsModule - </para> <para type="link" uri="https://www.powershellgallery.com/packages/SnsMsSqlPsModule/"> PowerShell Gallery - </para> <para type="link" uri="https://www.linkedin.com/in/svetoslavsavov"> Svetoslav Savov on LinkedIn - </para> <para type="link" uri="https://www.microsoft.com/en-us/sql-server/sql-server-downloads"> MS SQL Server Download - </para> <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql"> MS SQL Data Types - </para> <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/language-reference"> MS SQL Supported SQL Syntax - </para> <para type="link" uri="https://www.sqlservertutorial.net/"> MS SQL Tutorials - </para> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Computer"> <summary> <para type="description">Specifies A MS SQL Server And MS SQL Instance In Format:</para> <para type="description">"DataBase Server"</para> <para type="description">"DataBase Server"\"DataBase Instance"</para> <para type="description">"DataBase Server","Port Number"</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.DatabaseName"> <summary> <para type="description">Specifies A DataBase Name</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.UserName"> <summary> <para type="description">Specifies A UserName In Clear Text</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Password"> <summary> <para type="description">Specifies A [System.Security.SecureString] Or [System.String] Password To Use In The MS SQL Connection.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Credentials"> <summary> <para type="description">Specifies A [System.Management.Automation.PSCredential] Object</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.UseCurrentLogOnSession"> <summary> <para type="description">Specifies That Current User LogOnSession Will Be Used For The SQL Connection</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.Table"> <summary> <para type="description">Specifies A Table Where The Data Will Be Inserted.</para> <para type="description">The Table Must Exist Inside The Specified DataBase, The CmdLet Won't Create It.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.InputObject"> <summary> <para type="description">Specifies A Collection Of Input Objects To Be Inserted Into The DataBase Table.</para> <para type="description">Input Object Property Values Must Be Struct Or String.</para> <para type="description">Input Object Property Values Must Not Be Collections Or Other Objects.</para> <para type="description">Input Object Property Names Must Meet The Syntax Requirements For SQL Variables.</para> <para type="description">Input Object Property Names Must Match The Destination Table Column Names.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.ConflictClause"> <summary> <para type="description">Specifies A Conflict Clause To Use In Case A Conflict Occurs During Insert.</para> <para type="description"></para> <para type="description">Accepted Values: "Fail", "Ignore" And "Update". Default Value "Fail".</para> <para type="description"></para> <para type="description">When The Destination Table Have Autoincrement Column, Use The Default Value To Avoid Unexpected Results.</para> <para type="description"></para> <para type="description"> Using This Parameter Allows The CmdLet To Finish The Insert Without Errors Related With Primary Key Uniqueness Violation. This Is Possible With Additional Verifications. </para> <para type="description"></para> <para type="description"> Whenever The Destination Table Have No Primary Key Column The CmdLet Will Ignore The Value Specified In The Parameter And Will Use "Fail" Option. The User Will Be Notified With Warning. </para> <para type="description"> The Uniqueness Of The Inserted Entries Is Not Verified On The Properties / Columns Which Are Not Primary Key. Whenever The Destination Table Have Unique Constraints On Not Primary Key Columns And That Uniqueness Is Violated, The CmdLet Reverts SQL Error And The Destination Table Is Reverted Back To Its Original State. </para> <para type="description"></para> <para type="description"></para> <para type="description"></para> <para type="description"> "Fail" - This Is The Default Value. The CmdLet Does Not Make Any Additional Verifications. In Case Primary Key Uniqueness Violation Occur The CmdLet Will Revert SQL Error. The Destination Table Is Reverted To Its State Prior The CmdLet Execution. </para> <para type="description"> "Ignore" - The CmdLet Verifies Whether An Entry With The Value In The Primary Key Column Does Not Exists. Then It Inserts The New Entry. In Case The Entry Already Exists It Remain Intact And, New Entry Is Inserted. Whenever That Option Is Used The Generated SQL Query Will Look Like, Assuming "Col1" Is Primary Key: </para> <para type="description">IF NOT EXISTS</para> <para type="description">(SELECT [Col1] FROM [<Table>] WHERE [Col1] = @Var1)</para> <para type="description">BEGIN</para> <para type="description">INSERT INTO [<Table>]</para> <para type="description">([Col1], [Col2], ... [ColX])</para> <para type="description">VALUES (@Var1, @Var2, ... @VarX)</para> <para type="description">END</para> <para type="description"></para> <para type="description"> "Update" - The CmdLet Verifies Whether An Entry With The Value In The Primary Key Column Exists. Then Either Inserts A New Entry Or Updates The Existing Entry. Whenever That Option Is Used The Generated SQL Query Will Look Like, Assuming "Col1" Is Primary Key: </para> <para type="description"></para> <para type="description">MERGE INTO [<Table>] AS [target]</para> <para type="description">USING (SELECT @Var1 AS [Col1], @Var2 AS [Col2] ... @VarX AS [ColX]) AS [source]</para> <para type="description">ON [source].[Col1] = [target].[Col1]</para> <para type="description">WHEN MATCHED THEN</para> <para type="description">UPDATE SET [Col2] = [source].[Col2] ... [ColX] = [source].[ColX]</para> <para type="description">WHEN NOT MATCHED THEN</para> <para type="description">INSERT ([Col1], [Col2], ... [ColX])</para> <para type="description">VALUES ([source].[Col1], [source].[Col2], ... [source].[ColX]);</para> <para type="description"></para> <para type="description"></para> <para type="description"></para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlObjectInsert.QueryTimeout"> <summary> <para type="description">Sets The Wait Time In Seconds Before Terminating The Attempt To Execute A Command And Generating An Error</para> <para type="description">Use 0 For The Default TimeOut Value In .NET And MS SQL Server</para> </summary> </member> <member name="T:SnsMsSqlPsModule.InvokeSnsMsSqlQuery"> <summary> <para type="synopsis"> This Cmdlet Connects To A Specified MS Transactional SQL DataBase, Executes SQL Query / Queries Against The DataBase, Reverts The Output In A Collection Of Specified DataType And Closes The SQL Session. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> This Cmdlet Connects To A Specified MS Transactional SQL DataBase, Executes SQL Query / Queries Against The DataBase, Reverts The Output In A Collection Of Specified DataType And Closes The SQL Session. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> The CmdLet Is Based On My Previous Work Related With SQLite Serverless DataBase: </para> <para type="description"> https://github.com/svesavov/SnsSqlitePsModule </para> <para type="description"> https://www.powershellgallery.com/packages/SnsSqlitePsModule </para> <para type="description"> Working With SQLite Is Nice And Free, It Is Good Tool For Learning SQL. Also It Is Fine To Be Used Within Automations And Scripts To Keep Temporary Data Or Configuration Data (If The Machine That Hosts The Automation Have Fast Hard Disks), However Where It Comes To Large Volumes Of Data Accessed By Different Automations Or Scripts Arises The Need Of Some More Powerful Server / Service Based DataBase Which Is Optimized For Large Volumes Of Data, And Can Handle The DataBase Locks Better. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> The CmdLet Have Six Parameter Sets Which Represent The Allowed Combination Of Input Parameters. </para> <para type="description"> Like The Original CmdLet The Input Is Validated At The Beginning Within The Parameter Definitions. For That Purpose, Additional Validation Attributes Were Developed, Because "[ValidateScript({})]" Validation Attribute Is Not Supported In C#. </para> <para type="description"> The CmdLet Connects To The Specified MS SQL Server Instance And Specified DataBase By Itself. There Is No Requirement SqlConnection To Be Established In Advance. In The Begin Method The CmdLet Evaluate The Provided Input, Imports Any .SQL Files If Such Are Provided With A Parameter, Connects To The MS SQL DataBase And Verifies The Connection Via Running A SQL Query That Reverts The DataBase System Time. </para> <para type="description"> Whenever The SQL Query Which Needs To Be Run Against The DataBase Is Too Complex Can Be Used SQL Query Input File. The CmdLet Does Allow Usage Of SQL Query File With Standard ".sql" Extension With Specifying Of SQL Parameters As HashTable, However It Does Not Support Usage Of Multiple ".sql" Files At Once. It Still Can Be Used To Import Single SQL Query And Using Of Collection Of SQL Parameter Dictionaries To Insert Multiple Rows At Once. The ".sql" File Read Happens In Begin Method, Which Exclude The Possibility To Execute Multiple SQL Queries At Once. I Don't Want File Operations In Process Method Because Of Performance Considerations. </para> <para type="description"> Usage Of ".sql" Files To Keep The SQL Queries Out Of The Main Code Might Sound Like Heresy For The Normal Developers, Because The Queries Are Kept There In Clear Text And Can Be Modified By The User, Not To Forget About Possible Leak Of Proprietary Information. However This Is A PowerShell Module Which Means That It Will Be Used In Scripts And Automation On ".ps1" Files Which Are Clear Text As Well And The Users Have Access To The Code Inside. Therefore We Might Use The Opportunity To Have The Complex SQL Queries On Separate Files. Which Leads To The Conveniency Of Not Modifying Them Whenever The Next Version Is Released Or Modify Only Them Whenever Something On The DataBase Is Changed. </para> <para type="description"> Because The Connection Is Established In The Begin Method It Is Done Once And All The Specified Queries Are Sent At Once In The Process Method Which Gain Huge Performance Boost. </para> <para type="description"> To Boost The Performance Even Further, The CmdLet Will Execute All The Specified SQL Queries Or Single Query With Multiple Parameters Using Single SQL Transaction. The Using Of Transactions Happens After Evaluation Of The Specified SQL Query. Whenever The Query Contains The Keyword "COMMIT" Would Mean That The Transactions Are Managed Within The Query And The Transactions Functionality Of The CmdLet Is Always Disabled. In Order The CmdLet To Use The Transaction Functionality The SQL Query Must Contains Either "INSERT" Or "UPDATE" Keywords, And The Transactions Must Not Be Managed Within The Query Itself. It Is Always Preferable The User To Manage The Transactions Within The SQL Query, In That Way Not Leaving Any Chance Of The CmdLet Making Wrong Decisions. </para> <para type="description"> The CmdLet Support Only Authenticated Connection The MS SQL Server Instance. There Are Three Possible Ways This To Be Achieved: </para> <para type="description"> - Using The Windows Authentication - The CmdLet Uses The Windows Single Sign On To Connect To The DataBase Server Instance As The Currently Logged On User. It Is Preferable For Scripts Executed In Interactive Mode Which Eliminates The Need Of Additional Logon Steps. Whenever The Script Or The Automation Are Executed Unattended On Schedule As A Service This Might Lead To Unexpected Behavior. In Those Cases Is Preferable To Be Used Some Of The Next Authentication Options. Using This Option Can Be Authenticated Only Domain Or Windows Local Accounts. </para> <para type="description"> </para> <para type="description"> - Via Specifying UserName And Password In Clear Text - In This Way, The UserName And The Password Are Included In The SQL Connection String, That Way Send Over The Network. In This Way, The User Relies Only On The Encryption Of The SQL Connection. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Either Domain Or Windows Local Accounts Or SQL Users. </para> <para type="description"> - Via Specifying [System.Management.Automation.PSCredential] Object. In This Way, The Password Is Not Kept In Clear Text. This Is Considered As More Secure. The Authentication Happens In The Same Way As The Windows Authenticates The Users. Therefore The Password Cannot Be Intercepted During The Authentication Even If The SQL Connection Encryption Is Already Breached. Using This Method Impersonation Of The User That Connects To The DataBase Is Possible. Which Does Allow The Automations To Be Run With One Service Account And The DataBase Connection To Be Established With Different Service Account. Using This Option Can Be Authenticated Only SQL Users. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> The CmdLet Parameters And Pipeline Were Designed, Based On The Experience Gained On My Previous Work Related With SQLite And The Purpose Of This Module, Preserving The Parameter Names And Aliases And The Pipeline Functionalities Whenever It Is Possible. Design Decisions Related With The Pipeline Are Always Based On Compromises. Having In Mind The Usage Of The SQLite Module It Makes More Sense To Design The Pipeline In A Way, To Allow Running Multiple Queries Or Single Query With Multiple Sets Of SQL Parameters Against A Single DataBase, Rather Than Running Single Query With Single SQL Parameter Set Against Multiple DataBases. If I Ever Need High Availability On My Automations Would Prefer To Schedule Them To Run On A Virtual Machine Which Is A Resource On ESX Or Other Virtualization Platform Which Allows The Virtual Machine To Be Replicated And Powered On Different Hosts, Rather Than Writing A Script Capable To Jump From One Machine To Another And Maintaining The Same Information In Multiple DataBases. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> With The Current Parameters And Pipeline Design The CmdLet Can: </para> <para type="description"> </para> <para type="description"> - Single SQL Query Without Any SQL Parameters. The SQL Query Can Be Provided Directly To The "Query" Parameter Or As Object Property Matching The Parameter Name Or Parameter Aliases Of An Object Coming From The Pipeline. Although This Is The Easiest Way Of Working With DataBases It Is Not Recommended Because Of SQL Injection High Risk Unless Your SQL Queries Have No Users Input. Even In That Case There Is Still Possibility Of SQL Injection. For Example If You Use Dynamic Enumeration Of Objects Which Have To Be Inserted Into A DataBase And The Name Or Property Of A Dynamically Enumerated Object Have Value That Makes SQL Injection. Let’s Give A Specific Example If You Insert In A Table “ACCOUNTS” The Dynamically Enumerated Via LDAP Query Active Directory Accounts And Someone Created An Account With Name "Robert'); DROP TABLE [ACCOUNTS];--". </para> <para type="description"> - Single SQL Query And Single SQL Parameters HashTable, Provided Directly To The Corresponding Parameters Or Via The Pipeline. Either The SQL Query And The SQL Parameters HashTable Provided As Properties Of An Object Coming From The Pipeline, Or The SQL Parameters HashTable Coming From The Pipeline And The SQL Query Specified In "Query" Parameter. Whenever SQL Parameters Are Used The Possibility Of SQL Injection Is Completely Eliminated. </para> <para type="description"> - Single SQL Query And Collection Of SQL Parameters HashTables. In This Case The Parameters Represents Multiple Rows Which Have To Be Inserted Within Single Table. The User Have To Keep In Mind That The SQL Query Must Have SQL Variables For Each Of The Columns And Each HashTable In The Collections Have To Have Keys Corresponding To Each Of The SQL Variables. The SQL Query And Its Corresponding HashTables Collection Can Be Specified Either Directly To The Corresponding Parameters, Or As Properties Of An Object Coming From Pipeline, Or Specifying The Query To The CmdLet "Query" Parameter And The HashTables Collection Coming From The Pipeline. </para> <para type="description"> The CmdLet Is Still Capable To Work With Collection Of SQL Queries, For That Purpose The Queries And Their Corresponding SQL Parameters HashTable / HashTables If Any, Can Be Provided Only Via Object Properties Of Objects Collection Coming From The Pipeline. Single Object Coming From The Pipeline Cannot Have Multiple SQL Queries. </para> <para type="description"> The CmdLet Will Not Perform Any Normalization Of The Data Provided In The SQL Parameters Or Directly In The SQL Queries. It Is User Responsibility To Normalize The Data In The Way Which The MS SQL Server To Understand It. For Example All The Dates Must Be Present To The SQL Server As Strings Which Corresponds To The Column Data Type. Sending [System.DateTime] Object To The SQL Server Might Lead To Unexpected Behavior Or Errors. Even Though The Most Appropriate Way To Work With Dates Is To Convert Them To Long And Keep Them In Columns With Data Type BIGINT, But That Is A Personal Choice Simplifying The Developers Life. </para> <para type="description"> </para> <para type="description"> </para> <para type="description"> </para> <list type="alertSet"> <item> <term> </term> <description> <para>AUTHOR: Svetoslav Nedyalkov Savov</para> <para>THIS CODE IS MADE AVAILABLE AS IS, WITHOUT WARRANTY OF ANY KIND. THE ENTIRE RISK</para> <para>OF THE USE OR THE RESULTS FROM THE USE OF THIS CODE REMAINS WITH THE USER.</para> <para></para> <para></para> <para></para> </description> </item> </list> <example> <code> $strQry = "SELECT [name], [create_date], [collation_name], [state_desc] FROM [sys].[databases]"; Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DataBase "TestDB" -Query "$($strQry)" ` -UseCurrentLogOnSession -As "PSCustomObject"; </code> <para> </para> <para> </para> <para> </para> <para> Initializes A SQL Query String Variable. </para> <para> Runs Single Query Against The MS SQL Server Instance About A Collection With The DataBases In It, Using The Currently Logged On User Security Context. </para> <para> The DataBase Parameter Is Required Because Of The Initial Catalog Property Of The SqlConnection String And Cannot Be Omitted. The "sys" Value Cannot Be Used. </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> $objCred = Get-Credential; Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -Credentials $objCred ` -As "PSCustomObject"; </code> <para> </para> <para> </para> <para> </para> <para> Asks The User To Specify Credentials And Store Them In Credential Object Variable. </para> <para> Runs Single Query Against The MS SQL Server Instance About A Collection With The Tables Within The Specified DataBase, Using PSCredential Object Created In The First Line. </para> <para> Because The Default Value Of "Query" Parameter Is The Query Needed To List The Tables, The Parameter Is Omitted. </para> <para> The Default Value In Query Parameter Contains <DataBaseName> Keyword, Which Indicates To The CmdLet To Replace It With The Value Specified In "DatabaseName" Parameter. </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> $strQry = "SELECT * FROM [<DataBaseName>].[INFORMATION_SCHEMA].[COLUMNS] WHERE "; $strQry += "[TABLE_CATALOG] = '<DataBaseName>' AND [TABLE_SCHEMA] = 'dbo' AND [TABLE_NAME] = @Table"; @{ "Table" = "TestTable"; } | Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" ` -Query "$($strQry)" -UserName "John.Smith" -Password "Pa$$w0rd" -As "PSCustomObject"; </code> <para> </para> <para> </para> <para> </para> <para> Initializes A SQL Query String Variable, About Select All Columns From Specified Table Within The Specified Database. </para> <para> The SQL Query Contains <DataBaseName> Keyword, Which Indicates To The CmdLet To Replace It With The Value Specified In "DatabaseName" Parameter. </para> <para> Runs A Single Query Specified In The Previous Line, Against The Specified MS SQL Server Instance, Using UserName And Password Specified In Clear Text. Of Course The UserName And The Password In The Example Are Fake. </para> <para> The Table Is Specified As SQL Parameter Sent In A Form Of HashTable Via Pipeline Using "ValueFromPipeline". </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> $strQry = "INSERT INTO [<DataBaseName>].[dbo].[Events] ([ID], [Message], [Severity], [Date])"; $strQry += " VALUES (@ID, @Message, @Severity, @Date)"; Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession ` -Query "$($strQry)" -SqlParameters ` @{ "@ID" = 1; "@Message" = "Some Dummy Message"; "Severity" = "Error"; "Date" = [System.DateTime]::Now.ToFileTime(); }; </code> <para> </para> <para> </para> <para> </para> <para> Initializes "INSERT" Query Against The Specified Table In The Specified DataBase. </para> <para> Runs Single Query Against The Specified DataBase In The Specified MS SQL Server Instance, Using The Currently Logged On User Security Context. </para> <para> In The SQL Parameters Keys Are Used Both Strings With And Without At (@) Character. The Keys Match Exactly The SQL Query Variables. Please Note That There Are SQL Variables For Each Of The Columns. </para> <para> </para> <para> The "DateTime" Is Converted To "Long" And Inserted In A Column With Data Type "BIGINT". Using Long Integer To Store The Dates Gives As Certain Benefits, Like No Need To Keep Track Of The Time Zones, No Need To Keep Track Of The Daylight Saving Start And End Date And Whether It Is Applied To The Date. No Dependency From The Culture Set On The Computer Which Is Running The CmdLet And The Culture Set On The SQL Server. Additionally There Is No Lost In The Value Precision And No Rounding's. Reverse From "Long" To "DateTime" Object Is Just As Easy As The Conversion From "DateTime" To "Long". </para> <para> [System.DateTime]::FromFileTime(108666144000000000) - Converts The Long Integer To Local DateTime With Time Zone And Daylight Saving Taken Into Consideration. </para> <para> [System.DateTime]::FromFileTimeUtc(108666144000000000) - Converts The Long Integer To Universal DateTime. (Guess What Is The Date From The Example). </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession ` -Query "INSERT INTO [<DataBaseName>].[dbo].[Computers] ([ID], [HostName]) VALUES (@ID, @HostName)" ` -SqlParameters ` @( @{"@ID" = 1; "HostName" = "Computer1.contoso.com"; }; @{"@ID" = 2; "HostName" = "Computer2.contoso.com"; }; ); </code> <para> </para> <para> </para> <para> </para> <para> Runs Single "INSERT" Query Against The Specified Table In The Specified DataBase In The Specified MS SQL Server Instance With Multiple SQL Parameters HashTables Using The Currently Logged On User Security Context. Because There Are Two HashTables In The SQL Parameters Collection This Creates Two Rows / Entries In The Destination Table. This Is Very Useful Whenever Large Amount Of Data Have To Be Inserted Into The DataBase. </para> <para> In The SQL Parameters Keys Are Used Both Strings With And Without At (@) Character. The Keys Match Exactly The SQL Query Variables. Please Note That There Are SQL Variables For Each Of The Columns. </para> <para> </para> <para> </para> <para> </para> </example> <example> <code> [System.Object[]]$arrInput = @(); [System.Object]$objObject = New-Object -TypeName "System.Object"; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "ID" -Value 1; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Event" -Value "Warning"; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Date" -Value([System.DateTime]::Now.ToFileTime()); [System.Object[]]$arrInput += $objObject; [System.Object]$objObject = New-Object -TypeName "System.Object"; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "ID" -Value 2; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Event" -Value "Error"; $objObject | Add-Member -Force -MemberType "NoteProperty" -Name "Date" -Value([System.DateTime]::Now.ToFileTime()); [System.Object[]]$arrInput += $objObject; [SnsMsSqlPsModule.PsObjectToHashTbl]::ToHashTbl($($arrInput | Select-Object *)) | ` Invoke-SnsMsSqlQuery -Computer "DB01.contoso.com\Test" -DatabaseName "TestDB" -UseCurrentLogOnSession ` -Query "INSERT INTO [<DataBaseName>].[dbo].[Events] ([ID], [Event], [Date]) VALUES (@ID, @Event, @Date)"; </code> <para> </para> <para> </para> <para> </para> <para> Initializes A Collection With Custom Objects Which Are Not As Easily Converted To HashTables As The "PSCustomObject". </para> <para> Converts The Custom Objects From The Previous Line To HashTable Collection And Send It To The Pipeline. For That Purpose We Are Using A .NET Method Made Publicly Available Which Is Present In The Current PowerShell Module. </para> <para> Runs Single "INSERT" Query Against The Specified Table In The Specified DataBase In The Specified MS SQL Server Instance With Multiple SQL Parameters HashTables Coming From The Pipeline Using The Currently Logged On User Security Context. Because There Are Two HashTables In The SQL Parameters Collection This Creates Two Rows / Entries In The Destination Table. </para> <para> This Is Useful Whenever We Need To Perform Bulk Inserts Or Updates In A Single Transaction Of Large Amount Of Data, Coming On A Collection Of All Kinds Of Classes, Including Imported From A File Using Import-Csv CmdLet. The Example Uses A Collection With Just Two Objects For Readability. </para> <para> In Order The Bulk Operations To Work Smoothly And To Avoid Errors: </para> <para> - The Input Objects Property Names Must Match Exactly The SQL Variable Names In The SQL Query. </para> <para> - The Input Objects Property Value Types Must Match Exactly The Corresponding Columns Data Types. </para> <para> - Any Input Object Property Values Must Be Normalized In Advance. </para> <para> </para> <para> </para> <para> </para> </example> </summary> <para type="link" uri="https://github.com/svesavov/SnsMsSqlPsModule"> svesavov / SnsMsSqlPsModule - </para> <para type="link" uri="https://www.powershellgallery.com/packages/SnsMsSqlPsModule/"> PowerShell Gallery - </para> <para type="link" uri="https://www.linkedin.com/in/svetoslavsavov"> Svetoslav Savov on LinkedIn - </para> <para type="link" uri="https://www.microsoft.com/en-us/sql-server/sql-server-downloads"> MS SQL Server Download - </para> <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/data-types/data-types-transact-sql"> MS SQL Data Types - </para> <para type="link" uri="https://docs.microsoft.com/en-us/sql/t-sql/language-reference"> MS SQL Supported SQL Syntax - </para> <para type="link" uri="https://www.sqlservertutorial.net/"> MS SQL Tutorials - </para> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Computer"> <summary> <para type="description">Specifies A MS SQL Server And MS SQL Instance In Format:</para> <para type="description">"DataBase Server"</para> <para type="description">"DataBase Server"\"DataBase Instance"</para> <para type="description">"DataBase Server","Port Number"</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.DatabaseName"> <summary> <para type="description">Specifies A DataBase Name</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.UserName"> <summary> <para type="description">Specifies A UserName In Clear Text</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Password"> <summary> <para type="description">Specifies A [System.Security.SecureString] Or [System.String] Password To Use In The MS SQL Connection.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Credentials"> <summary> <para type="description">Specifies A [System.Management.Automation.PSCredential] Object</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.UseCurrentLogOnSession"> <summary> <para type="description">Specifies That Current User LogOnSession Will Be Used For The SQL Connection</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.Query"> <summary> <para type="description">Specifies The SQL Query To Be Run.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.SqlParameters"> <summary> <para type="description">HashTable Of Parameters For Parameterized SQL Queries.</para> <para type="description">The HashTable Keys Must Match The SQL Query Variable Names.</para> <para type="description">The Keys Might Be Specified Either With Or Without At (@) Character In Front.</para> <para type="description">The CmdLet Will Take Care To Add Them If Missing.</para> <para type="description">Example:</para> <para type="description">-Query "SELECT * FROM [db].[dbo].[tblServers] WHERE [ServerFqdn] LIKE @Server"</para> <para type="description">-SqlParameters @{"@Server" = "Server01.contoso.com"}</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.InputFile"> <summary> <para type="description">Specifies A ".sql" File To Be Used As Query Input.</para> <para type="description">Works Only With Full Absolute UNC Path To The File.</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.QueryTimeout"> <summary> <para type="description">Sets The Wait Time In Seconds Before Terminating The Attempt To Execute A Command And Generating An Error</para> <para type="description">Use 0 For The Default TimeOut Value In .NET And MS SQL Server</para> </summary> </member> <member name="P:SnsMsSqlPsModule.InvokeSnsMsSqlQuery.As"> <summary> <para type="description">Specifies To The CmdLet To Revert The Output In One Of The Following Types:</para> <para type="description">[System.Data.DataSet]</para> <para type="description">[System.Data.DataTable]</para> <para type="description">[System.Data.DataRow]</para> <para type="description">[System.Management.Automation.PSCustomObject]</para> </summary> </member> </members> </doc> |