How many rows got inserted into SQL database with ts-postgres? - sql

I use ts-postgres and INSERT INTO to add new rows to my table.
import { Client } from 'ts-postgres';
let query = '...';
let res = await Client.query(query, [username, email]);
The result I get from Client.query is the following result:
Result {names: Array(0), rows: Array(0), status: "INSERT 0 1"}
Result {names: Array(0), rows: Array(0), status: "INSERT 0 0"}
In the first case 1 line got added, in the second 0. Do I really need to parse the status string in order to see how many rows got added?

Yep, that's something you have to deal with when working at low level (without ORM)
So here's a simple function to check for inserted rows
checkInserted(result): number {
const status = result.status.split();
return parseInt(status[status.length-1]);
}
you can customize it according to your requirements

It looks like the answer is yes, you really do need to parse the status string.
According to the Postgres protocol documentation, when the database finishes executing an insert command, it sends a CommandComplete message back to the client. The CommandComplete message consists of a byte that identifies the message type, a length, and a "tag," which is a string:
For an INSERT command, the tag is INSERT oid rows, where rows is the
number of rows inserted. oid used to be the object ID of the inserted
row if rows was 1 and the target table had OIDs, but OIDs system
columns are not supported anymore; therefore oid is always 0.
That tag is the status that you are seeing. There's nothing else in the CommandComplete message.
The Node Postgres client does include a rowCount member in result, but if you look at the code, you will see that it just parses it out of the string. The Java JDBC driver also parses the string.

Related

Using Athena to get terminatingrule from rulegrouplist in AWS WAF logs

I followed these instructions to get my AWS WAF data into an Athena table.
I would like to query the data to find the latest requests with an action of BLOCK. This query works:
SELECT
from_unixtime(timestamp / 1000e0) AS date,
action,
httprequest.clientip AS ip,
httprequest.uri AS request,
httprequest.country as country,
terminatingruleid,
rulegrouplist
FROM waf_logs
WHERE action='BLOCK'
ORDER BY date DESC
LIMIT 100;
My issue is cleanly identifying the "terminatingrule" - the reason the request was blocked. As an example, a result has
terminatingrule = AWS-AWSManagedRulesCommonRuleSet
And
rulegrouplist = [
{
"nonterminatingmatchingrules": [],
"rulegroupid": "AWS#AWSManagedRulesAmazonIpReputationList",
"terminatingrule": "null",
"excludedrules": "null"
},
{
"nonterminatingmatchingrules": [],
"rulegroupid": "AWS#AWSManagedRulesKnownBadInputsRuleSet",
"terminatingrule": "null",
"excludedrules": "null"
},
{
"nonterminatingmatchingrules": [],
"rulegroupid": "AWS#AWSManagedRulesLinuxRuleSet",
"terminatingrule": "null",
"excludedrules": "null"
},
{
"nonterminatingmatchingrules": [],
"rulegroupid": "AWS#AWSManagedRulesCommonRuleSet",
"terminatingrule": {
"rulematchdetails": "null",
"action": "BLOCK",
"ruleid": "NoUserAgent_HEADER"
},
"excludedrules":"null"
}
]
The piece of data I would like separated into a column is rulegrouplist[terminatingrule].ruleid which has a value of NoUserAgent_HEADER
AWS provide useful information on querying nested Athena arrays, but I have been unable to get the result I want.
I have framed this as an AWS question but since Athena uses SQL queries, it's likely that anyone with good SQL skills could work this out.
It's not entirely clear to me exactly what you want, but I'm going to assume you are after the array element where terminatingrule is not "null" (I will also assume that if there are multiple you want the first).
The documentation you link to say that the type of the rulegrouplist column is array<string>. The reason why it is string and not a complex type is because there seems to be multiple different schemas for this column, one example being that the terminatingrule property is either the string "null", or a struct/object – something that can't be described using Athena's type system.
This is not a problem, however. When dealing with JSON there's a whole set of JSON functions that can be used. Here's one way to use json_extract combined with filter and element_at to remove array elements where the terminatingrule property is the string "null" and then pick the first of the remaining elements:
SELECT
element_at(
filter(
rulegrouplist,
rulegroup -> json_extract(rulegroup, '$.terminatingrule') <> CAST('null' AS JSON)
),
1
) AS first_non_null_terminatingrule
FROM waf_logs
WHERE action = 'BLOCK'
ORDER BY date DESC
You say you want the "latest", which to me is ambiguous and could mean both first non-null and last non-null element. The query above will return the first non-null element, and if you want the last you can change the second argument to element_at to -1 (Athena's array indexing starts from 1, and -1 is counting from the end).
To return the individual ruleid element of the json:
SELECT from_unixtime(timestamp / 1000e0) AS date, action, httprequest.clientip AS ip, httprequest.uri AS request, httprequest.country as country, terminatingruleid, json_extract(element_at(filter(rulegrouplist,rulegroup -> json_extract(rulegroup, '$.terminatingrule') <> CAST('null' AS JSON) ),1), '$.terminatingrule.ruleid') AS ruleid
FROM waf_logs
WHERE action='BLOCK'
ORDER BY date DESC
I had the same issue but the solution posted by Theo didn't work for me, even though the table was created according to the instructions linked to in the original post.
Here is what worked for me, which is basically the same as Theo's solution, but without the json conversion:
SELECT
from_unixtime(timestamp / 1000e0) AS date,
action,
httprequest.clientip AS ip,
httprequest.uri AS request,
httprequest.country as country,
terminatingruleid,
rulegrouplist,
element_at(filter(ruleGroupList, ruleGroup -> ruleGroup.terminatingRule IS NOT NULL),1).terminatingRule.ruleId AS ruleId
FROM waf_logs
WHERE action='BLOCK'
ORDER BY date DESC
LIMIT 100;

always getting 0 for rows affected by dbexecute

I am trying to get the number of rows affected by an update query. I am using a pool connection to a SQL server database. The rows update fine, but I can't figure out how to get the number of rows affected which I want to display for the user. I have read the documentation of dbExecute and it says:
dbExecute() always returns a scalar numeric that specifies the number
of rows affected by the statement. An error is raised when issuing a
statement over a closed or invalid connection, if the syntax of the
statement is invalid, or if the statement is not a non-NA string.
I thought maybe the pool was the issue, so I also tried it with a regular dbConnect, and still got 0 for res.
Can someone show how I can pull out and display to the user the number of rows affected?
observeEvent(input$recupdate, {
res <-
#poolWithTransaction(con2, function(con2) {
dbExecute(con2, paste(
"UPDATE [Test_DB].[dbo].[mlt] SET testvar = 1 where idnum in ('",
trim(paste(filteredData()$idnum, collapse="','")),"')", sep=""))
#})
#print(res)
showModal(modalDialog(
title = "Rows affected:",
res,
easyClose = TRUE,
footer = NULL
))
})

How to query ODBC with Dapper with multiple parameters including a WHERE IN?

I've hit an issue using Dapper to query an ODBC provider. The query in question is supplied several parameters. One of the parameters is used to populate a WHERE IN operator.
So far I've tried supplying the query with: DynamicParameters, ? regular parameters, ?name? pseudo-positional parameters and some combinations of these.
SELECT companyId
,projectId
,contractId
,status
FROM certificate
WHERE companyId = ?companyId?
AND projectId = ?projectId?
AND contractId = ?contractId?
AND invoiceId IN ?invoiceIds?
var results = await connection
.QueryAsync<Certificate>(query, new { companyId, projectId, contractId, invoiceIds = new string[] { '1a', '1b', '2a' }});
Expected results are several rows being returned. If I hardcode the query with it's parameters it works fine, so the data I'm supplying should return rows.
Instead I'm getting an exception with the following message:
ERROR [07001] [DataDirect][ODBC Progress OpenEdge Wire Protocol driver]Value has not been specified for parameter 4.
ERROR [07001] [DataDirect][ODBC Progress OpenEdge Wire Protocol driver]Value has not been specified for parameter 5.
If that is your code, then your code doesn't compile. You are not supplying any values for the first three parameters and you are giving illegal characters instead of string in the fourth parameter. I see no reason why this shouldn't work:
var results = await connection
.QueryAsync<Certificate>(query,
new { companyId=1, projectId=1, contractId=1, invoiceIds = new [] { "1a", "1b", "2a" }});

TAG= 'Token line number = 1,Token line offset = 29,Token in error = FROM ' ... insert data from one column to another column of other table

Well... I have two tables named : "chefs" and "cust_Data".
I want to :
Put comments from cust_Data that are being posted into the comment column in chefs table.
By being posted means-> M giving a comment section so that customers will put their comments in "comment" column in "cust_Data" and as soon as those comments are in cust_Data...
Put those comment in "comments" column in "chefs" table.
RAZOR CODE:
#{
var db = Database.Open("vendors");
var selectCommand = "SELECT dish FROM chefs WHERE ID= #0";
var chefID = Request.QueryString["chefid"];
var selectedData = db.Query(selectCommand, chefID);
var dishName=Request.QueryString["dish"];
var chefName=Request.QueryString["chefName"];
var comments=Request["SELECT comments from chefs"]; //IMPORTANT
var ID=Request["SELECT ID from chefs"]; //IMPORTANT
var grid = new WebGrid(source: selectedData, rowsPerPage: 10);
Validation.RequireField("cust_fname", "You must enter your firstname");
Validation.RequireField("cust_lname", "You must enter your surname");
Validation.RequireField("cust_email", "You haven't entered a valid Email_ID");
Validation.RequireField("rating", "Rating is required");
Validation.RequireField("comment", "Please provide your Comment about Respective Homeschef");
var cust_fname = "";
var cust_lname= "";
var cust_email= "";
var rating= "";
var comment= ""; //IMPORTANT
if(IsPost && Validation.IsValid() && chefID.AsInt()!=0){
cust_fname = Request.Form["cust_fname"];
cust_lname = Request.Form["cust_lname"];
cust_email = Request.Form["cust_email"];
rating = Request.Form["rating"];
comment = Request.Form["comment"]; // IMPORTANT->TAKING COMMENT FROM USER
var insertCommand = "INSERT INTO cust_Data (cust_fname, cust_lname, cust_email, dish, chef_ID, rating, comment) Values(#0, #1, #2, #3, #4, #5, #6)";
db.Execute(insertCommand,cust_fname,cust_lname, cust_email, dishName, chefID, rating, comment); // EXECUTED SUCCESSFULLY IN cust_Data
var insert= "INSERT INTO chefs(comments) FROM cust_Data(comment) WHERE chefs(ID)=cust_Data(chef_ID) Values(#0,#1,#2,#3)"; //IMPORTANT
db.Execute(insert,comments,comment,ID,chefID); // IMPORTANT -> ERROR OCCURED ?????????
}
}
Exception Details: System.Data.SqlServerCe.SqlCeException: There was an error parsing the query. [ Token line number = 1,Token line offset = 29,Token in error = FROM ]
I inserted IMPORTANT TAG(to save your time for important things) from where i took the parameters I needed to parse.
Again, Thank you so much in advance...
Though this is not a complete answer, writing this as a answer as the content for explanation is too long.
Even though you've updated the insert into command, the select statement is still wrong. You are trying to write the select statement using the syntax of insert statement i.e. table_name(column_name) but it should be
SELECT column_name from table_name WHERE table_name.column_name=''
Insert into statement works by taking complete SQL select statement prefixed by Insert Into clause. So, your code should be something like this:
INSERT INTO chefs(comments) SELECT comments from cust_Data WHERE chefs.ID=cust_Data.chef_ID
But this won't work as you've not joined the table chefs and cust_data. So you will have to join both the tables and get the correct records, then use the insert into command to insert the records into the chefs table.
Looking more into your program, it looks like you want to update the existing records from the chefs table, or insert is the correct one? Your chefs table has other columns i.e. ID,dish which have been ignored in the insert statement. I doubt that this insert statement would run properly without these columns.
Also, why are you querying comments and ID values separately. And the rows are also not filtered, so it will fetch all the records from the database.
var comments=Request["SELECT comments from chefs"]; //IMPORTANT
var ID=Request["SELECT ID from chefs"]; //IMPORTANT
The database structure and your agenda that you are trying to achieve looks weird to me. I.e. Customer enters the data in cust_data.comments and you want to move/copy these comments to chefs.comment and as soon as the data comes in cust_data table. Why not directly insert into the chefs table instead of this 2 step approach.
Have you thought about the whole process from start to finish? Have you drawn it on paper or screen using UML diagram/process flow?
It looks like you do not have enough experience on the database side. My suggestion would be that you first try these statements directly on the database tool SSMS, get it confirmed that it's working and then apply it in your program. You can use hard coded values for testing and then substitute with parameters when applying it into the program.
Also it would help you if you can learn Stored Procedures and use that in your program. This will reduce such complications from program side and also reduce the database calls

CDbCommand::createCommand() returns zero affected rows inside migration

This code works just fine (all database items updated as expected):
foreach($idMap as $menuId=>$pageId)
{
$sql = "UPDATE `menus_items` SET link = '/content/show?id=".$pageId."' WHERE id = ".$menuId."; ";
$affectedRows = Yii::app()->db->createCommand($sql)->execute();
echo $affectedRows." affected rows\n";
}
But it prints 0 affected rows for each executed query. Why?
The same effect is, when executing many rows affecting statements in one SQL query:
$sql = '';
foreach($idMap as $menuId=>$pageId)
{
$sql .= "UPDATE `menus_items` SET link = '/content/show?id=".$pageId."' WHERE id = ".$menuId."; ";
}
$affectedRows = Yii::app()->db->createCommand($sql)->execute();
echo $affectedRows." affected rows\n";
What am I missing? Docs says, that CDbCommand::execute should return number of rows affected by the execution. Does this feature work, when used inside migration?
CDbCommand::execute returns the row Count from underlying PDO interface, PDOstatement::rowCount only returns the row count of the last statement.
I had tested this within migration to be sure that migrate script is not running any other commands for cleanup etc, this is not the case, I am able to get correct row values from within and outside migration as well.
The most likely reason you are getting 0 as the value is because of the update command did not affect any rows ( i.e. the link values were already set to the correct values), UPDATE will return 0 if no change has occurred.
Perhaps you already run the migration on your test db and migrated down to test it few more times, however during the subsequent passes no update actually happened.
Note in the second scenario only the count of the last command ( a single row update will be shown even if update changes the table as PDOstatement::rowCount only returns the count for last statement executed.