You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Min/Max on Datetimes result in interface{} as type #1574
so it looks like after putting a date through an aggregate-function sqlc can no longer determine its type? but it seems only to be the case for datetimes as far as I can see it.
Relevant log output
No response
Database schema
CREATETABLEactivities (
account_id BIGINTNOT NULL,
event_time TIMESTAMP WITH TIME ZONENOT NULL,
);
SQL queries
SELECTCOUNT(*) as NumOfActivities,
MIN(event_time) as MinDate,
MAX(event_time) as MaxDate
FROM activities
WHERE account_id = $1
Configuration
No response
Playground URL
No response
What operating system are you using?
macOS
What database engines are you using?
PostgreSQL
What type of code are you generating?
Go
Reacted by Mark, Sjors Gielen, hareku, Tadej Svetina, Hendrik Bursian and Mike Ross
Yeah I am experiencing this too. So then I changed it to cast the min or max so like: MIN(event_time)::timestamp as MinDate. However it fails when it returns null.
I then thought maybe I could use nulltime as a custom type but and I can't figure out how to use a custom type for custom queries like this.
Reacted by Jules Castéran, Louis Portay, Kirill Dubovikov, Dan Price, Radhi, Ed Harrod, Thành Phước and Brett
UPDATE I just built sqlc from the git to include all latest commits and this query still results into incorrect struct. min_col ends up being interface{}
UPDATE 2
Checked that this simple query also does not work
select
id,
min(col) as min_col -- col is an int column
from
table1
group by
id
Here, we can parse function parameters using outputColumnRefs and check if the argument type is nullable. Then, we can pass this info to Column structure. Minimalistic implementation that will work only for min looks like this (pardon for horrible code):
@kyleconroy , do you have an idea on how this issue should be handled? From the postgres doc it looks like all aggregation functions can return null, so maybe just mark all aggregation functions in catalogs for postgres, dolphin and sqlite? By that I mean to fill ReturnTypeNullable = true for every aggregation function. That would mean modifying sql-pc-gen somehow. And making manual edits for dolphin and sqlite catalogs.
For sql-pc-gen, I think this can be done by setting ReturnTypeNullable = true for all functions with prokind = 'a'. That is, for all aggregation functions.
I'm not the maintainer, but I can take a look at this soon because I hit this issue in our production project. I believe a refactor of this portion of the codebase just happened, so the preferred solution may change.
I was able to get around this for the time being by using:
COALESCE(extract(epoc from ....)), 0)
And then just using time.Unix(myIntColumn, 0) and using t.IsZero() to check for null times. Not ideal, but it will work until this issue gets closed.
@kdubovikov You are correct, it would just be sqlc-pg-gen that needs to change, and that the refactor didn't change much if Kyle goes with your other solution.
I was thinking about the sqlc-pg-gen a bit more:
For sql-pc-gen, I think this can be done by setting ReturnTypeNullable = true for all functions with prokind = 'a'. That is, for all aggregation functions.
And I realized that COUNT has a prokind of a, but cannot return a null value it will return 0 instead.
It should be noted that except for count, these functions return a null value when no rows are selected.
Looking at pg_catalog, I can't find any difference between MIN and COUNT that we could use to determine this, so we likely just need to make it a special case in the sql-pg-gen. I don't really like the idea of this though because it would be nice if we could determine nullability of the return type without manually annotating pg_catalog functions.
Bug 1
sqlc does not infer type correct for aggregated timestamp column
Workaround 1
Add cast to aggregate MAX(event_Time)::timestamptz
Bug 2
sqlc does not correctly infer the "nullability" of MIN / MAX
Workaround 2
Unfortunately, there is no workaround.
@kyleconroy I might use this opportunity to bring back up the "output nullability" idea I had a while ago, discussed here: #1525.
I think this would allow developers to help the sqlc type inferrence / null-inference in complex cases. Here is an example of how it would look. Of course I agree that the type-inferrence, null-inferrence should be better, but bugs like this are showstoppers for many teams.
CREATETABLEactivities (
account_id BIGINTNOT NULL,
event_time_nullable TIMESTAMP WITH TIME ZONE
);
-- name: Summary :oneSELECTCOUNT(*) as"NumOfActivities",
sqlc.nonnull(MIN(event_time_nullable)) as"MinDateNullable"FROM activities
WHERE account_id = $1AND event_time_nullable is not nullHAVINGCOUNT(*) >0;
Bug 1 sqlc does not infer type correct for aggregated timestamp column
Workaround 1 Add cast to aggregate MAX(event_Time)::timestamptz
Bug 2 sqlc does not correctly infer the "nullability" of MIN / MAX
Workaround 2 Unfortunately, there is no workaround.
@kyleconroy I might use this opportunity to bring back up the "output nullability" idea I had a while ago, discussed here: #1525.
I think this would allow developers to help the sqlc type inferrence / null-inference in complex cases. Here is an example of how it would look. Of course I agree that the type-inferrence, null-inferrence should be better, but bugs like this are showstoppers for many teams.
CREATETABLEactivities (
account_id BIGINTNOT NULL,
event_time_nullable TIMESTAMP WITH TIME ZONE
);
-- name: Summary :oneSELECTCOUNT(*) as"NumOfActivities",
sqlc.nonnull(MIN(event_time_nullable)) as"MinDateNullable"FROM activities
WHERE account_id = $1AND event_time_nullable is not nullHAVINGCOUNT(*) >0;
I like your solution, I did figure out a workaround for bug 2 though. You have to use with to assign a name to the table then you can override it in the sqlc.yaml
Query:
with agg as (select max(timestamp) as tstamp from prices where currency_id = $1 and source = $2)
select tstamp from agg;
@kyleconroy please correct me if I'm wrong, but doesn't the fix only apply for Postgres? I'm having the same issue here with SQLite.
I can reproduce the same issue with sqlite. I'm using a MAX aggregation function over a column of type TEXT, that I overwrite in my sqlc.yaml as a custom type DateTime. And the resulting model contains interface{} as a type.
The above mentioned workaround worked for me though. #1574 (comment)
Activity
jose-zenledger commentedon May 11, 2022
Yeah I am experiencing this too. So then I changed it to cast the
minormaxso like:MIN(event_time)::timestamp as MinDate. However it fails when it returns null.I then thought maybe I could use nulltime as a custom type but and I can't figure out how to use a custom type for custom queries like this.
Codelax commentedon May 16, 2022
I have this problem too. I would like to get
*time.Timebut I cannot get it when overriding type on aggregate.kdubovikov commentedon May 19, 2022
This problem reproduces not only for timestamp fields, but for int fields as well.
May be related to #1334
UPDATE I just built sqlc from the git to include all latest commits and this query still results into incorrect struct. min_col ends up being
interface{}UPDATE 2
Checked that this simple query also does not work
kdubovikov commentedon May 19, 2022
I think the problem lies in that definitions of
minfunction inpg_catalogdo not useFunction.ReturnTypeIsNullfield in any way. Here is the example: https://github.com/kyleconroy/sqlc/blob/996a73a27144a625080326f7340a11f837bd94fe/internal/engine/postgresql/pg_catalog.go#L14286But here this field is the only thing considered when we are inferring if the argument of the function is nullable or not https://github.com/kyleconroy/sqlc/blob/996a73a27144a625080326f7340a11f837bd94fe/internal/compiler/output_columns.go#L219
To solve this, I think we need to make so that for some built-in functions the output type should be inferred from their argument.
UPDATE
Ok, so I found how we can fix this.
https://github.com/kyleconroy/sqlc/blob/996a73a27144a625080326f7340a11f837bd94fe/internal/compiler/output_columns.go#L208
Here, we can parse function parameters using
outputColumnRefsand check if the argument type is nullable. Then, we can pass this info toColumnstructure. Minimalistic implementation that will work only forminlooks like this (pardon for horrible code):The code above solves issue for group by queries using
minfunction only.The problem is I am unsure for which functions we should apply such type inference and for which not. That will be dependent on the database and likely should be implemented somewhere at the catalog level. Also, aggregation functions may have array parameters, so those cases should be handled correctly too: https://www.postgresql.org/docs/9.5/functions-aggregate.html
@kyleconroy , do you have an idea on how this issue should be handled? From the postgres doc it looks like all aggregation functions can return null, so maybe just mark all aggregation functions in catalogs for postgres, dolphin and sqlite? By that I mean to fill
ReturnTypeNullable = truefor every aggregation function. That would mean modifyingsql-pc-gensomehow. And making manual edits fordolphinandsqlitecatalogs.For
sql-pc-gen, I think this can be done by settingReturnTypeNullable = truefor all functions withprokind = 'a'. That is, for all aggregation functions.ryan-berger commentedon May 24, 2022
I'm not the maintainer, but I can take a look at this soon because I hit this issue in our production project. I believe a refactor of this portion of the codebase just happened, so the preferred solution may change.
I was able to get around this for the time being by using:
COALESCE(extract(epoc from ....)), 0)And then just using
time.Unix(myIntColumn, 0)and usingt.IsZero()to check for null times. Not ideal, but it will work until this issue gets closed.kdubovikov commentedon May 24, 2022
@ryan-berger I think I have solved the issue in the linked pull request, but I need a bit more help from maintainers to fix tests
ryan-berger commentedon May 24, 2022
@kdubovikov You are correct, it would just be sqlc-pg-gen that needs to change, and that the refactor didn't change much if Kyle goes with your other solution.
I was thinking about the sqlc-pg-gen a bit more:
And I realized that
COUNThas aprokindofa, but cannot return a null value it will return 0 instead.The Postgres docs say:
Looking at
pg_catalog, I can't find any difference betweenMINandCOUNTthat we could use to determine this, so we likely just need to make it a special case in thesql-pg-gen. I don't really like the idea of this though because it would be nice if we could determine nullability of the return type without manually annotatingpg_catalogfunctions.@kyleconroy any thoughts on this?
kyleconroy commentedon Jun 4, 2022
A few thoughts. We should manually tag the aggregate functions that can return null. I honestly do do not know that min and max would return null.
As for type inference, it's a very large project to take on.
jose-zenledger commentedon Jun 6, 2022
I think it returns null if there are no rows in the set so:
skabbes commentedon Nov 27, 2022
I have summarized what has been discussed in this playground link:
https://play.sqlc.dev/p/cbe160df51a07333f3b91b2e13d9b81a0293c3c84f451e2f312e553cdc4ff81d
Bug 1
sqlc does not infer type correct for aggregated timestamp column
Workaround 1
Add cast to aggregate
MAX(event_Time)::timestamptzBug 2
sqlc does not correctly infer the "nullability" of MIN / MAX
Workaround 2
Unfortunately, there is no workaround.
@kyleconroy I might use this opportunity to bring back up the "output nullability" idea I had a while ago, discussed here:
#1525.
I think this would allow developers to help the sqlc type inferrence / null-inference in complex cases. Here is an example of how it would look. Of course I agree that the type-inferrence, null-inferrence should be better, but bugs like this are showstoppers for many teams.
@jose-zenledger @Neokil what do you think?
jose-zenledger commentedon Nov 27, 2022
I like your solution, I did figure out a workaround for bug 2 though. You have to use
withto assign a name to the table then you can override it in thesqlc.yamlQuery:
Then in
sqlc.yaml:feat(mysql): Implement cast function parser (#2473)
test(analyzer): Add testcase for #1574
fix(compiler): Pull in array information from analyzer (#2864)
kyleconroy commentedon Oct 24, 2023
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
ViRb3 commentedon Jul 1, 2024
@kyleconroy please correct me if I'm wrong, but doesn't the fix only apply for Postgres? I'm having the same issue here with SQLite.
hendrikbursian commentedon Aug 22, 2024
I can reproduce the same issue with sqlite. I'm using a
MAXaggregation function over a column of typeTEXT, that I overwrite in mysqlc.yamlas a custom typeDateTime. And the resulting model containsinterface{}as a type.The above mentioned workaround worked for me though.
#1574 (comment)
feat(mysql): Implement cast function parser (sqlc-dev#2473)
fix(compiler): Pull in array information from analyzer (sqlc-dev#2864)