Skip to content

Commit 4c59138

Browse files
authored
Use itoa for integer formatting (#990)
This optimization reduces the gap in build and to_string methods between the PostgreSQL backend and other backends.
1 parent 8ba44ec commit 4c59138

File tree

9 files changed

+92
-49
lines changed

9 files changed

+92
-49
lines changed

Cargo.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ jiff = { version = "0.2.15", default-features = false, optional = true, features
4343
ipnetwork = { version = "0.20", default-features = false, optional = true }
4444
mac_address = { version = "1.1", default-features = false, optional = true }
4545
ordered-float = { version = "4.6", default-features = false, optional = true }
46+
itoa = { version = "1.0.15", optional = true }
4647

4748
[dev-dependencies]
4849
sea-query = { path = ".", features = ["tests-cfg"] }
@@ -55,7 +56,7 @@ audit = []
5556
backend-mysql = []
5657
backend-postgres = []
5758
backend-sqlite = []
58-
default = ["derive", "audit", "backend-mysql", "backend-postgres", "backend-sqlite"]
59+
default = ["derive", "audit", "backend-mysql", "backend-postgres", "backend-sqlite", "itoa"]
5960
derive = ["sea-query-derive"]
6061
attr = ["sea-query-derive"]
6162
hashable-value = ["ordered-float"]
@@ -116,6 +117,7 @@ all-types = [
116117
]
117118
option-more-parentheses = []
118119
option-sqlite-exact-column-type = []
120+
itoa = ["dep:itoa"]
119121

120122
[[test]]
121123
name = "test-derive"

benches/value.rs

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,5 +64,16 @@ fn criterion_benchmark(c: &mut Criterion) {
6464
group.finish();
6565
}
6666

67-
criterion_group!(benches, criterion_benchmark);
67+
fn cfg() -> Criterion {
68+
Criterion::default()
69+
.configure_from_args()
70+
.measurement_time(std::time::Duration::new(10, 0))
71+
.sample_size(200)
72+
}
73+
74+
criterion_group! {
75+
name = benches;
76+
config = cfg();
77+
targets = criterion_benchmark
78+
}
6879
criterion_main!(benches);

src/backend/index_builder.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ pub trait IndexBuilder: QuotedBuilder + TableRefBuilder {
5050
fn write_column_index_prefix(&self, col_prefix: &Option<u32>, sql: &mut impl SqlWriter) {
5151
if let Some(prefix) = col_prefix {
5252
sql.write_str(" (").unwrap();
53-
write!(sql, "{prefix}").unwrap();
53+
write_int(sql, *prefix);
5454
sql.write_str(")").unwrap();
5555
}
5656
}

src/backend/mysql/table.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::write_int;
23

34
impl TableBuilder for MysqlQueryBuilder {
45
fn prepare_table_opt(&self, create: &TableCreateStatement, sql: &mut impl SqlWriter) {
@@ -27,15 +28,15 @@ impl TableBuilder for MysqlQueryBuilder {
2728
ColumnType::Char(length) => match length {
2829
Some(length) => {
2930
sql.write_str("char(").unwrap();
30-
write!(sql, "{length}").unwrap();
31+
write_int(sql, *length);
3132
sql.write_str(")")
3233
}
3334
None => sql.write_str("char"),
3435
},
3536
ColumnType::String(length) => match length {
3637
StringLen::N(length) => {
3738
sql.write_str("varchar(").unwrap();
38-
write!(sql, "{length}").unwrap();
39+
write_int(sql, *length);
3940
sql.write_char(')')
4041
}
4142
StringLen::None => sql.write_str("varchar(255)"),
@@ -51,9 +52,9 @@ impl TableBuilder for MysqlQueryBuilder {
5152
ColumnType::Decimal(precision) => match precision {
5253
Some((precision, scale)) => {
5354
sql.write_str("decimal(").unwrap();
54-
write!(sql, "{precision}").unwrap();
55+
write_int(sql, *precision);
5556
sql.write_str(", ").unwrap();
56-
write!(sql, "{scale}").unwrap();
57+
write_int(sql, *scale);
5758
sql.write_char(')')
5859
}
5960
None => sql.write_str("decimal"),
@@ -67,13 +68,13 @@ impl TableBuilder for MysqlQueryBuilder {
6768
ColumnType::Interval(_, _) => sql.write_str("unsupported"),
6869
ColumnType::Binary(length) => {
6970
sql.write_str("binary(").unwrap();
70-
write!(sql, "{length}").unwrap();
71+
write_int(sql, *length);
7172
sql.write_char(')')
7273
}
7374
ColumnType::VarBinary(length) => match length {
7475
StringLen::N(length) => {
7576
sql.write_str("varbinary(").unwrap();
76-
write!(sql, "{length}").unwrap();
77+
write_int(sql, *length);
7778
sql.write_char(')')
7879
}
7980
StringLen::None => sql.write_str("varbinary(255)"),
@@ -83,23 +84,23 @@ impl TableBuilder for MysqlQueryBuilder {
8384
ColumnType::Bit(length) => match length {
8485
Some(length) => {
8586
sql.write_str("bit(").unwrap();
86-
write!(sql, "{length}").unwrap();
87+
write_int(sql, *length);
8788
sql.write_char(')')
8889
}
8990
None => sql.write_str("bit"),
9091
},
9192
ColumnType::VarBit(length) => {
9293
sql.write_str("bit(").unwrap();
93-
write!(sql, "{length}").unwrap();
94+
write_int(sql, *length);
9495
sql.write_char(')')
9596
}
9697
ColumnType::Boolean => sql.write_str("bool"),
9798
ColumnType::Money(precision) => match precision {
9899
Some((precision, scale)) => {
99100
sql.write_str("decimal(").unwrap();
100-
write!(sql, "{precision}").unwrap();
101+
write_int(sql, *precision);
101102
sql.write_str(", ").unwrap();
102-
write!(sql, "{scale}").unwrap();
103+
write_int(sql, *scale);
103104
sql.write_char(')')
104105
}
105106
None => sql.write_str("decimal"),

src/backend/postgres/table.rs

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::write_int;
23

34
impl TableBuilder for PostgresQueryBuilder {
45
fn prepare_column_def(&self, column_def: &ColumnDef, sql: &mut impl SqlWriter) {
@@ -14,15 +15,15 @@ impl TableBuilder for PostgresQueryBuilder {
1415
ColumnType::Char(length) => match length {
1516
Some(length) => {
1617
sql.write_str("char(").unwrap();
17-
write!(sql, "{length}").unwrap();
18+
write_int(sql, *length);
1819
sql.write_char(')')
1920
}
2021
None => sql.write_str("char"),
2122
},
2223
ColumnType::String(length) => match length {
2324
StringLen::N(length) => {
2425
sql.write_str("varchar(").unwrap();
25-
write!(sql, "{length}").unwrap();
26+
write_int(sql, *length);
2627
sql.write_char(')')
2728
}
2829
_ => sql.write_str("varchar"),
@@ -37,9 +38,9 @@ impl TableBuilder for PostgresQueryBuilder {
3738
ColumnType::Decimal(precision) => match precision {
3839
Some((precision, scale)) => {
3940
sql.write_str("decimal(").unwrap();
40-
write!(sql, "{precision}").unwrap();
41+
write_int(sql, *precision);
4142
sql.write_str(", ").unwrap();
42-
write!(sql, "{scale}").unwrap();
43+
write_int(sql, *scale);
4344
sql.write_char(')')
4445
}
4546
None => sql.write_str("decimal"),
@@ -58,7 +59,7 @@ impl TableBuilder for PostgresQueryBuilder {
5859

5960
if let Some(precision) = precision {
6061
sql.write_char('(').unwrap();
61-
write!(sql, "{precision}").unwrap();
62+
write_int(sql, *precision);
6263
sql.write_char(')').unwrap();
6364
}
6465
Ok(())
@@ -69,23 +70,23 @@ impl TableBuilder for PostgresQueryBuilder {
6970
ColumnType::Bit(length) => match length {
7071
Some(length) => {
7172
sql.write_str("bit(").unwrap();
72-
write!(sql, "{length}").unwrap();
73+
write_int(sql, *length);
7374
sql.write_char(')')
7475
}
7576
None => sql.write_str("bit"),
7677
},
7778
ColumnType::VarBit(length) => {
7879
sql.write_str("varbit(").unwrap();
79-
write!(sql, "{length}").unwrap();
80+
write_int(sql, *length);
8081
sql.write_char(')')
8182
}
8283
ColumnType::Boolean => sql.write_str("bool"),
8384
ColumnType::Money(precision) => match precision {
8485
Some((precision, scale)) => {
8586
sql.write_str("money(").unwrap();
86-
write!(sql, "{precision}").unwrap();
87+
write_int(sql, *precision);
8788
sql.write_str(", ").unwrap();
88-
write!(sql, "{scale}").unwrap();
89+
write_int(sql, *scale);
8990
sql.write_char(')')
9091
}
9192
None => sql.write_str("money"),
@@ -100,7 +101,7 @@ impl TableBuilder for PostgresQueryBuilder {
100101
ColumnType::Vector(size) => match size {
101102
Some(size) => {
102103
sql.write_str("vector(").unwrap();
103-
write!(sql, "{size}").unwrap();
104+
write_int(sql, *size);
104105
sql.write_str(")")
105106
}
106107
None => sql.write_str("vector"),

src/backend/query_builder.rs

Lines changed: 26 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1020,13 +1020,13 @@ pub trait QueryBuilder:
10201020
sql.write_str("=").unwrap();
10211021
self.write_value(sql, value).unwrap();
10221022
sql.write_str(" THEN ").unwrap();
1023-
write!(sql, "{i}").unwrap();
1023+
write_int(sql, i);
10241024
sql.write_str(" ").unwrap();
10251025
i += 1;
10261026
}
10271027

10281028
sql.write_str("ELSE ").unwrap();
1029-
write!(sql, "{i}").unwrap();
1029+
write_int(sql, i);
10301030
sql.write_str(" END").unwrap();
10311031
}
10321032

@@ -1168,14 +1168,30 @@ pub trait QueryBuilder:
11681168
#[cfg(feature = "postgres-vector")]
11691169
Value::Vector(None) => buf.write_str("NULL")?,
11701170
Value::Bool(Some(b)) => buf.write_str(if *b { "TRUE" } else { "FALSE" })?,
1171-
Value::TinyInt(Some(v)) => write!(buf, "{v}")?,
1172-
Value::SmallInt(Some(v)) => write!(buf, "{v}")?,
1173-
Value::Int(Some(v)) => write!(buf, "{v}")?,
1174-
Value::BigInt(Some(v)) => write!(buf, "{v}")?,
1175-
Value::TinyUnsigned(Some(v)) => write!(buf, "{v}")?,
1176-
Value::SmallUnsigned(Some(v)) => write!(buf, "{v}")?,
1177-
Value::Unsigned(Some(v)) => write!(buf, "{v}")?,
1178-
Value::BigUnsigned(Some(v)) => write!(buf, "{v}")?,
1171+
Value::TinyInt(Some(v)) => {
1172+
write_int(buf, *v);
1173+
}
1174+
Value::SmallInt(Some(v)) => {
1175+
write_int(buf, *v);
1176+
}
1177+
Value::Int(Some(v)) => {
1178+
write_int(buf, *v);
1179+
}
1180+
Value::BigInt(Some(v)) => {
1181+
write_int(buf, *v);
1182+
}
1183+
Value::TinyUnsigned(Some(v)) => {
1184+
write_int(buf, *v);
1185+
}
1186+
Value::SmallUnsigned(Some(v)) => {
1187+
write_int(buf, *v);
1188+
}
1189+
Value::Unsigned(Some(v)) => {
1190+
write_int(buf, *v);
1191+
}
1192+
Value::BigUnsigned(Some(v)) => {
1193+
write_int(buf, *v);
1194+
}
11791195
Value::Float(Some(v)) => write!(buf, "{v}")?,
11801196
Value::Double(Some(v)) => write!(buf, "{v}")?,
11811197
Value::String(Some(v)) => self.write_string_quoted(v, buf),

src/backend/sqlite/table.rs

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use super::*;
2+
use crate::write_int;
23

34
impl TableBuilder for SqliteQueryBuilder {
45
fn prepare_column_def(&self, column_def: &ColumnDef, sql: &mut impl SqlWriter) {
@@ -107,15 +108,15 @@ impl SqliteQueryBuilder {
107108
ColumnType::Char(length) => match length {
108109
Some(length) => {
109110
sql.write_str("char(").unwrap();
110-
write!(sql, "{length}").unwrap();
111+
write_int(sql, *length);
111112
sql.write_char(')')
112113
}
113114
None => sql.write_str("char"),
114115
},
115116
ColumnType::String(length) => match length {
116117
StringLen::N(length) => {
117118
sql.write_str("varchar(").unwrap();
118-
write!(sql, "{length}").unwrap();
119+
write_int(sql, *length);
119120
sql.write_char(')')
120121
}
121122
_ => sql.write_str("varchar"),
@@ -142,9 +143,9 @@ impl SqliteQueryBuilder {
142143
panic!("precision cannot be larger than 16");
143144
}
144145
sql.write_str("real(").unwrap();
145-
write!(sql, "{precision}").unwrap();
146+
write_int(sql, *precision);
146147
sql.write_str(", ").unwrap();
147-
write!(sql, "{scale}").unwrap();
148+
write_int(sql, *scale);
148149
sql.write_char(')')
149150
}
150151
None => sql.write_str("real"),
@@ -157,13 +158,13 @@ impl SqliteQueryBuilder {
157158
ColumnType::Interval(_, _) => unimplemented!("Interval is not available in Sqlite."),
158159
ColumnType::Binary(length) => {
159160
sql.write_str("blob(").unwrap();
160-
write!(sql, "{length}").unwrap();
161+
write_int(sql, *length);
161162
sql.write_char(')')
162163
}
163164
ColumnType::VarBinary(length) => match length {
164165
StringLen::N(length) => {
165166
sql.write_str("varbinary_blob(").unwrap();
166-
write!(sql, "{length}").unwrap();
167+
write_int(sql, *length);
167168
sql.write_char(')')
168169
}
169170
_ => sql.write_str("varbinary_blob"),
@@ -173,9 +174,9 @@ impl SqliteQueryBuilder {
173174
ColumnType::Money(precision) => match precision {
174175
Some((precision, scale)) => {
175176
sql.write_str("real_money(").unwrap();
176-
write!(sql, "{precision}").unwrap();
177+
write_int(sql, *precision);
177178
sql.write_str(", ").unwrap();
178-
write!(sql, "{scale}").unwrap();
179+
write_int(sql, *scale);
179180
sql.write_char(')')
180181
}
181182
None => sql.write_str("real_money"),

src/prepare.rs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,12 +68,10 @@ impl std::fmt::Display for SqlWriterValues {
6868

6969
impl SqlWriter for SqlWriterValues {
7070
fn push_param<T: QueryBuilder>(&mut self, value: Value, _: &T) {
71-
self.counter += 1;
72-
self.string.write_str(&self.placeholder).unwrap();
71+
self.string.push_str(&self.placeholder);
7372
if self.numbered {
74-
let counter = self.counter;
75-
76-
write!(self.string, "{counter}").unwrap();
73+
self.counter += 1;
74+
write_int(&mut self.string, self.counter);
7775
}
7876
self.values.push(value)
7977
}
@@ -83,6 +81,20 @@ impl SqlWriter for SqlWriterValues {
8381
}
8482
}
8583

84+
#[cfg(feature = "itoa")]
85+
#[inline]
86+
pub(crate) fn write_int(w: &mut (impl Write + ?Sized), n: impl itoa::Integer) {
87+
let mut buf = itoa::Buffer::new();
88+
let s = buf.format(n);
89+
w.write_str(s).unwrap();
90+
}
91+
92+
#[cfg(not(feature = "itoa"))]
93+
#[inline(always)]
94+
pub(crate) fn write_int(w: &mut (impl Write + ?Sized), n: impl std::fmt::Display) {
95+
write!(w, "{n}").unwrap();
96+
}
97+
8698
pub fn inject_parameters(sql: &str, params: &[Value], query_builder: &impl QueryBuilder) -> String {
8799
let mut counter = 0;
88100
let mut output = String::new();

src/raw_sql.rs

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
pub mod seaql;
22

3-
use crate::QueryBuilder;
4-
use std::fmt::Write;
3+
use crate::{QueryBuilder, write_int};
54

65
#[derive(Debug)]
76
pub struct RawSqlQueryBuilder {
@@ -34,7 +33,7 @@ impl RawSqlQueryBuilder {
3433
}
3534
self.sql.push_str(self.placeholder);
3635
if self.numbered {
37-
write!(&mut self.sql, "{}", self.parameter_index).unwrap();
36+
write_int(&mut self.sql, self.parameter_index);
3837
self.parameter_index += 1;
3938
}
4039
}

0 commit comments

Comments
 (0)