Skip to content

Conversation

@mcraveiro
Copy link
Contributor

Summary

Add support for parameterized queries using PostgreSQL's $1, $2, ... placeholder syntax. This enables safe parameter binding for:

  • Calling PostgreSQL functions without defining custom types
  • Executing dynamic queries with SQL injection protection
  • Passing NULL values via std::optional or std::nullopt

API

// Variadic execute with automatic type conversion
conn->execute("SELECT my_func($1, $2)", tenant_id, user_email);

// Supported types: string, numeric, bool, optional, nullptr
conn->execute("INSERT INTO t (a, b, c) VALUES ($1, $2, $3)",
              std::string("text"), 42, std::nullopt);

Implementation

  • Add PostgresV2Result::make() overload using PQexecParams
  • Add variadic execute() template to Connection with to_param() helper
  • Internal execute_params() method handles the actual libpq call

Test plan

  • execute_with_string_params - string and int parameters
  • execute_with_null_param - NULL via std::optional
  • execute_with_numeric_params - int, double, bool
  • execute_call_function - calling PL/pgSQL function

🤖 Generated with Claude Code

Support variadic execute with $1, $2, ... placeholders for safe parameter
binding. This allows calling PostgreSQL functions without defining custom
types.

Example:
  conn->execute("SELECT my_func($1, $2)", arg1, arg2);

Supported types: string, numeric, bool, optional, nullptr/nullopt for NULL.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@liuzicheng1987
Copy link
Collaborator

@mcraveiro , what exactly is the use case for this? I think for something like SELECT and INSERT we already use stored procedures under-the-hood. In my view, executing raw strings should really be the exception, not the norm. So I would be interested what specific problem you are trying to solve here to figure out whether we could find a solution that does not rely on raw strings. If we were to find out that the problem cannot be solved without raw strings, I would be willing to merge this.

@mcraveiro
Copy link
Contributor Author

mcraveiro commented Feb 3, 2026

Hi, thanks for the review. So, we have a large number of utility stored procs we run on our database. These will increase in the future but for now we do things such as provisioning a new tenant, de-provisioning an existing tenant, de-provision all test tenents and so on. These are "true" stored procedures, in the sense that we are not really trying to return a result set. In addition, sometimes we need to supply arguments (e.g. tenant id). Sometimes it's just a function with no arguments. Basically all of our non-CRUD infrastructure relies on these.

How I bumped into this approach: I was using session to execute SQL. Claude started to use libpq to address Gemini code review comments about SQL injection as we were using raw strings concatenated to call the procs. Then we did some investigation to figure out the idiomatic way to do this in sqlgen. We explored adding types, but it does not make a lot of sense to create types for these procs because we'll end up with a myriad of tiny types just to call the proc and retrieve the result. So I thought this change could handle both issues at the same time - I don't want to use raw libpq if I can avoid it.

But yes, this is useful only to call "real" stored procedures.

@mcraveiro
Copy link
Contributor Author

hm, actually the more we discuss this the more I am thinking maybe the correct solution is to add types for each stored proc (_arguments and _results?). The types could be named after the stored proc, have the stored proc name (much like we can do for schema and table). Does sqlgen support selecting from a proc at present? Let's take this proc as an example:

create or replace function ores_iam_purge_tenant_fn(
    p_tenant_id uuid
) returns void as $$
...

I could create a ores_iam_purge_tenant_fn_arguments type, and then use sqlgen to generate the select for it which would map to:

select ores_iam_purge_tenant_fn("SOME_TENANT");

Am I going in the right direction?

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.

2 participants