sqlc icon indicating copy to clipboard operation
sqlc copied to clipboard

sqlc Forgets the Column Name When Functions Applied on Both Sides

Open mohammed90 opened this issue 4 years ago • 6 comments

Correct Behavior

The following query:

-- name: selectUserByUsername :one
SELECT
	*
FROM
	users
WHERE 
	LOWER(username) = $1 AND age = $2
LIMIT 1;

Generates this Go code:

type selectUserByUsernameParams struct {
	Username string
	Age      sql.NullInt32
}

Unexpected Behavior

Note how here I applied the LOWER() function on both sides of the comparison:

-- name: selectUserByUsername :one
SELECT
	*
FROM
	users
WHERE 
	LOWER(username) = LOWER($1) AND age = $2
LIMIT 1;

The generated code ends up having Lower as the parameter name rather than Username, which is the column name:

type selectUserByUsernameParams struct {
	Lower string
	Age   sql.NullInt32
}

The AND age = $2 is just there to force the generation of the struct. Eliminating the AND age = $2 will still name the argument/parameter lower rather than username. I think the struct makes it more obvious to catch.

Playground

https://play.sqlc.dev/p/3d035e555d980f57e8a93672f9a1aec4a25fbcce02edff8946a528f3fc989b92

mohammed90 avatar Nov 20 '20 23:11 mohammed90

Dupe of https://github.com/kyleconroy/sqlc/issues/229, I think.

kevinburke1 avatar Nov 20 '20 23:11 kevinburke1

Kinda but not exactly. It's expected if it were part of the selected columns, because SQL engines end up naming the column with that. It's unexpected when it's one of the comparison parameters of WHERE. It generates the correct field name if I omit the use of LOWER on the right side.

mohammed90 avatar Nov 21 '20 00:11 mohammed90

Not sure if this is the same issue but:

-- name: GetUserByEmailAndPassword :one
SELECT
  "email",
  "updated_at",
  "created_at",
  "id"
FROM
  "users"
WHERE
  "email" = $1
  AND "password" = crypt($2, "password")
LIMIT 1;

results in:

type GetUserByEmailAndPasswordParams struct {
	Email string `json:"email"`
	Crypt string `json:"crypt"`
}

but perhaps should be:

type GetUserByEmailAndPasswordParams struct {
	Email string `json:"email"`
	Password string `json:"password"`
}

joshdcuneo avatar Oct 14 '21 12:10 joshdcuneo

This has been fixed

https://play.sqlc.dev/p/8b270e64462166a51326f9c26712b83cc39b5e69c67617b6d14185604ab33e07

kyleconroy avatar Jun 12 '22 17:06 kyleconroy

This has been fixed

https://play.sqlc.dev/p/8b270e64462166a51326f9c26712b83cc39b5e69c67617b6d14185604ab33e07

Is the field name becoming Lower expected behavior?

https://play.sqlc.dev/p/3d035e555d980f57e8a93672f9a1aec4a25fbcce02edff8946a528f3fc989b92

mohammed90 avatar Jun 12 '22 17:06 mohammed90

Whoops, I checked the first case instead of the second case. Reopened

kyleconroy avatar Jun 12 '22 17:06 kyleconroy

This happens when LOWER is used on the values side of an INSERT statement as well. The InsertUserParams struct name is "Lower" instead of "Username".

https://play.sqlc.dev/p/97df49dbc642f1d1c1a3e91c7594ea7da9e5ded863855db3af48b55d554b2e73

chrislentz avatar Jan 31 '23 02:01 chrislentz

I was able to get around with by using sqlc's named parameters. My problem was when I had a query like

-- name: GetUserByFirstAndLastName :many
SELECT * FROM users
WHERE LOWER(first_name) LIKE LOWER($1) AND LOWER(last_name) LIKE LOWER($2);

the generated result was

type GetUserByFirstAndLastNameParams struct {
	Lower   string
	Lower_2 string
}

To get around this, I used named parameters:

-- name: GetUserByFirstAndLastName :many
SELECT * FROM users
WHERE LOWER(first_name) LIKE LOWER(@first_name::text) AND LOWER(last_name) LIKE LOWER(@last_name::text);

which resulted in

type GetUserByFirstAndLastNameParams struct {
	FirstName string
	LastName  string
}

Hope this helps.

fetchkash avatar Aug 21 '23 18:08 fetchkash