Skip to content

Commit 0149faa

Browse files
committed
claude21
1 parent b6891e7 commit 0149faa

1 file changed

Lines changed: 75 additions & 0 deletions

File tree

sql_athame/base.py

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -669,10 +669,34 @@ def unnest(self, data: Iterable[Sequence[Any]], types: Iterable[str]) -> Fragmen
669669

670670

671671
def 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

675683
def 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

691715
def 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

695729
def 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

718779
def 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

Comments
 (0)