Source code for moltres.expressions.functions.misc

"""Miscellaneous functions for :class:`DataFrame` operations."""

from __future__ import annotations

from typing import TYPE_CHECKING, Optional, Union

from ..column import Column, ColumnLike, ensure_column, literal

if TYPE_CHECKING:
    from ...dataframe.core.dataframe import DataFrame


class When:
    """Builder for CASE WHEN expressions."""

    def __init__(self, condition: Column, value: ColumnLike):
        self._conditions = [(condition, ensure_column(value))]

    def when(self, condition: Column, value: ColumnLike) -> "When":
        """Add another WHEN clause."""
        self._conditions.append((condition, ensure_column(value)))
        return self

    def otherwise(self, value: ColumnLike) -> Column:
        """Complete the CASE expression with an ELSE clause.

        Args:
            value: Default value if no conditions match

        Returns:
            :class:`Column` expression for the complete CASE WHEN statement
        """
        return Column(op="case_when", args=(tuple(self._conditions), ensure_column(value)))


[docs] def lit(value: Union[bool, int, float, str, None]) -> Column: """Create a literal column expression from a Python value. Args: value: The literal value (bool, int, float, str, or None) Returns: :class:`Column` expression representing the literal value Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> db = connect("sqlite:///:memory:") >>> from moltres.table.schema import column >>> _ = db.create_table("test", [column("x", "INTEGER")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"x": 10}], _database=db).insert_into("test") >>> # Use lit() to create literal values in expressions >>> df = db.table("test").select((col("x") + F.lit(5)).alias("x_plus_5")) >>> results = df.collect() >>> results[0]["x_plus_5"] 15 >>> # String literals >>> df2 = db.table("test").select(F.lit("constant").alias("constant_value")) >>> results2 = df2.collect() >>> results2[0]["constant_value"] 'constant' >>> db.close() """ return literal(value)
[docs] def coalesce(*columns: ColumnLike) -> Column: """Return the first non-null value from multiple columns. Args: *columns: :class:`Column` expressions to check Returns: :class:`Column` expression for the first non-null value Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("a", "INTEGER"), column("b", "INTEGER"), column("c", "INTEGER")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"a": None, "b": None, "c": 5}], _database=db).insert_into("data") >>> df = db.table("data").select(F.coalesce(col("a"), col("b"), col("c")).alias("first_non_null")) >>> results = df.collect() >>> results[0]["first_non_null"] 5 >>> db.close() """ if not columns: raise ValueError("coalesce requires at least one column") return Column(op="coalesce", args=tuple(ensure_column(c) for c in columns))
[docs] def greatest(*columns: ColumnLike) -> Column: """Get the greatest value from multiple columns. Args: *columns: :class:`Column` expressions to compare Returns: :class:`Column` expression for the greatest value Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("a", "REAL"), column("b", "REAL"), column("c", "REAL")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"a": 10.0, "b": 20.0, "c": 15.0}], _database=db).insert_into("data") >>> # Note: greatest() requires database-specific support (not available in SQLite) >>> # For PostgreSQL/MySQL: F.greatest(col("a"), col("b"), col("c")) >>> db.close() """ if not columns: raise ValueError("greatest requires at least one column") return Column(op="greatest", args=tuple(ensure_column(c) for c in columns))
[docs] def least(*columns: ColumnLike) -> Column: """Get the least value from multiple columns. Args: *columns: :class:`Column` expressions to compare Returns: :class:`Column` expression for the least value Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("a", "REAL"), column("b", "REAL"), column("c", "REAL")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"a": 10.0, "b": 20.0, "c": 15.0}], _database=db).insert_into("data") >>> # Note: least() requires database-specific support (not available in SQLite) >>> # For PostgreSQL/MySQL: F.least(col("a"), col("b"), col("c")) >>> db.close() """ if not columns: raise ValueError("least requires at least one column") return Column(op="least", args=tuple(ensure_column(c) for c in columns))
[docs] def when(condition: Column, value: ColumnLike) -> When: """Start a CASE WHEN expression. Args: condition: Boolean condition value: Value if condition is true Returns: When builder for chaining additional WHEN clauses Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("users", [column("age", "INTEGER")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"age": 20}, {"age": 15}], _database=db).insert_into("users") >>> df = db.table("users").select(col("age"), F.when(col("age") >= 18, "adult").otherwise("minor").alias("status")) >>> results = df.collect() >>> results[0]["status"] 'adult' >>> results[1]["status"] 'minor' >>> db.close() """ return When(condition, value)
[docs] def isnan(column: ColumnLike) -> Column: """Check if a numeric column value is NaN. Args: column: Numeric column to check Returns: :class:`Column` expression for isnan Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("value", "REAL")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"value": 1.0}], _database=db).insert_into("data") >>> df = db.table("data").select(F.isnan(col("value"))) >>> results = df.collect() >>> # isnan returns 1 for NaN, 0 for non-NaN >>> any(r[list(r.keys())[0]] in [0, 1] for r in results) True >>> db.close() """ return Column(op="isnan", args=(ensure_column(column),))
[docs] def isnull(column: ColumnLike) -> Column: """Check if a column value is NULL (alias for is_null()). Args: column: :class:`Column` to check Returns: :class:`Column` expression for isnull (same as is_null()) Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("users", [column("name", "TEXT")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"name": None}, {"name": "Alice"}], _database=db).insert_into("users") >>> df = db.table("users").select(F.isnull(col("name"))) >>> results = df.collect() >>> # isnull returns 1 for NULL, 0 for non-NULL >>> any(r[list(r.keys())[0]] == 1 for r in results) True >>> any(r[list(r.keys())[0]] == 0 for r in results) True >>> db.close() """ return Column(op="is_null", args=(ensure_column(column),))
[docs] def isnotnull(column: ColumnLike) -> Column: """Check if a column value is NOT NULL (alias for is_not_null()). Args: column: :class:`Column` to check Returns: :class:`Column` expression for isnotnull (same as is_not_null()) Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("users", [column("name", "TEXT")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"name": None}, {"name": "Alice"}], _database=db).insert_into("users") >>> df = db.table("users").select(F.isnotnull(col("name"))) >>> results = df.collect() >>> # isnotnull returns 1 for non-NULL, 0 for NULL >>> any(r[list(r.keys())[0]] == 1 for r in results) True >>> any(r[list(r.keys())[0]] == 0 for r in results) True >>> db.close() """ return Column(op="is_not_null", args=(ensure_column(column),))
[docs] def isinf(column: ColumnLike) -> Column: """Check if a numeric column value is infinite. Args: column: Numeric column to check Returns: :class:`Column` expression for isinf Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("value", "REAL")]).collect() # doctest: +ELLIPSIS >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"value": 1.0}], _database=db).insert_into("data") >>> df = db.table("data").select(F.isinf(col("value"))) >>> results = df.collect() >>> # isinf returns 1 for infinite, 0 for finite >>> any(r[list(r.keys())[0]] in [0, 1] for r in results) True >>> db.close() """ return Column(op="isinf", args=(ensure_column(column),))
[docs] def scalar_subquery(subquery: "DataFrame") -> Column: """Use a :class:`DataFrame` as a scalar subquery in SELECT clause. Args: subquery: :class:`DataFrame` representing the subquery (must return a single row/column) Returns: :class:`Column` expression for scalar subquery Example: >>> # Note: scalar_subquery() requires database-specific support >>> # SQLite supports scalar subqueries >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("orders", [column("amount", "REAL")]).collect() >>> _ = db.create_table("customers", [column("name", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"amount": 100.0}, {"amount": 200.0}], _database=db).insert_into("orders") >>> _ = :class:`Records`(_data=[{"name": "Alice"}], _database=db).insert_into("customers") >>> max_order = db.table("orders").select(F.max(col("amount"))) >>> df = db.table("customers").select(col("name"), F.scalar_subquery(max_order).alias("max_order_amount")) >>> results = df.collect() >>> results[0]["max_order_amount"] 200.0 >>> db.close() """ if not hasattr(subquery, "plan"): raise TypeError("scalar_subquery() requires a DataFrame (subquery)") return Column(op="scalar_subquery", args=(subquery.plan,))
[docs] def exists(subquery: "DataFrame") -> Column: """Check if a subquery returns any rows (EXISTS clause). Args: subquery: :class:`DataFrame` representing the subquery to check Returns: :class:`Column` expression for EXISTS clause Example: >>> # Note: exists() requires database-specific support >>> # SQLite supports EXISTS subqueries >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("orders", [column("status", "TEXT")]).collect() >>> _ = db.create_table("customers", [column("name", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"status": "active"}], _database=db).insert_into("orders") >>> _ = :class:`Records`(_data=[{"name": "Alice"}], _database=db).insert_into("customers") >>> active_orders = db.table("orders").select().where(col("status") == "active") >>> df = db.table("customers").select().where(F.exists(active_orders)) >>> results = df.collect() >>> len(results) > 0 True >>> db.close() """ if not hasattr(subquery, "plan"): raise TypeError("exists() requires a DataFrame (subquery)") return Column(op="exists", args=(subquery.plan,))
[docs] def not_exists(subquery: "DataFrame") -> Column: """Check if a subquery returns no rows (NOT EXISTS clause). Args: subquery: :class:`DataFrame` representing the subquery to check Returns: :class:`Column` expression for NOT EXISTS clause Example: >>> # Note: not_exists() requires database-specific support >>> # SQLite supports NOT EXISTS subqueries >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("orders", [column("status", "TEXT")]).collect() >>> _ = db.create_table("customers", [column("name", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"status": "active"}], _database=db).insert_into("orders") >>> _ = :class:`Records`(_data=[{"name": "Alice"}], _database=db).insert_into("customers") >>> inactive_orders = db.table("orders").select().where(col("status") == "inactive") >>> df = db.table("customers").select().where(F.not_exists(inactive_orders)) >>> results = df.collect() >>> len(results) > 0 True >>> db.close() """ if not hasattr(subquery, "plan"): raise TypeError("not_exists() requires a DataFrame (subquery)") return Column(op="not_exists", args=(subquery.plan,))
[docs] def explode(column: ColumnLike) -> Column: """Explode an array/JSON column into multiple rows (one row per element). This function can be used in select() to expand array or JSON columns, similar to PySpark's explode() function. Args: column: :class:`Column` expression to explode (must be array or JSON) Returns: :class:`Column` expression for explode operation Example: >>> # Note: explode() requires database-specific array/JSON support (PostgreSQL/MySQL) >>> # SQLite does not support arrays natively >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> db = connect("duckdb:///:memory:") >>> # Use raw SQL to create table with proper array type >>> _ = db.sql("CREATE TABLE data (id INTEGER, tags TEXT[])").collect() # doctest: +ELLIPSIS >>> _ = db.sql("INSERT INTO data VALUES (1, ['python', 'sql'])").collect() >>> df = db.table("data").select(col("id"), F.explode(col("tags")).alias("tag")) >>> results = df.collect() >>> len(results) 2 >>> db.close() """ return Column(op="explode", args=(ensure_column(column),))
[docs] def rand(seed: Optional[int] = None) -> Column: """Generate a random number between 0 and 1. Args: seed: Optional random seed (not all databases support this) Returns: :class:`Column` expression for rand Example: >>> # Note: rand() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have rand function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("id", "INTEGER")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"id": 1}], _database=db).insert_into("data") >>> df = db.table("data").select(F.rand().alias("random")) >>> results = df.collect() >>> 0.0 <= results[0]["random"] <= 1.0 True >>> db.close() """ if seed is not None: return Column(op="rand", args=(seed,)) return Column(op="rand", args=())
[docs] def randn(seed: Optional[int] = None) -> Column: """Generate a random number from a standard normal distribution. Note: Limited database support. May require extensions. Args: seed: Optional random seed (not all databases support this) Returns: :class:`Column` expression for randn Example: >>> # Note: randn() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have randn function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("id", "INTEGER")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"id": 1}], _database=db).insert_into("data") >>> df = db.table("data").select(F.randn().alias("random_normal")) >>> results = df.collect() >>> isinstance(results[0]["random_normal"], (int, float)) True >>> db.close() """ if seed is not None: return Column(op="randn", args=(seed,)) return Column(op="randn", args=())
[docs] def hash(*columns: ColumnLike) -> Column: """Compute a hash value for one or more columns. Args: *columns: :class:`Column` expressions to hash Returns: :class:`Column` expression for hash Example: >>> # Note: hash() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have hash function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("id", "INTEGER"), column("name", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"id": 1, "name": "Alice"}], _database=db).insert_into("data") >>> df = db.table("data").select(F.hash(col("id"), col("name")).alias("hash_val")) >>> results = df.collect() >>> isinstance(results[0]["hash_val"], (int, str)) True >>> db.close() """ if not columns: raise ValueError("hash requires at least one column") return Column(op="hash", args=tuple(ensure_column(c) for c in columns))
[docs] def md5(column: ColumnLike) -> Column: """Compute the MD5 hash of a column. Args: column: :class:`Column` expression to hash Returns: :class:`Column` expression for md5 (returns hex string) Example: >>> # Note: md5() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have md5 function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("password", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"password": "secret"}], _database=db).insert_into("data") >>> df = db.table("data").select(F.md5(col("password")).alias("md5_hash")) >>> results = df.collect() >>> len(results[0]["md5_hash"]) == 32 True >>> db.close() """ return Column(op="md5", args=(ensure_column(column),))
[docs] def sha1(column: ColumnLike) -> Column: """Compute the SHA-1 hash of a column. Args: column: :class:`Column` expression to hash Returns: :class:`Column` expression for sha1 (returns hex string) Example: >>> # Note: sha1() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have sha1 function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("password", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"password": "secret"}], _database=db).insert_into("data") >>> df = db.table("data").select(F.sha1(col("password")).alias("sha1_hash")) >>> results = df.collect() >>> len(results[0]["sha1_hash"]) == 40 True >>> db.close() """ return Column(op="sha1", args=(ensure_column(column),))
[docs] def sha2(column: ColumnLike, num_bits: int = 256) -> Column: """Compute the SHA-2 hash of a column. Args: column: :class:`Column` expression to hash num_bits: Number of bits (224, 256, 384, or 512, default: 256) Returns: :class:`Column` expression for sha2 (returns hex string) Example: >>> # Note: sha2() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have sha2 function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("password", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"password": "secret"}], _database=db).insert_into("data") >>> df = db.table("data").select(F.sha2(col("password"), 256).alias("sha2_hash")) >>> results = df.collect() >>> len(results[0]["sha2_hash"]) == 64 True >>> db.close() """ if num_bits not in (224, 256, 384, 512): raise ValueError("num_bits must be 224, 256, 384, or 512") return Column(op="sha2", args=(ensure_column(column), num_bits))
[docs] def base64(column: ColumnLike) -> Column: """Encode a column to base64. Args: column: :class:`Column` expression to encode Returns: :class:`Column` expression for base64 encoding Example: >>> # Note: base64() requires database-specific support (PostgreSQL/MySQL) (DuckDB/PostgreSQL/MySQL) >>> # SQLite does not have base64 function >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> db = connect("duckdb:///:memory:") >>> _ = db.create_table("data", [column("text", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"text": "hello"}], _database=db).insert_into("data") >>> df = db.table("data").select(F.base64(col("text")).alias("encoded")) >>> results = df.collect() >>> isinstance(results[0]["encoded"], str) True >>> db.close() """ return Column(op="base64", args=(ensure_column(column),))
[docs] def monotonically_increasing_id() -> Column: """Generate a monotonically increasing unique ID for each row. Note: This uses ROW_NUMBER() window function, so it requires a window context or will generate IDs based on row order. Returns: :class:`Column` expression for monotonically_increasing_id Example: >>> from moltres import connect, col >>> from moltres.expressions import functions as F >>> from moltres.table.schema import column >>> from moltres.expressions.window import Window >>> db = connect("sqlite:///:memory:") >>> _ = db.create_table("data", [column("name", "TEXT")]).collect() >>> from moltres.io.records import :class:`Records` >>> _ = :class:`Records`(_data=[{"name": "Alice"}, {"name": "Bob"}], _database=db).insert_into("data") >>> df = db.table("data").select(col("name"), F.monotonically_increasing_id().over(partition_by=None, order_by=col("name")).alias("id")) >>> results = df.collect() >>> results[0]["id"] >= 1 True >>> db.close() """ return Column(op="monotonically_increasing_id", args=())
[docs] def crc32(column: ColumnLike) -> Column: """Compute the CRC32 checksum of a column. Args: column: :class:`Column` expression to compute checksum for Returns: :class:`Column` expression for crc32 Example: >>> # Note: crc32() requires database-specific support (MySQL) >>> # SQLite and DuckDB do not have crc32 function >>> from moltres import connect, col # doctest: +SKIP >>> from moltres.expressions import functions as F # doctest: +SKIP >>> from moltres.table.schema import column # doctest: +SKIP >>> db = connect("mysql://...") # doctest: +SKIP >>> _ = db.create_table("data", [column("text", "TEXT")]).collect() # doctest: +SKIP >>> from moltres.io.records import :class:`Records` # doctest: +SKIP >>> _ = :class:`Records`(_data=[{"text": "hello"}], _database=db).insert_into("data") # doctest: +SKIP >>> df = db.table("data").select(F.crc32(col("text")).alias("checksum")) # doctest: +SKIP >>> results = df.collect() # doctest: +SKIP >>> isinstance(results[0]["checksum"], (int, str)) # doctest: +SKIP True # doctest: +SKIP >>> db.close() # doctest: +SKIP """ return Column(op="crc32", args=(ensure_column(column),))
[docs] def soundex(column: ColumnLike) -> Column: """Compute the Soundex code for phonetic matching. Args: column: String column expression Returns: :class:`Column` expression for soundex Example: >>> # Note: soundex() requires database-specific support (PostgreSQL/MySQL) >>> # SQLite and DuckDB do not have soundex function >>> from moltres import connect, col # doctest: +SKIP >>> from moltres.expressions import functions as F # doctest: +SKIP >>> from moltres.table.schema import column # doctest: +SKIP >>> db = connect("postgresql://...") # doctest: +SKIP >>> _ = db.create_table("data", [column("name", "TEXT")]).collect() # doctest: +SKIP >>> from moltres.io.records import :class:`Records` # doctest: +SKIP >>> _ = :class:`Records`(_data=[{"name": "Smith"}], _database=db).insert_into("data") # doctest: +SKIP >>> df = db.table("data").select(F.soundex(col("name")).alias("soundex_code")) # doctest: +SKIP >>> results = df.collect() # doctest: +SKIP >>> isinstance(results[0]["soundex_code"], str) # doctest: +SKIP True # doctest: +SKIP >>> db.close() # doctest: +SKIP """ return Column(op="soundex", args=(ensure_column(column),))