-
Notifications
You must be signed in to change notification settings - Fork 29
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: go type mapping per query argument #73
Conversation
Extends the query annotation to specify custom go types using the same qualified go type format as for the general custom type mapping. Closes jschaf#46
@jschaf This is something I need for one of my use case (see #46 for details) so I'm working on an implementation. It's missing the mapping of return columns, tests, and docs, but you can have a look at the mapping of query arguments (inputs) already if you have the time. If you think this is something you are definitely not interested in merging, I'd appreciate if you would let me know so I don't waste too much time on it 😉 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thank you for diving into the codebase. I'm in favor of the change so happy to keep working with you on this PR.
The main thing I'm focused on is how to present the options for the user to keep the interface as simple as possible. I've had a number of requests which essentially boil down to more configurable type mappings (#65). I think the answer is to support an intermediate unmarshal type that's responsible for converting the Postgres type into the destination Go type, so timestamptz
to time.Time
in the below example.
--go-type timestamptz=github.com/jschaf.TimeUnmarshaller=time.Time
I'd like the syntax to be pretty similar between the query files and command line so I'm interested in what the SQL file would look like (ignoring the intermediate idea since that's out of scope for this work).
-- name: FooBar go-type: timestamptz=time.Time :many
paramTypeOverrides := make(map[string]string, 4) | ||
annotationPos := len(doc.List) - 1 | ||
|
||
for ; annotationPos > 0; annotationPos-- { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is a bit messier than I'm comfortable with. Might be time to do a proper parse, something like:
- split the line on whitespace
- parse each token
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was taking insipiration from how the name: :kind
annotation was parsed but I agree it is a bit messy and can be improved. (N.B.)I've moved the parsing of go types into it's own function to clean things up already. I just need to push it.
Can you elaborate on what you mean by proper parser? Do you want a custom built DSL and parser or do you have something in mind to be reused?
One idea would be to find the name:
line, then parse everything that's afterwards in a known language like TOML (and we can enforce the structure of it). This would have the advantage to reusue a known configuration language and libs to parse it. However while it's close to the CLI it is a bit different, notably spaces around the equal are allowed and the values (i.e. go types) have to be quoted.
Which way would you like to go?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To give an example of what I mean with TOML, it would work like this:
-- name: GetDailyFlightsFromAircraft :many
-- [arg]
-- day = "github.com/0xjac/custom-project/types.Day"
-- [return]
-- departure = "time.Time"
-- eta = "*time.Time"
SELECT f.flight_number, f.departure, f.arrival, f.eta
FROM flights f
WHERE pggen.arg("day") <= f.departure AND f.departure < pggen.arg("day") + 1
ORDER BY f.departure DESC;
We could also add the name
and kind
as top-level keys but it should still support then current name:
notation for compatibility.
SourceSQL string // the complete sql query as it appeared in the source file | ||
PreparedSQL string // the sql query with args replaced by $1, $2, etc. | ||
ParamNames []string // the name of each param in the PreparedSQL, the nth entry is the $n+1 param | ||
ParamTypeOverrides map[string]string // map of Go type to override the Pg type of a param |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: I think this can be ParamGoTypes
with a doc of something like:
map of user-specified Go types to use for the arg.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sounds good, and for the return value I would then name it accordingly:
ResultGoTypes map[string]string // map of user-specified Go type for the result columns.
Interesting. I can definitely adapt the parsing to be more like the command line arguments. The current format matches what is specified in #46, which in turn is inspired by the current ( -- name: GetDailyFlightsFromAircraft :many
-- arg: day=github.com/0xjac/custom-project/types.Day
-- return: departure=time.Time
-- return: eta=*time.Time
SELECT f.flight_number, f.departure, f.arrival, f.eta
FROM flights f
WHERE pggen.arg("day") <= f.departure AND f.departure < pggen.arg("day") + 1
ORDER BY f.departure DESC; Few things to notice:
The intermediate unmarshal type idea is interesting but indeed out of scope. Here is a few thoughts that might help tho: |
Nice, detailed writeup! Agreed that we probably need multi-line with this change. Couple tweaks:
-- pggen: name: GetDailyFlightsFromAircraft :many
-- pggen: argType: day=github.com/0xjac/custom-project/types.Day
-- pggen: columnType: departure=time.Time I think you nailed the precedence. Enforcing order of I approve of the design with the naming changes ( Sounds like you have a reasonable handle on how to approach the implementation but let me know if a high-level breakdown would help. From my end, I'll be looking closely at:
|
@jschaf Overall it all seems good. There are just a few things to note:
Regarding tests, I will obviously provide tests with this PR. I'm still in the playground/specs phase now but once that is done, I'll start adding the required tests. |
I think not. Easier to start strict and allow flexibility later.
Agreed, we're aligned there.
Sweet, sounds like a plan. |
Extends the query annotation to specify custom go types using the same
qualified go type format as for the general custom type mapping.
Closes #46