A Rust SQL query builder with a pleasant fluent API closely imitating actual SQL

Related tags

Database scooby
Overview

Scooby

Latest Version docs

An SQL query builder with a pleasant fluent API closely imitating actual SQL. Meant to comfortably build dynamic queries with a little bit of safety checks sprinkled on top to ensure you don't forget important things like ON clauses. Does not do quoting, does not do validation.

Supports only PostgreSQL syntax at the moment.

Requires Rust 1.54.

Supported statements, clauses and features

  1. SELECT

    • WITH
    • WHERE
    • GROUP BY
    • HAVING
    • ALL, DISTINCT and DISTINCT ON
    • ORDER BY
      • ASC
      • DESC
      • NULLS FIRST
      • NULLS LAST
    • LIMIT and OFFSET
    • FROM with subselects and joins with a nice API:
      • JOIN, INNER JOIN and CROSS JOIN
      • LEFT JOIN and LEFT OUTER JOIN
      • RIGHT JOIN and RIGHT OUTER JOIN
      • FULL JOIN and FULL OUTER JOIN
  2. INSERT INTO

    • WITH
    • DEFAULT VALUES
    • VALUES with compile-time checking that lengths of all values are the same as columns
    • RETURNING
  3. DELETE FROM

    • WITH
    • WHERE
    • RETURNING
  4. UPDATE

    • WITH
    • SET with compile-time checking that you've actually set at least something
    • WHERE
    • RETURNING
  5. Convenient x AS y aliasing

  6. Convenient $1, $2... parameter placeholder builder

Examples

SELECT

use scooby::postgres::{select, Aliasable, Joinable, Orderable};

// SELECT
//     country.name AS name,
//     COUNT(*) AS count
// FROM
//     Country AS country
//     INNER JOIN City AS city ON city.country_id = country.id
// WHERE
//     city.population > 1000000
// GROUP BY country.id
// ORDER BY count DESC
// LIMIT 10
select(("country.name".as_("name"), "COUNT(*)".as_("count")))
    .from(
        "Country"
            .as_("country")
            .inner_join("City".as_("city"))
            .on("city.country_id = country.id"),
    )
    .where_("city.population > 1000000")
    .group_by("country.id")
    .order_by("count".desc())
    .limit(10)
    .to_string();

INSERT INTO

use scooby::postgres::insert_into;

// INSERT INTO Dummy (col1, col2) VALUES (a, b), (c, d), (e, f) RETURNING id
insert_into("Dummy")
    .columns(("col1", "col2"))
    .values([("a", "b"), ("c", "d")])
    .values([("e", "f")])
    .returning("id")
    .to_string();

// INSERT INTO Dummy DEFAULT VALUES
insert_into("Dummy").default_values().to_string();

DELETE FROM

use scooby::postgres::delete_from;

// DELETE FROM Dummy WHERE x > 0 AND y > 30
delete_from("Dummy").where_(("x > 0", "y > 30")).to_string();

WITH (CTE — Common Table Expression)

use scooby::postgres::{with, select};

// WITH regional_sales AS (
//         SELECT region, SUM(amount) AS total_sales
//         FROM orders
//         GROUP BY region
//      ), top_regions AS (
//         SELECT region
//         FROM regional_sales
//         WHERE total_sales > (SELECT SUM(total_sales)/10 FROM regional_sales)
//      )
// SELECT region,
//        product,
//        SUM(quantity) AS product_units,
//        SUM(amount) AS product_sales
// FROM orders
// WHERE region IN (SELECT region FROM top_regions)
// GROUP BY region, product;
with("regional_sales")
    .as_(
        select(("region", "SUM(amount)".as_("total_sales")))
            .from("orders")
            .group_by("region"),
    )
    .and("top_regions")
    .as_(select("region").from("regional_sales").where_(format!(
        "total_sales > ({})",
        select("SUM(total_sales)/10").from("regional_sales")
    )))
    .select((
        "region",
        "product",
        "SUM(quantity)".as_("product_units"),
        "SUM(amount)".as_("product_sales"),
    ))
    .from("orders")
    .where_(format!(
        "region IN ({})",
        select("region").from("top_regions")
    ))
    .group_by(("region", "product"))
    .to_string();

Parameters

use scooby::postgres::{select, Parameters};

let mut params = Parameters::new();

// SELECT id FROM Thing WHERE x > $1 AND y < $2
select("id")
    .from("Thing")
    .where_(format!("x > {}", params.next()))
    .where_(format!("y < {}", params.next()))
    .to_string();

Testing

Normally:

cargo test

To check syntax:

  1. Run a local postgresql server on your machine at default port
  2. cargo test --features validate-postgres-syntax
You might also like...
A Toy Query Engine & SQL interface
A Toy Query Engine & SQL interface

Naive Query Engine (Toy for Learning) 😄 This is a Query Engine which support SQL interface. And it is only a Toy for learn query engine only. You can

Query system statistics with SQL.

pg_stat_sysinfo Collects system statistics. ---- CREATE EXTENSION pg_stat_sysinfo; CREATE EXTENSION ---- SELECT * FROM pg_stat_sysinfo_collect();

🧰 The Rust SQL Toolkit. An async, pure Rust SQL crate featuring compile-time checked queries without a DSL. Supports PostgreSQL, MySQL, SQLite, and MSSQL.

SQLx 🧰 The Rust SQL Toolkit Install | Usage | Docs Built with ❤️ by The LaunchBadge team SQLx is an async, pure Rust† SQL crate featuring compile-tim

SQL validator tool for BigQuery standard SQL.

bqvalid What bqvalid does bqvalid is the SQL validator tool for BigQuery standard SQL. bqvalid fails with error message if there's the expression that

Query is a Rust server for your remote SQLite databases and a CLI to manage them.

Query Query is a Rust server for your remote SQLite databases and a CLI to manage them. Table Of Contents Run A Query Server CLI Install Use The Insta

Qoo - Query Object Oriented. Pronunciation is Kuu.

qoo Query Object Oriented. Pronunciation is Kuu. demo use qoo::base::*; use qoo::select::*; use qoo::insert::*; use qoo::update::*; use qoo::delete::*

rust_arango enables you to connect with ArangoDB server, access to database, execute AQL query, manage ArangoDB in an easy and intuitive way, both async and plain synchronous code with any HTTP ecosystem you love.

rust_arango enables you to connect with ArangoDB server, access to database, execute AQL query, manage ArangoDB in an easy and intuitive way, both async and plain synchronous code with any HTTP ecosystem you love.

XLite - query Excel (.xlsx, .xls) and Open Document spreadsheets (.ods) as SQLite virtual tables

XLite - query Excel (.xlsx, .xls) and Open Document spreadsheets (.ods) as SQLite virtual tables XLite is a SQLite extension written in Rust. The main

Bind the Prisma ORM query engine to any programming language you like ❤️

Prisma Query Engine C API Bind the Prisma ORM query engine to any programming language you like ❤️ Features Rust bindings for the C API Static link li

Comments
  • Support basic `CREATE TABLE` statements

    Support basic `CREATE TABLE` statements

    Tentative syntax:

    create_table("Film")
        .columns((
            ("code", "char(5)").primary_key(),
            ("imdb_id", "char(40)").unique(),
            ("title", "varchar(40)").not_null(),
            ("did", "integer").not_null(),
            ("date_prod", "date"),
            ("kind", "varchar(10)"),
            ("len", "interval hour to minute").default("0"),
        ))
    
    • [x] Duplicate or conflicting constraints on columns should not be allowed (i.e. doing .unique().unique() or .null().not_null(). Achievable at compile-time with some generics abuse.
    • [x] Creating a statement without columns should not be allowed (also easily achievable at compile time)
    • [ ] Basic table constraints
      • [x] UNIQUE
      • [ ] PRIMARY KEY (wonder if it would be possible to check that there's no primary key column at compile time?)
    • [ ] Basic indexes support
    • [ ] Documentation
    enhancement 
    opened by despawnerer 1
  • Consider disallowing `DELETE FROM` and `UPDATE` queries without explicit `WHERE` clauses

    Consider disallowing `DELETE FROM` and `UPDATE` queries without explicit `WHERE` clauses

    Generally speaking, people may want to delete everything, so they should have an option to do that. But that's a relatively rare case and the default can be safer:

    delete_from("Dummy").to_string();  // ❌ should not compile
    delete_from("Dummy").all().to_string();  // ✅ good, explicitly deletes everything
    delete_from("Dummy").where_("x > 1").to_string();  // ✅ good, explicitly has a condition
    
    enhancement 
    opened by despawnerer 0
Owner
Aleksei Voronov
Aleksei Voronov
A safe, extensible ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Diesel 9.7k Jan 3, 2023
Diesel - A safe, extensible ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Takayuki Maeda 0 Aug 31, 2020
Diesel - ORM and Query Builder for Rust

A safe, extensible ORM and Query Builder for Rust API Documentation: latest release – master branch Homepage Diesel gets rid of the boilerplate for da

Diesel 9.7k Jan 6, 2023
A query builder that builds and typechecks queries at compile time

typed-qb: a compile-time typed "query builder" typed-qb is a compile-time, typed, query builder. The goal of this crate is to explore the gap between

ferrouille 3 Jan 22, 2022
Gh-sql - Query GitHub Projects (beta) with SQL

gh-sql: Query GitHub Projects (beta) with SQL Installation gh extension install KOBA789/gh-sql Features SELECT items DELETE items UPDATE item fields

Hidekazu Kobayashi 108 Dec 7, 2022
Rust library to parse, deparse and normalize SQL queries using the PostgreSQL query parser

This Rust library uses the actual PostgreSQL server source to parse SQL queries and return the internal PostgreSQL parse tree.

pganalyze 37 Dec 18, 2022
ReefDB is a minimalistic, in-memory and on-disk database management system written in Rust, implementing basic SQL query capabilities and full-text search.

ReefDB ReefDB is a minimalistic, in-memory and on-disk database management system written in Rust, implementing basic SQL query capabilities and full-

Sacha Arbonel 75 Jun 12, 2023
Query LDAP and AD with SQL

SQLDAP Ever wanted to query AD or LDAP with SQL like queries ? I'm going to answer this question myself: yes ! Why ? Because I never could remember al

null 9 Nov 15, 2022
An object-relational in-memory cache, supports queries with an SQL-like query language.

qlcache An object-relational in-memory cache, supports queries with an SQL-like query language. Warning This is a rather low-level library, and only p

null 3 Nov 14, 2021
Type-safe SQL query wrappers

fnsql   The fnsql crate provides simple type-safe optional wrappers around SQL queries. Instead of calling type-less .query() and .execute(), you call

Dan Aloni 9 Apr 29, 2022