sqlspec 0.9.0__tar.gz → 0.9.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of sqlspec might be problematic. Click here for more details.
- {sqlspec-0.9.0 → sqlspec-0.9.1}/PKG-INFO +141 -2
- {sqlspec-0.9.0 → sqlspec-0.9.1}/README.md +140 -1
- {sqlspec-0.9.0 → sqlspec-0.9.1}/pyproject.toml +2 -2
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/driver.py +135 -3
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/driver.py +136 -3
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/driver.py +136 -3
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/driver.py +136 -2
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/driver.py +141 -11
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/driver.py +270 -4
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/config.py +6 -14
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/driver.py +149 -3
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/driver.py +306 -58
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/driver.py +136 -34
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/base.py +413 -9
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/plugin.py +2 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/uv.lock +4 -4
- {sqlspec-0.9.0 → sqlspec-0.9.1}/.gitignore +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/.pre-commit-config.yaml +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/CONTRIBUTING.rst +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/LICENSE +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/Makefile +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/NOTICE +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/__metadata__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/_serialization.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/_typing.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_asyncio.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_common.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_sync.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_async.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_common.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_sync.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/exceptions.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/_utils.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/handlers.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/filters.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/py.typed +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/statement.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/typing.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/deprecation.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/fixtures.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/module_loader.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/sync_tools.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/text.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/conftest.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/example_usage.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/sql_utils.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/conftest.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_bigquery.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_duckdb.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_postgres.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_sqlite.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_driver_async.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_driver_sync.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/test_connection.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/test_driver.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_adbc/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_adbc/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_aiosqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_aiosqlite/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncmy/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncmy/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncpg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncpg/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_duckdb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_duckdb/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/test_async_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/test_sync_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/test_async_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/test_sync_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_sqlite/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_sqlite/test_config.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_base.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_typing.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_module_loader.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_sync_tools.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_text.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/build_docs.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/pypi_readme.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/sphinx_ext/__init__.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/sphinx_ext/changelog.py +0 -0
- {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/sphinx_ext/missing_references.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: sqlspec
|
|
3
|
-
Version: 0.9.
|
|
3
|
+
Version: 0.9.1
|
|
4
4
|
Summary: SQL Experiments in Python
|
|
5
5
|
Author-email: Cody Fincher <cody@litestar.dev>
|
|
6
6
|
Maintainer-email: Litestar Developers <hello@litestar.dev>
|
|
@@ -83,6 +83,145 @@ SQLSpec is an experimental Python library designed to streamline and modernize y
|
|
|
83
83
|
|
|
84
84
|
SQLSpec is a work in progress. While it offers a solid foundation for modern SQL interactions, it does not yet include every feature you might find in a mature ORM or database toolkit. The focus is on building a robust, flexible core that can be extended over time.
|
|
85
85
|
|
|
86
|
+
## Examples
|
|
87
|
+
|
|
88
|
+
We've talked about what SQLSpec is not, so let's look at what it can do.
|
|
89
|
+
|
|
90
|
+
These are just a few of the examples that demonstrate SQLSpec's flexibility and each of the bundled adapters offer the same config and driver interfaces.
|
|
91
|
+
|
|
92
|
+
### DuckDB LLM
|
|
93
|
+
|
|
94
|
+
This is a quick implementation using some of the built in Secret and Extension management features of SQLSpec's DuckDB integration.
|
|
95
|
+
|
|
96
|
+
It allows you to communicate with any compatible OpenAPI conversations endpoint (such as Ollama). This examples:
|
|
97
|
+
|
|
98
|
+
- auto installs the `open_prompt` DuckDB extensions
|
|
99
|
+
- automatically creates the correct `open_prompt` comptaible secret required to use the extension
|
|
100
|
+
|
|
101
|
+
```py
|
|
102
|
+
# /// script
|
|
103
|
+
# dependencies = [
|
|
104
|
+
# "sqlspec[duckdb,performance]",
|
|
105
|
+
# ]
|
|
106
|
+
# ///
|
|
107
|
+
import os
|
|
108
|
+
|
|
109
|
+
from sqlspec import SQLSpec
|
|
110
|
+
from sqlspec.adapters.duckdb import DuckDBConfig
|
|
111
|
+
from pydantic import BaseModel
|
|
112
|
+
|
|
113
|
+
class ChatMessage(BaseModel):
|
|
114
|
+
message: str
|
|
115
|
+
|
|
116
|
+
sql = SQLSpec()
|
|
117
|
+
etl_config = sql.add_config(
|
|
118
|
+
DuckDBConfig(
|
|
119
|
+
extensions=[{"name": "open_prompt"}],
|
|
120
|
+
secrets=[
|
|
121
|
+
{
|
|
122
|
+
"secret_type": "open_prompt",
|
|
123
|
+
"name": "open_prompt",
|
|
124
|
+
"value": {
|
|
125
|
+
"api_url": "http://127.0.0.1:11434/v1/chat/completions",
|
|
126
|
+
"model_name": "gemma3:1b",
|
|
127
|
+
"api_timeout": "120",
|
|
128
|
+
},
|
|
129
|
+
}
|
|
130
|
+
],
|
|
131
|
+
)
|
|
132
|
+
)
|
|
133
|
+
with sql.provide_session(etl_config) as session:
|
|
134
|
+
result = session.select_one("SELECT open_prompt(?)", data.message, schema_type=ChatMessage)
|
|
135
|
+
print(result) # result is a ChatMessage pydantic model
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
### DuckDB Gemini Embeddings
|
|
139
|
+
|
|
140
|
+
In this example, we are again using DuckDB. However, we are going to use the built in to call the Google Gemini embeddings service directly from the database.
|
|
141
|
+
|
|
142
|
+
This example will
|
|
143
|
+
|
|
144
|
+
- auto installs the `http_client` and `vss` (vector similarity search) DuckDB extensions
|
|
145
|
+
- when a connection is created, it ensures that the `generate_embeddings` macro exists in the DuckDB database.
|
|
146
|
+
- Execute a simple query to call the Google API
|
|
147
|
+
|
|
148
|
+
```py
|
|
149
|
+
# /// script
|
|
150
|
+
# dependencies = [
|
|
151
|
+
# "sqlspec[duckdb,performance]",
|
|
152
|
+
# ]
|
|
153
|
+
# ///
|
|
154
|
+
import os
|
|
155
|
+
|
|
156
|
+
from sqlspec import SQLSpec
|
|
157
|
+
from sqlspec.adapters.duckdb import DuckDBConfig
|
|
158
|
+
|
|
159
|
+
EMBEDDING_MODEL = "gemini-embedding-exp-03-07"
|
|
160
|
+
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
|
|
161
|
+
API_URL = (
|
|
162
|
+
f"https://generativelanguage.googleapis.com/v1beta/models/{EMBEDDING_MODEL}:embedContent?key=${GOOGLE_API_KEY}"
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
sql = SQLSpec()
|
|
166
|
+
etl_config = sql.add_config(
|
|
167
|
+
DuckDBConfig(
|
|
168
|
+
extensions=[{"name": "vss"}, {"name": "http_client"}],
|
|
169
|
+
on_connection_create=lambda connection: connection.execute(f"""
|
|
170
|
+
CREATE IF NOT EXISTS MACRO generate_embedding(q) AS (
|
|
171
|
+
WITH __request AS (
|
|
172
|
+
SELECT http_post(
|
|
173
|
+
'{API_URL}',
|
|
174
|
+
headers => MAP {{
|
|
175
|
+
'accept': 'application/json',
|
|
176
|
+
}},
|
|
177
|
+
params => MAP {{
|
|
178
|
+
'model': 'models/{EMBEDDING_MODEL}',
|
|
179
|
+
'parts': [{{ 'text': q }}],
|
|
180
|
+
'taskType': 'SEMANTIC_SIMILARITY'
|
|
181
|
+
}}
|
|
182
|
+
) AS response
|
|
183
|
+
)
|
|
184
|
+
SELECT *
|
|
185
|
+
FROM __request,
|
|
186
|
+
);
|
|
187
|
+
"""),
|
|
188
|
+
)
|
|
189
|
+
)
|
|
190
|
+
with sql.provide_session(etl_config) as session:
|
|
191
|
+
result = session.select_one("SELECT generate_embedding('example text')")
|
|
192
|
+
print(result) # result is a dictionary when `schema_type` is omitted.
|
|
193
|
+
```
|
|
194
|
+
|
|
195
|
+
### Basic Litestar Integration
|
|
196
|
+
|
|
197
|
+
In this example we are going to demonstrate how to create a basic configuration that integrates into Litestar.
|
|
198
|
+
|
|
199
|
+
```py
|
|
200
|
+
# /// script
|
|
201
|
+
# dependencies = [
|
|
202
|
+
# "sqlspec[aiosqlite]",
|
|
203
|
+
# "litestar[standard]",
|
|
204
|
+
# ]
|
|
205
|
+
# ///
|
|
206
|
+
|
|
207
|
+
from aiosqlite import Connection
|
|
208
|
+
from litestar import Litestar, get
|
|
209
|
+
|
|
210
|
+
from sqlspec.adapters.aiosqlite import AiosqliteConfig, AiosqliteDriver
|
|
211
|
+
from sqlspec.extensions.litestar import SQLSpec
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
@get("/")
|
|
215
|
+
async def simple_sqlite(db_session: AiosqliteDriver) -> dict[str, str]:
|
|
216
|
+
return await db_session.select_one("SELECT 'Hello, world!' AS greeting")
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
sqlspec = SQLSpec(config=DatabaseConfig(
|
|
220
|
+
config=[AiosqliteConfig(), commit_mode="autocommit")],
|
|
221
|
+
)
|
|
222
|
+
app = Litestar(route_handlers=[simple_sqlite], plugins=[sqlspec])
|
|
223
|
+
```
|
|
224
|
+
|
|
86
225
|
## Inspiration and Future Direction
|
|
87
226
|
|
|
88
227
|
SQLSpec originally drew inspiration from features found in the `aiosql` library. This is a great library for working with and executed SQL stored in files. It's unclear how much of an overlap there will be between the two libraries, but it's possible that some features will be contributed back to `aiosql` where appropriate.
|
|
@@ -121,7 +260,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
|
|
|
121
260
|
- `sqlspec/`:
|
|
122
261
|
- `adapters/`: Contains all database drivers and associated configuration.
|
|
123
262
|
- `extensions/`:
|
|
124
|
-
- `litestar/`:
|
|
263
|
+
- `litestar/`: Litestar framework integration ✅
|
|
125
264
|
- `fastapi/`: Future home of `fastapi` integration.
|
|
126
265
|
- `flask/`: Future home of `flask` integration.
|
|
127
266
|
- `*/`: Future home of your favorite framework integration 🔌 ✨
|
|
@@ -22,6 +22,145 @@ SQLSpec is an experimental Python library designed to streamline and modernize y
|
|
|
22
22
|
|
|
23
23
|
SQLSpec is a work in progress. While it offers a solid foundation for modern SQL interactions, it does not yet include every feature you might find in a mature ORM or database toolkit. The focus is on building a robust, flexible core that can be extended over time.
|
|
24
24
|
|
|
25
|
+
## Examples
|
|
26
|
+
|
|
27
|
+
We've talked about what SQLSpec is not, so let's look at what it can do.
|
|
28
|
+
|
|
29
|
+
These are just a few of the examples that demonstrate SQLSpec's flexibility and each of the bundled adapters offer the same config and driver interfaces.
|
|
30
|
+
|
|
31
|
+
### DuckDB LLM
|
|
32
|
+
|
|
33
|
+
This is a quick implementation using some of the built in Secret and Extension management features of SQLSpec's DuckDB integration.
|
|
34
|
+
|
|
35
|
+
It allows you to communicate with any compatible OpenAPI conversations endpoint (such as Ollama). This examples:
|
|
36
|
+
|
|
37
|
+
- auto installs the `open_prompt` DuckDB extensions
|
|
38
|
+
- automatically creates the correct `open_prompt` comptaible secret required to use the extension
|
|
39
|
+
|
|
40
|
+
```py
|
|
41
|
+
# /// script
|
|
42
|
+
# dependencies = [
|
|
43
|
+
# "sqlspec[duckdb,performance]",
|
|
44
|
+
# ]
|
|
45
|
+
# ///
|
|
46
|
+
import os
|
|
47
|
+
|
|
48
|
+
from sqlspec import SQLSpec
|
|
49
|
+
from sqlspec.adapters.duckdb import DuckDBConfig
|
|
50
|
+
from pydantic import BaseModel
|
|
51
|
+
|
|
52
|
+
class ChatMessage(BaseModel):
|
|
53
|
+
message: str
|
|
54
|
+
|
|
55
|
+
sql = SQLSpec()
|
|
56
|
+
etl_config = sql.add_config(
|
|
57
|
+
DuckDBConfig(
|
|
58
|
+
extensions=[{"name": "open_prompt"}],
|
|
59
|
+
secrets=[
|
|
60
|
+
{
|
|
61
|
+
"secret_type": "open_prompt",
|
|
62
|
+
"name": "open_prompt",
|
|
63
|
+
"value": {
|
|
64
|
+
"api_url": "http://127.0.0.1:11434/v1/chat/completions",
|
|
65
|
+
"model_name": "gemma3:1b",
|
|
66
|
+
"api_timeout": "120",
|
|
67
|
+
},
|
|
68
|
+
}
|
|
69
|
+
],
|
|
70
|
+
)
|
|
71
|
+
)
|
|
72
|
+
with sql.provide_session(etl_config) as session:
|
|
73
|
+
result = session.select_one("SELECT open_prompt(?)", data.message, schema_type=ChatMessage)
|
|
74
|
+
print(result) # result is a ChatMessage pydantic model
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### DuckDB Gemini Embeddings
|
|
78
|
+
|
|
79
|
+
In this example, we are again using DuckDB. However, we are going to use the built in to call the Google Gemini embeddings service directly from the database.
|
|
80
|
+
|
|
81
|
+
This example will
|
|
82
|
+
|
|
83
|
+
- auto installs the `http_client` and `vss` (vector similarity search) DuckDB extensions
|
|
84
|
+
- when a connection is created, it ensures that the `generate_embeddings` macro exists in the DuckDB database.
|
|
85
|
+
- Execute a simple query to call the Google API
|
|
86
|
+
|
|
87
|
+
```py
|
|
88
|
+
# /// script
|
|
89
|
+
# dependencies = [
|
|
90
|
+
# "sqlspec[duckdb,performance]",
|
|
91
|
+
# ]
|
|
92
|
+
# ///
|
|
93
|
+
import os
|
|
94
|
+
|
|
95
|
+
from sqlspec import SQLSpec
|
|
96
|
+
from sqlspec.adapters.duckdb import DuckDBConfig
|
|
97
|
+
|
|
98
|
+
EMBEDDING_MODEL = "gemini-embedding-exp-03-07"
|
|
99
|
+
GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY")
|
|
100
|
+
API_URL = (
|
|
101
|
+
f"https://generativelanguage.googleapis.com/v1beta/models/{EMBEDDING_MODEL}:embedContent?key=${GOOGLE_API_KEY}"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
sql = SQLSpec()
|
|
105
|
+
etl_config = sql.add_config(
|
|
106
|
+
DuckDBConfig(
|
|
107
|
+
extensions=[{"name": "vss"}, {"name": "http_client"}],
|
|
108
|
+
on_connection_create=lambda connection: connection.execute(f"""
|
|
109
|
+
CREATE IF NOT EXISTS MACRO generate_embedding(q) AS (
|
|
110
|
+
WITH __request AS (
|
|
111
|
+
SELECT http_post(
|
|
112
|
+
'{API_URL}',
|
|
113
|
+
headers => MAP {{
|
|
114
|
+
'accept': 'application/json',
|
|
115
|
+
}},
|
|
116
|
+
params => MAP {{
|
|
117
|
+
'model': 'models/{EMBEDDING_MODEL}',
|
|
118
|
+
'parts': [{{ 'text': q }}],
|
|
119
|
+
'taskType': 'SEMANTIC_SIMILARITY'
|
|
120
|
+
}}
|
|
121
|
+
) AS response
|
|
122
|
+
)
|
|
123
|
+
SELECT *
|
|
124
|
+
FROM __request,
|
|
125
|
+
);
|
|
126
|
+
"""),
|
|
127
|
+
)
|
|
128
|
+
)
|
|
129
|
+
with sql.provide_session(etl_config) as session:
|
|
130
|
+
result = session.select_one("SELECT generate_embedding('example text')")
|
|
131
|
+
print(result) # result is a dictionary when `schema_type` is omitted.
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Basic Litestar Integration
|
|
135
|
+
|
|
136
|
+
In this example we are going to demonstrate how to create a basic configuration that integrates into Litestar.
|
|
137
|
+
|
|
138
|
+
```py
|
|
139
|
+
# /// script
|
|
140
|
+
# dependencies = [
|
|
141
|
+
# "sqlspec[aiosqlite]",
|
|
142
|
+
# "litestar[standard]",
|
|
143
|
+
# ]
|
|
144
|
+
# ///
|
|
145
|
+
|
|
146
|
+
from aiosqlite import Connection
|
|
147
|
+
from litestar import Litestar, get
|
|
148
|
+
|
|
149
|
+
from sqlspec.adapters.aiosqlite import AiosqliteConfig, AiosqliteDriver
|
|
150
|
+
from sqlspec.extensions.litestar import SQLSpec
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
@get("/")
|
|
154
|
+
async def simple_sqlite(db_session: AiosqliteDriver) -> dict[str, str]:
|
|
155
|
+
return await db_session.select_one("SELECT 'Hello, world!' AS greeting")
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
sqlspec = SQLSpec(config=DatabaseConfig(
|
|
159
|
+
config=[AiosqliteConfig(), commit_mode="autocommit")],
|
|
160
|
+
)
|
|
161
|
+
app = Litestar(route_handlers=[simple_sqlite], plugins=[sqlspec])
|
|
162
|
+
```
|
|
163
|
+
|
|
25
164
|
## Inspiration and Future Direction
|
|
26
165
|
|
|
27
166
|
SQLSpec originally drew inspiration from features found in the `aiosql` library. This is a great library for working with and executed SQL stored in files. It's unclear how much of an overlap there will be between the two libraries, but it's possible that some features will be contributed back to `aiosql` where appropriate.
|
|
@@ -60,7 +199,7 @@ This list is not final. If you have a driver you'd like to see added, please ope
|
|
|
60
199
|
- `sqlspec/`:
|
|
61
200
|
- `adapters/`: Contains all database drivers and associated configuration.
|
|
62
201
|
- `extensions/`:
|
|
63
|
-
- `litestar/`:
|
|
202
|
+
- `litestar/`: Litestar framework integration ✅
|
|
64
203
|
- `fastapi/`: Future home of `fastapi` integration.
|
|
65
204
|
- `flask/`: Future home of `flask` integration.
|
|
66
205
|
- `*/`: Future home of your favorite framework integration 🔌 ✨
|
|
@@ -7,7 +7,7 @@ maintainers = [{ name = "Litestar Developers", email = "hello@litestar.dev" }]
|
|
|
7
7
|
name = "sqlspec"
|
|
8
8
|
readme = "README.md"
|
|
9
9
|
requires-python = ">=3.9, <4.0"
|
|
10
|
-
version = "0.9.
|
|
10
|
+
version = "0.9.1"
|
|
11
11
|
|
|
12
12
|
[project.optional-dependencies]
|
|
13
13
|
adbc = ["adbc_driver_manager", "pyarrow"]
|
|
@@ -109,7 +109,7 @@ packages = ["sqlspec"]
|
|
|
109
109
|
allow_dirty = true
|
|
110
110
|
commit = false
|
|
111
111
|
commit_args = "--no-verify"
|
|
112
|
-
current_version = "0.9.
|
|
112
|
+
current_version = "0.9.1"
|
|
113
113
|
ignore_missing_files = false
|
|
114
114
|
ignore_missing_version = false
|
|
115
115
|
message = "chore(release): bump to v{new_version}"
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import logging
|
|
3
3
|
import re
|
|
4
|
-
from collections.abc import Generator
|
|
4
|
+
from collections.abc import Generator, Sequence
|
|
5
5
|
from contextlib import contextmanager
|
|
6
|
-
from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast
|
|
6
|
+
from typing import TYPE_CHECKING, Any, ClassVar, Optional, Union, cast, overload
|
|
7
7
|
|
|
8
8
|
from adbc_driver_manager.dbapi import Connection, Cursor
|
|
9
9
|
|
|
@@ -160,6 +160,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
160
160
|
stmt = SQLStatement(sql=sql, parameters=parameters, dialect=self.dialect, kwargs=kwargs or None)
|
|
161
161
|
return stmt.process()
|
|
162
162
|
|
|
163
|
+
@overload
|
|
164
|
+
def select(
|
|
165
|
+
self,
|
|
166
|
+
sql: str,
|
|
167
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
168
|
+
/,
|
|
169
|
+
*,
|
|
170
|
+
connection: "Optional[Connection]" = None,
|
|
171
|
+
schema_type: None = None,
|
|
172
|
+
**kwargs: Any,
|
|
173
|
+
) -> "Sequence[dict[str, Any]]": ...
|
|
174
|
+
@overload
|
|
175
|
+
def select(
|
|
176
|
+
self,
|
|
177
|
+
sql: str,
|
|
178
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
179
|
+
/,
|
|
180
|
+
*,
|
|
181
|
+
connection: "Optional[Connection]" = None,
|
|
182
|
+
schema_type: "type[ModelDTOT]",
|
|
183
|
+
**kwargs: Any,
|
|
184
|
+
) -> "Sequence[ModelDTOT]": ...
|
|
163
185
|
def select(
|
|
164
186
|
self,
|
|
165
187
|
sql: str,
|
|
@@ -169,7 +191,7 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
169
191
|
connection: Optional["Connection"] = None,
|
|
170
192
|
schema_type: "Optional[type[ModelDTOT]]" = None,
|
|
171
193
|
**kwargs: Any,
|
|
172
|
-
) -> "
|
|
194
|
+
) -> "Sequence[Union[ModelDTOT, dict[str, Any]]]":
|
|
173
195
|
"""Fetch data from the database.
|
|
174
196
|
|
|
175
197
|
Returns:
|
|
@@ -189,6 +211,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
189
211
|
return [cast("ModelDTOT", schema_type(**dict(zip(column_names, row)))) for row in results] # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
190
212
|
return [dict(zip(column_names, row)) for row in results] # pyright: ignore[reportUnknownArgumentType,reportUnknownVariableType]
|
|
191
213
|
|
|
214
|
+
@overload
|
|
215
|
+
def select_one(
|
|
216
|
+
self,
|
|
217
|
+
sql: str,
|
|
218
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
219
|
+
/,
|
|
220
|
+
*,
|
|
221
|
+
connection: "Optional[Connection]" = None,
|
|
222
|
+
schema_type: None = None,
|
|
223
|
+
**kwargs: Any,
|
|
224
|
+
) -> "dict[str, Any]": ...
|
|
225
|
+
@overload
|
|
226
|
+
def select_one(
|
|
227
|
+
self,
|
|
228
|
+
sql: str,
|
|
229
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
230
|
+
/,
|
|
231
|
+
*,
|
|
232
|
+
connection: "Optional[Connection]" = None,
|
|
233
|
+
schema_type: "type[ModelDTOT]",
|
|
234
|
+
**kwargs: Any,
|
|
235
|
+
) -> "ModelDTOT": ...
|
|
192
236
|
def select_one(
|
|
193
237
|
self,
|
|
194
238
|
sql: str,
|
|
@@ -215,6 +259,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
215
259
|
return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
|
|
216
260
|
return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
|
|
217
261
|
|
|
262
|
+
@overload
|
|
263
|
+
def select_one_or_none(
|
|
264
|
+
self,
|
|
265
|
+
sql: str,
|
|
266
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
267
|
+
/,
|
|
268
|
+
*,
|
|
269
|
+
connection: "Optional[Connection]" = None,
|
|
270
|
+
schema_type: None = None,
|
|
271
|
+
**kwargs: Any,
|
|
272
|
+
) -> "Optional[dict[str, Any]]": ...
|
|
273
|
+
@overload
|
|
274
|
+
def select_one_or_none(
|
|
275
|
+
self,
|
|
276
|
+
sql: str,
|
|
277
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
278
|
+
/,
|
|
279
|
+
*,
|
|
280
|
+
connection: "Optional[Connection]" = None,
|
|
281
|
+
schema_type: "type[ModelDTOT]",
|
|
282
|
+
**kwargs: Any,
|
|
283
|
+
) -> "Optional[ModelDTOT]": ...
|
|
218
284
|
def select_one_or_none(
|
|
219
285
|
self,
|
|
220
286
|
sql: str,
|
|
@@ -242,6 +308,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
242
308
|
return dict(zip(column_names, result)) # pyright: ignore[reportUnknownArgumentType, reportUnknownVariableType]
|
|
243
309
|
return schema_type(**dict(zip(column_names, result))) # type: ignore[return-value]
|
|
244
310
|
|
|
311
|
+
@overload
|
|
312
|
+
def select_value(
|
|
313
|
+
self,
|
|
314
|
+
sql: str,
|
|
315
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
316
|
+
/,
|
|
317
|
+
*,
|
|
318
|
+
connection: "Optional[Connection]" = None,
|
|
319
|
+
schema_type: None = None,
|
|
320
|
+
**kwargs: Any,
|
|
321
|
+
) -> "Any": ...
|
|
322
|
+
@overload
|
|
323
|
+
def select_value(
|
|
324
|
+
self,
|
|
325
|
+
sql: str,
|
|
326
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
327
|
+
/,
|
|
328
|
+
*,
|
|
329
|
+
connection: "Optional[Connection]" = None,
|
|
330
|
+
schema_type: "type[T]",
|
|
331
|
+
**kwargs: Any,
|
|
332
|
+
) -> "T": ...
|
|
245
333
|
def select_value(
|
|
246
334
|
self,
|
|
247
335
|
sql: str,
|
|
@@ -267,6 +355,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
267
355
|
return result[0] # pyright: ignore[reportUnknownVariableType]
|
|
268
356
|
return schema_type(result[0]) # type: ignore[call-arg]
|
|
269
357
|
|
|
358
|
+
@overload
|
|
359
|
+
def select_value_or_none(
|
|
360
|
+
self,
|
|
361
|
+
sql: str,
|
|
362
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
363
|
+
/,
|
|
364
|
+
*,
|
|
365
|
+
connection: "Optional[Connection]" = None,
|
|
366
|
+
schema_type: None = None,
|
|
367
|
+
**kwargs: Any,
|
|
368
|
+
) -> "Optional[Any]": ...
|
|
369
|
+
@overload
|
|
370
|
+
def select_value_or_none(
|
|
371
|
+
self,
|
|
372
|
+
sql: str,
|
|
373
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
374
|
+
/,
|
|
375
|
+
*,
|
|
376
|
+
connection: "Optional[Connection]" = None,
|
|
377
|
+
schema_type: "type[T]",
|
|
378
|
+
**kwargs: Any,
|
|
379
|
+
) -> "Optional[T]": ...
|
|
270
380
|
def select_value_or_none(
|
|
271
381
|
self,
|
|
272
382
|
sql: str,
|
|
@@ -314,6 +424,28 @@ class AdbcDriver(SyncArrowBulkOperationsMixin["Connection"], SyncDriverAdapterPr
|
|
|
314
424
|
cursor.execute(sql, parameters) # pyright: ignore[reportUnknownMemberType]
|
|
315
425
|
return cursor.rowcount if hasattr(cursor, "rowcount") else -1
|
|
316
426
|
|
|
427
|
+
@overload
|
|
428
|
+
def insert_update_delete_returning(
|
|
429
|
+
self,
|
|
430
|
+
sql: str,
|
|
431
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
432
|
+
/,
|
|
433
|
+
*,
|
|
434
|
+
connection: "Optional[Connection]" = None,
|
|
435
|
+
schema_type: None = None,
|
|
436
|
+
**kwargs: Any,
|
|
437
|
+
) -> "dict[str, Any]": ...
|
|
438
|
+
@overload
|
|
439
|
+
def insert_update_delete_returning(
|
|
440
|
+
self,
|
|
441
|
+
sql: str,
|
|
442
|
+
parameters: "Optional[StatementParameterType]" = None,
|
|
443
|
+
/,
|
|
444
|
+
*,
|
|
445
|
+
connection: "Optional[Connection]" = None,
|
|
446
|
+
schema_type: "type[ModelDTOT]",
|
|
447
|
+
**kwargs: Any,
|
|
448
|
+
) -> "ModelDTOT": ...
|
|
317
449
|
def insert_update_delete_returning(
|
|
318
450
|
self,
|
|
319
451
|
sql: str,
|