# LibPQ.jl in 2026: Maintenance status and PostgreSQL alternatives

**URL:** <https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495>\
**Category:** Package Management\
**Created:** [September 16, 2026, 11:09am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495 "2026-09-16T11:09:05Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 16, 2026, 11:09am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/1 "2026-09-16T11:09:05Z")

</div>

Hi everyone,

With the upcoming Julia 1.13 release, [LibPQ.jl](https://github.com/JuliaDatabases/LibPQ.jl) seems to be showing signs of being unmaintained, and there are breaking/compatibility issues emerging.

What is the current status of the package within JuliaDatabases? Are there plans to transfer maintenance, cut a new release, or is the community rallying around an alternative PostgreSQL interface?

I’d appreciate any insights into the roadmap or how to help move it forward

---

<div class="post-metadata">

**Author:** ![grahamstark](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/grahamstark/32/5200_2.png) [@grahamstark](https://discourse.julialang.org/u/grahamstark)\
**Post date:** [September 16, 2026, 12:39pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/2 "2026-09-16T12:39:36Z")

</div>

What are the compatibility issues you see? I’ve been using LibPQ recently, including with the 1.13 Julia release, and haven’t encountered a problem so far.

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 16, 2026, 1:47pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/3 "2026-09-16T13:47:57Z")

</div>

This post was temporarily hidden by the community for possibly being off-topic, unfocused, inappropriate, or spammy.

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 16, 2026, 4:50pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/4 "2026-09-16T16:50:20Z")

</div>

I’ve used AI to fix my bad english in the last post and got a ban. So, in my bad english now…

“Broken on 1.13” was an unhappy phrase. But the package has pinned apps like Decimals, and others.

So, this could be a problem in the near future.

---

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [September 16, 2026, 10:18pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/5 "2026-09-16T22:18:56Z")

</div>

Your post reminded me I’ve been meaning to publicly announce [GitHub - JuliaDatabases/Postgres.jl · GitHub](https://github.com/JuliaDatabases/Postgres.jl), a native-Julia implementation of the Postgres wire protocol. I’ve been using it in a few production apps for well over a year, so a lot of the basics work great, but recently (with help of AI!), I’ve been trying to build out additional support for features. Try it out if you’re interested!

---

<div class="post-metadata">

**Author:** ![Jake](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jake/32/46007_2.png) [@Jake](https://discourse.julialang.org/u/Jake)\
**Post date:** [September 17, 2026, 12:29am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/6 "2026-09-17T00:29:43Z")

</div>

How is interoperability with FunSQL.jl?

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 17, 2026, 2:13am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/7 "2026-09-17T02:13:16Z")

</div>

Hi @quinnj

I’m (plus AI) developing [PormG.jl](https://github.com/PingoLee/PormG.jl), I’m making a lot of progress, but there’s still so much to do. I’ll take a look in your pkg, but I have just a question: why don’t you do a wrapper from c++?

And just one more question about Postgres.jl, are you welcome for contribution?

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 17, 2026, 12:58pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/8 "2026-09-17T12:58:31Z")

</div>

Sorry @quinnj, looking at your project, I see you want to build a pure Julia app.

However, TLS is a real blocker for me. Would a pluggable TLS provider (like OpenSSL.jl) be feasible to you? If so, I can draft a proposal.

---

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [September 17, 2026, 1:59pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/9 "2026-09-17T13:59:23Z")

</div>

> [@Jake](#):
>
> How is interoperability with [FunSQL.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/FunSQL)?

Should work fine! As far as I understand, FunSQL.jl is based on DBInterface.jl, which Postgres.jl fully supports.

> [@Rafael\_Brus](#):
>
> And just one more question about [Postgres.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/Postgres), are you welcome for contribution?

Of course! Always welcome contributions via either issue reports or PRs.

> [@Rafael\_Brus](#):
>
> However, TLS is a real blocker for me. Would a pluggable TLS provider (like [OpenSSL.jl](https://juliaregistries.github.io/General/packages/redirect_to_repo/OpenSSL)) be feasible to you? If so, I can draft a proposal.

Postgres.jl already fully supports TLS via the Réseau.jl package, a native Julia TCP/TLS library.

---

<div class="post-metadata">

**Author:** ![tp2750](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tp2750/32/207806_2.png) [@tp2750](https://discourse.julialang.org/u/tp2750)\
**Post date:** [September 17, 2026, 7:36pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/10 "2026-09-17T19:36:45Z")

</div>

Thank you for sharing @quinnj .  
Do you plan to support Kerberos / GSSAPI authentication?

---

<div class="post-metadata">

**Author:** ![dnb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnb/32/47121_2.png) [@dnb](https://discourse.julialang.org/u/dnb)\
**Post date:** [September 17, 2026, 9:47pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/11 "2026-09-17T21:47:13Z")

</div>

I’ve been briefly testing out Postgres.jl and so far so good. One question - will you be adding the ability to set the default schema in the connection string? In LibPQ.Connection, one can pass in the options=-csearch\_path=\<schema\_name\> parameter.

Thanks!

---

<div class="post-metadata">

**Author:** ![kobusherbst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kobusherbst/32/19682_2.png) [@kobusherbst](https://discourse.julialang.org/u/kobusherbst)\
**Post date:** [September 18, 2026, 3:38am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/12 "2026-09-18T03:38:19Z")

</div>

> [@Rafael\_Brus](#):
>
> [LibPQ.jl](https://github.com/JuliaDatabases/LibPQ.jl)

I developed a private fork of LibPQ.jl to support PostgreSQL 18 OAuth authentication, if that is of interest to anyone. I didn’t make changes to any other aspect of LibPQ.

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 18, 2026, 6:27pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/13 "2026-09-18T18:27:25Z")

</div>

I would appreciate it if you could share it or, even better, make your repository public.

---

<div class="post-metadata">

**Author:** ![quinnj](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/quinnj/32/11_2.png) [@quinnj](https://discourse.julialang.org/u/quinnj)\
**Post date:** [September 18, 2026, 8:45pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/14 "2026-09-18T20:45:56Z")

</div>

> [@tp2750](#):
>
> Do you plan to support Kerberos / GSSAPI authentication?

Yes, this is now supported as of the 2.1.0 release.

> [@dnb](#):
>
> One question - will you be adding the ability to set the default schema in the connection string? In LibPQ.Connection, one can pass in the options=-csearch\_path=\<schema\_name\> parameter.

Yes, this is also supported as of the 2.1.0 release.

---

<div class="post-metadata">

**Author:** ![dnb](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dnb/32/47121_2.png) [@dnb](https://discourse.julialang.org/u/dnb)\
**Post date:** [September 18, 2026, 9:56pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/15 "2026-09-18T21:56:10Z")

</div>

Indeed it does. Thank you!

---

<div class="post-metadata">

**Author:** ![tp2750](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tp2750/32/207806_2.png) [@tp2750](https://discourse.julialang.org/u/tp2750)\
**Post date:** [September 19, 2026, 1:00am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/16 "2026-09-19T01:00:58Z")

</div>

Awesome!  
Thank you very much.

---

<div class="post-metadata">

**Author:** ![kobusherbst](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/kobusherbst/32/19682_2.png) [@kobusherbst](https://discourse.julialang.org/u/kobusherbst)\
**Post date:** [September 21, 2026, 12:38pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/17 "2026-09-21T12:38:18Z")

</div>

> **[GitHub - AHRIORG/LibPQ.jl: A Julia wrapper for libpq](https://github.com/AHRIORG/LibPQ.jl)**
>
> A Julia wrapper for libpq

It requires this version of LibPQ\_jll.jl compiled against PostgreSQL v18

> **[GitHub - AHRIORG/LibPQ\_jll.jl: AHRI\_TRE specific fork of LibPQ\_jll.jl](https://github.com/AHRIORG/LibPQ_jll.jl)**
>
> AHRI\_TRE specific fork of LibPQ\_jll.jl

This provides helper functions to make the OAuth use simpler:

> **[GitHub - AHRIORG/LibPQ\_OAuthFlows.jl: LibPQ\_OAuthFlows is a Julia package that adds...](https://github.com/AHRIORG/LibPQ_OAuthFlows.jl)**
>
> LibPQ\_OAuthFlows is a Julia package that adds reusable OAuth/OIDC login flows (including token caching and callback handling) for LibPQ, turning ID tokens into bearer-token provider state for PostgreSQL OAuth authentication.

These have only been tested with ORCID authentication.

---

<div class="post-metadata">

**Author:** ![Roger\_Powell](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roger_powell/32/21586_2.png) [@Roger\_Powell](https://discourse.julialang.org/u/Roger_Powell)\
**Post date:** [September 25, 2026, 12:20pm UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/18 "2026-09-25T12:20:34Z")

</div>

Hi @Rafael_Brus Your work on PormG.jl looks awesome! The other Julia ORM packages, AFAIK, are [PostgresORM.jl](https://github.com/JuliaPostgresORM) and [Jorm.jl](https://github.com/jcharistech/Jorm.jl). But I haven’t tried either. How does PormG.jl compare with these?

Your documentation is very full indeed, which is great to see. A long time ago I used Java Persistence API, Hybernate and Glassfish (for a physics-related project). I found ORM was a great way to organise code in Java.

It strikes me that you could store physical models from (say) [ModelingTooolkit.jl](https://github.com/SciML/ModelingToolkit.jl) neatly in a relational form. This would nicely de-couple data from function. My LibPQ - PostgreSQL - Julia experience was essentially in constructing a dynamic (not just kinematic) model of a robot arm from from a set of “components” (including gearboxes etc) from a database, auto-magically, into a [RigidBodyDynamics.jl](https://juliarobotics.org/RigidBodyDynamics.jl/stable/) model. The idea was that the physical data could be updated via a web app without that person needing to know anything about Julia. The schema was at worst in 3rd (Boyce/Codd) normal form so that the components were recognisable to the technicians and could be connected or disconnected at will. But there was no Julia ORM at that time, just LibPQ.jl

I just wish you would change PormG’s name as it rhymes too closely with something unpleasant! E.g. just “ORM.jl” ? Julia has structs, types, and functors but I am not sure that the “O” in ORM is a perfect match. You seem to be mapping functions to database tables. I have started having a play and, after a couple of weeks, I may report back!

---

<div class="post-metadata">

**Author:** ![Rafael\_Brus](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rafael_brus/32/23767_2.png) [@Rafael\_Brus](https://discourse.julialang.org/u/Rafael_Brus)\
**Post date:** [September 29, 2026, 2:21am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/19 "2026-09-29T02:21:27Z")

</div>

Hi @Roger_Powell,

I’ve been working on this project for a couple of years. I work with Django web apps for public health; Django is amazing, but Python is slow, so initially I built an API server using Genie.jl.

At that time, AI didn’t exist, and I was looking for syntax similar to the Django ORM; then, with my lack of imagination, I named it as **P** ingoLee **ORM** form **G** enie.jl

But I’m facing a lot of gaps in time and knowledge, so with AI, I’m starting another project, Nitro.jl (a fork of Oxygen.jl). Initially, I’m tried to contribute to both (Genie and Oxygen), but I didn’t have much luck.

So, Nitro.jl is a framework to build SPA web packages and API packages. Nitro.jl is getting very fast; PormG.jl is still targeting features, so i haven’t started performance optimizations yet. Both are made to be async.

PormG.jl has a very large collection of features (and a lot more to implement), and it was built to work with AI, including importing models directly from Postgres or Django Python models.

However, neither is registered yet; it’s `0.x` with breaking changes between releases. To make those breaking changes cheap, every change ships as a machine-readable upgrade entry (more below).

You can do:

```julia
using Pkg
Pkg.add(url="https://github.com/PingoLee/PormG.jl"); Pkg.add("LibPQ")

using PormG, LibPQ
PormG.setup() # db/connection.yml + db/models.jl
PormG.Configuration.load("db")
PormG.Migrations.import_models_from_postgres("db") # reverse-engineer your existing schema

PormG.@import_models "db/models.jl" models
import .models as M

```

**Using it with an AI agent** (Claude Code, Copilot, Cursor):

```julia
PormG.install_ai_skills() # writes .github/skills/pormg-usage/ into your project

```

Then ask this for your agent:

```julia-auto
Before bumping the PormG dependency, run PormG.upgrade_guide(from = v"<current pin>") and apply what it lists.

```

Usage examples

Using if:

```julia
using PormG, LibPQ, DataFrames
using PormG: Q, Qor

function search_results(; year = nothing, nationality = nothing,
                          podium_only = false, team = nothing, limit = 20)
    query = M.Result.objects
    query.values("raceid __year", "raceid__ name", "driverid__surname",
                 "constructorid__name", "positionorder", "points")

    if year !== nothing
        query.filter("raceid__year" => year)
    end
    if nationality !== nothing
        query.filter("driverid__nationality" => nationality)
    end
    if podium_only
        query.filter("positionorder__@lte" => 3)
    end
    if team !== nothing
        # either the team name or its reference
        query.filter(Qor("constructorid __name" => team, "constructorid__ constructorref" => team))
    end

    query.order_by("-raceid__year", "positionorder")
    query.limit(limit)
    return query |> DataFrame
end

```

With the same function, you can combine the filters in countless ways:

```julia
search_results(nationality = "Brazilian", podium_only = true)
search_results(nationality = "Brazilian", podium_only = true, year = 1988)
search_results(nationality = "Brazilian", team = "McLaren")
search_results(year = 2023, podium_only = true, team = "Red Bull")
search_results(nationality = "British", year = 2020, limit = 5)
search_results() # no filters, latest 20 results

```

At last:

```julia
# pormg_demo.jl — self-contained PormG demo on SQLite.
#
# julia pormg_demo.jl (or, in the REPL: include("pormg_demo.jl"))
#
# First time only:
# using Pkg
# Pkg.add(url = "https://github.com/PingoLee/PormG.jl"); Pkg.add(["SQLite", "DataFrames"])

using PormG, SQLite, DataFrames, Dates
using PormG.Functions: Count, Sum, Avg, Min, Case, When

# ── 1. A throwaway project folder in your home directory ─────────────────────
demo_dir = joinpath(homedir(), "pormg_demo")
rm(demo_dir; recursive = true, force = true) # fresh run every time
mkpath(joinpath(demo_dir, "db"))
cd(demo_dir)

write("db/connection.yml", """
default_env: dev

dev:
  adapter: SQLite
  database: f1_demo.sqlite
  config:
    change_db: true
    change_data: true
    time_zone: 'UTC'
""")

write("db/models.jl", """
module models
import PormG.Models

Circuit = Models.Model(
  circuitid = Models.IDField(),
  name = Models.CharField(max_length = 100),
  country = Models.CharField(max_length = 50),
)

Driver = Models.Model(
  driverid = Models.IDField(),
  forename = Models.CharField(max_length = 50),
  surname = Models.CharField(max_length = 50),
  nationality = Models.CharField(max_length = 50),
)

Constructor = Models.Model(
  constructorid = Models.IDField(),
  constructorref = Models.CharField(max_length = 50),
  name = Models.CharField(max_length = 50),
)

Race = Models.Model(
  raceid = Models.IDField(),
  year = Models.IntegerField(),
  name = Models.CharField(max_length = 100),
  date = Models.DateField(),
  circuitid = Models.ForeignKey(Circuit, on_delete = "CASCADE"),
)

Result = Models.Model(
  resultid = Models.IDField(),
  raceid = Models.ForeignKey(Race, on_delete = "CASCADE"),
  driverid = Models.ForeignKey(Driver, on_delete = "RESTRICT"),
  constructorid = Models.ForeignKey(Constructor, on_delete = "RESTRICT"),
  grid = Models.IntegerField(),
  positionorder = Models.IntegerField(),
  points = Models.FloatField(),
)

Models.set_models(@ __MODULE__ , @ __DIR__ )
end
""")

# ── 2. Configuration, models, schema ─────────────────────────────────────────
# Absolute path: under include(), a relative "db" would resolve next to this script, not in demo_dir
db = joinpath(demo_dir, "db")
PormG.Configuration.load(db)
include(joinpath(db, "models.jl"))
import .models as M

PormG.Migrations.init_migrations(db)
PormG.Migrations.makemigrations(db; interactive = false)
PormG.Migrations.migrate(db; interactive = false)

# ── 3. Seed a small slice of real F1 history ─────────────────────────────────
bulk_insert(M.Circuit.objects, DataFrame(
  circuitid = [1, 2, 3],
  name = ["Autódromo José Carlos Pace", "Circuit de Monaco", "Suzuka Circuit"],
  country = ["Brazil", "Monaco", "Japan"],
))

bulk_insert(M.Driver.objects, DataFrame(
  driverid = [1, 2, 3, 4, 5],
  forename = ["Ayrton", "Nelson", "Alain", "Nigel", "Gerhard"],
  surname = ["Senna", "Piquet", "Prost", "Mansell", "Berger"],
  nationality = ["Brazilian", "Brazilian", "French", "British", "Austrian"],
))

bulk_insert(M.Constructor.objects, DataFrame(
  constructorid = [1, 2, 3, 4],
  constructorref = ["mclaren", "williams", "ferrari", "benetton"],
  name = ["McLaren", "Williams", "Ferrari", "Benetton"],
))

bulk_insert(M.Race.objects, DataFrame(
  raceid = [1, 2, 3, 4],
  year = [1988, 1988, 1990, 1991],
  name = ["Monaco Grand Prix", "Japanese Grand Prix", "Japanese Grand Prix", "Brazilian Grand Prix"],
  date = [Date(1988, 5, 15), Date(1988, 10, 30), Date(1990, 10, 21), Date(1991, 3, 24)],
  circuitid = [2, 3, 3, 1],
))

bulk_insert(M.Result.objects, DataFrame(
  resultid = 1:12,
  raceid = [1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4],
  driverid = [3, 5, 1, 1, 3, 4, 2, 4, 1, 1, 4, 2],
  constructorid = [1, 3, 1, 1, 1, 2, 4, 3, 1, 1, 2, 4],
  grid = [2, 3, 1, 1, 2, 5, 6, 3, 1, 1, 3, 7],
  positionorder = [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3],
  points = [9.0, 6.0, 4.0, 9.0, 6.0, 4.0, 9.0, 6.0, 4.0, 10.0, 6.0, 4.0],
))

# ── 4. One function, many queries ────────────────────────────────────────────
"""
    search_results(; columns, group_by, metrics, year, nationality, podium_only, team,
                     min_wins, order, limit, show_sql)

Plain mode: `columns` picks what each result row shows.
Aggregate mode: pass `group_by` and every result collapses into one row per group, with the
`metrics` you ask for. The `GROUP BY` is built by PormG from the non-aggregate columns.

  group_by e.g. ["driverid__surname"]
  year 1988 or 1988:1990
  nationality "Brazilian" or ["Brazilian", "French"]
  team name ("McLaren") or reference ("mclaren")
  min_wins needs :wins in metrics; becomes HAVING
"""
function search_results(; columns = ["raceid __year", "raceid__ name", "driverid __surname", "constructorid__ name", "positionorder", "points"],
                          group_by = nothing, metrics = [:races, :wins, :points],
                          year = nothing, nationality = nothing, podium_only = false, team = nothing,
                          min_wins = nothing, order = nothing, limit = 20, show_sql = true)
    query = M.Result.objects

    # ── what to show ──
    if group_by === nothing
        query.values(columns...)
    else
        aggregates = Dict(
            :races => "races" => Count("resultid"),
            :wins => "wins" => Sum(Case(When("positionorder" => 1, then = 1), default = 0)),
            :podiums => "podiums" => Sum(Case(When("positionorder__@lte" => 3, then = 1), default = 0)),
            :points => "points" => Sum("points"),
            :avg_points => "avg_points" => Avg("points"),
            :best_grid => "best_grid" => Min("grid"),
        )
        query.values(group_by..., (aggregates[m] for m in metrics)...)
    end

    # ── which rows ──
    if year isa AbstractRange
        query.filter("raceid __year__ @gte" => first(year), "raceid __year__ @lte" => last(year))
    elseif year !== nothing
        query.filter("raceid__year" => year)
    end
    if nationality isa AbstractVector
        query.filter("driverid __nationality__ @in" => nationality)
    elseif nationality !== nothing
        query.filter("driverid__nationality" => nationality)
    end
    if podium_only
        query.filter("positionorder__@lte" => 3)
    end
    if team !== nothing
        query.filter(Qor("constructorid __name" => team, "constructorid__ constructorref" => team))
    end
    if min_wins !== nothing && group_by !== nothing
        query.filter("wins__@gte" => min_wins) # filter on an aggregate → HAVING
    end

    # ── how to sort ──
    if order !== nothing
        query.order_by(order...)
    elseif group_by === nothing
        query.order_by("-raceid__year", "positionorder")
    else
        query.order_by("-" * first(aggregates[m] for m in metrics).first)
    end

    query.limit(limit)

    if show_sql # inspection never runs the query
        info = inspect_query(query)
        println(info[:sql_text])
        println("-- parameters: ", info[:parameters])
    end
    return query |> DataFrame
end

# Prints the title, the SQL, then the rows
function demo(title; kwargs...)
    println("\n══ ", title)
    println(search_results(; kwargs...))
end

demo("Brazilian podiums";
     nationality = "Brazilian", podium_only = true)

demo("Only three columns, 1988";
     columns = ["driverid __surname", "raceid__ name", "points"], year = 1988)

demo("McLaren, by reference, 1988–1990";
     team = "mclaren", year = 1988:1990)

demo("Per driver: races, wins, points";
     group_by = ["driverid__surname"])

demo("Per driver, only winners, sorted by wins";
     group_by = ["driverid__surname"], metrics = [:wins, :podiums, :points],
     min_wins = 1, order = ["-wins", "driverid__surname"])

demo("Per team and season";
     group_by = ["constructorid __name", "raceid__ year"],
     metrics = [:points, :avg_points], order = ["raceid__year", "-points"])

demo("Per nationality, Brazil vs France";
     group_by = ["driverid__nationality"], nationality = ["Brazilian", "French"],
     metrics = [:podiums, :wins])

demo("Per circuit country";
     group_by = ["raceid __circuitid__ country"], metrics = [:races, :points])

```

---

<div class="post-metadata">

**Author:** ![Roger\_Powell](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/roger_powell/32/21586_2.png) [@Roger\_Powell](https://discourse.julialang.org/u/Roger_Powell)\
**Post date:** [September 29, 2026, 10:01am UTC](https://discourse.julialang.org/t/libpq-jl-in-2026-maintenance-status-and-postgresql-alternatives/139495/20 "2026-09-29T10:01:50Z")

</div>

@Rafael_Brus Many thanks for the very detailed reply. I am not used to using agents or AI in coding (too old) but you have shown me how to start migrating some of the work I’ve done. I will continue to explore. I’ve got quite a lot on at the moment so I don’t have many hours to devote to this. But what you’ve done looks amazing. Thank you! 😀
