pg_named_args 0.4.0

PostgreSQL named arguments
Documentation
# PostgreSQL named arguments

<!-- cargo-rdme start -->

This library allows one to use named arguments in PostgreSQL queries. This
library is especially aimed at supporting
[rust-postgres](https://github.com/sfackler/rust-postgres). A macro is provided
to rewrite queries with named arguments, into a query and its positional
arguments.

## Query Argument Syntax
The macro uses struct syntax for the named arguments.
The struct name `Args` is required to support rustfmt and rust-analyzer.
As can be seen from the example below, shorthand field initialization is also allowed for named arguments.

```rust
let location = "netherlands";
let period = Period {
    start: 2020,
    end: 2030,
};

let (query, args) = query_args!(
    r"
    SELECT location, time, report
    FROM weather_reports
    WHERE location = $location
        AND time BETWEEN $start AND $end
    ORDER BY location, time DESC
    ",
    Args {
        location,
        start: period.start,
        end: period.end,
    }
);
```
```rust
let rows = client.query(query, args).await?;
```
This expands to the equivalent of
```rust
let (query, args) = (
    r"
    SELECT location, time, report
    FROM weather_reports
    WHERE location = $1
        AND time BETWEEN $2 AND $3
    ORDER BY location, time DESC
    ",
    &[&location, &period.start, &period.end]
);
```

## Insert Syntax
For `INSERT`'s a special syntax is supported, which helps to avoid mismatches
between the list of column names and the values:

```rust
let location = "sweden";
let time = "monday";
let report = "sunny";

let (query, args) = query_args!(
    r"
    INSERT INTO weather_reports
        ( $[location, time, report] )
    VALUES
        ( $[..] )
    ",
    Args {
        location,
        time,
        report
    }
);
```
```rust
client.execute(query, args).await?;
```
The SQL would be
```sql
INSERT INTO weather_reports
    ( location, time, report )
VALUES
    ( $1, $2, $3 )
```

## Optional Parameter Syntax
When doing updates it can be useful to dynamically build SQL.

This is essential when for example a column must sometimes be updated, but the column is also nullable.
To differentiate between those cases, `pg_named_args` adds a `Update<T>` type which has the
`Update::Yes(val)` and `Update::No` variants. This type can be combined with the `Option` type as `Update<Option<T>>`.

The syntax to use optional parameter is like this:

```rust
fn update_report(id: i64, report: Update<Option<&str>>) {
    let (query, args) = query_args!(
        r"
        UPDATE weather_reports
        SET $[report?] = $[..]
        WHERE id = $id
        ",
        Args {
            report,
            id,
        }
    );
}

// Don't update the report.
update_report(0, Update::No);
// Set the report to `null`.
update_report(1, Update::Yes(None));
// Set the report to "sunny".
update_report(2, Update::Yes(Some("sunny")));
```
The SQL would be
```sql
UPDATE weather_reports
SET report = CASE WHEN $1 THEN $2 ELSE report END
WHERE id = $3
```
The optional parameter has been desugared to two parameters, one to indicate if the value should be updated
and another parameter with the new value.

Note that it is not possible to use the same argument with and without the `?` modifier in the same query.

## Fragment Syntax
```rust
let select = fragment!("
    SELECT location, time, report
    FROM weather_reports
");

let location = "sweden";

let (query, args) = query_args!(
    r"
    ${select}
    WHERE location = $location
    ",
    Args {
        // Temporary lifetime extension doesn't work when fragments are involved,
        // so we have to borrow the parameter here.
        location: &location,
    },
    Sql {
        select,
    }
);
```
The SQL would be
```sql
SELECT location, time, report
FROM weather_reports
WHERE location = $1
```

## IDE Support

First, the syntax used by this macro is compatible with rustfmt.
Run rustfmt as you would normally and it will format the macro.

Second, the macro is implemented in a way that is rust-analyzer "friendly".
This means that rust-analyzer knows which arguments are required and can complete them.
Use the code action "Fill struct fields" or ask rust-analyzer to complete a field name.

<!-- cargo-rdme end -->

## Goals

- Increase usability of executing PostgreSQL queries from Rust.

- Reduce the risk of mismatching query arguments.

- Support for rustfmt to help with formatting.

- Support for rust-analyzer completion and some code actions.

## Contributing

We welcome community contributions to this project.

Please read our [Contributor Terms](CONTRIBUTING.md#contributor-terms) before
you make any contributions.

Any contribution intentionally submitted for inclusion, shall comply with the
Rust standard licensing model (MIT OR Apache 2.0) and therefore be dual licensed
as described below, without any additional terms or conditions:

### License

This contribution is dual licensed under EITHER OF

- Apache License, Version 2.0, ([LICENSE-APACHE]LICENSE-APACHE or <http://www.apache.org/licenses/LICENSE-2.0>)
- MIT license ([LICENSE-MIT]LICENSE-MIT or <http://opensource.org/licenses/MIT>)

at your option.