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.
Related
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
}
I have tables that contain the same type of data for every year, but the data gathered varies slightly in that they may not have the same fields.
d_abc_2016
d_def_2016
d_ghi_2016
d_jkl_2016
There are certain constants for each table: company_id, employee_id, salary.
However, each one might or might not have these fields that are used to calculate total incentives: bonus, commission, cash_incentives. There are a lot more, but just using these as a examples. All numeric
I should note at this point, users only have the ability to run SELECT statements.
What I would like to be able to do is this:
Give the user the ability to call in SELECT and specify their own fields in addition to the call
Pass the table name being used into the function to use in conditional logic to determine how the query string should be constructed for the eventual total_incentives calculation in addition to passing the whole table so a ton of arguments don't have to be passed into the function
Basically this:
SELECT employee_id, salary, total_incentives(t, 'd_abc_2016')
FROM d_abc_2016 t;
So the function being called will calculate total_incentives which is numeric for that employee_id and also show their salary. But the user might choose to add other fields to look at.
For the function, because the fields used in the total_incentives function will vary from table to table, I need to create logic to construct the query string dynamically.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS numeric AS
$$
DECLARE
-- table name lower case in case user typed wrong
tbl varchar(255) := lower($2;
-- parse out the table code to use in conditional logic
tbl_code varchar(255) := split_part(survey, '_', 2);
-- the starting point if the query string
base_calc varchar(255) := 'salary + '
-- query string
query_string varchar(255);
-- have to declare this to put computation INTO
total_incentives_calc numeric;
BEGIN
IF tbl_code = 'abc' THEN
query_string := base_calc || 'bonus';
ELSIF tbl_code = 'def' THEN
query_string := base_calc || 'bonus + commission';
ELSIF tbl_code = 'ghi' THEN
-- etc...
END IF;
EXECUTE format('SELECT $1 FROM %I', tbl)
INTO total_incentives_calc
USING query_string;
RETURN total_incentives_calc;
END;
$$
LANGUAGE plpgsql;
This results in an:
ERROR: invalid input syntax for type numeric: "salary + bonus"
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 16 at EXECUTE
Since it should be returning a set of numeric values. Change it to the following:
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS SETOF numeric AS
$$
...
RETURN;
Get the same error.
Figure well, maybe it is a table it is trying to return.
CREATE OR REPLACE FUNCTION total_incentives(ANYELEMENT, t text)
RETURNS TABLE(tot_inc numeric) AS
$$
...
Get the same error.
Really, any variation produces that result. So really not sure how to get this to work.
Look at RESULT QUERY, RESULT NEXT, or RESULT QUERY EXECUTE.
https://www.postgresql.org/docs/9.6/static/plpgsql-control-structures.html
RESULT QUERY won't work because it takes a hard coded query from what I can tell, which won't take in variables.
RESULT NEXT iterates through each record, which I don't think will be suitable for my needs and seems like it will be really slow... and it takes a hard coded query from what I can tell.
RESULT QUERY EXECUTE sounds promising.
-- EXECUTE format('SELECT $1 FROM %I', tbl)
-- INTO total_incentives_calc
-- USING query_string;
RETURN QUERY
EXECUTE format('SELECT $1 FROM %I', tbl)
USING query_string;
And get:
ERROR: structure of query does not match function result type
DETAIL: Returned type character varying does not match expected type numeric in column 1.
CONTEXT: PL/pgSQL function total_incentives(anyelement,text) line 20 at RETURN QUERY
It should be returning numeric.
Lastly, I can get this to work, but it won't be DRY. I'd rather not make a bunch of separate functions for each table with duplicative code. Most of the working examples I have seen have the whole query in the function and are called like such:
SELECT total_incentives(d_abc_2016, 'd_abc_2016');
So any additional columns would have to be specified in the function as:
EXECUTE format('SELECT employee_id...)
Given the users will only be able to run SELECT in query this really isn't an option. They need to specify any additional columns they want to see inside a query.
I've posted a similar question but was told it was unclear, so hopefully this lengthier version will more clearly explain what I am trying to do.
The column names and tables names should not be used as query parameters passed by USING clause.
Probably lines:
RETURN QUERY
EXECUTE format('SELECT $1 FROM %I', tbl)
USING query_string;
should be:
RETURN QUERY
EXECUTE format('SELECT %s FROM %I', query_string, tbl);
This case is example why too DRY principle is sometimes problematic. If you write it directly, then your code will be simpler, cleaner and probably shorter.
Dynamic SQL is one from last solution - not first. Use dynamic SQL only when your code will be significantly shorter with dynamic sql than without dynamic SQL.
In PostgreSQL, I would like to create a safe-wrapping mechanism which returns empty result if an exception occurs. Consider the following:
SELECT * FROM myschema.mytable;
I could do the safe-wrapping in the client application:
try {
result = execute_query('SELECT value FROM myschema.mytable').fetchall();
}
catch(pg_exception) {
result = []
}
But could I do such a thing in SQL directly? I would like to make the following code work, but it seems like it should by put into DO $$ ... $$ block and here I'm getting lost.
BEGIN
SELECT * FROM myschema.mytable;
EXCEPTION WHEN others THEN
SELECT unnest(ARRAY[]::TEXT[])
END
Exception handling in PL/pgSQL
PL/pgSQL code is always wrapped into a BEGIN ... END block. That can be inside the body of a DO statement or a function. Blocks can be nested inside - but they cannot exist outside, don't confuse it with plain SQL.
Each block can optionally contain an EXCEPTION clause for handling exceptions, but functions that need to trap exceptions are more expensive, so it's best to avoid exceptions a priori. Postgres needs to prepare for the possibility of rolling back to a point in the transaction before the exception happened, similar to an SQL SAVEPOINT. The manual:
A block containing an EXCEPTION clause is significantly more
expensive to enter and exit than a block without one. Therefore, don't
use EXCEPTION without need.
Example:
Is SELECT or INSERT in a function prone to race conditions?
How to avoid an exception in the example
A DO statement can't return anything. Create a function that takes table and schema name as parameters and returns whatever you want:
CREATE OR REPLACE FUNCTION f_tbl_value(_tbl text, _schema text = 'public')
RETURNS TABLE (value text)
LANGUAGE plpgsql AS
$func$
DECLARE
_t regclass := to_regclass(_schema || '.' || _tbl);
BEGIN
IF _t IS NULL THEN
value := ''; RETURN NEXT; -- return single empty string
ELSE
RETURN QUERY EXECUTE
'SELECT value FROM ' || _t; -- return set of values
END IF;
END
$func$;
Call:
SELECT * FROM f_tbl_value('my_table');
Or:
SELECT * FROM f_tbl_value('my_table', 'my_schema');
Assuming you want a set of rows with a single text column or an empty string if the table does not exist.
Also assuming that a column value exists if the given table exists. You could test for that, too, but you didn't ask for that.
Both input parameters are only case sensitive if double-quoted. Just like identifiers are handled in SQL statements.
The schema name defaults to 'public' in my example. Adapt to your needs. You could even ignore the schema completely and default to the current search_path.
to_regclass() is new in Postgres 9.4. For older versions substitute:
IF EXISTS (
SELECT FROM information_schema.tables
WHERE table_schema = _schema
AND table_name = _tbl
) THEN ...
This is actually more accurate, because it tests exactly what you need. More options and detailed explanation:
Table name as a PostgreSQL function parameter
Always defend against SQL injection when working with dynamic SQL! The cast to regclass does the trick here. More details:
How to check if a table exists in a given schema
If you are selecting only one column then the COALESCE() function should be able to do the trick for you
SELECT COALESCE( value, '{}'::text[] ) FROM myschema.mytable
If you require more rows you may require to create a function with types.
I'd like to write a PLPGSQL (targeting PostgreSQL 9.3) function which will return multiple result sets (i.e. when accessed through JDBC I'll call getMoreResults() to move to the next set of rows), but everything I've tried either gives me a syntax error or simply concatenates everything together into a single result set.
Here is a simple example that illustrates the issue:
CREATE TYPE my_type AS (a BIGINT, b TEXT, c DOUBLE PRECISION);
CREATE FUNCTION my_func(_arg1 TEXT, _arg2 TEXT)
RETURNS SETOF my_type
AS $$
BEGIN
RETURN QUERY SELECT a, b, c FROM table1 WHERE d = _arg1 AND e = _arg2;
RETURN QUERY SELECT a, b, c FROM table2 WHERE d = _arg1 AND e = _arg2;
END;
$$ LANGUAGE PLPGSQL;
When I run this the rows from both table1 and table2 get concatenated together into a single result set.
I've also tried something like
CREATE FUNCTION my_func(_arg1 TEXT, _arg2 TEXT)
RETURNS SETOF TABLE(a BIGINT, b TEXT, c DOUBLE PRECISION)
But this just results in a cryptic syntax error: ERROR: syntax error at end of input.
For completeness sake here is the Java code I'd like to use to process the results. I'm fairly certain the issue is on the db function side, but it's possible I'm misunderstanding how the JDBC API is supposed to work.
(Error handling and resource closing removed for readability)
PreparedStatement statement = connection.prepareStatement("SELECT * FROM my_func(?, ?);");
statement.setString(1, "foo");
statement.setString(2, "bar");
statement.execute();
ResultSet rs1 = statement.getResultSet();
while(rs1.next()) {
// Process first result set
}
statement.getMoreResults();
ResultSet rs2 = statement.getResultSet();
while(rs2.next()) {
// Process second result set
}
Based on the searching I've done so far it seems like this type of solution is supposed to be supported by JDBC and PostgreSQL, but I can't find any explicit examples of it in action.
The only way you can get multiple resultsets from PostgreSQL (at time of writing - current as of PostgreSQL 9.4) is by returning refcursors.
Define your function RETURNS SETOF refcursor then return a cursor for each resultset. PgJDBC will recognise that it's a refcursor and fetch it for you; see the documentation.
Slightly modifying your question, but if you don't have to use a function, you can get multiple ResultSets from PostgreSQL exactly as you normally would with JDBC. Just concatenate your queries and process each result set using getMoreResults(). See Queries returning multiple result sets.
I'm executing an UPDATE statement in a TADOQuery and I'm using parameters for a few things. Initially, this was working just fine, but I added another parameter for the table name and field name, and now it's breaking.
The code looks like this:
Q.SQL.Text:= 'update :tablename set :fieldname = :newid where :fieldname = :oldid';
Q.Parameters.ParamValues['tablename']:= TableName;
Q.Parameters.ParamValues['fieldname']:= FieldName;
Q.Parameters.ParamValues['oldid']:= OldID;
Q.Parameters.ParamValues['newid']:= NewID;
And the error I get:
I'm assuming this is because I'm using this field name twice. I can overcome this by using another unique field name for the second time it's used, however I still have another error:
How do I use the parameters to specify the table and field to update?
Query parameters aren't designed to parameterize table names.
What you can do is use placeholders for the table name(s) in your SQL, and then use the Format function to replace those with the table name(s), and then use parameters for the other values as usual. This is still relatively safe from SQL injection (the malevolent person would have to know the precise table names, the specific SQL statement being used, and values to provide for parameters).
const
QryText = 'update %s set :fieldname = :newid where :fieldname = :oldid';
begin
Q.SQL.Text := Format(QryText, [TableName]);
Q.Parameters.ParamValues['fieldname'] := FieldName;
Q.Parameters.ParamValues['oldid'] := OldID;
Q.Parameters.ParamValues['newid'] := NewID;
...
end;