How to create a transaction that inserts but then returns the inserted data - sql

I am working on a REST API so I am trying to implement a way for users to create a new service ticket.
Everything is working fine except for when it comes to storing things in the db (postgres).
Here's a snippet of the transaction once it is generated:
BEGIN;
INSERT INTO service_request (id, ...)
VALUES (...);
INSERT INTO media (id, filename, ...)
VALUES (...),
(...),
(...);
INSERT INTO servicerequest_media(service_request_id, media_id)
values (..., ...),
(..., ...),
(...,...);
COMMIT;
Using sqlx prepared statements, I know that the result contains some metadata such as the last inserted id. However, how can I add a select query to my transaction and get the results of that query?
stmt, err := s.db.Prepare(CREATE_SERVICE_REQUEST)
if err != nil {
////
}
res, err := stmt.Exec()
if err != nil {
////
}
Or, do I need to do a 2nd query to get the result?
I am pretty new to this, so please let me know if I need to give more context.

Exec does not return the rows created or inserted. It only returns the last inserted element id.
If you would like to get the rows, you can consider using Query or QueryRow.
rows, err := stmt.Query()
if rows.Next {
// declare variable of any type string, int or struct
rows.Scan(&variable) // string, int
// or
rows.Scan(&variable.field1, &variable.field2) // where field1, field2 are the properties of your struct
// do whatever you want with the variable
}

Related

Golang Route for insert - PrepareContext error

I want to create route /bid/:id/:time/:offer for inserting row in database. But my struct consists of two more rows:userid and time_set. Userid is id of user that made the bid and time_set is timestamp in the moment of inserting or now(). This is my postgres repository in golang.
func (m *postgresBidRepository) CreateNewBid(ctx context.Context, id int64, time int64, offer int64) (err error) {
query := `INSERT bid SET id=? , time=? ,offer=? ,setAt=? ,userId=?`
stmt, err := m.Conn.PrepareContext(ctx, //WHAT HERE)
I want to take id,time and offer from header and current timestamp and userId and insert it. What should I write inside PrepareContext?? when I write id, time,offer... it returs error:
cannot use id (variable of type int64) as string value in argument to m.Conn.PrepareContext
PrepareContext() except two arguments ctx and query string. You should pass the query string like this :
stmt, err := m.Conn.PrepareContext(ctx,query)
Since, you are using interpolation mode. In this mode, driver actually does three actions :
Prepare a statement.
Execute the prepared statement using given args.
Close the prepared statement.
That is exactly the slogan of prepared statement Prepare Once, Execute Many.
After preparing the statement you should execute it like this :
res, err := stmt.ExecContext(ctx, id, time, offer, setAt, userId)
Make sure you should pass the values for all the placeholder(?) query string else it will through an error.
In your case either you can initialize the value inside CreateNewBid() or make a external function and call it inside CreateNewBid() as per the requirement.

Inserting data into table using COPY causes constraint violation

I have an SQL file that contains the schema of the database (creating of tables and some functions to check the constraints). One of the functions has different actions to do for the first entered value and for the rest. What I mean is, that in the function I am firstly counting the number of elements in a table:
CREATE TABLE table_name(
column1 INT,
column2 INT CHECK(func())
);
CREATE OR REPLACE FUNCTION func1()
returns BOOLEAN
LANGUAGE plpgsql
AS
$$
BEGIN
SELECT COUNT(*) INTO var1 FROM table_name;
RAISE NOTICE 'count: %', var1;
IF var1 = 0 THEN
...
return true;
ELSE
...
return true;
END IF;
return false;
END;
$$;
...
and if it is 0, then it does some checks for the first element, and else it does a bit different checks. Then to insert the data I am using COPY(in another SQL file):
COPY table_name(column1, column2) FROM stdin;
5 2
8 7
\.
but I am getting an error:
ERROR: new row for relation "table_name" violates check constraint "table_name_check"
for the second row. So, I have added RAISE NOTICE to print the count in my schema(as shown in the first block of code above):
RAISE NOTICE 'count: %', var1;
and for the first insert, it gives me count = 0, but for the second I also get count = 0. That is why the function treats the second insert as if it is first and it causes errors. What could be the reason for this problem? It looks like even if the first row has been inserted, there are still no rows on the table after the first insertion.

Modyfing result in postgresql funtion that watches modification on table

I need some help
I needed something to watch changes on specific postresql table and send it back as event, so I used something like this:
DROP TRIGGER IF EXISTS "OnDataChange" on public."SystemStates";
DROP FUNCTION IF EXISTS "NotifyOnDataChange";
CREATE FUNCTION public."NotifyOnDataChange"()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
data JSON;
notification JSON;
BEGIN
IF (TG_OP = 'DELETE') THEN
data = row_to_json(OLD);
ELSE
data = row_to_json(NEW);
END IF;
notification = json_build_object('table',TG_TABLE_NAME, 'action', TG_OP, 'data', data);
PERFORM pg_notify('datachange', notification::TEXT);
RETURN NEW;
END
$BODY$;
CREATE TRIGGER "OnDataChange"
AFTER INSERT OR DELETE OR UPDATE
ON public."SystemStates"
FOR EACH ROW
EXECUTE PROCEDURE public."NotifyOnDataChange"();
And it works whenever I made changes on table public."SystemStates" i get notified i my application.
If anyone is interested here is event reader in c#:
static async Task Main(string[] args)
{
await using var conn = new NpgsqlConnection("<conn string>");
await conn.OpenAsync();
conn.Notification += (o, e) => Console.WriteLine("Received notification: " + e.Payload);
await using (var cmd = new NpgsqlCommand("LISTEN datachange;", conn))
cmd.ExecuteNonQuery();
while (true)
conn.Wait();
}
and result would look like:
{"table" : "SystemStates", "action" : "UPDATE", "data" : {"Id":23,"ExtComm":0,"IntComm":0,"State":0,"SystemId":1,"LastCommunicationTimestamp":"2021-01-20T11:56:34.704435"}}
But I was wondering, because I don't need whole row information only couple of columns, on other hand I would like to get also one information from related table. So my table would look like:
Id | ExtComm | IntComm | State | SystemId | LastCommTimestamp
----+---------+---------+-------+----------+------------------+
All I need is LastCommTimestamp and one column from table System with this table is related by SystemId (column name is System Name).
As far I can deduct result and serialization are here :
IF (TG_OP = 'DELETE') THEN
data = row_to_json(OLD);
ELSE
data = row_to_json(NEW);
END IF;
But I don't know how to bite this thing
EDIT:
I tried doing some sql in
data = row_to_json(NEW);
Something like:
SELECT row_to_json(ROW)
INTO data
FROM (
SELECT PUBLIC."Systems"."MPV01CabinetId" AS "MpvCid", PUBLIC."SystemStates"."LastCommTimestamp" AS "Timestamp"
FROM PUBLIC."SystemStates"
LEFT JOIN PUBLIC."Systems" ON PUBLIC."SystemStates"."SystemId" = PUBLIC."Systems"."Id"
) ROW;
Well it worked but I've got last record of the table (witch in my opinion is logical as I am returning record type as json).
So now I am trying to use NOW as object so it means replacing PUBLIC."SystemStates" with NOW."LastCommTimestamp" but postresql says that this property does not exists.
Am I using It wrong?
Ok I found what was wrong, I misinterpreted function row_to_json and when I wanted to get simple value form NOW (for example NOW.id) it expected record not typed value.
Also my SQL query was wrong and I tried using FROM NOW, now I am using this as a parameter and it is working as expected (But if anyone sees room for improvement in this sql query I will be most fully grateful)
DROP TRIGGER IF EXISTS "OnDataChange" on public."SystemStates";
DROP FUNCTION IF EXISTS "NotifyOnDataChange";
CREATE FUNCTION public."NotifyOnDataChange"()
RETURNS trigger
LANGUAGE 'plpgsql'
AS $BODY$
DECLARE
recordvalues json;
data JSON;
notification JSON;
BEGIN
IF (TG_OP = 'DELETE') THEN
data = row_to_json(OLD);
ELSE
SELECT row_to_json(ROW)
INTO data
FROM (
SELECT PUBLIC."Systems"."MPV01CabinetId" AS "MpvCid", PUBLIC."SystemStates"."LastCommunicationTimestamp" AS "Timestamp"
FROM PUBLIC."Systems"
right JOIN PUBLIC."SystemStates" ON PUBLIC."SystemStates"."SystemId" = PUBLIC."Systems"."Id"
WHERE PUBLIC."Systems"."Id" = NEW."SystemId"
) ROW;
END IF;
notification = json_build_object('table',TG_TABLE_NAME, 'action', TG_OP, 'data', data);
PERFORM pg_notify('datachange', notification::TEXT);
RETURN NEW;
END
$BODY$;
CREATE TRIGGER "OnDataChange"
AFTER INSERT OR DELETE OR UPDATE
ON public."SystemStates"
FOR EACH ROW
EXECUTE PROCEDURE public."NotifyOnDataChange"();

Begin with mutiple inserts giving syntax error

new to SQL here, and been stuck on this tiny issue that I cannot solve for the love of my life.
What I want to do: Insert into table1, then insert into table2 in the same database query.
I have this SQL statement:
BEGIN TRANSACTION
INSERT INTO "questionsGroup" DEFAULT VALUES;
INSERT INTO questions (name, category, explanation, "belongsToGroup") VALUES ($1, $2, $3, lastval('questionsGroup'));
END;
When running, I get this error:
error: syntax error at or near "INSERT"
This is how I call the query:
const response = await database.insertQuestionsIntoDatabase(query, ["cevin", 1, "test", 8]);
And the function:
public async insertQuestionsIntoDatabase(sqlStatement: string, values: Array<string | number>): Promise<any> {
console.log(sqlStatement);
try {
return await this.databaseClient.query(sqlStatement, values);
} catch (e) {
console.log(e);
throw new Error("Something went wrong with DatabaseQueries Query")
}
}
What am I missing, most likely something trivial? Thank you.
You seem to just want to include the quationsGroup id into the qustions table. If so, you can use insert in a CTE:
WITH qg AS (
INSERT INTO questionsGroup
DEFAULT VALUES
RETURNING *
)
INSERT INTO questions (name, category, explanation, belongsToGroup
SELECT $1, $2, $3, qg.questionsGroupId
FROM qg;
Note it is not considered a good practice to use double quotes for identifiers. It just makes writing queries that much more complicated.
Example found in the PostgreSQL Documentation for the BEGIN statement:
To begin a transaction block:
BEGIN;
Notice the ;.
BEGIN and END starts and stops a transaction. They are not structural constructs.

TADOQuery does not return any record

I am using Delphi 7 and I have a table named Table_1 which has two fields say, IMageCode Varchar(50), ActImage [Blob in Oracle, VarBinary(Max) in SQL Server], it has four records inserted including images and respective image code.
When I write a SQL as below in Oracle:
Select * from Table_1 where Upper(ImageCode) ='SUNSET'
TADOQuery doesn't return any record, when I check TADOQuery.RecordCount it shows 0, when I try to see TADOQuery.IsEmpty it says True. Then same query when I execute in the Oracle editor it returns one record as expected, but in delphi the TADOQuery doen't return any record.
But when I write following simple sql for the oracle database, it returns all four records using TADOQuery:
Select * from Table_1
I don't find any issue in SQL server database, as discussed above using TADOQuery.
ADOConnection.Connected := False;
ADOConnection.LoginPrompt := False;
ADOConnection.ConnectionString := <Connection String>;
ADOConnection.Connected := True;
ADOQuery1.Connection := ADOConnection;
with ADOQuery1 do begin
Active := False;
SQL.Clear;
//SqL.Add('Select * from Table_1 where Upper(ImageCode) = ' + QuotedStr(Uppercase(Trim(edtImageCode.Text))));
SqL.Add('Select * from Table_1 where Upper(ImageCode) = ''SUNSET''');
Active := True;
end;
Could anybody put focus what could be the issue?
Issue is resolved, I tried making "ImageCode" as a primary key, and now TADOQuery is returning the peroper record count i.e. 1 as expected. Previously there was no primary key, but still, if there is primary key or not TADOQuery should return the required data sets.
Any guess ?