Skip to content

Commit b6891e7

Browse files
committed
claude20
1 parent 8a3edf5 commit b6891e7

1 file changed

Lines changed: 211 additions & 0 deletions

File tree

sql_athame/base.py

Lines changed: 211 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -358,9 +358,49 @@ def join(self, parts: Iterable["Fragment"]) -> "Fragment":
358358

359359

360360
class SQLFormatter:
361+
"""Main SQL formatting class providing the sql() function and utility methods.
362+
363+
This class is instantiated as the global 'sql' object that provides the primary
364+
interface for building SQL fragments. It supports format string syntax with
365+
placeholders and provides utility methods for common SQL operations.
366+
"""
361367
def __call__(
362368
self, fmt: str, *args: Any, preserve_formatting: bool = False, **kwargs: Any
363369
) -> Fragment:
370+
"""Create a SQL Fragment from a format string with placeholders.
371+
372+
The format string contains literal SQL and may contain positional references
373+
marked by {} and named references marked by {name}. Positional references
374+
must have matching arguments in *args. Named references may have matching
375+
arguments in **kwargs; if not provided, they remain as named slots to be
376+
filled later.
377+
378+
If a referenced argument is a Fragment, it is substituted into the SQL
379+
along with its embedded placeholders. Otherwise, it becomes a placeholder value.
380+
381+
Args:
382+
fmt: SQL format string with {} placeholders
383+
*args: Positional arguments for {} placeholders
384+
preserve_formatting: If True, preserve whitespace; if False, normalize whitespace
385+
**kwargs: Named arguments for {name} placeholders
386+
387+
Returns:
388+
Fragment containing the SQL with placeholders
389+
390+
Raises:
391+
ValueError: If there are unfilled positional arguments
392+
393+
Example:
394+
>>> sql("SELECT * FROM users WHERE id = {}", 42)
395+
Fragment(['SELECT * FROM users WHERE id = ', Placeholder('0', 42)])
396+
397+
>>> sql("SELECT * FROM users WHERE id = {id} AND name = {name}", id=42, name="Alice")
398+
Fragment(['SELECT * FROM users WHERE id = ', Placeholder('id', 42), ' AND name = ', Placeholder('name', 'Alice')])
399+
400+
>>> # Fragments can be embedded
401+
>>> where_clause = sql("active = {}", True)
402+
>>> sql("SELECT * FROM users WHERE {}", where_clause)
403+
"""
364404
if not preserve_formatting:
365405
fmt = newline_whitespace_re.sub(" ", fmt)
366406
fmtr = string.Formatter()
@@ -389,23 +429,110 @@ def __call__(
389429

390430
@staticmethod
391431
def value(value: Any) -> Fragment:
432+
"""Create a Fragment with a single placeholder value.
433+
434+
Equivalent to sql("{}", value) but more explicit.
435+
436+
Args:
437+
value: The value to create a placeholder for
438+
439+
Returns:
440+
Fragment containing a single placeholder
441+
442+
Example:
443+
>>> sql.value(42)
444+
Fragment([Placeholder('value', 42)])
445+
"""
392446
placeholder = Placeholder("value", value)
393447
return Fragment([placeholder])
394448

395449
@staticmethod
396450
def escape(value: Any) -> Fragment:
451+
"""Create a Fragment with a value escaped and embedded into the SQL.
452+
453+
Unlike placeholders, escaped values are embedded directly into the SQL text.
454+
Types currently supported are strings, floats, ints, UUIDs, None, and
455+
sequences of the above. Use with caution and only for trusted values.
456+
457+
Args:
458+
value: The value to escape and embed
459+
460+
Returns:
461+
Fragment with the escaped value as literal SQL
462+
463+
Example:
464+
>>> list(sql("SELECT * FROM tbl WHERE qty = ANY({})", sql.escape([1, 3, 5])))
465+
['SELECT * FROM tbl WHERE qty = ANY(ARRAY[1, 3, 5])']
466+
467+
>>> # Compare to placeholder version:
468+
>>> list(sql("SELECT * FROM tbl WHERE qty = ANY({})", [1, 3, 5]))
469+
['SELECT * FROM tbl WHERE qty = ANY($1)', [1, 3, 5]]
470+
471+
Note:
472+
Burning invariant values into the query can potentially help the query optimizer.
473+
"""
397474
return lit(escape(value))
398475

399476
@staticmethod
400477
def slot(name: str) -> Fragment:
478+
"""Create a Fragment with a single empty slot.
479+
480+
Equivalent to sql("{name}") but more explicit.
481+
482+
Args:
483+
name: The name of the slot
484+
485+
Returns:
486+
Fragment containing a single slot
487+
488+
Example:
489+
>>> template = sql("SELECT * FROM users WHERE {}", sql.slot("condition"))
490+
>>> query = template.fill(condition=sql("active = {}", True))
491+
"""
401492
return Fragment([Slot(name)])
402493

403494
@staticmethod
404495
def literal(text: str) -> Fragment:
496+
"""Create a Fragment with literal SQL text.
497+
498+
No substitution of any kind is performed. Be very careful of SQL injection
499+
when using this method.
500+
501+
Args:
502+
text: Raw SQL text to include
503+
504+
Returns:
505+
Fragment containing the literal SQL
506+
507+
Warning:
508+
Only use with trusted SQL text to avoid SQL injection vulnerabilities.
509+
510+
Example:
511+
>>> sql.literal("ORDER BY created_at DESC")
512+
Fragment(['ORDER BY created_at DESC'])
513+
"""
405514
return Fragment([text])
406515

407516
@staticmethod
408517
def identifier(name: str, prefix: Optional[str] = None) -> Fragment:
518+
"""Create a Fragment with a quoted SQL identifier.
519+
520+
Creates a properly quoted identifier name, optionally with a dotted prefix
521+
for schema or table qualification.
522+
523+
Args:
524+
name: The identifier name to quote
525+
prefix: Optional prefix (schema, table, etc.)
526+
527+
Returns:
528+
Fragment containing the quoted identifier
529+
530+
Example:
531+
>>> list(sql("SELECT {col} FROM {table}",
532+
... col=sql.identifier("user_name"),
533+
... table=sql.identifier("users", prefix="public")))
534+
['SELECT "user_name" FROM "public"."users"']
535+
"""
409536
if prefix:
410537
return lit(f"{quote_identifier(prefix)}.{quote_identifier(name)}")
411538
else:
@@ -418,6 +545,24 @@ def all(self, parts: Iterable[Fragment]) -> Fragment: ... # pragma: no cover
418545
def all(self, *parts: Fragment) -> Fragment: ... # pragma: no cover
419546

420547
def all(self, *parts) -> Fragment: # type: ignore
548+
"""Join fragments with AND, returning TRUE if no parts provided.
549+
550+
Creates a SQL Fragment joining the fragments in parts together with AND.
551+
If parts is empty, returns TRUE. Each fragment is wrapped in parentheses.
552+
553+
Args:
554+
*parts: Fragment objects to join, or single iterable of fragments
555+
556+
Returns:
557+
Fragment containing the AND-joined conditions
558+
559+
Example:
560+
>>> where = [sql("a = {}", 42), sql("x <> {}", "foo")]
561+
>>> list(sql("SELECT * FROM tbl WHERE {}", sql.all(where)))
562+
['SELECT * FROM tbl WHERE (a = $1) AND (x <> $2)', 42, 'foo']
563+
>>> list(sql("SELECT * FROM tbl WHERE {}", sql.all([])))
564+
['SELECT * FROM tbl WHERE TRUE']
565+
"""
421566
if parts and not isinstance(parts[0], Fragment):
422567
parts = parts[0]
423568
return any_all(list(parts), "AND", "TRUE")
@@ -429,6 +574,24 @@ def any(self, parts: Iterable[Fragment]) -> Fragment: ... # pragma: no cover
429574
def any(self, *parts: Fragment) -> Fragment: ... # pragma: no cover
430575

431576
def any(self, *parts) -> Fragment: # type: ignore
577+
"""Join fragments with OR, returning FALSE if no parts provided.
578+
579+
Creates a SQL Fragment joining the fragments in parts together with OR.
580+
If parts is empty, returns FALSE. Each fragment is wrapped in parentheses.
581+
582+
Args:
583+
*parts: Fragment objects to join, or single iterable of fragments
584+
585+
Returns:
586+
Fragment containing the OR-joined conditions
587+
588+
Example:
589+
>>> where = [sql("a = {}", 42), sql("x <> {}", "foo")]
590+
>>> list(sql("SELECT * FROM tbl WHERE {}", sql.any(where)))
591+
['SELECT * FROM tbl WHERE (a = $1) OR (x <> $2)', 42, 'foo']
592+
>>> list(sql("SELECT * FROM tbl WHERE {}", sql.any([])))
593+
['SELECT * FROM tbl WHERE FALSE']
594+
"""
432595
if parts and not isinstance(parts[0], Fragment):
433596
parts = parts[0]
434597
return any_all(list(parts), "OR", "FALSE")
@@ -440,18 +603,66 @@ def list(self, parts: Iterable[Fragment]) -> Fragment: ... # pragma: no cover
440603
def list(self, *parts: Fragment) -> Fragment: ... # pragma: no cover
441604

442605
def list(self, *parts) -> Fragment: # type: ignore
606+
"""Join fragments with commas.
607+
608+
Creates a SQL Fragment joining the fragments in parts together with commas.
609+
Commonly used for column lists, value lists, etc.
610+
611+
Args:
612+
*parts: Fragment objects to join, or single iterable of fragments
613+
614+
Returns:
615+
Fragment containing the comma-separated fragments
616+
617+
Example:
618+
>>> cols = [sql.identifier("id"), sql.identifier("name"), sql.identifier("email")]
619+
>>> list(sql("SELECT {} FROM users", sql.list(cols)))
620+
['SELECT "id", "name", "email" FROM users']
621+
"""
443622
if parts and not isinstance(parts[0], Fragment):
444623
parts = parts[0]
445624
return Fragment(list(join_parts(parts, infix=", ")))
446625

447626
def unnest(self, data: Iterable[Sequence[Any]], types: Iterable[str]) -> Fragment:
627+
"""Create a Fragment containing an UNNEST expression with associated data.
628+
629+
The data is specified as tuples (in the "database columns" sense) in data,
630+
and the PostgreSQL types must be specified in types. The data is transposed
631+
into the correct form for UNNEST and embedded as placeholders.
632+
633+
Args:
634+
data: Iterable of sequences, where each sequence represents a row
635+
types: Iterable of PostgreSQL type names for each column
636+
637+
Returns:
638+
Fragment containing UNNEST expression with typed array placeholders
639+
640+
Example:
641+
>>> list(sql("SELECT * FROM {}", sql.unnest([("a", 1), ("b", 2), ("c", 3)], ["text", "integer"])))
642+
['SELECT * FROM UNNEST($1::text[], $2::integer[])', ('a', 'b', 'c'), (1, 2, 3)]
643+
644+
>>> # Useful for bulk operations
645+
>>> users_data = [("Alice", 25), ("Bob", 30), ("Charlie", 35)]
646+
>>> insert_query = sql("INSERT INTO users (name, age) SELECT * FROM {}",
647+
... sql.unnest(users_data, ["text", "integer"]))
648+
"""
448649
nested = [nest_for_type(x, t) for x, t in zip(zip(*data), types)]
449650
if not nested:
450651
nested = [nest_for_type([], t) for t in types]
451652
return Fragment(["UNNEST(", self.list(nested), ")"])
452653

453654

454655
sql = SQLFormatter()
656+
"""Global SQLFormatter instance providing the main sql() function and utilities.
657+
658+
This is the primary interface for creating SQL fragments. Use it to:
659+
- Create fragments: sql("SELECT * FROM users WHERE id = {}", user_id)
660+
- Join fragments: sql.all([condition1, condition2])
661+
- Create identifiers: sql.identifier("table_name")
662+
- And much more
663+
664+
See SQLFormatter class documentation for all available methods.
665+
"""
455666

456667

457668
json_types = ("JSON", "JSONB")

0 commit comments

Comments
 (0)