Skip to content

Tool for capturing query parameters as if they were string interpolations #213

@c42f

Description

@c42f

I've recently written a macro tool for conveniently capturing query parameters as part of a private project. I've found it handy, so I wondered whether people would be interested in having this as part of LibPQ. Or if not, whether you have suggestions for a better home for it.

The general idea is that we should be able to build up queries by pasting together fragments of SQL and interpolated parameters like one might do with strings or Cmd backtick interpolation. But with all interpolations turned into SQL parameters for safety and consistency in converting those to SQL types.

To just paste the readme in here:

The readme

The main thing provided here is the @query macro to allow queries to be
constructed by normal-looking string interpolation but without danger of SQL
injection attacks.

Note that @query does not parse or understand the SQL source text as this
would be a lot of work. Instead, it keeps any literal SQL text you write as-is
and only treats the Julia-level string interpolations specially.

Use runquery to execute queries generated by @query.

Simple usage

Creating a table and inserting some values

conn = LibPQ.connection(your_connection_string)

runquery(conn, @query "create table foo (email text, userid integer)")

for (email,id) in [ ("[email protected]", 1)
                    ("[email protected]",   2)]
    runquery(conn, @query "insert into foo values ($email, $id)")
end

Thence:

julia> runquery(conn, @query "select * from foo") |> DataFrame
2×2 DataFrame
 Row │ email              userid
     │ String?            Int32?
─────┼───────────────────────────
   1 │ [email protected]       1
   2 │ [email protected]         2

Howto: Inserting values from a Julia array into a row

In some circumstances it can be useful to use splatting syntax to interpolate a
Julia collection into a comma-separated list of values. Generally simple scalar
parameters should be preferred for simplicity, but splatting can be useful on
occasion:

email_and_id = ("[email protected]", 3)
runquery(conn, @query "insert into foo values ($(email_and_id...))")

Howto: Using the in operator with a Julia collection

There's two ways to do this. First, using in and splatting syntax

julia> ids = (1,2)
       runquery(conn, @query "select * from foo where userid in ($(ids...))") |> DataFrame
       2×2 DataFrame
        Row │ email              userid
            │ String?            Int32?
       ─────┼───────────────────────────
          1 │ admin@example.com       1
          2 │ foo@example.com         2

Second, using the SQL any operator and simply passing a single SQL array parameter:

julia> ids = [1,2]
       runquery(conn, @query "select * from foo where userid = any($ids)") |> DataFrame
       2×2 DataFrame
        Row │ email              userid
            │ String?            Int32?
       ─────┼───────────────────────────
          1 │ [email protected]       1
          2 │ [email protected]         2

Howto: Building up a query from fragments

conn = LibPQ.connection(your_connection_string)

some_condition = true

x = 100
x = 20
# Example of an optional clauses - use empty @query() to disable it.
and_clause = some_condition ? @query("and y=$y") : @query()

# Interpolation of values produces SQL parameters; interpolating @query
# fragments adds them to the query.
q = @query "select * from table where x=$x $and_clause"
runquery(conn, q)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions