@@ -127,6 +127,7 @@ pub struct InputField<'a> {
127127 field_types : Vec < InputType < ' a > > ,
128128 is_required : bool ,
129129 requires_other_fields : Vec < Cow < ' a , str > > ,
130+ is_parameterizable : bool ,
130131}
131132
132133impl < ' a > InputField < ' a > {
@@ -142,6 +143,7 @@ impl<'a> InputField<'a> {
142143 field_types,
143144 is_required,
144145 requires_other_fields : Vec :: new ( ) ,
146+ is_parameterizable : false ,
145147 }
146148 }
147149
@@ -204,6 +206,30 @@ impl<'a> InputField<'a> {
204206 pub ( crate ) fn nullable_if ( self , condition : bool ) -> Self {
205207 if condition { self . nullable ( ) } else { self }
206208 }
209+
210+ /// Returns whether this field accepts placeholder values for parameterized queries.
211+ pub fn is_parameterizable ( & self ) -> bool {
212+ self . is_parameterizable
213+ }
214+
215+ /// Marks the field as parameterizable (accepts placeholder values in queries).
216+ ///
217+ /// Parameterizable fields can have their values substituted with placeholders
218+ /// for query plan caching. This is typically used for filter values and data
219+ /// fields, but not for structural fields like `take`, `skip`, `orderBy`, etc.
220+ #[ allow( dead_code) ] // Used in a follow-up change
221+ pub ( crate ) fn parameterizable ( mut self ) -> Self {
222+ self . is_parameterizable = true ;
223+ self
224+ }
225+
226+ /// Marks the field as parameterizable if the condition is true.
227+ ///
228+ /// See [`Self::parameterizable`].
229+ #[ allow( dead_code) ] // Used in a follow-up change
230+ pub ( crate ) fn parameterizable_if ( self , condition : bool ) -> Self {
231+ if condition { self . parameterizable ( ) } else { self }
232+ }
207233}
208234
209235#[ derive( Clone ) ]
@@ -320,3 +346,43 @@ impl<'a> InputType<'a> {
320346 }
321347 }
322348}
349+
350+ #[ cfg( test) ]
351+ mod tests {
352+ use super :: * ;
353+
354+ #[ test]
355+ fn input_field_default_not_parameterizable ( ) {
356+ let field = InputField :: new ( "test" . into ( ) , vec ! [ InputType :: int( ) ] , None , true ) ;
357+ assert ! ( !field. is_parameterizable( ) ) ;
358+ }
359+
360+ #[ test]
361+ fn input_field_parameterizable_builder ( ) {
362+ let field = InputField :: new ( "test" . into ( ) , vec ! [ InputType :: int( ) ] , None , true ) . parameterizable ( ) ;
363+ assert ! ( field. is_parameterizable( ) ) ;
364+ }
365+
366+ #[ test]
367+ fn input_field_parameterizable_if_true ( ) {
368+ let field = InputField :: new ( "test" . into ( ) , vec ! [ InputType :: int( ) ] , None , true ) . parameterizable_if ( true ) ;
369+ assert ! ( field. is_parameterizable( ) ) ;
370+ }
371+
372+ #[ test]
373+ fn input_field_parameterizable_if_false ( ) {
374+ let field = InputField :: new ( "test" . into ( ) , vec ! [ InputType :: int( ) ] , None , true ) . parameterizable_if ( false ) ;
375+ assert ! ( !field. is_parameterizable( ) ) ;
376+ }
377+
378+ #[ test]
379+ fn input_field_builder_chain_preserves_parameterizable ( ) {
380+ let field = InputField :: new ( "test" . into ( ) , vec ! [ InputType :: int( ) ] , None , true )
381+ . parameterizable ( )
382+ . optional ( )
383+ . nullable ( ) ;
384+
385+ assert ! ( field. is_parameterizable( ) ) ;
386+ assert ! ( !field. is_required( ) ) ;
387+ }
388+ }
0 commit comments