Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions docs/usage/drivers_and_querying.rst
Original file line number Diff line number Diff line change
Expand Up @@ -388,6 +388,52 @@ Execute a SELECT query returning a single scalar value.
:dedent: 4
:caption: `select_value`

asyncpg-Compatible Method Aliases (fetch*)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

For users transitioning from asyncpg or preferring its naming convention, SQLSpec provides ``fetch*`` aliases for all ``select*`` methods. Both naming styles are fully supported and produce identical results.

.. list-table:: Method Aliases
:header-rows: 1
:widths: 40 60

* - Primary Method
- asyncpg-Compatible Alias
* - ``select()``
- ``fetch()``
* - ``select_one()``
- ``fetch_one()``
* - ``select_one_or_none()``
- ``fetch_one_or_none()``
* - ``select_value()``
- ``fetch_value()``
* - ``select_value_or_none()``
- ``fetch_value_or_none()``
* - ``select_to_arrow()``
- ``fetch_to_arrow()``
* - ``select_with_total()``
- ``fetch_with_total()``

**Example using fetch() instead of select():**

.. code-block:: python

# Both styles work identically
async with spec.provide_session(config) as session:
# Primary naming (recommended in docs)
users = await session.select("SELECT * FROM users WHERE age > ?", 18)

# asyncpg-compatible naming (identical behavior)
users = await session.fetch("SELECT * FROM users WHERE age > ?", 18)

# fetch_one() is equivalent to select_one()
user = await session.fetch_one("SELECT * FROM users WHERE id = ?", 1)

# fetch_value() is equivalent to select_value()
count = await session.fetch_value("SELECT COUNT(*) FROM users")

**Note:** Both naming conventions are fully supported and will not be deprecated. Choose the style that feels most natural for your codebase. The SQLSpec documentation primarily uses ``select*`` methods, but ``fetch*`` aliases are equally valid.

Working with Results
--------------------

Expand Down
254 changes: 254 additions & 0 deletions sqlspec/driver/_async.py
Original file line number Diff line number Diff line change
Expand Up @@ -398,6 +398,51 @@ async def select_one(
except ValueError as error:
handle_single_row_error(error)

@overload
async def fetch_one(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT]",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "SchemaT": ...

@overload
async def fetch_one(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: None = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "dict[str, Any]": ...

async def fetch_one(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT] | None" = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "SchemaT | dict[str, Any]":
"""Execute a select statement and return exactly one row.

This is an alias for :meth:`select_one` provided for users familiar
with asyncpg's fetch_one() naming convention.

Raises an exception if no rows or more than one row is returned.

See Also:
select_one(): Primary method with identical behavior
"""
return await self.select_one(
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
)

@overload
async def select_one_or_none(
self,
Expand Down Expand Up @@ -437,6 +482,52 @@ async def select_one_or_none(
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
return result.one_or_none(schema_type=schema_type)

@overload
async def fetch_one_or_none(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT]",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "SchemaT | None": ...

@overload
async def fetch_one_or_none(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: None = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "dict[str, Any] | None": ...

async def fetch_one_or_none(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT] | None" = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "SchemaT | dict[str, Any] | None":
"""Execute a select statement and return at most one row.

This is an alias for :meth:`select_one_or_none` provided for users familiar
with asyncpg's fetch_one_or_none() naming convention.

Returns None if no rows are found.
Raises an exception if more than one row is returned.

See Also:
select_one_or_none(): Primary method with identical behavior
"""
return await self.select_one_or_none(
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
)

@overload
async def select(
self,
Expand Down Expand Up @@ -472,6 +563,49 @@ async def select(
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
return result.get_data(schema_type=schema_type)

@overload
async def fetch(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT]",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "list[SchemaT]": ...

@overload
async def fetch(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: None = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "list[dict[str, Any]]": ...

async def fetch(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT] | None" = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "list[SchemaT] | list[dict[str, Any]]":
"""Execute a select statement and return all rows.

This is an alias for :meth:`select` provided for users familiar
with asyncpg's fetch() naming convention.

See Also:
select(): Primary method with identical behavior
"""
return await self.select(
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
)

async def select_to_arrow(
self,
statement: "Statement | QueryBuilder",
Expand Down Expand Up @@ -549,6 +683,37 @@ async def select_to_arrow(
metadata=result.metadata,
)

async def fetch_to_arrow(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
statement_config: "StatementConfig | None" = None,
return_format: "ArrowReturnFormat" = "table",
native_only: bool = False,
batch_size: int | None = None,
arrow_schema: Any = None,
**kwargs: Any,
) -> "ArrowResult":
"""Execute query and return results as Apache Arrow format (async).

This is an alias for :meth:`select_to_arrow` provided for users familiar
with asyncpg's fetch() naming convention.

See Also:
select_to_arrow(): Primary method with identical behavior and full documentation
"""
return await self.select_to_arrow(
statement,
*parameters,
statement_config=statement_config,
return_format=return_format,
native_only=native_only,
batch_size=batch_size,
arrow_schema=arrow_schema,
**kwargs,
)

async def select_value(
self,
statement: "Statement | QueryBuilder",
Expand All @@ -568,6 +733,27 @@ async def select_value(
except ValueError as error:
handle_single_row_error(error)

async def fetch_value(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> Any:
"""Execute a select statement and return a single scalar value.

This is an alias for :meth:`select_value` provided for users familiar
with asyncpg's fetch_value() naming convention.

Expects exactly one row with one column.
Raises an exception if no rows or more than one row/column is returned.

See Also:
select_value(): Primary method with identical behavior
"""
return await self.select_value(statement, *parameters, statement_config=statement_config, **kwargs)

async def select_value_or_none(
self,
statement: "Statement | QueryBuilder",
Expand All @@ -585,6 +771,28 @@ async def select_value_or_none(
result = await self.execute(statement, *parameters, statement_config=statement_config, **kwargs)
return result.scalar_or_none()

async def fetch_value_or_none(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> Any:
"""Execute a select statement and return a single scalar value or None.

This is an alias for :meth:`select_value_or_none` provided for users familiar
with asyncpg's fetch_value_or_none() naming convention.

Returns None if no rows are found.
Expects at most one row with one column.
Raises an exception if more than one row is returned.

See Also:
select_value_or_none(): Primary method with identical behavior
"""
return await self.select_value_or_none(statement, *parameters, statement_config=statement_config, **kwargs)

@overload
async def select_with_total(
self,
Expand Down Expand Up @@ -641,6 +849,52 @@ async def select_with_total(

return (select_result.get_data(schema_type=schema_type), count_result.scalar())

@overload
async def fetch_with_total(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT]",
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "tuple[list[SchemaT], int]": ...

@overload
async def fetch_with_total(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: None = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "tuple[list[dict[str, Any]], int]": ...

async def fetch_with_total(
self,
statement: "Statement | QueryBuilder",
/,
*parameters: "StatementParameters | StatementFilter",
schema_type: "type[SchemaT] | None" = None,
statement_config: "StatementConfig | None" = None,
**kwargs: Any,
) -> "tuple[list[SchemaT] | list[dict[str, Any]], int]":
"""Execute a select statement and return both the data and total count.

This is an alias for :meth:`select_with_total` provided for users familiar
with asyncpg's fetch() naming convention.

This method is designed for pagination scenarios where you need both
the current page of data and the total number of rows that match the query.

See Also:
select_with_total(): Primary method with identical behavior and full documentation
"""
return await self.select_with_total(
statement, *parameters, schema_type=schema_type, statement_config=statement_config, **kwargs
)

async def _execute_stack_operation(self, operation: "StackOperation") -> "SQLResult | ArrowResult | None":
kwargs = dict(operation.keyword_arguments) if operation.keyword_arguments else {}

Expand Down
Loading