Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ debug = ["dep:anstream", "dep:anstyle", "dep:okhsl"]
[dependencies]
anstream = { version = "0.6.21", optional = true }
anstyle = { version = "1.0.13", optional = true }
bon = "3.8.1"
okhsl = { version = "1.0.1", optional = true }
unicode_categories = "0.1.1"
winnow = { version = "0.7.0", features = ["simd"] }
Expand Down
88 changes: 47 additions & 41 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ This crate is a Rust port of [sql-formatter-plus](https://github.com/kufii/sql-f
## Quick start

```rust
use sqlformat::{format, FormatOptions, Indent, QueryParams};
use sqlformat::FormatOptions;

fn main() {
let sql = "SELECT id, name FROM users WHERE created_at > NOW();";
let options = FormatOptions::default();
let formatted = format(sql, &QueryParams::None, &options);
let formatted = options.format(sql);
println!("{}", formatted);
}
```
Expand Down Expand Up @@ -66,103 +66,109 @@ Minimum Supported Rust Version (MSRV): `1.84`.
### Basic formatting

```rust
use sqlformat::{format, FormatOptions, QueryParams};
use sqlformat::{FormatOptions};

let sql = "SELECT count(*), col FROM t WHERE a = 1 AND b = 2;";
let out = format(sql, &QueryParams::None, &FormatOptions::default());
let out = FormatOptions::default().format(sql);
```

### Indentation

```rust
use sqlformat::{format, FormatOptions, Indent, QueryParams};
use sqlformat::{FormatOptions, Indent};

let options = FormatOptions { indent: Indent::Spaces(4), ..Default::default() };
let out = format("SELECT a, b FROM t;", &QueryParams::None, &options);
let options = FormatOptions::builder().ident(4);
let out = options.format("SELECT a, b FROM t;");

let options = FormatOptions { indent: Indent::Tabs, ..Default::default() };
let out = format("SELECT a, b FROM t;", &QueryParams::None, &options);
let options = FormatOptions::builder().indent(Indent::Tabs);
let out = options.format("SELECT a, b FROM t;");
```

### Keyword case conversion

```rust
use sqlformat::{format, FormatOptions, QueryParams};
use sqlformat::FormatOptions;

// Uppercase reserved keywords
let options = FormatOptions { uppercase: Some(true), ..Default::default() };
let out = format("select distinct * from foo where bar = 1", &QueryParams::None, &options);
let options = FormatOptions::builder().uppercase(true);
let out = options.format("select distinct * from foo where bar = 1");

// Lowercase reserved keywords
let options = FormatOptions { uppercase: Some(false), ..Default::default() };
let out = format("SELECT DISTINCT * FROM FOO WHERE BAR = 1", &QueryParams::None, &options);
let options = FormatOptions::builder().uppercase(false);
let out = options.format("SELECT DISTINCT * FROM FOO WHERE BAR = 1");

// Preserve case with exceptions
let options = FormatOptions {
uppercase: Some(true),
ignore_case_convert: Some(vec!["from", "where"]),
..Default::default()
};
let out = format("select * from foo where bar = 1", &QueryParams::None, &options);
let options = FormatOptions::builder()
.uppercase(true)
.ignore_case_convert(vec!["from", "where"]);

let out = options.format("select * from foo where bar = 1");
```

### Inline/compact formatting

Control how aggressively short blocks and argument lists are kept on one line.

```rust
use sqlformat::{format, FormatOptions, QueryParams};

let options = FormatOptions {
inline: false, // when true, forces single-line output
max_inline_block: 50, // characters allowed to keep a parenthesized block inline
max_inline_arguments: Some(40),
max_inline_top_level: Some(40),
..Default::default()
};
let out = format("SELECT a, b, c, d, e, f, g, h FROM t;", &QueryParams::None, &options);
use sqlformat::FormatOptions;

let options = FormatOptions::builder()
.inline(false) // when true, forces single-line output
.max_inline_block(50) // characters allowed to keep a parenthesized block inline
.max_inline_arguments(40),
.max_inline_top_level(40);

let out = options.format("SELECT a, b, c, d, e, f, g, h FROM t;");
```

### JOIN layout

Treat any JOIN as a top-level keyword (affects line breaks):

```rust
use sqlformat::{format, FormatOptions, QueryParams};
use sqlformat::FormatOptions;

let options = FormatOptions { joins_as_top_level: true, ..Default::default() };
let out = format("SELECT * FROM a INNER JOIN b ON a.id = b.a_id", &QueryParams::None, &options);
let options = FormatOptions::builder().joins_as_top_level(true);
let out = options.format("SELECT * FROM a INNER JOIN b ON a.id = b.a_id");
```

### Parameter interpolation

`sqlformat` can substitute placeholders using `QueryParams`:

```rust
use sqlformat::{format, FormatOptions, QueryParams};
use sqlformat::FormatOptions;

// Set the options only once
let options = FormatOptions::default();

// Numbered / positional (e.g., ?, ?1, $1)
let sql = "SELECT ?1, ?, $2;";
let params = QueryParams::Indexed(vec!["first".to_string(), "second".to_string(), "third".to_string()]);
let out = format(sql, &params, &FormatOptions::default());
let params = ["first".to_string(), "second".to_string(), "third".to_string()];
// Pass params by reference
let out = options.with_params(&params).format(sql);

// Named (e.g., $name, :name, @name, :\"weird name\")
let sql = "SELECT $hash, :name, @`var name`;";
let params = QueryParams::Named(vec![
let params = vec![
("hash".to_string(), "hash value".to_string()),
("name".to_string(), "Alice".to_string()),
("var name".to_string(), "Bob".to_string()),
]);
let out = format(sql, &params, &FormatOptions::default());
let out = options.with_params(&params).format(sql);

// Named using a format-like syntax
let sql = "SELECT {hash}, {name}, {var name};"
let out = options.with_params(&params).format(sql);
```

### Controlling blank lines between statements

```rust
use sqlformat::{format, FormatOptions, QueryParams};
use sqlformat::FormatOptions;

let options = FormatOptions { lines_between_queries: 2, ..Default::default() };
let out = format("SELECT 1; SELECT 2;", &QueryParams::None, &options);
let options = FormatOptions::builder().lines_between_queries(2);
let out = options.format("SELECT 1; SELECT 2;");
```

### Temporarily disabling the formatter
Expand Down
70 changes: 14 additions & 56 deletions benches/bench.rs
Original file line number Diff line number Diff line change
@@ -1,46 +1,34 @@
use criterion::{black_box, criterion_group, criterion_main, Criterion};
use std::hint::black_box;

use criterion::{criterion_group, criterion_main, Criterion};
use sqlformat::*;

fn simple_query(c: &mut Criterion) {
let input = "SELECT * FROM my_table WHERE id = 1";
c.bench_function("simple query", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::None),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().format(input))
});
}

fn complex_query(c: &mut Criterion) {
let input = "SELECT t1.id, t1.name, t1.title, t1.description, t2.mothers_maiden_name, t2.first_girlfriend\nFROM my_table t1 LEFT JOIN other_table t2 ON t1.id = t2.other_id WHERE t2.order BETWEEN 17 AND 30";
c.bench_function("complex query", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::None),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().format(input))
});
}

fn query_with_named_params(c: &mut Criterion) {
let input = "SELECT * FROM my_table WHERE id = :first OR id = :second OR id = :third";
let params = vec![
let params = &[
("first".to_string(), "1".to_string()),
("second".to_string(), "2".to_string()),
("third".to_string(), "3".to_string()),
];
c.bench_function("named params", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::Named(params.clone())),
black_box(&FormatOptions::default()),
)
FormatOptions::default()
.with_params(params.as_ref())
.format(input)
})
});
}
Expand All @@ -49,27 +37,15 @@ fn query_with_explicit_indexed_params(c: &mut Criterion) {
let input = "SELECT * FROM my_table WHERE id = ?1 OR id = ?2 OR id = ?0";
let params = vec!["0".to_string(), "1".to_string(), "2".to_string()];
c.bench_function("explicit indexed params", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::Indexed(params.clone())),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().with_params(&params).format(input))
});
}

fn query_with_implicit_indexed_params(c: &mut Criterion) {
let input = "SELECT * FROM my_table WHERE id = ? OR id = ? OR id = ?";
let params = vec!["0".to_string(), "1".to_string(), "2".to_string()];
c.bench_function("implicit indexed params", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::Indexed(params.clone())),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().with_params(&params).format(input))
});
}

Expand Down Expand Up @@ -131,27 +107,15 @@ VALUES

let input = generate_insert_query();
c.bench_function("issue 633", |b| {
b.iter(|| {
format(
black_box(&input),
black_box(&QueryParams::None),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().format(&input))
});
}

fn issue_633_2(c: &mut Criterion) {
let input = "SELECT\n d.uuid AS uuid,\n\td.name_of_document AS name,\n\td.slug_name AS slug,\n\td.default_contract_uuid AS default_contract_uuid,\n\ta.uuid AS parent_uuid,\n\ta.name_of_agreement AS agreement_name,\n\td.icon_name AS icon\nFROM `documents` d\nLEFT JOIN agreements a ON a.uuid = d.parent_uuid\n WHERE d.uuid = ? LIMIT 1";
let params = vec!["0".to_string()];
c.bench_function("issue 633 query 2", |b| {
b.iter(|| {
format(
black_box(input),
black_box(&QueryParams::Indexed(params.clone())),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().with_params(&params).format(input))
});
}

Expand All @@ -171,13 +135,7 @@ fn issue_633_3(c: &mut Criterion) {
}

c.bench_function("issue 633 query 3", |b| {
b.iter(|| {
format(
black_box(&input),
black_box(&QueryParams::None),
black_box(&FormatOptions::default()),
)
})
b.iter(|| FormatOptions::default().format(&input))
});
}

Expand Down
Loading
Loading