@@ -32,7 +32,8 @@ use std::iter::FromIterator;
3232use std:: str:: FromStr ;
3333
3434use crate :: relational:: {
35- Column , ColumnType , IdType , Layout , SqlName , Table , PRIMARY_KEY_COLUMN , STRING_PREFIX_SIZE ,
35+ Column , ColumnType , IdType , Layout , SqlName , Table , BYTE_ARRAY_PREFIX_SIZE , PRIMARY_KEY_COLUMN ,
36+ STRING_PREFIX_SIZE ,
3637} ;
3738use crate :: sql_value:: SqlValue ;
3839use crate :: {
@@ -665,40 +666,110 @@ impl Comparison {
665666 }
666667}
667668
669+ enum PrefixType < ' a > {
670+ String ( & ' a Column ) ,
671+ Bytes ( & ' a Column ) ,
672+ }
673+
674+ impl < ' a > PrefixType < ' a > {
675+ fn new ( column : & ' a Column ) -> QueryResult < Self > {
676+ match column. column_type {
677+ ColumnType :: String => Ok ( PrefixType :: String ( column) ) ,
678+ ColumnType :: Bytes => Ok ( PrefixType :: Bytes ( column) ) ,
679+ _ => Err ( constraint_violation ! (
680+ "cannot setup prefix comparison for column {} of type {}" ,
681+ column. name( ) ,
682+ column. column_type( ) . sql_type( )
683+ ) ) ,
684+ }
685+ }
686+
687+ /// Push the SQL expression for a prefix of values in our column. That
688+ /// should be the same expression that we used when creating an index
689+ /// for the column
690+ fn push_column_prefix ( & self , out : & mut AstPass < Pg > ) -> QueryResult < ( ) > {
691+ match self {
692+ PrefixType :: String ( column) => {
693+ out. push_sql ( "left(" ) ;
694+ out. push_identifier ( column. name . as_str ( ) ) ?;
695+ out. push_sql ( ", " ) ;
696+ out. push_sql ( & STRING_PREFIX_SIZE . to_string ( ) ) ;
697+ out. push_sql ( ")" ) ;
698+ }
699+ PrefixType :: Bytes ( column) => {
700+ out. push_sql ( "substring(" ) ;
701+ out. push_identifier ( column. name . as_str ( ) ) ?;
702+ out. push_sql ( ", 1, " ) ;
703+ out. push_sql ( & BYTE_ARRAY_PREFIX_SIZE . to_string ( ) ) ;
704+ out. push_sql ( ")" ) ;
705+ }
706+ }
707+ Ok ( ( ) )
708+ }
709+
710+ fn is_large ( & self , value : & Value ) -> Result < bool , ( ) > {
711+ match ( self , value) {
712+ ( PrefixType :: String ( _) , Value :: String ( s) ) => Ok ( s. len ( ) > STRING_PREFIX_SIZE - 1 ) ,
713+ ( PrefixType :: Bytes ( _) , Value :: Bytes ( b) ) => Ok ( b. len ( ) > BYTE_ARRAY_PREFIX_SIZE - 1 ) ,
714+ ( PrefixType :: Bytes ( _) , Value :: String ( s) ) => {
715+ let len = if s. starts_with ( "0x" ) {
716+ ( s. len ( ) - 2 ) / 2
717+ } else {
718+ s. len ( ) / 2
719+ } ;
720+ Ok ( len > BYTE_ARRAY_PREFIX_SIZE - 1 )
721+ }
722+ _ => Err ( ( ) ) ,
723+ }
724+ }
725+ }
726+
668727/// Produce a comparison between the string column `column` and the string
669728/// value `text` that makes it obvious to Postgres' optimizer that it can
670729/// first consult the partial index on `left(column, STRING_PREFIX_SIZE)`
671730/// instead of going straight to a sequential scan of the underlying table.
672731/// We do this by writing the comparison `column op text` in a way that
673732/// involves `left(column, STRING_PREFIX_SIZE)`
674- #[ derive( Constructor ) ]
675733struct PrefixComparison < ' a > {
676734 op : Comparison ,
735+ kind : PrefixType < ' a > ,
677736 column : & ' a Column ,
678737 text : & ' a Value ,
679738}
680739
681740impl < ' a > PrefixComparison < ' a > {
682- fn push_column_prefix ( column : & Column , mut out : AstPass < Pg > ) -> QueryResult < ( ) > {
683- out. push_sql ( "left(" ) ;
684- out. push_identifier ( column. name . as_str ( ) ) ?;
685- out. push_sql ( ", " ) ;
686- out. push_sql ( & STRING_PREFIX_SIZE . to_string ( ) ) ;
687- out. push_sql ( ")" ) ;
688- Ok ( ( ) )
741+ fn new ( op : Comparison , column : & ' a Column , text : & ' a Value ) -> QueryResult < Self > {
742+ let kind = PrefixType :: new ( column) ?;
743+ Ok ( Self {
744+ op,
745+ kind,
746+ column,
747+ text,
748+ } )
689749 }
690750
691751 fn push_value_prefix ( & self , mut out : AstPass < Pg > ) -> QueryResult < ( ) > {
692- out. push_sql ( "left(" ) ;
693- QueryValue ( self . text , & self . column . column_type ) . walk_ast ( out. reborrow ( ) ) ?;
694- out. push_sql ( ", " ) ;
695- out. push_sql ( & STRING_PREFIX_SIZE . to_string ( ) ) ;
696- out. push_sql ( ")" ) ;
752+ match self . kind {
753+ PrefixType :: String ( column) => {
754+ out. push_sql ( "left(" ) ;
755+ QueryValue ( self . text , & column. column_type ) . walk_ast ( out. reborrow ( ) ) ?;
756+ out. push_sql ( ", " ) ;
757+ out. push_sql ( & STRING_PREFIX_SIZE . to_string ( ) ) ;
758+ out. push_sql ( ")" ) ;
759+ }
760+ PrefixType :: Bytes ( column) => {
761+ out. push_sql ( "substring(" ) ;
762+ QueryValue ( self . text , & column. column_type ) . walk_ast ( out. reborrow ( ) ) ?;
763+ out. push_sql ( ", 1, " ) ;
764+ out. push_sql ( & BYTE_ARRAY_PREFIX_SIZE . to_string ( ) ) ;
765+ out. push_sql ( ")" ) ;
766+ }
767+ }
697768 Ok ( ( ) )
698769 }
699770
700771 fn push_prefix_cmp ( & self , op : Comparison , mut out : AstPass < Pg > ) -> QueryResult < ( ) > {
701- Self :: push_column_prefix ( self . column , out . reborrow ( ) ) ?;
772+ self . kind . push_column_prefix ( & mut out ) ?;
702773 out. push_sql ( op. as_str ( ) ) ;
703774 self . push_value_prefix ( out. reborrow ( ) )
704775 }
@@ -749,18 +820,16 @@ impl<'a> QueryFragment<Pg> for PrefixComparison<'a> {
749820 //
750821 // For `op` either `<=` or `>=`, we can write (using '<=' as an example)
751822 // uv <= st <=> u < s || u = s && uv <= st
752- let large = if let Value :: String ( s) = self . text {
753- // We need to check the entire string
754- s. len ( ) > STRING_PREFIX_SIZE - 1
755- } else {
756- return Err ( constraint_violation ! (
823+ let large = self . kind . is_large ( & self . text ) . map_err ( |( ) | {
824+ constraint_violation ! (
757825 "column {} has type {} and can't be compared with the value `{}` using {}" ,
758826 self . column. name( ) ,
759827 self . column. column_type( ) . sql_type( ) ,
760828 self . text,
761829 self . op. as_str( )
762- ) ) ;
763- } ;
830+ )
831+ } ) ?;
832+
764833 match self . op {
765834 Equal => {
766835 if large {
@@ -961,35 +1030,25 @@ impl<'a> QueryFilter<'a> {
9611030 ) -> QueryResult < ( ) > {
9621031 let column = self . column ( attribute) ;
9631032
964- if column. has_arbitrary_size ( ) {
965- PrefixComparison :: new ( op, column, value) . walk_ast ( out. reborrow ( ) ) ?;
1033+ if matches ! ( value, Value :: Null ) {
1034+ // Deal with nulls first since they always need special
1035+ // treatment
1036+ out. push_identifier ( column. name . as_str ( ) ) ?;
1037+ match op {
1038+ Comparison :: Equal => out. push_sql ( " is null" ) ,
1039+ Comparison :: NotEqual => out. push_sql ( " is not null" ) ,
1040+ _ => unreachable ! ( "we only call equals with '=' or '!='" ) ,
1041+ }
1042+ } else if column. has_arbitrary_size ( ) {
1043+ PrefixComparison :: new ( op, column, value) ?. walk_ast ( out. reborrow ( ) ) ?;
9661044 } else if column. is_fulltext ( ) {
9671045 out. push_identifier ( column. name . as_str ( ) ) ?;
9681046 out. push_sql ( Comparison :: Match . as_str ( ) ) ;
9691047 QueryValue ( value, & column. column_type ) . walk_ast ( out) ?;
9701048 } else {
9711049 out. push_identifier ( column. name . as_str ( ) ) ?;
972-
973- match value {
974- Value :: String ( _)
975- | Value :: BigInt ( _)
976- | Value :: Bool ( _)
977- | Value :: Bytes ( _)
978- | Value :: BigDecimal ( _)
979- | Value :: Int ( _)
980- | Value :: List ( _) => {
981- out. push_sql ( op. as_str ( ) ) ;
982- QueryValue ( value, & column. column_type ) . walk_ast ( out) ?;
983- }
984- Value :: Null => {
985- use Comparison as c;
986- match op {
987- c:: Equal => out. push_sql ( " is null" ) ,
988- c:: NotEqual => out. push_sql ( " is not null" ) ,
989- _ => unreachable ! ( "we only call equals with '=' or '!='" ) ,
990- }
991- }
992- }
1050+ out. push_sql ( op. as_str ( ) ) ;
1051+ QueryValue ( value, & column. column_type ) . walk_ast ( out) ?;
9931052 }
9941053 Ok ( ( ) )
9951054 }
@@ -1004,7 +1063,7 @@ impl<'a> QueryFilter<'a> {
10041063 let column = self . column ( attribute) ;
10051064
10061065 if column. has_arbitrary_size ( ) {
1007- PrefixComparison :: new ( op, column, value) . walk_ast ( out. reborrow ( ) ) ?;
1066+ PrefixComparison :: new ( op, column, value) ? . walk_ast ( out. reborrow ( ) ) ?;
10081067 } else {
10091068 out. push_identifier ( column. name . as_str ( ) ) ?;
10101069 out. push_sql ( op. as_str ( ) ) ;
@@ -1086,7 +1145,7 @@ impl<'a> QueryFilter<'a> {
10861145 // Postgres' query optimizer
10871146 // See PrefixComparison for a more detailed discussion of what
10881147 // is happening here
1089- PrefixComparison :: push_column_prefix ( column, out . reborrow ( ) ) ?;
1148+ PrefixType :: new ( column) ? . push_column_prefix ( & mut out ) ?;
10901149 } else {
10911150 out. push_identifier ( column. name . as_str ( ) ) ?;
10921151 }
0 commit comments