sqlx-data 0.1.4

Zero-boilerplate Repository Pattern for modern Rust applications. Write SQL traits, get async implementations with sophisticated pagination, streaming, batch operations, and more. Seamlessly integrates with existing SQLx code.
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
<h1 align="center">SQLx-Data</h1>
<div align="center">
 <strong>
   ๐Ÿ—๏ธ An advanced SQLx companion for type-safe query repositories
 </strong>
</div>

<br />

<div align="center">
  <p align="center">
    <img src="https://github.com/josercarmo/sqlx-data/raw/HEAD/resources/sqlxdata.jpg" alt="sqlx-data banner" width="50%">
  </p>
</div>

<br />

<div align="center">
  <!-- Crates.io -->
  <a href="https://crates.io/crates/sqlx-data">
    <img src="https://img.shields.io/crates/v/sqlx-data.svg?style=flat-square"
    alt="Crates.io version" /></a>
  <!-- Docs -->
  <a href="https://docs.rs/sqlx-data">
    <img src="https://img.shields.io/badge/docs-latest-blue.svg?style=flat-square" alt="docs.rs docs" /></a>
  <!-- Downloads -->
  <a href="https://crates.io/crates/sqlx-data">
    <img src="https://img.shields.io/crates/d/sqlx-data.svg?style=flat-square" alt="Download" />
  </a>
  <!-- License -->
  <a href="LICENSE">
    <img src="https://img.shields.io/badge/license-MIT-blue.svg?style=flat-square" alt="License" /></a>
</div>

<div align="center">
  <h4>
    <a href="#quick-start">
      Quick Start
    </a>
    <span> | </span>
    <a href="#features">
      Features
    </a>
    <span> | </span>
    <a href="https://docs.rs/sqlx-data">
      Docs
    </a>
    <span> | </span>
    <a href="#examples">
      Examples
    </a>
  </h4>
</div>

<br />

Zero-boilerplate Repository Pattern for modern Rust applications.

Automatic SQLx parameter binding and result parsing with trait-based repositories. Write SQL traits, get async implementations with sophisticated `pagination`, `streaming`, `batch operations`, and `more`. Seamlessly integrates with existing SQLx code โ€” continue using SQLx queries normally, override generated methods, and reuse generated `_query` methods across different contexts.

```rust
#[repo]
trait UserRepo {
    #[dml("SELECT * FROM users WHERE id = ?")]
    async fn find_by_id(&self, id: i64) -> Result<User>;

    #[dml("SELECT * FROM users WHERE age >= ?")]
    async fn find_adults(&self, min_age: u8) -> Result<Vec<User>>;

    #[dml("SELECT * FROM users ORDER BY id")]
    async fn find_all_cursor(&self, params: FilterParams) -> Result<Cursor<User>>;

    #[dml("SELECT * FROM users WHERE age >= 18")]
    async fn stream_active(&self) -> Result<impl Stream<Item = Result<User>>>;
}
```

---

## Features

### ๐ŸŽฏ Core

- **Zero boilerplate** โ€” Write traits, not implementations
- **Compile-time safety** โ€” Always uses SQLx's compile-time macros (`query_as!`, `query!`, `query_scalar!`) ensuring type safety and SQL validation at build time
- **Smart type inference** โ€” Returns `T`, `Option<T>`, `Vec<T>`, tuples, scalars and others
- **Multi-database** โ€” PostgreSQL, MySQL, SQLite

### ๐Ÿš€ Advanced

- **[Pagination & Dynamic Queries]#pagination--dynamic-queries**
  Built-in Serial, Slice, and Cursor strategies. Automatically generates the correct `COUNT(*)` query for pagination metadata and handles complex dynamic filters via `ParamsBuilder`.

- **[Parameter Naming]#parameter-naming**
  Use named parameters with `@parameter_name` syntax for cleaner, more readable queries. Parameters can appear in any order and be reused multiple times within the same query.

- **[Aliases]#aliases**
  Keep your SQL DRY by defining reusable fragments (like common column sets or table joins) and injecting them using `{{mustache}}` syntax.

- **[Scopes]#scopes**
  Rails-inspired query composability. Define reusable `WHERE` clauses or orderings that are **automatically injected** into every query in the repository. Supports Alias interpolation (e.g., `age > {{min_age}}`). Ideal for global patterns like 'SoftDeletable', 'Multi-tenancy', or 'Activable'.

- **[Batch Operations]#batch-operations**
  Perform ultra-fast bulk inserts (up to 40x faster) by taking `Vec<T>` arguments, automatically generating efficient multi-value SQL statements.

- **[JSON Support]#json-support**
  First-class JSON support. Use the `json` attribute to automatically serialize/deserialize complex Rust structs into database JSON columns without manual boilerplate.

- **[Unchecked Queries]#unchecked-queries**
  An escape hatch for DDL, legacy queries, or dynamic SQL that cannot be verified at compile time. Use `unchecked` to bypass the macro's strict validation when necessary.

- **[Method Variants]#method-variants**
  Automatically generate `_with_tx`, `_with_conn`, and `_with_pool` variants for every repository method, ensuring you never get stuck with the wrong executor type.

- **[Streaming]#streaming**
  Return `impl Stream<Item = Result<T>>` to process large datasets efficiently row-by-row, keeping memory usage constant. Leverages SQLx's native [`fetch`]https://docs.rs/sqlx/latest/sqlx/query/struct.QueryAs.html#method.fetch for zero-overhead streaming.

- **[Tracing]#tracing**
  Zero-config observability powered by the [`tracing`]https://crates.io/crates/tracing crate. Automatically instruments every query with [`#[instrument]`]https://docs.rs/tracing/latest/tracing/attr.instrument.html spans, capturing execution time, arguments, and errors.

- **[Generics & Lifetimes]#generics--lifetimes**
  Full support for Rust's generic type system, allowing repositories to handle generic executors, lifetimes, and complex trait bounds.

- **[Hover to inspect]#hover-to-inspect**
  Hover over any repository method in your IDE to see the exact SQL query and generated implementation code.

## Feature Flags

SQLx-Data uses feature flags to enable database and type support. **You must specify both a database and typically `json`**:

### Database Features (choose one)
- `sqlite` โ€” SQLite database support
- `mysql` โ€” MySQL database support
- `postgres` โ€” PostgreSQL database support

### Type Features
- `json` โ€” JSON support with automatic serialization (recommended)
- `chrono` โ€” Chrono date/time types
- `time` โ€” Time crate support
- `uuid` โ€” UUID type support
- `bigdecimal` โ€” BigDecimal support
- `rust_decimal` โ€” Rust Decimal support
- `ipnet` โ€” IP network types
- `bit-vec` โ€” Bit vector support
- `mac_address` โ€” MAC address types

### Other Features
- `tracing` โ€” Automatic query instrumentation
- `tls-native` โ€” Native TLS support
- `tls-rustls` โ€” Rustls TLS support

### Example Usage
```toml
# For SQLite with JSON
[dependencies]
sqlx-data = { version = "0.1.4", features = ["sqlite", "json"] }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio", "macros", "migrate"] }

# For PostgreSQL with multiple types
[dependencies]
sqlx-data = { version = "0.1.4", features = ["postgres", "json", "chrono", "uuid"] }
sqlx = { version = "0.8", features = ["postgres", "runtime-tokio", "macros", "migrate"] }

# For MySQL with tracing
[dependencies]
sqlx-data = { version = "0.1.4", features = ["mysql", "json", "tracing"] }
sqlx = { version = "0.8", features = ["mysql", "runtime-tokio", "macros", "migrate"] }
```

---

## Quick Start

```toml
[dependencies]
sqlx-data = { version = "0.1.4", features = ["sqlite","json"] }
sqlx = { version = "0.8", features = ["sqlite", "runtime-tokio"] }
tokio = { version = "1", features = ["full"] }
```

```rust
use sqlx_data::{repo, dml, Pool, Result, QueryResult};

#[derive(sqlx::FromRow)]
struct User {
    id: i64,
    name: String,
    email: String,
}

#[repo]
trait UserRepo {
    #[dml("SELECT * FROM users WHERE id = ?")]
    async fn find_by_id(&self, id: i64) -> Result<User>;
    
    #[dml("SELECT * FROM users WHERE age >= ?")]
    async fn find_adults(&self, min_age: u8) -> Result<Vec<User>>;
    
    #[dml("INSERT INTO users (name, email) VALUES (?, ?)")]
    async fn create(&self, name: String, email: String) -> Result<QueryResult>;
}

struct App { pool: Pool }

impl UserRepo for App {
    fn get_pool(&self) -> &Pool { &self.pool }
}

#[tokio::main]
async fn main() -> Result<()> {
    let pool = Pool::connect("mysql://...").await?;
    let app = App { pool };
    
    let user = app.find_by_id(1).await?;
    println!("{}", user.name);
    
    Ok(())
}
```

---

## Under the Hood

When you define a repository trait with `#[repo]` and `#[dml]`, the macros generate additional `_query` methods that contain the actual SQL execution logic. Your original methods become default implementations that call these generated methods, giving you the flexibility to override them with custom logic.

```rust
// What you write:
#[repo]
trait UserRepo {
    #[dml("SELECT * FROM users WHERE id = ?")]
    async fn find_by_id(&self, id: i64) -> Result<User>;

    #[dml("SELECT * FROM users WHERE age >= ?")]
    async fn find_adults(&self, min_age: u8) -> Result<Vec<User>>;
}

// What gets generated (simplified):
trait UserRepo {
    // Generated _query methods with actual SQL execution
    async fn find_by_id_query(&self, id: i64) -> Result<User> {
        sqlx::query_as!(User, "SELECT * FROM users WHERE id = ?", id)
            .fetch_one(self.get_pool())
            .await
    }

    async fn find_adults_query(&self, min_age: u8) -> Result<Vec<User>> {
        sqlx::query_as!(User, "SELECT * FROM users WHERE age >= ?", min_age)
            .fetch_all(self.get_pool())
            .await
    }

    // Original methods become default implementations
    async fn find_by_id(&self, id: i64) -> Result<User> {
        self.find_by_id_query(id).await
    }

    async fn find_adults(&self, min_age: u8) -> Result<Vec<User>> {
        self.find_adults_query(min_age).await
    }

    // Must be implemented by user
    fn get_pool(&self) -> &Pool;
}
```

This design allows you to:
- **Use generated methods directly** by just implementing `get_pool()`
- **Override specific methods** with custom logic while still calling `_query` methods
- **Reuse `_query` methods** in different contexts or custom implementations

---

## Pagination & Dynamic Queries

Handle complex pagination scenarios with the fluent `ParamsBuilder` API.

### 1. Zero-Boilerplate Filters
Combine pagination, filtering, and sorting in a single object:

```rust
use sqlx_data::{Serial, IntoParams, ParamsBuilder, FilterValue};

#[repo]
trait UserRepo {
    // One argument handles everything: defaults + client overrides
    #[dml("SELECT * FROM users")]
    async fn find_users(&self, params: impl IntoParams) -> Result<Serial<User>>;
}

// Client usage:
let params = ParamsBuilder::new()
    .serial()
        .page(1, 20)      // Page 1, 20 items per page
        .done()
    .filter()
        .gt("age", 18)    // WHERE age > 18
        .like("name", "%Alice%") // AND name LIKE '%Alice%'
        .done()
    .sort()
        .desc("id")       // ORDER BY id DESC
        .asc("name")      // THEN BY name ASC
        .done()
    .build();

let result = repo.find_users(params).await?;
```

### 2. Cursor Pagination (Infinite Scroll)
Best for high-performance feeds. Supports `after`, `before` based on specific fields.

```rust
use sqlx_data::{Cursor, ParamsBuilder};

#[repo]
trait FeedRepo {
    // Automatically handles `before`/`after` cursors based on sorted fields
    #[dml("SELECT * FROM posts")]
    async fn user_feed(&self, params: impl IntoParams) -> Result<Cursor<Post>>;
}

// Initial Request:
let params = ParamsBuilder::new()
    .cursor()
        .first_page()   // Start from beginning
        .done()
    .sort()
        .desc("id")     // Critical: Cursor relies on stable sorting
        .done()
    .limit(10)          // Set limit on ParamsBuilder
    .build();

let page = repo.user_feed(params).await?;

// Next Page:
if let Some(next_cursor) = page.next_cursor {
    let next_params = ParamsBuilder::new()
        .cursor()
            .next_cursor::<Post>(&next_cursor) // Type-safe continuation
            .done()
        .sort()
            .desc("id")
            .done()
        .limit(10)          // Set limit on ParamsBuilder
        .build();
        
    let next_page = repo.user_feed(next_params).await?;
}
```

### 3. Dynamic Search
Built-in text search construction:

```rust
let params = ParamsBuilder::new()
    .slice()
        .page(1, 50)
        .done()
    .search()
        .query("alice")        // Search term
        .fields(["username", "email"]) // Columns to search
        .case_sensitive(false)
        .done()
    .build();

// Generates:
// WHERE ... AND (username LIKE '%alice%' OR email LIKE '%alice%')
```

---

## Parameter Naming

Clean, readable queries with named parameters using `@parameter_name` syntax. Parameters can be defined in any order and reused multiple times.

### Basic Named Parameters
```rust
#[repo]
trait UserRepo {
    // Single named parameter
    #[dml("SELECT * FROM users WHERE name = @name")]
    async fn find_by_name(&self, name: String) -> Result<Vec<User>>;

    // Multiple named parameters in any order
    #[dml("SELECT * FROM users WHERE age > @min_age AND name LIKE @pattern")]
    async fn find_by_age_and_name(&self, pattern: String, min_age: u8) -> Result<Vec<User>>;
}
```

### Parameter Reuse
Same parameter can be used multiple times in a single query(only for postgres and sqlite):
```rust
#[repo]
trait UserRepo {
    // @search_term used twice
    #[dml("SELECT * FROM users WHERE (name = @search_term OR email = @search_term) AND age > @min_age")]
    async fn search_user(&self, search_term: String, min_age: u8) -> Result<Vec<User>>;
}
```

### Mixed with Positional
Named parameters work alongside traditional positional parameters:
```rust
#[repo]
trait UserRepo {
    // Mix named and positional
    #[dml("SELECT * FROM users WHERE name = @name AND id > $1")]
    async fn find_recent_by_name(&self, min_id: i64, name: String) -> Result<Vec<User>>;
}
```

### Complex Queries
Named parameters shine in complex queries with many conditions:
```rust
#[repo]
trait UserRepo {
    #[dml("
        SELECT u.*, COUNT(o.id) as order_count
        FROM users u
        LEFT JOIN orders o ON u.id = o.user_id
        WHERE u.age BETWEEN @min_age AND @max_age
          AND u.created_at >= @start_date
          AND u.status = @status
        GROUP BY u.id
        HAVING COUNT(o.id) >= @min_orders
        ORDER BY u.created_at DESC
    ")]
    async fn find_active_customers(
        &self,
        min_age: u8,
        max_age: u8,
        start_date: chrono::NaiveDateTime,
        status: String,
        min_orders: i32
    ) -> Result<Vec<CustomerStats>>;
}
```

**Benefits:**
- ๐Ÿ” **Readable**: Parameter names explain their purpose in the query
- ๐Ÿ”„ **Flexible Order**: Function parameters can be in any order
- โ™ป๏ธ **Reusable**: Same parameter used multiple times without repetition
- ๐Ÿ”— **Compatible**: Works with existing positional parameter code
- ๐Ÿ›ก๏ธ **Type Safe**: Same compile-time verification as positional parameters

---

## Aliases

Reusable SQL fragments for DRY code:

```rust
```rust
#[repo]
#[alias(user_columns = "id, name, email, age")]
#[alias(user_table = "users")]
#[alias(active_filter = "WHERE age >= 18")]
trait UserRepo {
    #[dml("SELECT {{user_columns}} FROM {{user_table}} {{active_filter}}")]
    async fn find_adults(&self) -> Result<Vec<User>>;
    
    #[dml("SELECT COUNT(*) FROM {{user_table}} {{active_filter}}")]
    async fn count_adults(&self) -> Result<i64>;
}
```

---

## Scopes

Automatic query enhancement โ€” define once, apply everywhere:

**Pro tip:** Perfect for Rails-like patterns such as multi-tenancy (`tenant_id = ?`), soft deletes (`archived_at IS NULL`), and active records (`status = 'active'`).

```rust
```rust
#[repo]
#[alias(min_age = "18")]
#[scope(adults = "age >= {{min_age}}")]
#[scope(named = "name IS NOT NULL")]
#[scope(recent_birth = "birth_year > 2000")]
#[scope(ordered = "age DESC", target = "order_by")]
trait UserRepo {
    // All scopes automatically applied
    #[dml("SELECT * FROM users")]
    async fn find_all(&self) -> Result<Vec<User>>;
    
    // Ignore specific scopes when needed
    #[scope_ignore(ordered)]
    #[dml("SELECT * FROM users ORDER BY name")]
    async fn find_alphabetical(&self) -> Result<Vec<User>>;
}
```

**Generated SQL:**
```sql
-- find_all() becomes:
SELECT * FROM users 
WHERE age >= 18 
  AND name IS NOT NULL 
  AND birth_year > 2000
ORDER BY age DESC

-- find_alphabetical() becomes:
SELECT * FROM users 
WHERE age >= 18 
  AND name IS NOT NULL 
  AND birth_year > 2000
ORDER BY name
```

---

## Batch Operations

Efficient bulk inserts:

```rust
#[repo]
trait UserRepo {
    #[dml("INSERT INTO users (name, email, age) VALUES")]
    async fn insert_batch(&self, rows: Vec<(String, String, u8)>) -> Result<QueryResult>;
}

// Usage
let users = vec![
    ("Alice".into(), "alice@example.com".into(), 30),
    ("Bob".into(), "bob@example.com".into(), 25),
    ("Charlie".into(), "charlie@example.com".into(), 35),
];

repo.insert_batch(users).await?;
```

**Generated Code:**
```rust
// The macro generates this optimized batch insert method:
async fn insert_batch_query(&self, rows: Vec<(String, String, u8)>) -> Result<QueryResult> {
    if rows.is_empty() {
        return Ok(QueryResult::default());
    }

    // Uses SQLx's efficient QueryBuilder with push_values
    let mut qb = sqlx::QueryBuilder::new("INSERT INTO users (name, email, age) ");
    qb.push_values(rows, |mut b, tuple| {
        b.push_bind(tuple.0)    // name
         .push_bind(tuple.1)    // email
         .push_bind(tuple.2);   // age
    });

    qb.build().execute(self.get_pool()).await
}
```

**Performance:** Inserts 1000 rows in ~50ms vs ~2000ms with individual inserts

---

### Streaming
Best for: Large datasets, memory efficiency

Uses SQLx's native [`fetch`](https://docs.rs/sqlx/latest/sqlx/query/struct.QueryAs.html#method.fetch) method for zero-overhead row-by-row processing, keeping memory usage constant regardless of result set size.

```rust
use futures::Stream;

#[repo]
trait UserRepo {
    // Return impl Stream for memory-efficient processing
    #[dml("SELECT * FROM users WHERE age >= 18")]
    fn stream_active(&self) -> impl Stream<Item = Result<User>> + Send;
}

// Usage
let mut stream = repo.stream_active();
while let Some(user) = stream.next().await {
    println!("Processing {}", user?.name);
}
```

---

### JSON Support
Automatic JSON serialization/deserialization:

```rust
use sqlx::types::Json;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct UserProfile {
    email: String,
    age: u32,
    department: String,
}

#[repo]
trait UserRepo {
    // Automatic JSON serialization for parameters using 'json' attribute
    #[dml("INSERT INTO json_users (name, profile_json) VALUES (?, ?)", json)]
    async fn save_profile(&self, name: String, profile: UserProfile) -> Result<QueryResult>;

    // Type-safe JSON retrieval
    #[dml("SELECT id, name, profile_json, preferences FROM json_users WHERE id = ?")]
    async fn find_raw_json(&self, id: i64) -> Result<Option<(i64, String, Json<JsonValue>, Option<JsonValue>)>>;
}
```

---

### Unchecked Queries
Bypass compile-time verification for complex queries or DDL:

```rust
#[repo]
trait AdminRepo {
    // Use 'unchecked' to skip SQL validation
    #[dml("CREATE TABLE IF NOT EXISTS logs (id INT)", unchecked)]
    async fn create_table(&self) -> Result<QueryResult>;

    #[dml("SELECT * FROM users WHERE id = " + "1", unchecked)]
    async fn dynamic_query(&self) -> Result<Vec<User>>;
}
```

---

### Binary Data (BLOBs)
Efficient handling of binary data:

```rust
use bytes::Bytes;

#[repo]
trait FileRepo {
    #[dml("INSERT INTO files (name, content_type, file_size, data, is_compressed) VALUES (?, ?, ?, ?, ?)")]
    async fn upload(
        &self, 
        name: String, 
        content_type: String, 
        file_size: u32, 
        data: bytes::Bytes,
        is_compressed: bool
    ) -> Result<QueryResult>;

    #[dml("SELECT data FROM files WHERE id = ?")]
    async fn download(&self, id: i64) -> Result<Vec<u8>>;
}
```

---

### Generics & Lifetimes
Full support for Rust's type system:

```rust
#[repo]
trait GenericRepo {
    // Support for lifetime parameters
    #[dml("SELECT * FROM users WHERE name = ?")]
    async fn find_by_name<'a>(&self, name: &'a str) -> Result<Option<User>>;

    // Support for custom executors (transactions, connections)
    #[dml("INSERT INTO logs (msg) VALUES (?)")]
    async fn log_with_executor<'e, E>(&self, executor: E, msg: &str) -> Result<QueryResult>
    where
        E: sqlx::Executor<'e, Database = sqlx::MySql>;
}
```

---

### Method Variants

Generate multiple executor variants automatically:

```rust
#[repo]
trait UserRepo {
    #[generate_versions(pool, tx, conn, exec)]
    #[dml("UPDATE users SET name = ? WHERE id = ?")]
    async fn update_name(&self, name: String, id: i64) -> Result<QueryResult>;
}

// Generates 5 methods:
// - update_name(&self, ...)                           // uses get_pool()
// - update_name_with_pool(&self, pool: &Pool, ...)    // explicit pool
// - update_name_with_tx(&self, tx: &mut Transaction, ...)
// - update_name_with_conn(&self, conn: &mut Connection, ...)
// - update_name_with_executor(&self, exec: impl Executor, ...)

// Usage
let mut tx = pool.begin().await?;
repo.update_name_with_tx(&mut tx, "Alice".into(), 1).await?;
repo.update_name_with_tx(&mut tx, "Bob".into(), 2).await?;
tx.commit().await?;
```

---

## Tracing

Built-in observability powered by the [`tracing`](https://crates.io/crates/tracing) library. Zero configuration required to get detailed logs automatically instrumented with [`#[instrument]`](https://docs.rs/tracing/latest/tracing/attr.instrument.html):

```rust
use tracing::instrument;

#[repo]
trait UserRepo {
    #[dml("SELECT * FROM users WHERE id = ?")]
    #[instrument(skip(self))]
    async fn find_by_id(&self, id: i64) -> Result<User>;
}

// Automatically logs:
// - Method entry/exit
// - Parameters (except skipped ones)
// - Execution time
// - Errors
```

---

## Hover to Inspect

See the generated SQL and implementation in your IDE:

![Hover to see generated code](https://github.com/josercarmo/sqlx-data/raw/HEAD/resources/hover_to_inspect.gif)

**Pro tip:** Copy the generated code to override methods or call `_query` methods from custom logic:

```rust
impl UserRepo for App {
    fn get_pool(&self) -> &Pool { &self.pool }

    // Override generated method with custom logic
    async fn find_by_id(&self, id: i64) -> Result<User> {
        // Add logging, caching, validation, etc.
        log::info!("Finding user with id: {}", id);
        self.find_by_id_query(id).await
    }

    // Use _query method in custom implementations
    async fn find_user_with_cache(&self, id: i64) -> Result<User> {
        if let Some(cached) = get_from_cache(id) {
            return Ok(cached);
        }
        let user = self.find_by_id_query(id).await?;
        cache_user(&user);
        Ok(user)
    }
}
```

---

### Complex Queries

```rust
#[repo]
trait UserRepo {
    #[dml("
        SELECT 
            u.id,
            u.name,
            COUNT(o.id) as order_count,
            SUM(o.total) as total_spent
        FROM users u
        LEFT JOIN orders o ON u.id = o.user_id
        WHERE u.age >= ?
        GROUP BY u.id, u.name
        HAVING COUNT(o.id) > ?
        ORDER BY total_spent DESC
    ")]
    async fn find_top_customers(
        &self, 
        min_age: u8, 
        min_orders: i32
    ) -> Result<Vec<CustomerStats>>;
}
```

### File-based Queries

```rust
#[repo]
trait UserRepo {
    #[dml(file = "queries/complex_user_report.sql")]
    async fn generate_report(&self) -> Result<Vec<ReportRow>>;
}
```



---

## Supported Return Types

| Return Type | Example | Fetch Strategy |
|------------|---------|----------------|
| `T` | `User` | `fetch_one` |
| `Option<T>` | `Option<User>` | `fetch_optional` |
| `Vec<T>` | `Vec<User>` | `fetch_all` |
| Scalar | `i64`, `String`, `bool` | `fetch_one` |
| Tuple | `(String, i64)` | `fetch_one` |
| `Vec<Tuple>` | `Vec<(String, i64)>` | `fetch_all` |
| `Serial<T>` | `Serial<User>` | Paginated |
| `Slice<T>` | `Slice<User>` | Paginated |
| `Cursor<T>` | `Cursor<User>` | Paginated |
| Database-specific | `MySqlQueryResult`, `PgQueryResult` | `execute` |

---

## Database Support

| Database | Placeholder | Example |
|----------|-------------|---------|
| **MySQL** | `?` | `WHERE id = ?` |
| **PostgreSQL** | `$1`, `$2` | `WHERE id = $1` |
| **SQLite** | `$1`, `$2` | `WHERE id = $1` |

---

## Performance

- **Compile-time overhead:** ~20-90ยตs per query (macro expansion)
- **Runtime overhead:** Zero โ€” generates the same code you'd write manually
- **Batch inserts:** 40x faster than individual inserts (1000 rows: 50ms vs 2000ms)

---

## Examples

- [`hello-world`]examples/hello-world โ€” A minimal setup to get started.
- [`axum-crud-api`]examples/axum-crud-api โ€” A full-featured REST API using Axum, showcasing pagination, filtering, and best practices.

---

## Contributing

Contributions are welcome! Please see [CONTRIBUTING.md](CONTRIBUTING.md).

---

## License

Licensed under either of:

- Apache License, Version 2.0 ([LICENSE-APACHE]LICENSE-APACHE)
- MIT license ([LICENSE-MIT]LICENSE-MIT)

at your option.

---

## Acknowledgments

Built on top of the excellent [SQLx](https://github.com/launchbadge/sqlx) library.
Powered by [syn](https://github.com/dtolnay/syn), [quote](https://github.com/dtolnay/quote), and [proc-macro2](https://github.com/dtolnay/proc-macro2) for macro expansion.
SQL parsing and validation leverage [sqlparser](https://github.com/sqlparser-rs/sqlparser-rs).