sqlc icon indicating copy to clipboard operation
sqlc copied to clipboard

Paramaters matched to JSON fields are the wrong type

Open kyleconroy opened this issue 5 years ago • 1 comments

These parameters should end up as interface{}, not json.RawMessage.

CREATE TABLE "user" (
    "id" INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    "metadata" JSONB
)

-- name: FindByAddress :one
SELECT * FROM "user" WHERE "metadata"->>'address1' = $1 LIMIT 1
type FindByAddressParams struct {
	metadata json.RawMessage
}

https://play.sqlc.dev/p/cd6227106fb8fb969d1fc9699dd9c03046e26578c589268e30d23573f67037bb

kyleconroy avatar Oct 23 '20 16:10 kyleconroy

Hi @kyleconroy, I have the same problem as in issue #738, I have table like this:

CREATE TABLE "user" (
    "id" INT PRIMARY KEY GENERATED ALWAYS AS IDENTITY,
    "metadata" JSONB
)

but instead of having an object, I have an array of users:

[
    {
        "lic":"1137868E",
        "firstname":"Joe",
        "lastname":"Bag"
    },
    {
        "lic":"1295364R",
        "firstname":"HA",
        "lastname":"Dégue"
    },
]

then I wrote a query to get a user by "lic"

-- name: FindUserByLic :one
SELECT * FROM user AS u WHERE u.deleted_at IS NULL AND u.metadata @> $1; -- > $1 suppose to be like this '[{"lic":"1295364R"}]'

and I got this parameter:

type FindUserByLic struct {
	metadata json.RawMessage
}

here is my code:

	lic, err := json.Marshal(fmt.Sprintf(`'[{"lic":"%s"}]'`, req.Lic))
	if err != nil {
		return err
	}
	arg := db.FindUserByLicParams{
		Metadata: lic,
	}

	contests, err := server.store.FindUserByLic(ctx, arg) //find user by lic
	if err != nil {
		return err
	}

but in the end I had an empty array :/

Abdillah-Epi avatar May 31 '21 07:05 Abdillah-Epi

That is not a bug, I have a solution for that or any parametrized representation of jsonb operations, the Postgres way.

First you need to define the datatype of your parameter on your sqlc definitions, for example:

... and metadata @> sqlc.arg(metadata)::jsonb -- Here you expect to input: '[{"id":"1234567890"}]'

After generating the code, you would expect the input to be of type pgtype.JSONB.

Then in your go code, you would have to have an interface conversion to JSONB:

func InterfaceToJSONB(data interface{}) pgtype.JSONB {
	var jsonB pgtype.JSONB
	err := jsonB.Set(data)
	return jsonB
}

Last, running your query, you have this:

param3 := "1234567890"
someData, err := q.SomeQueryName(ctx, db.SomeQueryNameParams{
		ParamName1:  param1,
		ParamName2: param2,
		Metadata: InterfaceToJSONB(`[{"id":"` + param3 + `"}]`),
	})

And works as you would normally write your query in the console.

speix avatar Sep 26 '23 17:09 speix

This is fixed in v1.23.0 by enabling the database-backed query analyzer. We added a test case for this issue so it won’t break in the future.

You can play around with the working example on the playground

kyleconroy avatar Oct 24 '23 23:10 kyleconroy