Macro include_sql

Source
include_sql!() { /* proc-macro */ }
Expand description

Reads and parses the specified SQL file, and generates impl_sql macro call.

For example, if the SQL file “library.sql” has these 2 statements:

-- name: get_loaned_books?
-- Returns the list of books loaned to a patron
-- # Parameters
-- param: user_id: &str - user ID
SELECT book_title FROM library WHERE loaned_to = :user_id ORDER BY 1
/

-- name: loan_books!
-- Updates the book records to reflect loan to a patron
-- # Parameters
-- param: book_ids: usize - book IDs
-- param: user_id: &str - user ID
UPDATE library SET loaned_to = :user_id, loaned_on = current_timestamp WHERE book_id IN ( :book_ids )
/

This method would generate:

impl_sql!{ LibrarySql =
  {
    ? get_loaned_books (: user_id (&str))
    " Returns the list of books loaned to a patron\n # Parameters\n * `user_id` - user ID"
    $ "SELECT book_title FROM library WHERE loaned_to = " : user_id "ORDER BY 1"
  },
  {
    ! loan_books (# book_ids (usize) : user_id (&str))
    " Updates the book records to reflect loan to a patron\n # Parameters\n * `user_id` - user ID\n * `book_ids` - book IDs"
    $ "UPDATE library SET loaned_to = " : user_id ", loaned_on = current_timestamp WHERE book_id IN ( " # book_ids " )"
  }
}

Where:

  • LibrarySql is a camel-cased ident derived from the SQL file name. It might be used by impl_sql to generate a trait (like include-postgres-sql and include-sqlite-sql do).
  • ? or ! is a statement variant selector
  • get_loaned_books and loan_books are idents created from the statement names that can be used to name generated methods
  • user_id and book_ids are idents that represent parameter names.
  • : and # in front of the parameter names are parameter variant tags:
    • : indicates that the following parameter is a scalar
    • # tags IN-list parameters.
  • The following (&str) and (usize) are Rust parameter types as declared in the SQL.
  • $ is a helper token that could be used to generate repetitions if generated artifacts are macros.

Note that param: types are passed as parenthesized types. This is done to allow impl_sql match them as token trees. If a parameter type is not defined in SQL, _ will be used in its place (this _ drives the need to match parameter types as token trees) for which impl_sql is expected to generate an appropriate generic type.

Note also that parameter order is defined by the param declarations. SQL parameters that are present in the SQL code, but that are not declared as one of the params, will be follow the param parameters in the order they are found in the SQL code.

§Async

When include-sql is built with the async feature, impl_sql macro will be generated with additional lifetimes for reference parameters. For example, the above LibrarySql example will look like this:

impl_sql!{ LibrarySql =
  {
    ? get_loaned_books (: user_id ('user_id &str))
    " Returns the list of books loaned to a patron\n # Parameters\n * `user_id` - user ID"
    $ "SELECT book_title FROM library WHERE loaned_to = " : user_id "ORDER BY 1"
  },
  {
    ! loan_books (# book_ids ('book_ids usize) : user_id ('user_id &str))
    " Updates the book records to reflect loan to a patron\n # Parameters\n * `user_id` - user ID\n * `book_ids` - book IDs"
    $ "UPDATE library SET loaned_to = " : user_id ", loaned_on = current_timestamp WHERE book_id IN ( " # book_ids " )"
  }
}

Note that for IN list parameters where the list item is a reference itself additional lifetime that covers list items is also generated. For example, for this query:

-- name: get_users_who_loaned_books?
-- Returns names patrons that at one time or another have loaned specified books
-- # Parameters
-- param: book_titles: &str - book titles
SELECT DISTINCT first_name, last_name
  FROM patrons
  JOIN library ON library.loaned_to = patrons.user_id
 WHERE book_title IN (:book_titles);

include-sql will generate:

impl_sql!{ LibrarySql =
  {
    ? get_users_who_loaned_books (# book_titles ('book_titles 'book_titles_item &str))
    " Returns names patrons that at one time or another have loaned specified books\n # Parameters\n * `book_titles` - book titles"
    $ "SELECT DISTINCT first_name, last_name  FROM patrons  JOIN library ON library.loaned_to = patrons.user_id WHERE book_title IN (" # book_titles ")"
  }
}