5

So I have this PostgreSQL function, which takes variable number of named arguments and returns list of corresponding items:

CREATE OR REPLACE FUNCTION read_user(
  _id BIGINT DEFAULT NULL,
  _phone VARCHAR(30) DEFAULT NULL,
  _type VARCHAR(15) DEFAULT NULL,
  _last VARCHAR(50) DEFAULT NULL,
  _first VARCHAR(50) DEFAULT NULL
) 
RETURNS setof T_USERS
AS $$ 
BEGIN
  RETURN QUERY
  SELECT * FROM T_USERS
  WHERE ( id = _id OR _id IS NULL )
    AND ( phone = _phone OR _phone IS NULL )
    AND ( type = _type OR _type IS NULL )
    AND ( last = _last OR _last IS NULL )
    AND ( first = _first OR _first IS NULL );
    EXCEPTION WHEN others THEN
      RAISE WARNING 'Transaction failed and was rolled back';
      RAISE NOTICE '% %', SQLERRM, SQLSTATE;
END
$$ LANGUAGE plpgsql;

So I can run polymorphic queries like these:

SELECT read_user(_id := 2);
SELECT read_user(_first := 'John', _last := 'Doe');

In Golang I can make something like:

stmt, err := db.Prepare("SELECT read_user(_id = ?)")

But how can I do the same, but with variable amount of read_user arguments? I'm using pq driver https://github.com/lib/pq.

2
  • What db driver are you using to talk to postgres? Commented Apr 29, 2017 at 21:35
  • @mkopriva link pq Commented Apr 29, 2017 at 21:40

1 Answer 1

4

You can construct your one statement by enumerating all the parameters with their placeholders and then you could pass nil explicitly where you don't have the parameter value.

stmt, err := db.Prepare("SELECT read_user(_id := $1, _phone := $2, _type := $3, _last := $4, _first := $5)")
if err != nil {
    // ...
}
stmt.Query(2, nil, nil, nil, nil) // result should be equivalent to `SELECT read_user(_id := 2)`
stmt.Query(nil, nil, nil, "Doe", "John") // result should be equivalent to `SELECT read_user(_first := 'John', _last := 'Doe')`

And if you want to have named parameters in Go as well, you can create a struct type to represent the parameters and a wrapper func that'll map that parameter type's fields into the query:

type readUserParams struct {
    Id    interface{}
    Phone interface{}
    Type  interface{}
    Last  interface{}
    First interface{}
}

func readUser(p *readUserParams) {
    stmt.Query(p.Id, p.Phone, p.Type, p.Last, p.First)
    // ...
}

readUser(&readUserParams{Id: 2})
readUser(&readUserParams{First: "John", Last:"Doe"})
Sign up to request clarification or add additional context in comments.

4 Comments

It works, but could you explain how nil is translated to named parameter in postgres? I expected postgres throw some kind of error considering assigning parameter to type, which is not expected.
I believe nil gets translated into NULL and calling the postgres function like this SELECT read_user(_id := 2, _phone := NULL, _type := NULL...) is valid.
This solution also works with the snowflake implementation of go :) godoc.org/github.com/snowflakedb/gosnowflake
@Pat - Your comment resolved my issue after 7 hours. Thanks.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.