Skip to content

join and select now accept both tables and subquery aliases - #19

Merged
kiranandcode merged 5 commits into
kiranandcode:masterfrom
royneary:subquery-aliases
May 16, 2026
Merged

kiranandcode merged 5 commits into
kiranandcode:masterfrom
royneary:subquery-aliases

Conversation

@royneary

@royneary royneary commented Apr 28, 2026

Copy link
Copy Markdown
Contributor

Hi Kiran,

thank you for your reply here. I actually implemented something along those lines before you replied and I'm putting it here to start a discussion.

My main motivation is to fix the issues mentioned in #3, caused by the automatic subquery aliases in the current implementation of Query.join. I also like the flexibility of joining with a table directly, as proposed in #11. I think joining with a table is a more common use case than joining with a subquery.

I implemented Query.as_ to assign an alias to a select query. I think it is similar to the named (<sql-query>) ~_as:"..." function that you proposed. It does not return a Query.t though, but instead a table reference and a list of expressions, just like StaticSchema.declare_table does. I then changed Query.join to accept a table reference instead of a query.

Given the following tables

module Person = struct
  let t, Expr.[ id; name ] =
    StaticSchema.declare_table
      schema
      ~name:"person"
      Schema.[ field "id" ~ty:Type.int; field "name" ~ty:Type.text ]
end

module Pet = struct
  let t, Expr.[ id; name; owner_id ] =
    StaticSchema.declare_table
      schema
      ~name:"pet"
      Schema.
        [ field "id" ~ty:Type.int
        ; field "name" ~ty:Type.text
        ; field "owner" ~ty:Type.int
        ]
end

this allows writing the following queries:

Query.select ~from:Person.t Expr.[ Person.id; Person.name; Pet.name ]
|> Query.join Pet.t ~on:Expr.(Person.id = Pet.owner_id)
|> Format.asprintf "%a" Query.pp

(* produces:
   SELECT person.id, person.name, pet.name
   FROM person INNER JOIN pet ON person.id = pet.owner
*)

let a_pets, Expr.[ a_pet_name; a_pet_owner_id ] =
  Query.select ~from:Pet.t Expr.[ Pet.name; Pet.owner_id ]
  |> Query.where Expr.(like ~pat:(s "A%") Pet.name)
  |> Query.as_ ~name:"a_pets"
in
Query.select ~from:Person.t Expr.[ Person.id; Person.name; a_pet_name ]
|> Query.join a_pets ~on:Expr.(Person.id = a_pet_owner_id)
|> Format.asprintf "%a" Query.pp

(* produces:
   SELECT person.id, person.name, a_pets.name
   FROM person INNER JOIN (
   SELECT pet.name, pet.owner
   FROM pet
   WHERE pet.name LIKE ?) AS a_pets ON person.id = a_pets.owner
*)

The most common use case (joining with a table) is now possible and it's very concise whereas the "join with a subquery" use case is longer, but less confusing than before because every subquery and the fields it returns are now only accessible through explicit aliases.

This required replacing the table_name type in the API with a parameterized type table_ref. It exists in two variants:

  • [`TABLE] table_ref represents physical tables
  • [`SUBQUERY`] table_ref represents subqueries

Either variant can be used as an argument of join and the ~from parameter of select. All other functions require the first variant.

I'm not fixed on names by the way. For example I would be fine with naming the as_ function named like you proposed, I just saw that Expr.as_ already exists and tried to be consistent.

So what do you think? This is definitely a breaking change. Maybe I overlooked a simpler route as I'm just starting to get familiar with the petrol code base.

There is still a FIXME remaining and no tests yet. But that should be the least problem once we know the general direction.

Fixes #3.
Requires #18.

@royneary
royneary marked this pull request as draft April 28, 2026 20:13
@royneary
royneary force-pushed the subquery-aliases branch from baa718b to 077d4f7 Compare May 14, 2026 11:34
royneary added 5 commits May 14, 2026 13:40
- make tests compile with caqti >= 2.0.0
- fix an sqlite error message
- use postgresql's peer authentication method
- allow running 'dune test' without the --profile=test flag
- add Query.as_
- replace table_name with table_ref in the public API
- join and select accept a [< `TABLE | `SUBQUERY] table_ref
- all other functions require a [`TABLE] table_ref
@royneary
royneary force-pushed the subquery-aliases branch from 077d4f7 to 60341fa Compare May 14, 2026 11:40
@royneary

Copy link
Copy Markdown
Contributor Author

I resolved the FIXME and added tests.

I also renamed the test binaries for consistency reasons (that's why suddenly there are 43 changed files).

@royneary
royneary marked this pull request as ready for review May 14, 2026 11:45
@royneary royneary changed the title allow passing subquery aliases or tables to join and select join and select now accept both tables and subquery aliases May 14, 2026
@kiranandcode

Copy link
Copy Markdown
Owner

Oh, nice, thanks Christian, this looks great!

@kiranandcode
kiranandcode merged commit 1486535 into kiranandcode:master May 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Example of join

2 participants