Can I tell if an ado.net DbCommand is a query or not (before executing it) - ado.net

I am trying to write a Powershell script to run a general SQL command against a database. The idea is that Run-SQL "select ..." will run the SQL text against the currently open database. If the SQL statement is a query, it should return a DataTable. If it is a non-query (DDL or DML) it should return nothing ($null).
In order to do this, I need to know which method (ExecuteReader or ExecuteNonQuery) to execute against the command. Is there a way to determine this? (I'm happy to prepare the command if that helps).
As an alternative, I can add a -query argument to be supplied by the user, which distinguishes the two cases, but as a potential user, I'd find this annoying (as, in my view, I have already said whether it's a query by the SQL I used, why say again?)
My key use is for Oracle databases, so an Oracle-specific answer is OK with me, although I'd prefer something generic.

I think you could just use ExecuteReader whether it's a query or not. It may be overkill but in some quick experiments with doing an UPDATE ($reader returns nothing) and a COUNT ($reader[0] outputs scalar result) - it just seems to work.
$connstr = "server=.\SQLEXPRESS;Database=AdventureWorks;" +
"Integrated Security=true;Persist Security Info=False"
$conn = new-object System.Data.SqlClient.SqlConnection $connstr
#$query = "UPDATE Production.Product SET Name = 'ACME' WHERE Name = 'Blade'"
$query = "SELECT Count(*) FROM Production.Product"
$cmd = new-object System.Data.SqlClient.SqlCommand $query,$conn
$conn.Open()
try
{
$reader = $cmd.ExecuteReader()
while ($reader.Read())
{
$reader[0]
}
}
finally
{
$conn.Dispose()
}

As an alternative to what Keith said you could try
$sql = 'Select count(1) From SomeTable;'
$sql = $sql.TrimStart(' ')
if ($sql -match "^select") { Write-Host 'ExecuteReader' }
else { Write-Host 'ExecuteNonQuery'}
The trim is there in case the SQL command has a leading space since $sql = ' select ' won't match "^select"

Related

Check if pg_prepare was already executed

Is there a way to check if pg_prepare already executed and remove it from the session?
Seems like pg_close doesn't remove prepared statement from the session. Kind of seems like a bug in php, but maybe I'm missing something or maybe there is a workaround.
public static function readSubdomains($dcName, $filter = null) {
// ...
$conn = pg_pconnect($connectionString);
// ...
$result = pg_prepare($conn, "subdomains", "SELECT subdomain
from tenants
where $where
order by 1 asc
");
$result = pg_execute($conn, "subdomains", $params);
// ...
pg_close($conn);
}
Second call to readSubdomains shows a warning like this:
Warning: pg_prepare(): Query failed: ERROR: prepared expression "subdomains" already exists in inc/TestHelper.php on line 121
Always check the official manuals for this sort of stuff.
https://www.postgresql.org/docs/current/view-pg-prepared-statements.html
Oh - if pg_close isn't dropping prepared statements then it isn't closing the connection. You might have some connection pooling involved.

ODBC Data Reader HasRows=True but No Data

I'm connecting to an Access database through ODBC using Powershell, reading data from Access to convert it to another database. I have one query that is causing me problems because although HasRows returns True, when I attempt to loop through the reader, it acts as if it is at the end of the reader. Here is the code I'm using
$FMconn = new-object System.Data.Odbc.OdbcConnection
$FMconn.ConnectionString = "DSN=AccessFileMaker"
$FMconn.Open()
$FMCmd = $FMconn.CreateCommand()
$FMCmd.CommandTimeout = 0
$sql = "
SELECT
fd__folder.id as folderid
,fd__folder.folderletter
,wl__wallet.id as walletid
,wl__wallet.walletnumber
FROM
fd__folder
INNER JOIN wl__wallet
ON fd__folder.id_wallet = wl__wallet.id
WHERE
fd__folder.folderLetter > ''
"
$FMCmd.CommandText = $sql
$Reader = $FMCmd.ExecuteReader()
if ($Reader.HasRows) {
while ($Reader.Read())
{
$folderID = $Reader["folderid"].ToString()
$folderletter = $Reader["folderletter"].ToString().TrimEnd()
$walletID = $Reader["walletid"].ToString()
$walletNum = "Wallet " + $Reader["walletnumber"].ToString().TrimEnd()
(other code here to insert data into the other DB but irrelevant to the problem)
}
$Reader.Close()
}
As I step through the code I get to the while($Reader.Read()) and it immediately steps to $Reader.Close()
The query runs find in Access as is and returns 56115 records. I've used this same process to extract other records from Access (although none of the queries involved a join). So why can't I loop through the records of this query if the Reader has rows? Any help debugging this would be greatly appreciated.

Powershell - ODBC MSAccess Commit transaction refresh data for Select Query after Insert

I'm connecting with ODBC in powershell to an .accdb Access Database. After inserting new data into a table (insert into via ExecuteNonQuery), I'm unable to pull the data with a select command for futher processing (create a PDF table). The resulting object of the query is empty (0 rows), though "Select ##IDENTITY" returns the most recent index identifier. The Workflow is something like this:
Data entered into a forms window -> new record is created -> data is pulled off and processed into a report -> report is sent by mail -> record is flagged as sent
I tried to use the SQL transaction mechanism, but even after the commit is done, the data is not immediately retrievable with a select-query. After inserting another record, the previously inserted one can be pulled with a select query, but not the new most recent one.
I have not found any working solution to this behavior. I've heard about this, when using Access forms, where the data will be available when the form has been moved to the next record. Any ideas how I can solve this?
Below are my getter and setter functions. In general they work as intended but using Set-Data with an insert first and firing Get-Data with a select afterwards does not provide any records.
Function Get-Data($SQLCommand){
try{
$Connection = New-Object System.Data.Odbc.OdbcConnection($DSN)
$Connection.Open()
$AccdbCommand = New-Object System.Data.Odbc.OdbcCommand($SQLCommand, $Connection)
$AccdbAdapter = New-Object System.Data.Odbc.OdbcDataAdapter($AccdbCommand)
$AccdbDataSet = New-Object System.Data.DataSet
$AccdbAdapter.Fill($AccdbDataSet)
$Connection.Close()
return $AccdbDataSet
}
catch
{
[System.Windows.Forms.MessageBox]::Show($_.Exception.Message + "`r`n" + $_.Exception.InnerException, "Fehler",'OK','Error')
}
}
Function Set-Data($SQLCommand){
try
{
$Connection = New-Object System.Data.Odbc.OdbcConnection($DSN)
$Connection.Open()
$Transaction = $Connection.BeginTransaction()
$AccdbCommand = New-Object System.Data.Odbc.OdbcCommand($SQLCommand, $Connection, $Transaction)
$AccdbCommand.ExecuteNonQuery()
$transaction.Commit()
$AccdbCommand = New-Object System.Data.Odbc.OdbcCommand("Select ##IDENTITY", $Connection)
$result = $AccdbCommand.ExecuteScalar()
$Connection.Close()
}
catch
{
[System.Windows.Forms.MessageBox]::Show($_.Exception.Message + "`r`n" + $_.Exception.InnerException, "Fehler", 'OK', 'Error')
}
}
I've resolved this Problem. The commit itself is working as intended. The missing data record in the output process was a result of an error in looping the rows in the dataset with a for Statement.

Powershell Multiple variables in foreach loop getting AWS-EC2 instance info

I need to get instanceId and PrivateIpAddress from all my EC2 instances in my environment and insert into table. I have this but doesn't seem to work. I seem to get everything not just the IP and ID.
$instancestate = (get-ec2instance).RunningInstance
foreach ($instances in $instancestate)
{
$query = "INSERT INTO prodinstances (Idinstance, IPAddress)
VALUES ('$instances.InstanceId','$instances.PrivateIpAddress')"
$Rows = Execute-MySQLNonQuery $conn $query
}
If I change the code
$instancestate = (get-ec2instance).RunningInstance.InstanceId
I get the ID and can insert it in the database. I can also change it to
$instancestate = (get-ec2instance).RunningInstance.PrivateIpAddress
and get the IPAddress and insert that into the database, but when i combine them I get all the info for the EC2 instances which does have .instanceId and .PrivateIpAddress in the list when I hover over the variable $instances. Any Idea how to get both those parameters. My code seems correct but alas it is not...
"VALUES ('$instances.InstanceId'"
is the same as
"VALUES ('" + $instances + ".InstanceId'"
Now it doesn't seem correct. You need $() around it, inside the string:
"VALUES ('$($instances.InstanceId)'"
Fixed works like a charm...
$instancestate = (get-ec2instance).RunningInstance
foreach ($instances in $instancestate)
{
$query = "insert into prodinstances (idinstance,IPAddress) VALUES ('$($instances.InstanceId)', '$($instances.PrivateIpAddress)')
ON duplicate key update idinstance='$($instances.InstanceId)',IPAddress='$($instances.PrivateIpAddr ess)'"
$Rows = Execute-MySQLNonQuery $conn $query
}

Help With Zend_Db_Stmt

I have a little problem with the Zend_Db_Stmt. This works:
$sql = " SELECT * FROM bugs";
$stmt = $this->_getDb()->query($sql);
return $stmt->fetchAll();
But I am trying to make sure the PDO gets used to query the database so I tried this:
$sql = "SELECT * FROM bugs";
$stmt = new Zend_Db_Statement_Pdo($this->_getDb(), $sql);
return $stmt->fetchAll();
And this doesn't work (it returns an empty array). Could you please help me figure this out? The above code works if I use execute() method for UPDATE or INSERT queries but fetchAll() doesn't work.
You need to execute!
$stmt->execute();
return $stmt->fetchAll();
See more examples in the PHP manual.