1use smallvec::SmallVec;
43use smol_str::SmolStr;
44use std::borrow::Cow;
45use std::fmt;
46
47#[derive(Clone, PartialEq, Eq, Hash)]
68pub struct Identifier(SmolStr);
69
70impl Identifier {
71 #[inline]
73 pub fn new(s: impl AsRef<str>) -> Self {
74 Self(SmolStr::new(s.as_ref()))
75 }
76
77 #[inline]
79 pub const fn from_static(s: &'static str) -> Self {
80 Self(SmolStr::new_static(s))
81 }
82
83 #[inline]
85 pub fn as_str(&self) -> &str {
86 self.0.as_str()
87 }
88
89 #[inline]
91 pub fn is_inline(&self) -> bool {
92 self.0.is_heap_allocated() == false
93 }
94
95 #[inline]
97 pub fn len(&self) -> usize {
98 self.0.len()
99 }
100
101 #[inline]
103 pub fn is_empty(&self) -> bool {
104 self.0.is_empty()
105 }
106}
107
108impl fmt::Debug for Identifier {
109 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
110 write!(f, "Identifier({:?})", self.0.as_str())
111 }
112}
113
114impl fmt::Display for Identifier {
115 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
116 write!(f, "{}", self.0)
117 }
118}
119
120impl From<&str> for Identifier {
121 #[inline]
122 fn from(s: &str) -> Self {
123 Self::new(s)
124 }
125}
126
127impl From<String> for Identifier {
128 #[inline]
129 fn from(s: String) -> Self {
130 Self(SmolStr::new(&s))
131 }
132}
133
134impl From<&String> for Identifier {
135 #[inline]
136 fn from(s: &String) -> Self {
137 Self(SmolStr::new(s))
138 }
139}
140
141impl From<Cow<'_, str>> for Identifier {
142 #[inline]
143 fn from(s: Cow<'_, str>) -> Self {
144 Self(SmolStr::new(&s))
145 }
146}
147
148impl AsRef<str> for Identifier {
149 #[inline]
150 fn as_ref(&self) -> &str {
151 self.as_str()
152 }
153}
154
155impl Default for Identifier {
156 fn default() -> Self {
157 Self(SmolStr::default())
158 }
159}
160
161#[derive(Clone, PartialEq, Eq, Hash)]
183pub struct CowIdentifier<'a>(Cow<'a, str>);
184
185impl<'a> CowIdentifier<'a> {
186 #[inline]
188 pub const fn borrowed(s: &'a str) -> Self {
189 Self(Cow::Borrowed(s))
190 }
191
192 #[inline]
194 pub fn owned(s: String) -> Self {
195 Self(Cow::Owned(s))
196 }
197
198 #[inline]
200 pub fn new(s: impl Into<Cow<'a, str>>) -> Self {
201 Self(s.into())
202 }
203
204 #[inline]
206 pub fn as_str(&self) -> &str {
207 &self.0
208 }
209
210 #[inline]
212 pub fn is_borrowed(&self) -> bool {
213 matches!(self.0, Cow::Borrowed(_))
214 }
215
216 #[inline]
218 pub fn into_owned(self) -> String {
219 self.0.into_owned()
220 }
221}
222
223impl<'a> fmt::Debug for CowIdentifier<'a> {
224 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
225 write!(
226 f,
227 "CowIdentifier({:?}, borrowed={})",
228 self.0.as_ref(),
229 self.is_borrowed()
230 )
231 }
232}
233
234impl<'a> fmt::Display for CowIdentifier<'a> {
235 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
236 write!(f, "{}", self.0)
237 }
238}
239
240impl<'a> From<&'a str> for CowIdentifier<'a> {
241 #[inline]
242 fn from(s: &'a str) -> Self {
243 Self::borrowed(s)
244 }
245}
246
247impl From<String> for CowIdentifier<'static> {
248 #[inline]
249 fn from(s: String) -> Self {
250 Self::owned(s)
251 }
252}
253
254impl<'a> AsRef<str> for CowIdentifier<'a> {
255 #[inline]
256 fn as_ref(&self) -> &str {
257 self.as_str()
258 }
259}
260
261impl<'a> Default for CowIdentifier<'a> {
262 fn default() -> Self {
263 Self::borrowed("")
264 }
265}
266
267pub type ColumnList = SmallVec<[Identifier; 8]>;
276
277pub type ColumnNameList = SmallVec<[String; 8]>;
279
280pub type CowColumnList<'a> = SmallVec<[Cow<'a, str>; 8]>;
282
283pub type OrderByList = SmallVec<[(Identifier, crate::types::SortOrder); 4]>;
285
286pub type PartitionByList = SmallVec<[Identifier; 4]>;
288
289pub type ExprList = SmallVec<[String; 8]>;
291
292pub type ValueList<T> = SmallVec<[T; 16]>;
294
295#[derive(Debug, Clone)]
320pub struct ReusableBuilder {
321 buffer: String,
322 initial_capacity: usize,
324}
325
326impl ReusableBuilder {
327 #[inline]
329 pub fn new() -> Self {
330 Self {
331 buffer: String::new(),
332 initial_capacity: 0,
333 }
334 }
335
336 #[inline]
338 pub fn with_capacity(capacity: usize) -> Self {
339 Self {
340 buffer: String::with_capacity(capacity),
341 initial_capacity: capacity,
342 }
343 }
344
345 #[inline]
347 pub fn push(&mut self, s: &str) -> &mut Self {
348 self.buffer.push_str(s);
349 self
350 }
351
352 #[inline]
354 pub fn push_char(&mut self, c: char) -> &mut Self {
355 self.buffer.push(c);
356 self
357 }
358
359 #[inline]
361 pub fn push_fmt(&mut self, args: fmt::Arguments<'_>) -> &mut Self {
362 use std::fmt::Write;
363 let _ = self.buffer.write_fmt(args);
364 self
365 }
366
367 #[inline]
369 pub fn space(&mut self) -> &mut Self {
370 self.buffer.push(' ');
371 self
372 }
373
374 #[inline]
376 pub fn comma(&mut self) -> &mut Self {
377 self.buffer.push_str(", ");
378 self
379 }
380
381 #[inline]
383 pub fn as_str(&self) -> &str {
384 &self.buffer
385 }
386
387 #[inline]
389 pub fn len(&self) -> usize {
390 self.buffer.len()
391 }
392
393 #[inline]
395 pub fn is_empty(&self) -> bool {
396 self.buffer.is_empty()
397 }
398
399 #[inline]
401 pub fn build(&self) -> String {
402 self.buffer.clone()
403 }
404
405 #[inline]
407 pub fn take(&mut self) -> String {
408 std::mem::take(&mut self.buffer)
409 }
410
411 #[inline]
413 pub fn reset(&mut self) {
414 self.buffer.clear();
415 }
416
417 #[inline]
419 pub fn reset_shrink(&mut self) {
420 self.buffer.clear();
421 if self.buffer.capacity() > self.initial_capacity * 2 {
422 self.buffer.shrink_to(self.initial_capacity);
423 }
424 }
425
426 #[inline]
428 pub fn reserve(&mut self, additional: usize) {
429 self.buffer.reserve(additional);
430 }
431
432 #[inline]
434 pub fn capacity(&self) -> usize {
435 self.buffer.capacity()
436 }
437}
438
439impl Default for ReusableBuilder {
440 fn default() -> Self {
441 Self::new()
442 }
443}
444
445impl fmt::Display for ReusableBuilder {
446 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
447 write!(f, "{}", self.buffer)
448 }
449}
450
451impl From<ReusableBuilder> for String {
452 fn from(builder: ReusableBuilder) -> String {
453 builder.buffer
454 }
455}
456
457pub struct BuilderPool {
480 builders: parking_lot::Mutex<Vec<ReusableBuilder>>,
481 capacity: usize,
482}
483
484impl BuilderPool {
485 pub fn new(pool_size: usize, builder_capacity: usize) -> Self {
487 let builders: Vec<_> = (0..pool_size)
488 .map(|_| ReusableBuilder::with_capacity(builder_capacity))
489 .collect();
490 Self {
491 builders: parking_lot::Mutex::new(builders),
492 capacity: builder_capacity,
493 }
494 }
495
496 #[inline]
498 pub fn get(&self) -> ReusableBuilder {
499 self.builders
500 .lock()
501 .pop()
502 .unwrap_or_else(|| ReusableBuilder::with_capacity(self.capacity))
503 }
504
505 #[inline]
507 pub fn put(&self, mut builder: ReusableBuilder) {
508 builder.reset_shrink();
509 self.builders.lock().push(builder);
510 }
511
512 pub fn len(&self) -> usize {
514 self.builders.lock().len()
515 }
516
517 pub fn is_empty(&self) -> bool {
519 self.builders.lock().is_empty()
520 }
521}
522
523#[derive(Debug, Clone, Default)]
532pub struct OptimizedWindowSpec {
533 pub partition_by: PartitionByList,
535 pub order_by: SmallVec<[(Identifier, crate::types::SortOrder); 4]>,
537 pub frame: Option<WindowFrame>,
539 pub window_ref: Option<Identifier>,
541}
542
543#[derive(Debug, Clone)]
545pub struct WindowFrame {
546 pub frame_type: FrameType,
548 pub start: FrameBound,
550 pub end: Option<FrameBound>,
552}
553
554#[derive(Debug, Clone, Copy, PartialEq, Eq)]
556pub enum FrameType {
557 Rows,
558 Range,
559 Groups,
560}
561
562#[derive(Debug, Clone, PartialEq, Eq)]
564pub enum FrameBound {
565 UnboundedPreceding,
566 Preceding(u32),
567 CurrentRow,
568 Following(u32),
569 UnboundedFollowing,
570}
571
572impl OptimizedWindowSpec {
573 #[inline]
575 pub fn new() -> Self {
576 Self::default()
577 }
578
579 #[inline]
581 pub fn partition_by<I, S>(mut self, columns: I) -> Self
582 where
583 I: IntoIterator<Item = S>,
584 S: Into<Identifier>,
585 {
586 self.partition_by
587 .extend(columns.into_iter().map(Into::into));
588 self
589 }
590
591 #[inline]
593 pub fn partition_by_col(mut self, column: impl Into<Identifier>) -> Self {
594 self.partition_by.push(column.into());
595 self
596 }
597
598 #[inline]
600 pub fn order_by(
601 mut self,
602 column: impl Into<Identifier>,
603 order: crate::types::SortOrder,
604 ) -> Self {
605 self.order_by.push((column.into(), order));
606 self
607 }
608
609 #[inline]
611 pub fn rows(mut self, start: FrameBound, end: Option<FrameBound>) -> Self {
612 self.frame = Some(WindowFrame {
613 frame_type: FrameType::Rows,
614 start,
615 end,
616 });
617 self
618 }
619
620 #[inline]
622 pub fn rows_unbounded_preceding(self) -> Self {
623 self.rows(FrameBound::UnboundedPreceding, Some(FrameBound::CurrentRow))
624 }
625
626 #[inline]
628 pub fn window_ref(mut self, name: impl Into<Identifier>) -> Self {
629 self.window_ref = Some(name.into());
630 self
631 }
632
633 pub fn to_sql(&self, _db_type: crate::sql::DatabaseType) -> String {
635 let mut parts: SmallVec<[String; 4]> = SmallVec::new();
636
637 if let Some(ref name) = self.window_ref {
639 return format!("OVER {}", name);
640 }
641
642 if !self.partition_by.is_empty() {
644 let cols: Vec<_> = self.partition_by.iter().map(|c| c.as_str()).collect();
645 parts.push(format!("PARTITION BY {}", cols.join(", ")));
646 }
647
648 if !self.order_by.is_empty() {
650 let cols: Vec<_> = self
651 .order_by
652 .iter()
653 .map(|(col, order)| {
654 format!(
655 "{} {}",
656 col.as_str(),
657 match order {
658 crate::types::SortOrder::Asc => "ASC",
659 crate::types::SortOrder::Desc => "DESC",
660 }
661 )
662 })
663 .collect();
664 parts.push(format!("ORDER BY {}", cols.join(", ")));
665 }
666
667 if let Some(ref frame) = self.frame {
669 let frame_type = match frame.frame_type {
670 FrameType::Rows => "ROWS",
671 FrameType::Range => "RANGE",
672 FrameType::Groups => "GROUPS",
673 };
674
675 let start = frame_bound_to_sql(&frame.start);
676
677 if let Some(ref end) = frame.end {
678 let end_sql = frame_bound_to_sql(end);
679 parts.push(format!("{} BETWEEN {} AND {}", frame_type, start, end_sql));
680 } else {
681 parts.push(format!("{} {}", frame_type, start));
682 }
683 }
684
685 if parts.is_empty() {
686 "OVER ()".to_string()
687 } else {
688 format!("OVER ({})", parts.join(" "))
689 }
690 }
691}
692
693fn frame_bound_to_sql(bound: &FrameBound) -> &'static str {
694 match bound {
695 FrameBound::UnboundedPreceding => "UNBOUNDED PRECEDING",
696 FrameBound::Preceding(_) => "PRECEDING", FrameBound::CurrentRow => "CURRENT ROW",
698 FrameBound::Following(_) => "FOLLOWING", FrameBound::UnboundedFollowing => "UNBOUNDED FOLLOWING",
700 }
701}
702
703#[cfg(test)]
708mod tests {
709 use super::*;
710
711 #[test]
712 fn test_identifier_inline() {
713 let id = Identifier::new("user_id");
714 assert_eq!(id.as_str(), "user_id");
715 assert!(id.is_inline()); }
717
718 #[test]
719 fn test_identifier_from_static() {
720 let id = Identifier::from_static("email");
721 assert_eq!(id.as_str(), "email");
722 }
723
724 #[test]
725 fn test_cow_identifier_borrowed() {
726 let id = CowIdentifier::borrowed("user_id");
727 assert!(id.is_borrowed());
728 assert_eq!(id.as_str(), "user_id");
729 }
730
731 #[test]
732 fn test_cow_identifier_owned() {
733 let id = CowIdentifier::owned("dynamic".to_string());
734 assert!(!id.is_borrowed());
735 assert_eq!(id.as_str(), "dynamic");
736 }
737
738 #[test]
739 fn test_column_list_stack_allocation() {
740 let mut cols: ColumnList = SmallVec::new();
741 cols.push(Identifier::new("id"));
742 cols.push(Identifier::new("name"));
743 cols.push(Identifier::new("email"));
744 cols.push(Identifier::new("created_at"));
745
746 assert!(!cols.spilled());
748 assert_eq!(cols.len(), 4);
749 }
750
751 #[test]
752 fn test_column_list_heap_spillover() {
753 let mut cols: ColumnList = SmallVec::new();
754 for i in 0..10 {
755 cols.push(Identifier::new(format!("col_{}", i)));
756 }
757
758 assert!(cols.spilled());
760 assert_eq!(cols.len(), 10);
761 }
762
763 #[test]
764 fn test_reusable_builder() {
765 let mut builder = ReusableBuilder::with_capacity(64);
766
767 builder.push("SELECT * FROM users");
768 assert_eq!(builder.as_str(), "SELECT * FROM users");
769
770 builder.reset();
771 assert!(builder.is_empty());
772 assert!(builder.capacity() >= 64); builder.push("SELECT * FROM posts");
775 assert_eq!(builder.as_str(), "SELECT * FROM posts");
776 }
777
778 #[test]
779 fn test_reusable_builder_take() {
780 let mut builder = ReusableBuilder::new();
781 builder.push("test");
782
783 let taken = builder.take();
784 assert_eq!(taken, "test");
785 assert!(builder.is_empty());
786 }
787
788 #[test]
789 fn test_builder_pool() {
790 let pool = BuilderPool::new(4, 128);
791
792 let b1 = pool.get();
794 let b2 = pool.get();
795 let _b3 = pool.get();
796 let _b4 = pool.get();
797
798 assert!(pool.is_empty());
800
801 pool.put(b1);
803 pool.put(b2);
804
805 assert_eq!(pool.len(), 2);
806 }
807
808 #[test]
809 fn test_optimized_window_spec() {
810 use crate::types::SortOrder;
811
812 let spec = OptimizedWindowSpec::new()
813 .partition_by(["dept", "team"])
814 .order_by("salary", SortOrder::Desc)
815 .rows_unbounded_preceding();
816
817 let sql = spec.to_sql(crate::sql::DatabaseType::PostgreSQL);
818 assert!(sql.contains("PARTITION BY"));
819 assert!(sql.contains("ORDER BY"));
820 assert!(sql.contains("ROWS"));
821 }
822}