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.

Files changed (137) hide show
  1. {sqlspec-0.9.0 → sqlspec-0.9.1}/PKG-INFO +141 -2
  2. {sqlspec-0.9.0 → sqlspec-0.9.1}/README.md +140 -1
  3. {sqlspec-0.9.0 → sqlspec-0.9.1}/pyproject.toml +2 -2
  4. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/driver.py +135 -3
  5. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/driver.py +136 -3
  6. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/driver.py +136 -3
  7. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/driver.py +136 -2
  8. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/driver.py +141 -11
  9. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/driver.py +270 -4
  10. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/config.py +6 -14
  11. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/driver.py +149 -3
  12. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/driver.py +306 -58
  13. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/driver.py +136 -34
  14. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/base.py +413 -9
  15. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/plugin.py +2 -0
  16. {sqlspec-0.9.0 → sqlspec-0.9.1}/uv.lock +4 -4
  17. {sqlspec-0.9.0 → sqlspec-0.9.1}/.gitignore +0 -0
  18. {sqlspec-0.9.0 → sqlspec-0.9.1}/.pre-commit-config.yaml +0 -0
  19. {sqlspec-0.9.0 → sqlspec-0.9.1}/CONTRIBUTING.rst +0 -0
  20. {sqlspec-0.9.0 → sqlspec-0.9.1}/LICENSE +0 -0
  21. {sqlspec-0.9.0 → sqlspec-0.9.1}/Makefile +0 -0
  22. {sqlspec-0.9.0 → sqlspec-0.9.1}/NOTICE +0 -0
  23. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/__init__.py +0 -0
  24. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/__metadata__.py +0 -0
  25. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/_serialization.py +0 -0
  26. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/_typing.py +0 -0
  27. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/__init__.py +0 -0
  28. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/__init__.py +0 -0
  29. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/adbc/config.py +0 -0
  30. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/__init__.py +0 -0
  31. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/aiosqlite/config.py +0 -0
  32. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/__init__.py +0 -0
  33. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncmy/config.py +0 -0
  34. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/__init__.py +0 -0
  35. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/asyncpg/config.py +0 -0
  36. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/__init__.py +0 -0
  37. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/duckdb/config.py +0 -0
  38. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/__init__.py +0 -0
  39. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/__init__.py +0 -0
  40. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_asyncio.py +0 -0
  41. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_common.py +0 -0
  42. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/oracledb/config/_sync.py +0 -0
  43. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psqlpy/__init__.py +0 -0
  44. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/__init__.py +0 -0
  45. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/__init__.py +0 -0
  46. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_async.py +0 -0
  47. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_common.py +0 -0
  48. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/psycopg/config/_sync.py +0 -0
  49. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/__init__.py +0 -0
  50. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/adapters/sqlite/config.py +0 -0
  51. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/exceptions.py +0 -0
  52. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/__init__.py +0 -0
  53. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/__init__.py +0 -0
  54. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/_utils.py +0 -0
  55. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/config.py +0 -0
  56. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/extensions/litestar/handlers.py +0 -0
  57. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/filters.py +0 -0
  58. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/py.typed +0 -0
  59. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/statement.py +0 -0
  60. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/typing.py +0 -0
  61. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/__init__.py +0 -0
  62. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/deprecation.py +0 -0
  63. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/fixtures.py +0 -0
  64. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/module_loader.py +0 -0
  65. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/sync_tools.py +0 -0
  66. {sqlspec-0.9.0 → sqlspec-0.9.1}/sqlspec/utils/text.py +0 -0
  67. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/__init__.py +0 -0
  68. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/conftest.py +0 -0
  69. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/__init__.py +0 -0
  70. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/example_usage.py +0 -0
  71. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/fixtures/sql_utils.py +0 -0
  72. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/__init__.py +0 -0
  73. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/__init__.py +0 -0
  74. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/__init__.py +0 -0
  75. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/conftest.py +0 -0
  76. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_connection.py +0 -0
  77. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_bigquery.py +0 -0
  78. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_duckdb.py +0 -0
  79. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_postgres.py +0 -0
  80. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_adbc/test_driver_sqlite.py +0 -0
  81. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/__init__.py +0 -0
  82. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/test_connection.py +0 -0
  83. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_aiosqlite/test_driver.py +0 -0
  84. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/__init__.py +0 -0
  85. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/test_connection.py +0 -0
  86. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncmy/test_driver.py +0 -0
  87. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/__init__.py +0 -0
  88. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/test_connection.py +0 -0
  89. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_asyncpg/test_driver.py +0 -0
  90. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/__init__.py +0 -0
  91. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/test_connection.py +0 -0
  92. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_duckdb/test_driver.py +0 -0
  93. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/__init__.py +0 -0
  94. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_connection.py +0 -0
  95. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_driver_async.py +0 -0
  96. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_oracledb/test_driver_sync.py +0 -0
  97. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/__init__.py +0 -0
  98. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/test_connection.py +0 -0
  99. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psqlpy/test_driver.py +0 -0
  100. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/__init__.py +0 -0
  101. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/test_connection.py +0 -0
  102. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_psycopg/test_driver.py +0 -0
  103. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/__init__.py +0 -0
  104. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/test_connection.py +0 -0
  105. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/integration/test_adapters/test_sqlite/test_driver.py +0 -0
  106. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/__init__.py +0 -0
  107. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/__init__.py +0 -0
  108. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_adbc/__init__.py +0 -0
  109. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_adbc/test_config.py +0 -0
  110. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_aiosqlite/__init__.py +0 -0
  111. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_aiosqlite/test_config.py +0 -0
  112. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncmy/__init__.py +0 -0
  113. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncmy/test_config.py +0 -0
  114. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncpg/__init__.py +0 -0
  115. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_asyncpg/test_config.py +0 -0
  116. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_duckdb/__init__.py +0 -0
  117. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_duckdb/test_config.py +0 -0
  118. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/__init__.py +0 -0
  119. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/test_async_config.py +0 -0
  120. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_oracledb/test_sync_config.py +0 -0
  121. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/__init__.py +0 -0
  122. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/test_async_config.py +0 -0
  123. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_psycopg/test_sync_config.py +0 -0
  124. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_sqlite/__init__.py +0 -0
  125. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_adapters/test_sqlite/test_config.py +0 -0
  126. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_base.py +0 -0
  127. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_typing.py +0 -0
  128. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/__init__.py +0 -0
  129. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_module_loader.py +0 -0
  130. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_sync_tools.py +0 -0
  131. {sqlspec-0.9.0 → sqlspec-0.9.1}/tests/unit/test_utils/test_text.py +0 -0
  132. {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/__init__.py +0 -0
  133. {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/build_docs.py +0 -0
  134. {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/pypi_readme.py +0 -0
  135. {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/sphinx_ext/__init__.py +0 -0
  136. {sqlspec-0.9.0 → sqlspec-0.9.1}/tools/sphinx_ext/changelog.py +0 -0
  137. {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.0
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/`: Future home of `litestar` integration.
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/`: Future home of `litestar` integration.
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.0"
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.0"
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
- ) -> "list[Union[ModelDTOT, dict[str, Any]]]":
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,