HugSQL Documentation

repository·master·Indexed 20 days ago

https://github.com/layerware/hugsql

A Clojure library that allows developers to write raw SQL queries in separate files and automatically transform them into reusable Clojure functions. HugSQL features a protocol-based adapter system supporting next.jdbc, clojure.java.jdbc, and clojure.jdbc, and provides mechanisms for SQL composition via snippets and HoneySQL integration.

Tokens
19.5K
Snippets
76
Records
99
Agent score
66%

What's inside HugSQL

  1. What is HugSQL?

    master

    HugSQL is a Clojure library designed to bridge the gap between Clojure and SQL by using simple conventions within SQL files to define database functions in your Clojure namespaces. This approach maintains a clean separation between your application logic (Clojure) and your data access logic (SQL).

    Key capabilities include:

    • Runtime Parameter Replacement: Dynamically inject values, identifiers, lists, and tuples into your SQL.
    • Composability: Use Clojure expressions and SQL snippets to build complex queries.
    • Database Adapters: Supports multiple database libraries via protocol-based adapters, including clojure.java.jdbc (default), next.jdbc, and clojure.jdbc.
  2. Overview of HugSQL

    master
    HugSQL is a Clojure library designed for 'embracing SQL'. It allows developers to write raw SQL and transform it into reusable Clojure functions, bridging the gap between the power of SQL and the expressiveness of Clojure.
  3. Prevent SQL injection using Value Parameters

    master

    HugSQL protects against SQL injection by using standard SQL parameter binding for value-based parameters. When you use these parameter types, HugSQL converts Clojure data types to SQL and defers the actual binding to your underlying database library. This is the safest way to handle user-provided data.

    Supported value parameter types include:

    • Value Parameters
    • Value List Parameters
    • Tuple Parameters
    • Tuple List Parameters
  4. Specify the database command in HugSQL

    master

    In HugSQL, you use the :command keyword to specify how the underlying database command should be executed. This determines whether the SQL returns a result set, executes a statement without returning data, or handles special cases like RETURNING clauses or generated keys.

    Built-in Commands

    CommandShorthandDescription
    :query:?Executes a query with a result-set (this is the default).
    :execute:!Executes any statement (typically used for updates/deletes where no result set is expected).
    :returning-execute:<!Provides support for INSERT ... RETURNING syntax.
    :insert:i!Provides support for standard inserts and uses JDBC .getGeneratedKeys.

    Shorthand Syntax

    To reduce boilerplate, you can specify the command as the second value in the :name metadata line instead of using a separate :command key.

    -- :name all-characters :?
    SELECT * FROM characters;
    
    -- Using shorthand for :execute
    -- :name update-name :!
    UPDATE characters SET name = ? WHERE id = ?;
  5. Use SQL file comment conventions in HugSQL

    master

    HugSQL uses special single-line and multi-line comments within .sql files to define the metadata for the Clojure functions it generates. These comments must follow specific syntax patterns so the HugSQL parser can identify them.

    Single-line syntax

    Use a double dash followed by a colon and the key: -- :key value

    Multi-line syntax

    Use a C-style comment block containing the key and values:

    /* :key
     value
     */

    Standard SQL comments that do not follow the :key pattern are ignored by HugSQL.

    -- :name query-get-many :? :*
    -- :doc My query doc string to end of this line
    select * from my_table;
    
    -- :name query-get-one :? :1
    /* :doc
    My multi-line
    comment doc string
    */
    select * from my_table limit 1
  6. Use Clojure Expressions to conditionally compose SQL

    master

    HugSQL allows you to use Clojure expressions to conditionally compose portions of your SQL statements at runtime. These expressions are written inside SQL comments, maintaining a SQL-first workflow.

    Expressions are compiled after the initial parse and must return either a String or nil. The returned string can include HugSQL-specific parameter syntax (like :i:, :v:, etc.).

    At runtime, two symbols are available within your expressions:

    • params: A hashmap containing your parameter data.
    • options: A hashmap containing configuration options.
    -- :name clj-expr-single :? :1
    select
    --~ (if (seq (:cols params)) ":i*:cols" "*")
    from test
    order by id
  7. How HugSQL uses SQL file conventions to generate Clojure functions

    master

    HugSQL works by reading SQL files that follow specific naming and syntax conventions. Instead of writing SQL as strings in Clojure, you store SQL, DDL, and DML statements in .sql files. HugSQL parses these files to automatically generate Clojure functions that handle parameter replacement and execution logic.

    To generate functions correctly, your SQL files must use specific syntax to define:

    1. Function Names: Defined via naming conventions in the SQL file.
    2. Docstrings: Added to the generated Clojure functions.
    3. Command Types: Determines if the statement is a SELECT, DDL (create/drop), DML (insert/update/delete), or other statements (e.g., VACUUM).
    4. Result Types: Determines if the function returns a single hash-map (one row), a vector of hash-maps (many rows), affected rows, or a custom result.
    5. Parameter Replacement: Uses special tokens to inject values, lists, or identifiers into the query.
  8. Compose SQL using HoneySQL with HugSQL

    master

    HugSQL and HoneySQL are not mutually exclusive and can be used together to combine SQL-first development with Clojure-based SQL generation. You can use HoneySQL to generate a sqlvec format output and then pass that output into HugSQL snippets.

    To achieve this, use HugSQL Snippet Parameter Types :snip or :snip*, which are designed to consume the sqlvec format produced by HoneySQL's format function.

    ;; Conceptual workflow:
    ;; 1. Use HoneySQL to generate sqlvec
    (def query-vec (honey.sql/format {:select [:id :name] :from [:users]}))
    
    ;; 2. Pass the result into a HugSQL snippet using :snip or :snip*
    (hugsql/execute-snippet my-snippet :snip query-vec)
  9. Use Raw SQL Parameters with the :sql type

    master

    Raw SQL Parameters allow you to perform un-quoted, direct text replacement in your SQL statements. Unlike standard parameters that are treated as values, :sql parameters allow you to parameterize SQL keywords, such as ASC or DESC in an ORDER BY clause, or to compose multiple statements into one.

    Security Warning: Because these parameters perform direct text replacement without quoting, they are highly susceptible to SQL injection. Always validate and sanitize any user-provided input before passing it to a :sql parameter.

    --:name sql-keyword-param :? :*
    select * from example
    order by last_name :sql:last_name_sort
  10. Use SQL Tuple Parameters with :tuple type

    master

    Tuple Parameters allow you to pass a list of values that are enclosed in parentheses and joined by commas. They are useful for multi-column comparisons, such as (col1, col2) = :tuple:name.

    Unlike Value List Parameters (which expect a list of the same data type), Tuple Parameters can contain values of different data types. Each element in the provided list is treated as an individual Value Parameter.

    Supported Databases:

    • PostgreSQL
    • MySQL
    • H2

    Unsupported Databases:

    • Derby
    • HSQLDB
    • SQLite
    -- :name tuple-param
    -- :doc Tuple Param
    select * from test
    where (id, name) = :tuple:id-name
    (tuple-param-sqlvec {:id-name [1 "A"]})
    ;=> ["select * from test\nwhere (id, name) = (?,?)" 1 "A"]
  11. Define HugSQL metadata using SQL comments

    master

    HugSQL uses special SQL comments to define how queries behave. Key metadata keys include:

    • :name: The name of the generated Clojure function.
    • :command: The type of command (e.g., :execute).
    • :result: Defines the return type:
      • :raw: Returns the raw result.
      • :n: Returns the number of affected rows (use :! :n syntax).
      • :1: Returns a single record as a hashmap.
      • :?: Returns a sequence of results.
      • :? :*: Returns results with columns specified via the :i* parameter type.
    • :doc: A documentation string for the generated function.

    Special parameter types include:

    • :tuple*: For inserting multiple records.
    • :i*: Identifier list (for specifying columns).
    • :v*: Value list (for IN() clauses).
    -- :name insert-character :! :n
    -- :doc Insert a single character returning affected row count
    insert into characters (name, specialty)
    values (:name, :specialty)
    
    -- :name character-by-id :? :1
    -- :doc Get character by id
    select * from characters
    where id = :id
    
    -- :name characters-by-ids-specify-cols :? :*
    -- :doc Characters with returned columns specified
    select :i*:cols from characters
    where id in (:v*:ids)