sqlspec 0.26.0__py3-none-any.whl → 0.28.0__py3-none-any.whl
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/__init__.py +7 -15
- sqlspec/_serialization.py +55 -25
- sqlspec/_typing.py +155 -52
- sqlspec/adapters/adbc/_types.py +1 -1
- sqlspec/adapters/adbc/adk/__init__.py +5 -0
- sqlspec/adapters/adbc/adk/store.py +880 -0
- sqlspec/adapters/adbc/config.py +62 -12
- sqlspec/adapters/adbc/data_dictionary.py +74 -2
- sqlspec/adapters/adbc/driver.py +226 -58
- sqlspec/adapters/adbc/litestar/__init__.py +5 -0
- sqlspec/adapters/adbc/litestar/store.py +504 -0
- sqlspec/adapters/adbc/type_converter.py +44 -50
- sqlspec/adapters/aiosqlite/_types.py +1 -1
- sqlspec/adapters/aiosqlite/adk/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/adk/store.py +536 -0
- sqlspec/adapters/aiosqlite/config.py +86 -16
- sqlspec/adapters/aiosqlite/data_dictionary.py +34 -2
- sqlspec/adapters/aiosqlite/driver.py +127 -38
- sqlspec/adapters/aiosqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/aiosqlite/litestar/store.py +281 -0
- sqlspec/adapters/aiosqlite/pool.py +7 -7
- sqlspec/adapters/asyncmy/__init__.py +7 -1
- sqlspec/adapters/asyncmy/_types.py +1 -1
- sqlspec/adapters/asyncmy/adk/__init__.py +5 -0
- sqlspec/adapters/asyncmy/adk/store.py +503 -0
- sqlspec/adapters/asyncmy/config.py +59 -17
- sqlspec/adapters/asyncmy/data_dictionary.py +41 -2
- sqlspec/adapters/asyncmy/driver.py +293 -62
- sqlspec/adapters/asyncmy/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncmy/litestar/store.py +296 -0
- sqlspec/adapters/asyncpg/__init__.py +2 -1
- sqlspec/adapters/asyncpg/_type_handlers.py +71 -0
- sqlspec/adapters/asyncpg/_types.py +11 -7
- sqlspec/adapters/asyncpg/adk/__init__.py +5 -0
- sqlspec/adapters/asyncpg/adk/store.py +460 -0
- sqlspec/adapters/asyncpg/config.py +57 -36
- sqlspec/adapters/asyncpg/data_dictionary.py +48 -2
- sqlspec/adapters/asyncpg/driver.py +153 -23
- sqlspec/adapters/asyncpg/litestar/__init__.py +5 -0
- sqlspec/adapters/asyncpg/litestar/store.py +253 -0
- sqlspec/adapters/bigquery/_types.py +1 -1
- sqlspec/adapters/bigquery/adk/__init__.py +5 -0
- sqlspec/adapters/bigquery/adk/store.py +585 -0
- sqlspec/adapters/bigquery/config.py +36 -11
- sqlspec/adapters/bigquery/data_dictionary.py +42 -2
- sqlspec/adapters/bigquery/driver.py +489 -144
- sqlspec/adapters/bigquery/litestar/__init__.py +5 -0
- sqlspec/adapters/bigquery/litestar/store.py +327 -0
- sqlspec/adapters/bigquery/type_converter.py +55 -23
- sqlspec/adapters/duckdb/_types.py +2 -2
- sqlspec/adapters/duckdb/adk/__init__.py +14 -0
- sqlspec/adapters/duckdb/adk/store.py +563 -0
- sqlspec/adapters/duckdb/config.py +79 -21
- sqlspec/adapters/duckdb/data_dictionary.py +41 -2
- sqlspec/adapters/duckdb/driver.py +225 -44
- sqlspec/adapters/duckdb/litestar/__init__.py +5 -0
- sqlspec/adapters/duckdb/litestar/store.py +332 -0
- sqlspec/adapters/duckdb/pool.py +5 -5
- sqlspec/adapters/duckdb/type_converter.py +51 -21
- sqlspec/adapters/oracledb/_numpy_handlers.py +133 -0
- sqlspec/adapters/oracledb/_types.py +20 -2
- sqlspec/adapters/oracledb/adk/__init__.py +5 -0
- sqlspec/adapters/oracledb/adk/store.py +1628 -0
- sqlspec/adapters/oracledb/config.py +120 -36
- sqlspec/adapters/oracledb/data_dictionary.py +87 -20
- sqlspec/adapters/oracledb/driver.py +475 -86
- sqlspec/adapters/oracledb/litestar/__init__.py +5 -0
- sqlspec/adapters/oracledb/litestar/store.py +765 -0
- sqlspec/adapters/oracledb/migrations.py +316 -25
- sqlspec/adapters/oracledb/type_converter.py +91 -16
- sqlspec/adapters/psqlpy/_type_handlers.py +44 -0
- sqlspec/adapters/psqlpy/_types.py +2 -1
- sqlspec/adapters/psqlpy/adk/__init__.py +5 -0
- sqlspec/adapters/psqlpy/adk/store.py +483 -0
- sqlspec/adapters/psqlpy/config.py +45 -19
- sqlspec/adapters/psqlpy/data_dictionary.py +48 -2
- sqlspec/adapters/psqlpy/driver.py +108 -41
- sqlspec/adapters/psqlpy/litestar/__init__.py +5 -0
- sqlspec/adapters/psqlpy/litestar/store.py +272 -0
- sqlspec/adapters/psqlpy/type_converter.py +40 -11
- sqlspec/adapters/psycopg/_type_handlers.py +80 -0
- sqlspec/adapters/psycopg/_types.py +2 -1
- sqlspec/adapters/psycopg/adk/__init__.py +5 -0
- sqlspec/adapters/psycopg/adk/store.py +962 -0
- sqlspec/adapters/psycopg/config.py +65 -37
- sqlspec/adapters/psycopg/data_dictionary.py +91 -3
- sqlspec/adapters/psycopg/driver.py +200 -78
- sqlspec/adapters/psycopg/litestar/__init__.py +5 -0
- sqlspec/adapters/psycopg/litestar/store.py +554 -0
- sqlspec/adapters/sqlite/__init__.py +2 -1
- sqlspec/adapters/sqlite/_type_handlers.py +86 -0
- sqlspec/adapters/sqlite/_types.py +1 -1
- sqlspec/adapters/sqlite/adk/__init__.py +5 -0
- sqlspec/adapters/sqlite/adk/store.py +582 -0
- sqlspec/adapters/sqlite/config.py +85 -16
- sqlspec/adapters/sqlite/data_dictionary.py +34 -2
- sqlspec/adapters/sqlite/driver.py +120 -52
- sqlspec/adapters/sqlite/litestar/__init__.py +5 -0
- sqlspec/adapters/sqlite/litestar/store.py +318 -0
- sqlspec/adapters/sqlite/pool.py +5 -5
- sqlspec/base.py +45 -26
- sqlspec/builder/__init__.py +73 -4
- sqlspec/builder/_base.py +91 -58
- sqlspec/builder/_column.py +5 -5
- sqlspec/builder/_ddl.py +98 -89
- sqlspec/builder/_delete.py +5 -4
- sqlspec/builder/_dml.py +388 -0
- sqlspec/{_sql.py → builder/_factory.py} +41 -44
- sqlspec/builder/_insert.py +5 -82
- sqlspec/builder/{mixins/_join_operations.py → _join.py} +145 -143
- sqlspec/builder/_merge.py +446 -11
- sqlspec/builder/_parsing_utils.py +9 -11
- sqlspec/builder/_select.py +1313 -25
- sqlspec/builder/_update.py +11 -42
- sqlspec/cli.py +76 -69
- sqlspec/config.py +331 -62
- sqlspec/core/__init__.py +5 -4
- sqlspec/core/cache.py +18 -18
- sqlspec/core/compiler.py +6 -8
- sqlspec/core/filters.py +55 -47
- sqlspec/core/hashing.py +9 -9
- sqlspec/core/parameters.py +76 -45
- sqlspec/core/result.py +234 -47
- sqlspec/core/splitter.py +16 -17
- sqlspec/core/statement.py +32 -31
- sqlspec/core/type_conversion.py +3 -2
- sqlspec/driver/__init__.py +1 -3
- sqlspec/driver/_async.py +183 -160
- sqlspec/driver/_common.py +197 -109
- sqlspec/driver/_sync.py +189 -161
- sqlspec/driver/mixins/_result_tools.py +20 -236
- sqlspec/driver/mixins/_sql_translator.py +4 -4
- sqlspec/exceptions.py +70 -7
- sqlspec/extensions/adk/__init__.py +53 -0
- sqlspec/extensions/adk/_types.py +51 -0
- sqlspec/extensions/adk/converters.py +172 -0
- sqlspec/extensions/adk/migrations/0001_create_adk_tables.py +144 -0
- sqlspec/extensions/adk/migrations/__init__.py +0 -0
- sqlspec/extensions/adk/service.py +181 -0
- sqlspec/extensions/adk/store.py +536 -0
- sqlspec/extensions/aiosql/adapter.py +69 -61
- sqlspec/extensions/fastapi/__init__.py +21 -0
- sqlspec/extensions/fastapi/extension.py +331 -0
- sqlspec/extensions/fastapi/providers.py +543 -0
- sqlspec/extensions/flask/__init__.py +36 -0
- sqlspec/extensions/flask/_state.py +71 -0
- sqlspec/extensions/flask/_utils.py +40 -0
- sqlspec/extensions/flask/extension.py +389 -0
- sqlspec/extensions/litestar/__init__.py +21 -4
- sqlspec/extensions/litestar/cli.py +54 -10
- sqlspec/extensions/litestar/config.py +56 -266
- sqlspec/extensions/litestar/handlers.py +46 -17
- sqlspec/extensions/litestar/migrations/0001_create_session_table.py +137 -0
- sqlspec/extensions/litestar/migrations/__init__.py +3 -0
- sqlspec/extensions/litestar/plugin.py +349 -224
- sqlspec/extensions/litestar/providers.py +25 -25
- sqlspec/extensions/litestar/store.py +265 -0
- sqlspec/extensions/starlette/__init__.py +10 -0
- sqlspec/extensions/starlette/_state.py +25 -0
- sqlspec/extensions/starlette/_utils.py +52 -0
- sqlspec/extensions/starlette/extension.py +254 -0
- sqlspec/extensions/starlette/middleware.py +154 -0
- sqlspec/loader.py +30 -49
- sqlspec/migrations/base.py +200 -76
- sqlspec/migrations/commands.py +591 -62
- sqlspec/migrations/context.py +6 -9
- sqlspec/migrations/fix.py +199 -0
- sqlspec/migrations/loaders.py +47 -19
- sqlspec/migrations/runner.py +241 -75
- sqlspec/migrations/tracker.py +237 -21
- sqlspec/migrations/utils.py +51 -3
- sqlspec/migrations/validation.py +177 -0
- sqlspec/protocols.py +106 -36
- sqlspec/storage/_utils.py +85 -0
- sqlspec/storage/backends/fsspec.py +133 -107
- sqlspec/storage/backends/local.py +78 -51
- sqlspec/storage/backends/obstore.py +276 -168
- sqlspec/storage/registry.py +75 -39
- sqlspec/typing.py +30 -84
- sqlspec/utils/__init__.py +25 -4
- sqlspec/utils/arrow_helpers.py +81 -0
- sqlspec/utils/config_resolver.py +6 -6
- sqlspec/utils/correlation.py +4 -5
- sqlspec/utils/data_transformation.py +3 -2
- sqlspec/utils/deprecation.py +9 -8
- sqlspec/utils/fixtures.py +4 -4
- sqlspec/utils/logging.py +46 -6
- sqlspec/utils/module_loader.py +205 -5
- sqlspec/utils/portal.py +311 -0
- sqlspec/utils/schema.py +288 -0
- sqlspec/utils/serializers.py +113 -4
- sqlspec/utils/sync_tools.py +36 -22
- sqlspec/utils/text.py +1 -2
- sqlspec/utils/type_guards.py +136 -20
- sqlspec/utils/version.py +433 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/METADATA +41 -22
- sqlspec-0.28.0.dist-info/RECORD +221 -0
- sqlspec/builder/mixins/__init__.py +0 -55
- sqlspec/builder/mixins/_cte_and_set_ops.py +0 -253
- sqlspec/builder/mixins/_delete_operations.py +0 -50
- sqlspec/builder/mixins/_insert_operations.py +0 -282
- sqlspec/builder/mixins/_merge_operations.py +0 -698
- sqlspec/builder/mixins/_order_limit_operations.py +0 -145
- sqlspec/builder/mixins/_pivot_operations.py +0 -157
- sqlspec/builder/mixins/_select_operations.py +0 -930
- sqlspec/builder/mixins/_update_operations.py +0 -199
- sqlspec/builder/mixins/_where_clause.py +0 -1298
- sqlspec-0.26.0.dist-info/RECORD +0 -157
- sqlspec-0.26.0.dist-info/licenses/NOTICE +0 -29
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/WHEEL +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/entry_points.txt +0 -0
- {sqlspec-0.26.0.dist-info → sqlspec-0.28.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"""SQLite sync session store for Litestar integration."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime, timedelta, timezone
|
|
4
|
+
from typing import TYPE_CHECKING
|
|
5
|
+
|
|
6
|
+
from sqlspec.extensions.litestar.store import BaseSQLSpecStore
|
|
7
|
+
from sqlspec.utils.logging import get_logger
|
|
8
|
+
from sqlspec.utils.sync_tools import async_
|
|
9
|
+
|
|
10
|
+
if TYPE_CHECKING:
|
|
11
|
+
from sqlspec.adapters.sqlite.config import SqliteConfig
|
|
12
|
+
|
|
13
|
+
logger = get_logger("adapters.sqlite.litestar.store")
|
|
14
|
+
|
|
15
|
+
SECONDS_PER_DAY = 86400.0
|
|
16
|
+
JULIAN_EPOCH = 2440587.5
|
|
17
|
+
|
|
18
|
+
__all__ = ("SQLiteStore",)
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class SQLiteStore(BaseSQLSpecStore["SqliteConfig"]):
|
|
22
|
+
"""SQLite session store using synchronous SQLite driver.
|
|
23
|
+
|
|
24
|
+
Implements server-side session storage for Litestar using SQLite
|
|
25
|
+
via the synchronous sqlite3 driver. Uses Litestar's sync_to_thread
|
|
26
|
+
utility to provide an async interface compatible with the Store protocol.
|
|
27
|
+
|
|
28
|
+
Provides efficient session management with:
|
|
29
|
+
- Sync operations wrapped for async compatibility
|
|
30
|
+
- INSERT OR REPLACE for UPSERT functionality
|
|
31
|
+
- Automatic expiration handling
|
|
32
|
+
- Efficient cleanup of expired sessions
|
|
33
|
+
|
|
34
|
+
Args:
|
|
35
|
+
config: SqliteConfig instance.
|
|
36
|
+
|
|
37
|
+
Example:
|
|
38
|
+
from sqlspec.adapters.sqlite import SqliteConfig
|
|
39
|
+
from sqlspec.adapters.sqlite.litestar.store import SQLiteStore
|
|
40
|
+
|
|
41
|
+
config = SqliteConfig(database=":memory:")
|
|
42
|
+
store = SQLiteStore(config)
|
|
43
|
+
await store.create_table()
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
__slots__ = ()
|
|
47
|
+
|
|
48
|
+
def __init__(self, config: "SqliteConfig") -> None:
|
|
49
|
+
"""Initialize SQLite session store.
|
|
50
|
+
|
|
51
|
+
Args:
|
|
52
|
+
config: SqliteConfig instance.
|
|
53
|
+
|
|
54
|
+
Notes:
|
|
55
|
+
Table name is read from config.extension_config["litestar"]["session_table"].
|
|
56
|
+
"""
|
|
57
|
+
super().__init__(config)
|
|
58
|
+
|
|
59
|
+
def _get_create_table_sql(self) -> str:
|
|
60
|
+
"""Get SQLite CREATE TABLE SQL.
|
|
61
|
+
|
|
62
|
+
Returns:
|
|
63
|
+
SQL statement to create the sessions table with proper indexes.
|
|
64
|
+
|
|
65
|
+
Notes:
|
|
66
|
+
- Uses REAL type for expires_at (stores Julian Day number)
|
|
67
|
+
- Julian Day enables direct comparison with julianday('now')
|
|
68
|
+
- Partial index WHERE expires_at IS NOT NULL reduces index size
|
|
69
|
+
- This approach ensures the index is actually used by query optimizer
|
|
70
|
+
"""
|
|
71
|
+
return f"""
|
|
72
|
+
CREATE TABLE IF NOT EXISTS {self._table_name} (
|
|
73
|
+
session_id TEXT PRIMARY KEY,
|
|
74
|
+
data BLOB NOT NULL,
|
|
75
|
+
expires_at REAL
|
|
76
|
+
);
|
|
77
|
+
CREATE INDEX IF NOT EXISTS idx_{self._table_name}_expires_at
|
|
78
|
+
ON {self._table_name}(expires_at) WHERE expires_at IS NOT NULL;
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
def _get_drop_table_sql(self) -> "list[str]":
|
|
82
|
+
"""Get SQLite DROP TABLE SQL statements.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
List of SQL statements to drop indexes and table.
|
|
86
|
+
"""
|
|
87
|
+
return [f"DROP INDEX IF EXISTS idx_{self._table_name}_expires_at", f"DROP TABLE IF EXISTS {self._table_name}"]
|
|
88
|
+
|
|
89
|
+
def _datetime_to_julian(self, dt: "datetime | None") -> "float | None":
|
|
90
|
+
"""Convert datetime to Julian Day number for SQLite storage.
|
|
91
|
+
|
|
92
|
+
Args:
|
|
93
|
+
dt: Datetime to convert (must be UTC-aware).
|
|
94
|
+
|
|
95
|
+
Returns:
|
|
96
|
+
Julian Day number as REAL, or None if dt is None.
|
|
97
|
+
|
|
98
|
+
Notes:
|
|
99
|
+
Julian Day number is days since November 24, 4714 BCE (proleptic Gregorian).
|
|
100
|
+
This enables direct comparison with julianday('now') in SQL queries.
|
|
101
|
+
"""
|
|
102
|
+
if dt is None:
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
epoch = datetime(1970, 1, 1, tzinfo=timezone.utc)
|
|
106
|
+
delta_days = (dt - epoch).total_seconds() / SECONDS_PER_DAY
|
|
107
|
+
return JULIAN_EPOCH + delta_days
|
|
108
|
+
|
|
109
|
+
def _julian_to_datetime(self, julian: "float | None") -> "datetime | None":
|
|
110
|
+
"""Convert Julian Day number back to datetime.
|
|
111
|
+
|
|
112
|
+
Args:
|
|
113
|
+
julian: Julian Day number.
|
|
114
|
+
|
|
115
|
+
Returns:
|
|
116
|
+
UTC-aware datetime, or None if julian is None.
|
|
117
|
+
"""
|
|
118
|
+
if julian is None:
|
|
119
|
+
return None
|
|
120
|
+
|
|
121
|
+
days_since_epoch = julian - JULIAN_EPOCH
|
|
122
|
+
timestamp = days_since_epoch * SECONDS_PER_DAY
|
|
123
|
+
return datetime.fromtimestamp(timestamp, tz=timezone.utc)
|
|
124
|
+
|
|
125
|
+
def _create_table(self) -> None:
|
|
126
|
+
"""Synchronous implementation of create_table."""
|
|
127
|
+
sql = self._get_create_table_sql()
|
|
128
|
+
with self._config.provide_session() as driver:
|
|
129
|
+
driver.execute_script(sql)
|
|
130
|
+
logger.debug("Created session table: %s", self._table_name)
|
|
131
|
+
|
|
132
|
+
async def create_table(self) -> None:
|
|
133
|
+
"""Create the session table if it doesn't exist."""
|
|
134
|
+
await async_(self._create_table)()
|
|
135
|
+
|
|
136
|
+
def _get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
|
|
137
|
+
"""Synchronous implementation of get."""
|
|
138
|
+
sql = f"""
|
|
139
|
+
SELECT data, expires_at FROM {self._table_name}
|
|
140
|
+
WHERE session_id = ?
|
|
141
|
+
AND (expires_at IS NULL OR julianday(expires_at) > julianday('now'))
|
|
142
|
+
"""
|
|
143
|
+
|
|
144
|
+
with self._config.provide_connection() as conn:
|
|
145
|
+
cursor = conn.execute(sql, (key,))
|
|
146
|
+
row = cursor.fetchone()
|
|
147
|
+
|
|
148
|
+
if row is None:
|
|
149
|
+
return None
|
|
150
|
+
|
|
151
|
+
data, expires_at_julian = row
|
|
152
|
+
|
|
153
|
+
if renew_for is not None and expires_at_julian is not None:
|
|
154
|
+
new_expires_at = self._calculate_expires_at(renew_for)
|
|
155
|
+
new_expires_at_julian = self._datetime_to_julian(new_expires_at)
|
|
156
|
+
if new_expires_at_julian is not None:
|
|
157
|
+
update_sql = f"""
|
|
158
|
+
UPDATE {self._table_name}
|
|
159
|
+
SET expires_at = ?
|
|
160
|
+
WHERE session_id = ?
|
|
161
|
+
"""
|
|
162
|
+
conn.execute(update_sql, (new_expires_at_julian, key))
|
|
163
|
+
conn.commit()
|
|
164
|
+
|
|
165
|
+
return bytes(data)
|
|
166
|
+
|
|
167
|
+
async def get(self, key: str, renew_for: "int | timedelta | None" = None) -> "bytes | None":
|
|
168
|
+
"""Get a session value by key.
|
|
169
|
+
|
|
170
|
+
Args:
|
|
171
|
+
key: Session ID to retrieve.
|
|
172
|
+
renew_for: If given, renew the expiry time for this duration.
|
|
173
|
+
|
|
174
|
+
Returns:
|
|
175
|
+
Session data as bytes if found and not expired, None otherwise.
|
|
176
|
+
"""
|
|
177
|
+
return await async_(self._get)(key, renew_for)
|
|
178
|
+
|
|
179
|
+
def _set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
|
|
180
|
+
"""Synchronous implementation of set.
|
|
181
|
+
|
|
182
|
+
Notes:
|
|
183
|
+
Stores expires_at as Julian Day number (REAL) for optimal index usage.
|
|
184
|
+
"""
|
|
185
|
+
data = self._value_to_bytes(value)
|
|
186
|
+
expires_at = self._calculate_expires_at(expires_in)
|
|
187
|
+
expires_at_julian = self._datetime_to_julian(expires_at)
|
|
188
|
+
|
|
189
|
+
sql = f"""
|
|
190
|
+
INSERT OR REPLACE INTO {self._table_name} (session_id, data, expires_at)
|
|
191
|
+
VALUES (?, ?, ?)
|
|
192
|
+
"""
|
|
193
|
+
|
|
194
|
+
with self._config.provide_connection() as conn:
|
|
195
|
+
conn.execute(sql, (key, data, expires_at_julian))
|
|
196
|
+
conn.commit()
|
|
197
|
+
|
|
198
|
+
async def set(self, key: str, value: "str | bytes", expires_in: "int | timedelta | None" = None) -> None:
|
|
199
|
+
"""Store a session value.
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
key: Session ID.
|
|
203
|
+
value: Session data.
|
|
204
|
+
expires_in: Time until expiration.
|
|
205
|
+
"""
|
|
206
|
+
await async_(self._set)(key, value, expires_in)
|
|
207
|
+
|
|
208
|
+
def _delete(self, key: str) -> None:
|
|
209
|
+
"""Synchronous implementation of delete."""
|
|
210
|
+
sql = f"DELETE FROM {self._table_name} WHERE session_id = ?"
|
|
211
|
+
|
|
212
|
+
with self._config.provide_connection() as conn:
|
|
213
|
+
conn.execute(sql, (key,))
|
|
214
|
+
conn.commit()
|
|
215
|
+
|
|
216
|
+
async def delete(self, key: str) -> None:
|
|
217
|
+
"""Delete a session by key.
|
|
218
|
+
|
|
219
|
+
Args:
|
|
220
|
+
key: Session ID to delete.
|
|
221
|
+
"""
|
|
222
|
+
await async_(self._delete)(key)
|
|
223
|
+
|
|
224
|
+
def _delete_all(self) -> None:
|
|
225
|
+
"""Synchronous implementation of delete_all."""
|
|
226
|
+
sql = f"DELETE FROM {self._table_name}"
|
|
227
|
+
|
|
228
|
+
with self._config.provide_connection() as conn:
|
|
229
|
+
conn.execute(sql)
|
|
230
|
+
conn.commit()
|
|
231
|
+
logger.debug("Deleted all sessions from table: %s", self._table_name)
|
|
232
|
+
|
|
233
|
+
async def delete_all(self) -> None:
|
|
234
|
+
"""Delete all sessions from the store."""
|
|
235
|
+
await async_(self._delete_all)()
|
|
236
|
+
|
|
237
|
+
def _exists(self, key: str) -> bool:
|
|
238
|
+
"""Synchronous implementation of exists."""
|
|
239
|
+
sql = f"""
|
|
240
|
+
SELECT 1 FROM {self._table_name}
|
|
241
|
+
WHERE session_id = ?
|
|
242
|
+
AND (expires_at IS NULL OR julianday(expires_at) > julianday('now'))
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
with self._config.provide_connection() as conn:
|
|
246
|
+
cursor = conn.execute(sql, (key,))
|
|
247
|
+
result = cursor.fetchone()
|
|
248
|
+
return result is not None
|
|
249
|
+
|
|
250
|
+
async def exists(self, key: str) -> bool:
|
|
251
|
+
"""Check if a session key exists and is not expired.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
key: Session ID to check.
|
|
255
|
+
|
|
256
|
+
Returns:
|
|
257
|
+
True if the session exists and is not expired.
|
|
258
|
+
"""
|
|
259
|
+
return await async_(self._exists)(key)
|
|
260
|
+
|
|
261
|
+
def _expires_in(self, key: str) -> "int | None":
|
|
262
|
+
"""Synchronous implementation of expires_in."""
|
|
263
|
+
sql = f"""
|
|
264
|
+
SELECT expires_at FROM {self._table_name}
|
|
265
|
+
WHERE session_id = ?
|
|
266
|
+
"""
|
|
267
|
+
|
|
268
|
+
with self._config.provide_connection() as conn:
|
|
269
|
+
cursor = conn.execute(sql, (key,))
|
|
270
|
+
row = cursor.fetchone()
|
|
271
|
+
|
|
272
|
+
if row is None or row[0] is None:
|
|
273
|
+
return None
|
|
274
|
+
|
|
275
|
+
expires_at_julian = row[0]
|
|
276
|
+
expires_at = self._julian_to_datetime(expires_at_julian)
|
|
277
|
+
|
|
278
|
+
if expires_at is None:
|
|
279
|
+
return None
|
|
280
|
+
|
|
281
|
+
now = datetime.now(timezone.utc)
|
|
282
|
+
|
|
283
|
+
if expires_at <= now:
|
|
284
|
+
return 0
|
|
285
|
+
|
|
286
|
+
delta = expires_at - now
|
|
287
|
+
return int(delta.total_seconds())
|
|
288
|
+
|
|
289
|
+
async def expires_in(self, key: str) -> "int | None":
|
|
290
|
+
"""Get the time in seconds until the session expires.
|
|
291
|
+
|
|
292
|
+
Args:
|
|
293
|
+
key: Session ID to check.
|
|
294
|
+
|
|
295
|
+
Returns:
|
|
296
|
+
Seconds until expiration, or None if no expiry or key doesn't exist.
|
|
297
|
+
"""
|
|
298
|
+
return await async_(self._expires_in)(key)
|
|
299
|
+
|
|
300
|
+
def _delete_expired(self) -> int:
|
|
301
|
+
"""Synchronous implementation of delete_expired."""
|
|
302
|
+
sql = f"DELETE FROM {self._table_name} WHERE julianday(expires_at) <= julianday('now')"
|
|
303
|
+
|
|
304
|
+
with self._config.provide_connection() as conn:
|
|
305
|
+
cursor = conn.execute(sql)
|
|
306
|
+
conn.commit()
|
|
307
|
+
count = cursor.rowcount
|
|
308
|
+
if count > 0:
|
|
309
|
+
logger.debug("Cleaned up %d expired sessions", count)
|
|
310
|
+
return count
|
|
311
|
+
|
|
312
|
+
async def delete_expired(self) -> int:
|
|
313
|
+
"""Delete all expired sessions.
|
|
314
|
+
|
|
315
|
+
Returns:
|
|
316
|
+
Number of sessions deleted.
|
|
317
|
+
"""
|
|
318
|
+
return await async_(self._delete_expired)()
|
sqlspec/adapters/sqlite/pool.py
CHANGED
|
@@ -4,7 +4,7 @@ import contextlib
|
|
|
4
4
|
import sqlite3
|
|
5
5
|
import threading
|
|
6
6
|
from contextlib import contextmanager
|
|
7
|
-
from typing import TYPE_CHECKING, Any,
|
|
7
|
+
from typing import TYPE_CHECKING, Any, TypedDict, cast
|
|
8
8
|
|
|
9
9
|
from typing_extensions import NotRequired
|
|
10
10
|
|
|
@@ -14,15 +14,15 @@ if TYPE_CHECKING:
|
|
|
14
14
|
from collections.abc import Generator
|
|
15
15
|
|
|
16
16
|
|
|
17
|
-
class SqliteConnectionParams(TypedDict
|
|
17
|
+
class SqliteConnectionParams(TypedDict):
|
|
18
18
|
"""SQLite connection parameters."""
|
|
19
19
|
|
|
20
20
|
database: NotRequired[str]
|
|
21
21
|
timeout: NotRequired[float]
|
|
22
22
|
detect_types: NotRequired[int]
|
|
23
|
-
isolation_level: "NotRequired[
|
|
23
|
+
isolation_level: "NotRequired[str | None]"
|
|
24
24
|
check_same_thread: NotRequired[bool]
|
|
25
|
-
factory: "NotRequired[
|
|
25
|
+
factory: "NotRequired[type[SqliteConnection] | None]"
|
|
26
26
|
cached_statements: NotRequired[int]
|
|
27
27
|
uri: NotRequired[bool]
|
|
28
28
|
|
|
@@ -62,7 +62,7 @@ class SqliteConnectionPool:
|
|
|
62
62
|
|
|
63
63
|
if self._enable_optimizations:
|
|
64
64
|
database = self._connection_parameters.get("database", ":memory:")
|
|
65
|
-
is_memory = database == ":memory:" or
|
|
65
|
+
is_memory = database == ":memory:" or "mode=memory" in database
|
|
66
66
|
|
|
67
67
|
if not is_memory:
|
|
68
68
|
connection.execute("PRAGMA journal_mode = DELETE")
|
sqlspec/base.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import atexit
|
|
3
3
|
from collections.abc import Awaitable, Coroutine
|
|
4
|
-
from typing import TYPE_CHECKING, Any,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Union, cast, overload
|
|
5
5
|
|
|
6
6
|
from sqlspec.config import (
|
|
7
7
|
AsyncConfigT,
|
|
@@ -42,11 +42,11 @@ class SQLSpec:
|
|
|
42
42
|
|
|
43
43
|
__slots__ = ("_configs", "_instance_cache_config", "_sql_loader")
|
|
44
44
|
|
|
45
|
-
def __init__(self, *, loader: "
|
|
45
|
+
def __init__(self, *, loader: "SQLFileLoader | None" = None) -> None:
|
|
46
46
|
self._configs: dict[Any, DatabaseConfigProtocol[Any, Any, Any]] = {}
|
|
47
47
|
atexit.register(self._cleanup_sync_pools)
|
|
48
|
-
self._instance_cache_config:
|
|
49
|
-
self._sql_loader:
|
|
48
|
+
self._instance_cache_config: CacheConfig | None = None
|
|
49
|
+
self._sql_loader: SQLFileLoader | None = loader
|
|
50
50
|
|
|
51
51
|
@staticmethod
|
|
52
52
|
def _get_config_name(obj: Any) -> str:
|
|
@@ -117,7 +117,7 @@ class SQLSpec:
|
|
|
117
117
|
def add_config(self, config: "AsyncConfigT") -> "type[AsyncConfigT]": # pyright: ignore[reportInvalidTypeVarUse]
|
|
118
118
|
...
|
|
119
119
|
|
|
120
|
-
def add_config(self, config: "
|
|
120
|
+
def add_config(self, config: "SyncConfigT | AsyncConfigT") -> "type[SyncConfigT | AsyncConfigT]": # pyright: ignore[reportInvalidTypeVarUse]
|
|
121
121
|
"""Add a configuration instance to the registry.
|
|
122
122
|
|
|
123
123
|
Args:
|
|
@@ -139,7 +139,7 @@ class SQLSpec:
|
|
|
139
139
|
def get_config(self, name: "type[AsyncConfigT]") -> "AsyncConfigT": ...
|
|
140
140
|
|
|
141
141
|
def get_config(
|
|
142
|
-
self, name: "
|
|
142
|
+
self, name: "type[DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]] | Any"
|
|
143
143
|
) -> "DatabaseConfigProtocol[ConnectionT, PoolT, DriverT]":
|
|
144
144
|
"""Retrieve a configuration instance by its type or a key.
|
|
145
145
|
|
|
@@ -161,6 +161,15 @@ class SQLSpec:
|
|
|
161
161
|
logger.debug("Retrieved configuration: %s", self._get_config_name(name))
|
|
162
162
|
return config
|
|
163
163
|
|
|
164
|
+
@property
|
|
165
|
+
def configs(self) -> "dict[type, DatabaseConfigProtocol[Any, Any, Any]]":
|
|
166
|
+
"""Access the registry of database configurations.
|
|
167
|
+
|
|
168
|
+
Returns:
|
|
169
|
+
Dictionary mapping config types to config instances.
|
|
170
|
+
"""
|
|
171
|
+
return self._configs
|
|
172
|
+
|
|
164
173
|
@overload
|
|
165
174
|
def get_connection(
|
|
166
175
|
self,
|
|
@@ -195,7 +204,7 @@ class SQLSpec:
|
|
|
195
204
|
"NoPoolAsyncConfig[ConnectionT, DriverT]",
|
|
196
205
|
"AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
197
206
|
],
|
|
198
|
-
) -> "
|
|
207
|
+
) -> "ConnectionT | Awaitable[ConnectionT]":
|
|
199
208
|
"""Get a database connection for the specified configuration.
|
|
200
209
|
|
|
201
210
|
Args:
|
|
@@ -248,7 +257,7 @@ class SQLSpec:
|
|
|
248
257
|
"SyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
249
258
|
"AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
250
259
|
],
|
|
251
|
-
) -> "
|
|
260
|
+
) -> "DriverT | Awaitable[DriverT]":
|
|
252
261
|
"""Get a database session (driver adapter) for the specified configuration.
|
|
253
262
|
|
|
254
263
|
Args:
|
|
@@ -273,13 +282,23 @@ class SQLSpec:
|
|
|
273
282
|
async def _create_driver_async() -> "DriverT":
|
|
274
283
|
resolved_connection = await connection_obj # pyright: ignore
|
|
275
284
|
return cast( # pyright: ignore
|
|
276
|
-
"DriverT",
|
|
285
|
+
"DriverT",
|
|
286
|
+
config.driver_type(
|
|
287
|
+
connection=resolved_connection,
|
|
288
|
+
statement_config=config.statement_config,
|
|
289
|
+
driver_features=config.driver_features,
|
|
290
|
+
),
|
|
277
291
|
)
|
|
278
292
|
|
|
279
293
|
return _create_driver_async()
|
|
280
294
|
|
|
281
295
|
return cast( # pyright: ignore
|
|
282
|
-
"DriverT",
|
|
296
|
+
"DriverT",
|
|
297
|
+
config.driver_type(
|
|
298
|
+
connection=connection_obj,
|
|
299
|
+
statement_config=config.statement_config,
|
|
300
|
+
driver_features=config.driver_features,
|
|
301
|
+
),
|
|
283
302
|
)
|
|
284
303
|
|
|
285
304
|
@overload
|
|
@@ -322,7 +341,7 @@ class SQLSpec:
|
|
|
322
341
|
],
|
|
323
342
|
*args: Any,
|
|
324
343
|
**kwargs: Any,
|
|
325
|
-
) -> "
|
|
344
|
+
) -> "AbstractContextManager[ConnectionT] | AbstractAsyncContextManager[ConnectionT]":
|
|
326
345
|
"""Create and provide a database connection from the specified configuration.
|
|
327
346
|
|
|
328
347
|
Args:
|
|
@@ -383,7 +402,7 @@ class SQLSpec:
|
|
|
383
402
|
],
|
|
384
403
|
*args: Any,
|
|
385
404
|
**kwargs: Any,
|
|
386
|
-
) -> "
|
|
405
|
+
) -> "AbstractContextManager[DriverT] | AbstractAsyncContextManager[DriverT]":
|
|
387
406
|
"""Create and provide a database session from the specified configuration.
|
|
388
407
|
|
|
389
408
|
Args:
|
|
@@ -407,17 +426,17 @@ class SQLSpec:
|
|
|
407
426
|
@overload
|
|
408
427
|
def get_pool(
|
|
409
428
|
self,
|
|
410
|
-
name: "
|
|
429
|
+
name: "type[NoPoolSyncConfig[ConnectionT, DriverT] | NoPoolAsyncConfig[ConnectionT, DriverT]] | NoPoolSyncConfig[ConnectionT, DriverT] | NoPoolAsyncConfig[ConnectionT, DriverT]",
|
|
411
430
|
) -> "None": ...
|
|
412
431
|
@overload
|
|
413
432
|
def get_pool(
|
|
414
433
|
self,
|
|
415
|
-
name: "
|
|
434
|
+
name: "type[SyncDatabaseConfig[ConnectionT, PoolT, DriverT]] | SyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
416
435
|
) -> "type[PoolT]": ...
|
|
417
436
|
@overload
|
|
418
437
|
def get_pool(
|
|
419
438
|
self,
|
|
420
|
-
name: "
|
|
439
|
+
name: "type[AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]] | AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
421
440
|
) -> "Awaitable[type[PoolT]]": ...
|
|
422
441
|
|
|
423
442
|
def get_pool(
|
|
@@ -432,7 +451,7 @@ class SQLSpec:
|
|
|
432
451
|
"SyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
433
452
|
"AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
434
453
|
],
|
|
435
|
-
) -> "
|
|
454
|
+
) -> "type[PoolT] | Awaitable[type[PoolT]] | None":
|
|
436
455
|
"""Get the connection pool for the specified configuration.
|
|
437
456
|
|
|
438
457
|
Args:
|
|
@@ -450,7 +469,7 @@ class SQLSpec:
|
|
|
450
469
|
|
|
451
470
|
if config.supports_connection_pooling:
|
|
452
471
|
logger.debug("Getting pool for config: %s", config_name, extra={"config_type": config_name})
|
|
453
|
-
return cast("
|
|
472
|
+
return cast("type[PoolT] | Awaitable[type[PoolT]]", config.create_pool())
|
|
454
473
|
|
|
455
474
|
logger.debug("Config %s does not support connection pooling", config_name)
|
|
456
475
|
return None
|
|
@@ -489,7 +508,7 @@ class SQLSpec:
|
|
|
489
508
|
"NoPoolAsyncConfig[ConnectionT, DriverT]",
|
|
490
509
|
"AsyncDatabaseConfig[ConnectionT, PoolT, DriverT]",
|
|
491
510
|
],
|
|
492
|
-
) -> "
|
|
511
|
+
) -> "Awaitable[None] | None":
|
|
493
512
|
"""Close the connection pool for the specified configuration.
|
|
494
513
|
|
|
495
514
|
Args:
|
|
@@ -552,12 +571,12 @@ class SQLSpec:
|
|
|
552
571
|
@staticmethod
|
|
553
572
|
def configure_cache(
|
|
554
573
|
*,
|
|
555
|
-
sql_cache_size:
|
|
556
|
-
fragment_cache_size:
|
|
557
|
-
optimized_cache_size:
|
|
558
|
-
sql_cache_enabled:
|
|
559
|
-
fragment_cache_enabled:
|
|
560
|
-
optimized_cache_enabled:
|
|
574
|
+
sql_cache_size: int | None = None,
|
|
575
|
+
fragment_cache_size: int | None = None,
|
|
576
|
+
optimized_cache_size: int | None = None,
|
|
577
|
+
sql_cache_enabled: bool | None = None,
|
|
578
|
+
fragment_cache_enabled: bool | None = None,
|
|
579
|
+
optimized_cache_enabled: bool | None = None,
|
|
561
580
|
) -> None:
|
|
562
581
|
"""Update cache configuration with partial values.
|
|
563
582
|
|
|
@@ -591,7 +610,7 @@ class SQLSpec:
|
|
|
591
610
|
)
|
|
592
611
|
)
|
|
593
612
|
|
|
594
|
-
def load_sql_files(self, *paths: "
|
|
613
|
+
def load_sql_files(self, *paths: "str | Path") -> None:
|
|
595
614
|
"""Load SQL files from paths or directories.
|
|
596
615
|
|
|
597
616
|
Args:
|
|
@@ -605,7 +624,7 @@ class SQLSpec:
|
|
|
605
624
|
self._sql_loader.load_sql(*paths)
|
|
606
625
|
logger.debug("Loaded SQL files: %s", paths)
|
|
607
626
|
|
|
608
|
-
def add_named_sql(self, name: str, sql: str, dialect: "
|
|
627
|
+
def add_named_sql(self, name: str, sql: str, dialect: "str | None" = None) -> None:
|
|
609
628
|
"""Add a named SQL query directly.
|
|
610
629
|
|
|
611
630
|
Args:
|
sqlspec/builder/__init__.py
CHANGED
|
@@ -24,21 +24,64 @@ from sqlspec.builder._ddl import (
|
|
|
24
24
|
Truncate,
|
|
25
25
|
)
|
|
26
26
|
from sqlspec.builder._delete import Delete
|
|
27
|
+
from sqlspec.builder._dml import (
|
|
28
|
+
DeleteFromClauseMixin,
|
|
29
|
+
InsertFromSelectMixin,
|
|
30
|
+
InsertIntoClauseMixin,
|
|
31
|
+
InsertValuesMixin,
|
|
32
|
+
UpdateFromClauseMixin,
|
|
33
|
+
UpdateSetClauseMixin,
|
|
34
|
+
UpdateTableClauseMixin,
|
|
35
|
+
)
|
|
36
|
+
from sqlspec.builder._expression_wrappers import (
|
|
37
|
+
AggregateExpression,
|
|
38
|
+
ConversionExpression,
|
|
39
|
+
FunctionExpression,
|
|
40
|
+
MathExpression,
|
|
41
|
+
StringExpression,
|
|
42
|
+
)
|
|
43
|
+
from sqlspec.builder._factory import SQLFactory, sql
|
|
27
44
|
from sqlspec.builder._insert import Insert
|
|
45
|
+
from sqlspec.builder._join import JoinBuilder
|
|
28
46
|
from sqlspec.builder._merge import Merge
|
|
29
|
-
from sqlspec.builder.
|
|
47
|
+
from sqlspec.builder._parsing_utils import (
|
|
48
|
+
extract_expression,
|
|
49
|
+
parse_column_expression,
|
|
50
|
+
parse_condition_expression,
|
|
51
|
+
parse_order_expression,
|
|
52
|
+
parse_table_expression,
|
|
53
|
+
to_expression,
|
|
54
|
+
)
|
|
55
|
+
from sqlspec.builder._select import (
|
|
56
|
+
Case,
|
|
57
|
+
CaseBuilder,
|
|
58
|
+
CommonTableExpressionMixin,
|
|
59
|
+
HavingClauseMixin,
|
|
60
|
+
LimitOffsetClauseMixin,
|
|
61
|
+
OrderByClauseMixin,
|
|
62
|
+
PivotClauseMixin,
|
|
63
|
+
ReturningClauseMixin,
|
|
64
|
+
Select,
|
|
65
|
+
SelectClauseMixin,
|
|
66
|
+
SetOperationMixin,
|
|
67
|
+
SubqueryBuilder,
|
|
68
|
+
UnpivotClauseMixin,
|
|
69
|
+
WhereClauseMixin,
|
|
70
|
+
WindowFunctionBuilder,
|
|
71
|
+
)
|
|
30
72
|
from sqlspec.builder._update import Update
|
|
31
|
-
from sqlspec.builder.mixins import WhereClauseMixin
|
|
32
|
-
from sqlspec.builder.mixins._join_operations import JoinBuilder
|
|
33
|
-
from sqlspec.builder.mixins._select_operations import Case, SubqueryBuilder, WindowFunctionBuilder
|
|
34
73
|
from sqlspec.exceptions import SQLBuilderError
|
|
35
74
|
|
|
36
75
|
__all__ = (
|
|
76
|
+
"AggregateExpression",
|
|
37
77
|
"AlterTable",
|
|
38
78
|
"Case",
|
|
79
|
+
"CaseBuilder",
|
|
39
80
|
"Column",
|
|
40
81
|
"ColumnExpression",
|
|
41
82
|
"CommentOn",
|
|
83
|
+
"CommonTableExpressionMixin",
|
|
84
|
+
"ConversionExpression",
|
|
42
85
|
"CreateIndex",
|
|
43
86
|
"CreateMaterializedView",
|
|
44
87
|
"CreateSchema",
|
|
@@ -47,22 +90,48 @@ __all__ = (
|
|
|
47
90
|
"CreateView",
|
|
48
91
|
"DDLBuilder",
|
|
49
92
|
"Delete",
|
|
93
|
+
"DeleteFromClauseMixin",
|
|
50
94
|
"DropIndex",
|
|
51
95
|
"DropSchema",
|
|
52
96
|
"DropTable",
|
|
53
97
|
"DropView",
|
|
54
98
|
"FunctionColumn",
|
|
99
|
+
"FunctionExpression",
|
|
100
|
+
"HavingClauseMixin",
|
|
55
101
|
"Insert",
|
|
102
|
+
"InsertFromSelectMixin",
|
|
103
|
+
"InsertIntoClauseMixin",
|
|
104
|
+
"InsertValuesMixin",
|
|
56
105
|
"JoinBuilder",
|
|
106
|
+
"LimitOffsetClauseMixin",
|
|
107
|
+
"MathExpression",
|
|
57
108
|
"Merge",
|
|
109
|
+
"OrderByClauseMixin",
|
|
110
|
+
"PivotClauseMixin",
|
|
58
111
|
"QueryBuilder",
|
|
59
112
|
"RenameTable",
|
|
113
|
+
"ReturningClauseMixin",
|
|
60
114
|
"SQLBuilderError",
|
|
115
|
+
"SQLFactory",
|
|
61
116
|
"SafeQuery",
|
|
62
117
|
"Select",
|
|
118
|
+
"SelectClauseMixin",
|
|
119
|
+
"SetOperationMixin",
|
|
120
|
+
"StringExpression",
|
|
63
121
|
"SubqueryBuilder",
|
|
64
122
|
"Truncate",
|
|
123
|
+
"UnpivotClauseMixin",
|
|
65
124
|
"Update",
|
|
125
|
+
"UpdateFromClauseMixin",
|
|
126
|
+
"UpdateSetClauseMixin",
|
|
127
|
+
"UpdateTableClauseMixin",
|
|
66
128
|
"WhereClauseMixin",
|
|
67
129
|
"WindowFunctionBuilder",
|
|
130
|
+
"extract_expression",
|
|
131
|
+
"parse_column_expression",
|
|
132
|
+
"parse_condition_expression",
|
|
133
|
+
"parse_order_expression",
|
|
134
|
+
"parse_table_expression",
|
|
135
|
+
"sql",
|
|
136
|
+
"to_expression",
|
|
68
137
|
)
|