externalLibs/SQLPSX/adoLib/adoLib.psm1
# --------------------------------------------------------------------------- ### <Script> ### <Author> ### Mike Shepard ### </Author> ### <Description> ### Defines functions for executing Ado.net queries ### </Description> ### <Usage> ### import-module adolib ### </Usage> ### </Script> # --------------------------------------------------------------------------- <# .SYNOPSIS Tests to see if a value is a SQL NULL or not .DESCRIPTION Returns $true if the value is a SQL NULL. .PARAMETER value The value to test .EXAMPLE PS C:\> Is-NULL $row.columnname .INPUTS None. You cannot pipe objects to New-Connection .OUTPUTS Boolean #> function Is-NULL{ param([Parameter(Position=0, Mandatory=$true)]$value) return [System.DBNull]::Value.Equals($value) } <# .SYNOPSIS Create a SQLConnection object with the given parameters .DESCRIPTION This function creates a SQLConnection object, using the parameters provided to construct the connection string. You may optionally provide the initial database, and SQL credentials (to use instead of NT Authentication). .PARAMETER Server The name of the SQL Server to connect to. To connect to a named instance, enclose the server name in quotes (e.g. "Laptop\SQLExpress") .PARAMETER Database The InitialDatabase for the connection. .PARAMETER User The SQLUser you wish to use for the connection (instead of using NT Authentication) .PARAMETER Password The password for the user specified by the User parameter. .EXAMPLE PS C:\> New-Connection -server MYSERVER -database master .EXAMPLE PS C:\> Get-Something -server MYSERVER -user sa -password sapassword .INPUTS None. You cannot pipe objects to New-Connection .OUTPUTS System.Data.SqlClient.SQLConnection #> function New-Connection{ param([Parameter(Position=0, Mandatory=$true)][string]$server, [Parameter(Position=1, Mandatory=$false)][string]$database='', [string]$user='', [string]$password='') if($database -ne ''){ $dbclause="Database=$database;" } $conn=new-object System.Data.SqlClient.SQLConnection if ($user -ne ''){ $conn.ConnectionString="Server=$server;$dbclause`User ID=$user;Password=$password;Pooling=false" } else { $conn.ConnectionString="Server=$server;$dbclause`Integrated Security=True" } $conn.Open() write-debug $conn.ConnectionString return $conn } function Get-Connection{ param([System.Data.SqlClient.SQLConnection]$conn, [string]$server, [string]$database, [string]$user, [string]$password) if (-not $conn){ if ($server){ $conn=New-Connection -server $server -database $database -user $user -password $password } else { throw "No connection or connection information supplied" } } return $conn } function Put-OutputParameters{ param([Parameter(Position=0, Mandatory=$true)][System.Data.SqlClient.SQLCommand]$cmd, [Parameter(Position=1, Mandatory=$false)][hashtable]$outparams) if ($outparams){ foreach($outp in $outparams.Keys){ $paramtype=get-paramtype $outparams[$outp] $p=$cmd.Parameters.Add("@$outp",$paramtype) $p.Direction=[System.Data.ParameterDirection]::Output if ($paramtype -like '*char*'){ $p.Size=[int]$outparams[$outp].Replace($paramtype.ToString().ToLower(),'').Replace('(','').Replace(')','') } } } } function Get-Outputparameters{ param([Parameter(Position=0, Mandatory=$true)][System.Data.SqlClient.SQLCommand]$cmd, [Parameter(Position=1, Mandatory=$true)][hashtable]$outparams) foreach($p in $cmd.Parameters){ if ($p.Direction -eq [System.Data.ParameterDirection]::Output){ $outparams[$p.ParameterName.Replace("@","")]=$p.Value } } } function Get-ParamType{ param([string]$typename) $type=switch -wildcard ($typename.ToLower()) { 'uniqueidentifier' {[System.Data.SqlDbType]::UniqueIdentifier} 'int' {[System.Data.SQLDbType]::Int} 'datetime' {[System.Data.SQLDbType]::Datetime} 'tinyint' {[System.Data.SQLDbType]::tinyInt} 'smallint' {[System.Data.SQLDbType]::smallInt} 'bigint' {[System.Data.SQLDbType]::BigInt} 'bit' {[System.Data.SQLDbType]::Bit} 'char*' {[System.Data.SQLDbType]::char} 'nchar*' {[System.Data.SQLDbType]::nchar} 'date' {[System.Data.SQLDbType]::date} 'datetime' {[System.Data.SQLDbType]::datetime} 'varchar*' {[System.Data.SqlDbType]::Varchar} 'nvarchar*' {[System.Data.SqlDbType]::nVarchar} default {[System.Data.SqlDbType]::Int} } return $type } function Copy-HashTable{ param([hashtable]$hash, [String[]]$include, [String[]]$exclude) if($include){ $newhash=@{} foreach ($key in $include){ if ($hash.ContainsKey($key)){ $newhash.Add($key,$hash[$key]) | Out-Null } } } else { $newhash=$hash.Clone() if ($exclude){ foreach ($key in $exclude){ if ($newhash.ContainsKey($key)) { $newhash.Remove($key) | Out-Null } } } } return $newhash } <# Helper function figure out what kind of returned object to build from the results of a sql call (ds). Options are: 1. Dataset (multiple lists of rows) 2. Datatable (list of datarows) 3. Nothing (no rows and no output variables 4. Dataset with output parameter dictionary 5. Datatable with output parameter dictionary 6. A dictionary of output parameters #> function Get-CommandResults{ param([Parameter(Position=0, Mandatory=$true)][System.Data.Dataset]$ds, [Parameter(Position=1, Mandatory=$true)][HashTable]$outparams) if ($ds.tables.count -eq 1){ $retval= $ds.Tables[0] } elseif ($ds.tables.count -eq 0){ $retval=$null } else { [system.Data.DataSet]$retval= $ds } if ($outparams.Count -gt 0){ if ($retval){ return @{Results=$retval; OutputParameters=$outparams} } else { return $outparams } } else { return $retval } } <# .SYNOPSIS Create a sql command object .DESCRIPTION This function uses the information contained in the parameters to create a sql command object. In general, you will want to use the invoke- functions directly, but if you need to manipulate a command object in ways that those functions don't allow, you will need this. Also, the invoke-bulkcopy function allows you to pass a command object instead of a set of records in order to "stream" the records into the destination in cases where there are a lot of records and you don't want to allocate memory to hold the entire result set. .PARAMETER sql The sql to be executed by the command object (although it is not executed by this function). .PARAMETER connection An existing connection to perform the sql statement with. .PARAMETER parameters A hashtable of input parameters to be supplied with the query. See example 2. .PARAMETER timeout The commandtimeout value (in seconds). The command will fail and be rolled back if it does not complete before the timeout occurs. .PARAMETER Server The server to connect to. If both Server and Connection are specified, Server is ignored. .PARAMETER Database The initial database for the connection. If both Database and Connection are specified, Database is ignored. .PARAMETER User The sql user to use for the connection. If both User and Connection are specified, User is ignored. .PARAMETER Password The password for the sql user named by the User parameter. .PARAMETER Transaction A transaction to execute the sql statement in. .EXAMPLE PS C:\> $cmd=new-sqlcommand "ALTER DATABASE AdventureWorks Modify Name = Northwind" -server MyServer PS C:\> $cmd.ExecuteNonQuery() .EXAMPLE PS C:\> $cmd=new-sqlcommand -server MyServer -sql "Select * from MyTable" PS C:\> invoke-sqlbulkcopy -records $cmd -server MyOtherServer -table CopyOfMyTable .INPUTS None. You cannot pipe objects to new-sqlcommand .OUTPUTS System.Data.SqlClient.SqlCommand #> function New-SQLCommand{ param([Parameter(Position=0, Mandatory=$true)][Alias('storedProcName')][string]$sql, [Parameter(ParameterSetName="SuppliedConnection",Position=1, Mandatory=$false)][System.Data.SqlClient.SQLConnection]$connection, [Parameter(Position=2, Mandatory=$false)][hashtable]$parameters=@{}, [Parameter(Position=3, Mandatory=$false)][int]$timeout=30, [Parameter(ParameterSetName="AdHocConnection",Position=4, Mandatory=$false)][string]$server, [Parameter(ParameterSetName="AdHocConnection",Position=5, Mandatory=$false)][string]$database, [Parameter(ParameterSetName="AdHocConnection",Position=6, Mandatory=$false)][string]$user, [Parameter(Position=7, Mandatory=$false)][string]$password, [Parameter(Position=8, Mandatory=$false)][System.Data.SqlClient.SqlTransaction]$transaction=$null, [Parameter(Position=9, Mandatory=$false)][hashtable]$outparameters=@{}) $dbconn=Get-Connection -conn $connection -server $server -database $database -user $user -password $password $close=($dbconn.State -eq [System.Data.ConnectionState]'Closed') if ($close) { $dbconn.Open() } $cmd=new-object system.Data.SqlClient.SqlCommand($sql,$dbconn) $cmd.CommandTimeout=$timeout foreach($p in $parameters.Keys){ $parm=$cmd.Parameters.AddWithValue("@$p",$parameters[$p]) if (Is-NULL $parameters[$p]){ $parm.Value=[DBNull]::Value } } put-outputparameters $cmd $outparameters if ($transaction -is [System.Data.SqlClient.SqlTransaction]){ $cmd.Transaction = $transaction } return $cmd } <# .SYNOPSIS Execute a sql statement, ignoring the result set. Returns the number of rows modified by the statement (or -1 if it was not a DML staement) .DESCRIPTION This function executes a sql statement, using the parameters provided and returns the number of rows modified by the statement. You may optionally provide a connection or sufficient information to create a connection, as well as input parameters, command timeout value, and a transaction to join. .PARAMETER sql The SQL Statement .PARAMETER connection An existing connection to perform the sql statement with. .PARAMETER parameters A hashtable of input parameters to be supplied with the query. See example 2. .PARAMETER timeout The commandtimeout value (in seconds). The command will fail and be rolled back if it does not complete before the timeout occurs. .PARAMETER Server The server to connect to. If both Server and Connection are specified, Server is ignored. .PARAMETER Database The initial database for the connection. If both Database and Connection are specified, Database is ignored. .PARAMETER User The sql user to use for the connection. If both User and Connection are specified, User is ignored. .PARAMETER Password The password for the sql user named by the User parameter. .PARAMETER Transaction A transaction to execute the sql statement in. .EXAMPLE PS C:\> invoke-sql "ALTER DATABASE AdventureWorks Modify Name = Northwind" -server MyServer .EXAMPLE PS C:\> $con=New-Connection MyServer PS C:\> invoke-sql "Update Table1 set Col1=null where TableID=@ID" -parameters @{ID=5} .INPUTS None. You cannot pipe objects to invoke-sql .OUTPUTS Integer #> function Invoke-Sql{ param([Parameter(Position=0, Mandatory=$true)][string]$sql, [Parameter(ParameterSetName="SuppliedConnection",Position=1, Mandatory=$false)][System.Data.SqlClient.SQLConnection]$connection, [Parameter(Position=2, Mandatory=$false)][hashtable]$parameters=@{}, [Parameter(Position=3, Mandatory=$false)][hashtable]$outparameters=@{}, [Parameter(Position=4, Mandatory=$false)][int]$timeout=30, [Parameter(ParameterSetName="AdHocConnection",Position=5, Mandatory=$false)][string]$server, [Parameter(ParameterSetName="AdHocConnection",Position=6, Mandatory=$false)][string]$database, [Parameter(ParameterSetName="AdHocConnection",Position=7, Mandatory=$false)][string]$user, [Parameter(ParameterSetName="AdHocConnection",Position=8, Mandatory=$false)][string]$password, [Parameter(Position=9, Mandatory=$false)][System.Data.SqlClient.SqlTransaction]$transaction=$null) $cmd=new-sqlcommand @PSBoundParameters #if it was an ad hoc connection, close it if ($server){ $cmd.connection.close() } return $cmd.ExecuteNonQuery() } <# .SYNOPSIS Execute a sql statement, returning the results of the query. .DESCRIPTION This function executes a sql statement, using the parameters provided (both input and output) and returns the results of the query. You may optionally provide a connection or sufficient information to create a connection, as well as input and output parameters, command timeout value, and a transaction to join. .PARAMETER sql The SQL Statement .PARAMETER connection An existing connection to perform the sql statement with. .PARAMETER parameters A hashtable of input parameters to be supplied with the query. See example 2. .PARAMETER outparameters A hashtable of input parameters to be supplied with the query. Entries in the hashtable should have names that match the parameter names, and string values that are the type of the parameters. See example 3. .PARAMETER timeout The commandtimeout value (in seconds). The command will fail and be rolled back if it does not complete before the timeout occurs. .PARAMETER Server The server to connect to. If both Server and Connection are specified, Server is ignored. .PARAMETER Database The initial database for the connection. If both Database and Connection are specified, Database is ignored. .PARAMETER User The sql user to use for the connection. If both User and Connection are specified, User is ignored. .PARAMETER Password The password for the sql user named by the User parameter. .PARAMETER Transaction A transaction to execute the sql statement in. .EXAMPLE This is an example of a query that returns a single result. PS C:\> $c=New-Connection '.\sqlexpress' PS C:\> $res=invoke-query 'select * from master.dbo.sysdatabases' -conn $c PS C:\> $res .EXAMPLE This is an example of a query that returns 2 distinct result sets. PS C:\> $c=New-Connection '.\sqlexpress' PS C:\> $res=invoke-query 'select * from master.dbo.sysdatabases; select * from master.dbo.sysservers' -conn $c PS C:\> $res.Tables[1] .EXAMPLE This is an example of a query that returns a single result and uses a parameter. It also generates its own (ad hoc) connection. PS C:\> invoke-query 'select * from master.dbo.sysdatabases where name=@dbname' -param @{dbname='master'} -server '.\sqlexpress' -database 'master' .INPUTS None. You cannot pipe objects to invoke-query .OUTPUTS Several possibilities (depending on the structure of the query and the presence of output variables) 1. A list of rows 2. A dataset (for multi-result set queries) 3. An object that contains a dictionary of ouptut parameters and their values and either 1 or 2 (for queries that contain output parameters) #> function Invoke-Query{ param( [Parameter(Position=0, Mandatory=$true)][string]$sql, [Parameter(ParameterSetName="SuppliedConnection", Position=1, Mandatory=$false)][System.Data.SqlClient.SqlConnection]$connection, [Parameter(Position=2, Mandatory=$false)][hashtable]$parameters=@{}, [Parameter(Position=3, Mandatory=$false)][hashtable]$outparameters=@{}, [Parameter(Position=4, Mandatory=$false)][int]$timeout=30, [Parameter(ParameterSetName="AdHocConnection",Position=5, Mandatory=$false)][string]$server, [Parameter(ParameterSetName="AdHocConnection",Position=6, Mandatory=$false)][string]$database, [Parameter(ParameterSetName="AdHocConnection",Position=7, Mandatory=$false)][string]$user, [Parameter(ParameterSetName="AdHocConnection",Position=8, Mandatory=$false)][string]$password, [Parameter(Position=9, Mandatory=$false)][System.Data.SqlClient.SqlTransaction]$transaction=$null, [Parameter(Position=10, Mandatory=$false)] [ValidateSet("DataSet", "DataTable", "DataRow", "Dynamic")] [string]$AsResult="Dynamic" ) $connectionparameters=copy-hashtable $PSBoundParameters -exclude AsResult $cmd=new-sqlcommand @connectionparameters $ds=New-Object system.Data.DataSet $da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd) $da.fill($ds) | Out-Null #if it was an ad hoc connection, close it if ($server){ $cmd.connection.close() } get-outputparameters $cmd $outparameters switch ($AsResult) { 'DataSet' { $result = $ds } 'DataTable' { $result = $ds.Tables } 'DataRow' { $result = $ds.Tables[0] } 'Dynamic' { $result = get-commandresults $ds $outparameters } } return $result } <# .SYNOPSIS Execute a stored procedure, returning the results of the query. .DESCRIPTION This function executes a stored procedure, using the parameters provided (both input and output) and returns the results of the query. You may optionally provide a connection or sufficient information to create a connection, as well as input and output parameters, command timeout value, and a transaction to join. .PARAMETER sql The SQL Statement .PARAMETER connection An existing connection to perform the sql statement with. .PARAMETER parameters A hashtable of input parameters to be supplied with the query. See example 2. .PARAMETER outparameters A hashtable of input parameters to be supplied with the query. Entries in the hashtable should have names that match the parameter names, and string values that are the type of the parameters. Note: not all types are accounted for by the code. int, uniqueidentifier, varchar(n), and char(n) should all work, though. .PARAMETER timeout The commandtimeout value (in seconds). The command will fail and be rolled back if it does not complete before the timeout occurs. .PARAMETER Server The server to connect to. If both Server and Connection are specified, Server is ignored. .PARAMETER Database The initial database for the connection. If both Database and Connection are specified, Database is ignored. .PARAMETER User The sql user to use for the connection. If both User and Connection are specified, User is ignored. .PARAMETER Password The password for the sql user named by the User parameter. .PARAMETER Transaction A transaction to execute the sql statement in. .EXAMPLE #Calling a simple stored procedure with no parameters PS C:\> $c=New-Connection -server '.\sqlexpress' PS C:\> invoke-storedprocedure 'sp_who2' -conn $c .EXAMPLE #Calling a stored procedure that has an output parameter and multiple result sets PS C:\> $c=New-Connection '.\sqlexpress' PS C:\> $res=invoke-storedprocedure -storedProcName 'AdventureWorks2008.dbo.stp_test' -outparameters @{LogID='int'} -conne $c PS C:\> $res.Results.Tables[1] PS C:\> $res.OutputParameters For reference, here's the stored procedure: CREATE procedure [dbo].[stp_test] @LogID int output as set @LogID=5 select * from master.dbo.sysdatabases select * from master.dbo.sysservers .EXAMPLE #Calling a stored procedure that has an input parameter PS C:\> invoke-storedprocedure 'sp_who2' -conn $c -parameters @{loginame='sa'} .INPUTS None. You cannot pipe objects to invoke-storedprocedure .OUTPUTS Several possibilities (depending on the structure of the query and the presence of output variables) 1. A list of rows 2. A dataset (for multi-result set queries) 3. An object that contains a hashtables of ouptut parameters and their values and either 1 or 2 (for queries that contain output parameters) #> function Invoke-StoredProcedure{ param([Parameter(Position=0, Mandatory=$true)][string]$storedProcName, [Parameter(ParameterSetName="SuppliedConnection",Position=1, Mandatory=$false)][System.Data.SqlClient.SqlConnection]$connection, [Parameter(Position=2, Mandatory=$false)][hashtable] $parameters=@{}, [Parameter(Position=3, Mandatory=$false)][hashtable]$outparameters=@{}, [Parameter(Position=4, Mandatory=$false)][int]$timeout=30, [Parameter(ParameterSetName="AdHocConnection",Position=5, Mandatory=$false)][string]$server, [Parameter(ParameterSetName="AdHocConnection",Position=6, Mandatory=$false)][string]$database, [Parameter(ParameterSetName="AdHocConnection",Position=7, Mandatory=$false)][string]$user, [Parameter(ParameterSetName="AdHocConnection",Position=8, Mandatory=$false)][string]$password, [Parameter(Position=9, Mandatory=$false)][System.Data.SqlClient.SqlTransaction]$transaction=$null) $cmd=new-sqlcommand @PSBoundParameters $cmd.CommandType=[System.Data.CommandType]::StoredProcedure $ds=New-Object system.Data.DataSet $da=New-Object system.Data.SqlClient.SqlDataAdapter($cmd) $da.fill($ds) | out-null get-outputparameters $cmd $outparameters #if it was an ad hoc connection, close it if ($server){ $cmd.connection.close() } return (get-commandresults $ds $outparameters) } <# .SYNOPSIS Uses the .NET SQLBulkCopy class to quickly copy rows into a destination table. .DESCRIPTION Also, the invoke-bulkcopy function allows you to pass a command object instead of a set of records in order to "stream" the records into the destination in cases where there are a lot of records and you don't want to allocate memory to hold the entire result set. .PARAMETER records Either a datatable (like one returned from invoke-query or invoke-storedprocedure) or A command object (e.g. new-sqlcommand), or a datareader object. Note that the command object or datareader object can come from any class that inherits from System.Data.Common.DbCommand or System.Data.Common.DataReader, so this will work with most ADO.NET client libraries (not just SQL Server). .PARAMETER Server The destination server to connect to. .PARAMETER Database The initial database for the connection. .PARAMETER User The sql user to use for the connection. If user is not passed, NT Authentication is used. .PARAMETER Password The password for the sql user named by the User parameter. .PARAMETER Table The destination table for the bulk copy operation. .PARAMETER Mapping A dictionary of column mappings of the form DestColumn=SourceColumn .PARAMETER BatchSize The batch size for the bulk copy operation. .PARAMETER Transaction A transaction to execute the bulk copy operation in. .PARAMETER NotifyAfter The number of rows to fire the notification event after transferring. 0 means don't notify. Ex: 1000 means to fire the notify event after each 1000 rows are transferred. .PARAMETER NotifyFunction A scriptblock to be executed after each $notifyAfter records has been copied. The second parameter ($param[1]) is a SqlRowsCopiedEventArgs object, which has a RowsCopied property. The default value for this parameter echoes the number of rows copied to the console .PARAMETER Options An object containing special options to modify the bulk copy operation. See http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlbulkcopyoptions.aspx for values. .EXAMPLE PS C:\> $cmd=new-sqlcommand -server MyServer -sql "Select * from MyTable" PS C:\> invoke-sqlbulkcopy -records $cmd -server MyOtherServer -table CopyOfMyTable .EXAMPLE PS C:\> $rows=invoke-query -server MyServer -sql "Select * from MyTable" PS C:\> invoke-sqlbulkcopy -records $rows -server MyOtherServer -table CopyOfMyTable .INPUTS None. You cannot pipe objects to invoke-bulkcopy .OUTPUTS System.Data.SqlClient.SqlCommand #> function Invoke-Bulkcopy{ param([Parameter(Position=0, Mandatory=$true)]$records, [Parameter(Position=1, Mandatory=$true)]$server, [Parameter(Position=2, Mandatory=$false)]$database, [Parameter(Position=3, Mandatory=$false)][string]$user, [Parameter(Position=4, Mandatory=$false)][string]$password, [Parameter(Position=5, Mandatory=$true)][string]$table, [Parameter(Position=6, Mandatory=$false)]$mapping=@{}, [Parameter(Position=7, Mandatory=$false)]$batchsize=0, [Parameter(Position=8, Mandatory=$false)][System.Data.SqlClient.SqlTransaction]$transaction=$null, [Parameter(Position=9, Mandatory=$false)]$notifyAfter=0, [Parameter(Position=10, Mandatory=$false)][scriptblock]$notifyFunction={Write-Host "$($args[1].RowsCopied) rows copied."}, [Parameter(Position=11, Mandatory=$false)][System.Data.SqlClient.SqlBulkCopyOptions]$options=[System.Data.SqlClient.SqlBulkCopyOptions]::Default) #use existing "New-Connection" function to create a connection string. $connection=New-Connection -server $server -database $Database -User $user -password $password $connectionString = $connection.ConnectionString $connection.close() #Use a transaction if one was specified if ($transaction -is [System.Data.SqlClient.SqlTransaction]){ $bulkCopy=new-object "Data.SqlClient.SqlBulkCopy" $connectionString $options $transaction } else { $bulkCopy = new-object "Data.SqlClient.SqlBulkCopy" $connectionString } $bulkCopy.BatchSize=$batchSize $bulkCopy.DestinationTableName = $table $bulkCopy.BulkCopyTimeout=10000000 if ($notifyAfter -gt 0){ $bulkCopy.NotifyAfter=$notifyafter $bulkCopy.Add_SQlRowscopied($notifyFunction) } #Add column mappings if they were supplied foreach ($key in $mapping.Keys){ $bulkCopy.ColumnMappings.Add($mapping[$key],$key) | out-null } write-debug "Bulk copy starting at $(get-date)" if ($records -is [System.Data.Common.DBCommand]){ #if passed a command object (rather than a datatable), ask it for a datareader to stream the records $bulkCopy.WriteToServer($records.ExecuteReader()) } elsif ($records -is [System.Data.Common.DbDataReader]){ #if passed a Datareader object use it to stream the records $bulkCopy.WriteToServer($records) } else { $bulkCopy.WriteToServer($records) } write-debug "Bulk copy finished at $(get-date)" } export-modulemember New-Connection export-modulemember new-sqlcommand export-modulemember invoke-sql export-modulemember invoke-query export-modulemember invoke-storedprocedure export-modulemember invoke-bulkcopy |