@@ -669,10 +669,34 @@ def unnest(self, data: Iterable[Sequence[Any]], types: Iterable[str]) -> Fragmen
669669
670670
671671def is_json_type (typename : str ) -> bool :
672+ """Check if a type name represents a JSON type.
673+
674+ Args:
675+ typename: PostgreSQL type name to check
676+
677+ Returns:
678+ True if typename is JSON or JSONB (case insensitive)
679+ """
672680 return typename .upper () in json_types
673681
674682
675683def nest_for_type (data : Sequence [Any ], typename : str ) -> Fragment :
684+ """Create a typed array fragment for UNNEST operations.
685+
686+ Converts a sequence of data into a properly typed PostgreSQL array fragment.
687+ Handles JSON/JSONB types specially by converting objects to JSON strings.
688+
689+ Args:
690+ data: Sequence of values to convert to array
691+ typename: PostgreSQL type name for the array
692+
693+ Returns:
694+ Fragment containing a typed array placeholder
695+
696+ Note:
697+ For JSON/JSONB types, non-string values are converted to JSON strings
698+ to work around asyncpg limitations.
699+ """
676700 if is_json_type (typename ):
677701 # https://github.com/MagicStack/asyncpg/issues/345
678702
@@ -689,10 +713,33 @@ def nest_for_type(data: Sequence[Any], typename: str) -> Fragment:
689713
690714
691715def lit (text : str ) -> Fragment :
716+ """Create a Fragment containing literal SQL text.
717+
718+ Convenience function equivalent to Fragment([text]).
719+
720+ Args:
721+ text: Literal SQL text
722+
723+ Returns:
724+ Fragment containing the literal text
725+ """
692726 return Fragment ([text ])
693727
694728
695729def any_all (frags : list [Fragment ], op : str , base_case : str ) -> Fragment :
730+ """Join fragments with a logical operator, with a base case for empty lists.
731+
732+ Used by sql.all() and sql.any() to implement AND/OR joining with appropriate
733+ base cases (TRUE for AND, FALSE for OR).
734+
735+ Args:
736+ frags: List of fragments to join
737+ op: Operator to use for joining ("AND" or "OR")
738+ base_case: Value to return if frags is empty ("TRUE" or "FALSE")
739+
740+ Returns:
741+ Fragment with fragments joined by operator, or base case if empty
742+ """
696743 if not frags :
697744 return lit (base_case )
698745 parts = join_parts (frags , prefix = "(" , infix = f") { op } (" , suffix = ")" )
@@ -705,6 +752,20 @@ def join_parts(
705752 prefix : Optional [Part ] = None ,
706753 suffix : Optional [Part ] = None ,
707754) -> Iterator [Part ]:
755+ """Join parts with a separator, optionally adding prefix and suffix.
756+
757+ Generator function that yields parts with infix separators between them,
758+ and optional prefix/suffix parts.
759+
760+ Args:
761+ parts: Parts to join
762+ infix: Separator to place between parts
763+ prefix: Optional part to yield first
764+ suffix: Optional part to yield last
765+
766+ Yields:
767+ Parts with separators, prefix, and suffix as appropriate
768+ """
708769 if prefix :
709770 yield prefix
710771 for i , part in enumerate (parts ):
@@ -716,5 +777,19 @@ def join_parts(
716777
717778
718779def quote_identifier (name : str ) -> str :
780+ """Quote a SQL identifier with double quotes, escaping internal quotes.
781+
782+ Args:
783+ name: Identifier name to quote
784+
785+ Returns:
786+ Quoted identifier with internal quotes escaped
787+
788+ Example:
789+ >>> quote_identifier("user_name")
790+ '"user_name"'
791+ >>> quote_identifier('table"with"quotes')
792+ '"table""with""quotes"'
793+ """
719794 quoted = name .replace ('"' , '""' )
720795 return f'"{ quoted } "'
0 commit comments