Erlquery - compile query dsl's into erlang modules

I made a rebar plugin to precompile ‘erlquery’ files into erlang modules.

erlquery files are meant to feel like erlang modules. Inside the functions of the modules are the raw queries for whatever database dsl you are using. Calling these functions in erlang will trigger the query to your database using whatever configured query executor specified.

My motivation for making this tool was being able to write raw sql queries without string literals. raw queries are powerful but they are not aesthetically pleasing in erlang and can be error prone.

I have published erlquery to hex and have used it for a few small projects.

You can read more about erlquery in the links in below.

Links:
erlquery
rebar3_erlquery
blog post

6 Likes

Thank you for sharing!

I also tend to use a similar approach in my projects, although a bit different: I load the queries from the priv directory at load time using on_load, compile the queries to convert named parameters to positional parameters and store them into a persistent_term.

What I like about your approach is that it would in theory be possible to provide type specs and type the queries to make dialyzer happy.

In short, what would you think if erlquery:

  • Supported named parameters, so that the query functions took a map instead of a list;
  • Made code generation optional;
  • Added runtime type validation both for parameters and results;
  • Added typespecs when using code generation.

Thanks again.

2 Likes

Hi! That approach works, and is similar to my initial dumb idea of using a gen server to store the queries at runtime. I think that since you are storing in persistent_term that is closer to my precompiled approach. I’m curious how you are turning the named params into positional params. I feel like in erlang this might be helpful, but how this relates back to the sql query I would need to see.

Adding typespecs to erlquery seems like a natural addition. I almost did it, but I wasnt sure how the precompiled module would behave with dialyzer so I think that is a next step for experimentation. Few things I’m not sure about, does dialyzer look in the project src or the _build/src? If the former, than erlquery wont help, but if the latter than it will work. If it can be proven to work than I think typespecs need to be added to erlquery.

Named params seem like a good idea. I feel they are less helpful if typespecs arent included. Putting the args in a list feels primitive, but it is easy and intuitive how that interacts with the query driver. Sql langs are all positional anyways so you have to think in positional params at some point.

What would be a good reason to make code generation optional? Having a hard time thinking of one since the whole point of erlquery is to generate code :stuck_out_tongue_closed_eyes:. Open to learning one of them though.

I do think there is a natural synergy with runtime validation. I found myself going down a deeper rabbit hole that felt out of scope for erlquery. It would be really cool to generate the runtime validators at compile time. I think I would like to see the bulk of that work going to a different library like liver. And then erlquery can use it as a dependency or a plugin.

So basically, we’ve had the same ideas!!

1 Like

I’ve released a new version of erlquery with some updates I would like to share.

You can read the now published docs to see all the features!

Updates:

  • query attribute is optional. In this case only the query text will be returned from generated functions. Seems trivial, but there are use cases where it is helpful.
  • query attribute supports arity 3. In this case the first argument to the generated functions is the database connection, the other arguments are the same as query/2.
  • Erlang like comments are supported in erlquery modules. Ignores % inside of quotes (only single quotes for now) as this is common pattern in SQL queries.
  • behaviour attributes are supported in erlquery modules.
  • codegen generates erlang forms instead of transpiling Erlang code. This means compile:forms is used on the output of erlquery:codegen instead of compile:file.
  • Actually using ex_doc to publish docs to hexpm.

rebar3_erlquery has also been updated to the latest version of erlquery where it uses compile:forms instead of compiling a temporary text file. It also uses the debug_info flag on compilation so that the outputted beam files can be used by tools like Dialyzer.

Most of these changes are quality of life improvements that have been inspired by direct usage of erlquery. The only major feature I can think of is parsing/generating typespecs. For now I’m quite happy with erlquery and I hope others will enjoy it as well!

2 Likes