Skip to content

Commit

Permalink
docs: add recipe for adbc_prepare and low-level bindings
Browse files Browse the repository at this point in the history
Relates to apache#2040.
  • Loading branch information
lidavidm committed Aug 23, 2024
1 parent 09dffb3 commit b1da3c0
Show file tree
Hide file tree
Showing 3 changed files with 141 additions and 3 deletions.
19 changes: 16 additions & 3 deletions docs/source/python/recipe/driver_manager.rst
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@
.. specific language governing permissions and limitations
.. under the License.
======================
Driver Manager Recipes
======================
============================
DBAPI/Driver Manager Recipes
============================

These recipes show general functionality of the ADBC Python libraries that
isn't necessarily specific to any one driver.

Direct use of the low-level bindings
====================================

.. recipe:: driver_manager_lowlevel.py

Manually preparing a statement
==============================

.. recipe:: driver_manager_prepare.py
67 changes: 67 additions & 0 deletions docs/source/python/recipe/driver_manager_lowlevel.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# RECIPE STARTS HERE
#: While the DB-API_ bindings are recommended for general use, the low-level
#: bindings are also available. These mostly mirror the ADBC C API directly.
#: They can be useful to opt out of some behaviors of the DB-API wrapper.
#:
#: .. _DB-API: https://peps.python.org/pep-0249/

import pyarrow

import adbc_driver_manager
import adbc_driver_sqlite

#: The driver packages do still have conveniences to create the root
#: :class:`AdbcDatabase <adbc_driver_manager.AdbcDatabase>` object.
db: adbc_driver_manager.AdbcDatabase = adbc_driver_sqlite.connect()
#: The database must then be wrapped in a :class:`AdbcConnection
#: <adbc_driver_manager.AdbcConnection>`. This is similar in scope to the
#: DB-API :class:`Connection <adbc_driver_manager.dbapi.Connection>` class.
conn = adbc_driver_manager.AdbcConnection(db)
#: Finally, we can wrap the connection in a :class:`AdbcStatement
#: <adbc_driver_manager.AdbcStatement>`, which corresponds roughly to the
#: DB-API :class:`Cursor <adbc_driver_manager.dbapi.Cursor>` class.
stmt = adbc_driver_manager.AdbcStatement(conn)

#: Now we can directly set the query. Unlike the regular DB-API bindings, this
#: will not prepare the statement. (Depending on the driver, this may or may
#: not make a difference, especially if executing the same query multiple
#: times.)
stmt.set_sql_query("SELECT 1 AS THEANSWER")

#: When we execute the query, we get an `Arrow C Stream Interface`_ handle
#: (wrapped as a PyCapsule_) that we need to import using a library like
#: PyArrow_.
#:
#: .. _Arrow C Stream Interface:
#: https://arrow.apache.org/docs/format/CStreamInterface.html
#: .. _PyArrow: https://pypi.org/project/pyarrow/
#: .. _PyCapsule: https://docs.python.org/3/c-api/capsule.html
handle, rowcount = stmt.execute_query()
#: The SQLite driver does not know the row count of the result set up front
#: (other drivers, like the PostgreSQL driver, may know).
assert rowcount == -1
#: We can use the PyArrow APIs to read the result.
reader = pyarrow.RecordBatchReader._import_from_c_capsule(handle.__arrow_c_stream__())
assert reader.schema == pyarrow.schema([("THEANSWER", "int64")])
#: Finally, we have to clean up all the objects. (They also support the
#: context manager protocol.)
stmt.close()
conn.close()
db.close()
58 changes: 58 additions & 0 deletions docs/source/python/recipe/driver_manager_prepare.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
# Licensed to the Apache Software Foundation (ASF) under one
# or more contributor license agreements. See the NOTICE file
# distributed with this work for additional information
# regarding copyright ownership. The ASF licenses this file
# to you under the Apache License, Version 2.0 (the
# "License"); you may not use this file except in compliance
# with the License. You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing,
# software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
# KIND, either express or implied. See the License for the
# specific language governing permissions and limitations
# under the License.

# RECIPE STARTS HERE
#: The DBAPI bindings prepare all statements before execution, because of this
#: part of the `DB-API specification`_:
#:
#: A reference to the operation will be retained by the cursor. If the same
#: operation object is passed in again, then the cursor can optimize its
#: behavior.
#:
#: However, you may want to prepare the statement yourself, mostly because this
#: will give you the schema of the parameters (if supported by the server).
#: This can be done with :meth:`Cursor.adbc_prepare
#: <adbc_driver_manager.dbapi.Cursor.adbc_prepare>`.
#:
#: We'll demo this with the SQLite driver, though other drivers also support
#: this.
#:
#: .. _DB-API specification: https://peps.python.org/pep-0249/#id19

import pyarrow

import adbc_driver_sqlite.dbapi

conn = adbc_driver_sqlite.dbapi.connect()

with conn.cursor() as cur:
param_schema = cur.adbc_prepare("SELECT ? + 1")
assert param_schema == pyarrow.schema([("0", "null")])

#: Note that the type of the parameter here is NULL, because the driver
#: does not know the exact type.

#: If we now execute the same query with the parameter, the statement will
#: not be prepared a second time.

cur.execute("SELECT ? + 1", parameters=(1,))
assert cur.fetchone() == (2,)

cur.execute("SELECT ? + 1", parameters=(41,))
assert cur.fetchone() == (42,)

conn.close()

0 comments on commit b1da3c0

Please sign in to comment.