altertable-lakehouse 0.2.0__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.
- altertable_lakehouse-0.2.0/LICENSE +21 -0
- altertable_lakehouse-0.2.0/PKG-INFO +99 -0
- altertable_lakehouse-0.2.0/README.md +78 -0
- altertable_lakehouse-0.2.0/pyproject.toml +23 -0
- altertable_lakehouse-0.2.0/src/altertable_lakehouse/__init__.py +5 -0
- altertable_lakehouse-0.2.0/src/altertable_lakehouse/client.py +186 -0
- altertable_lakehouse-0.2.0/src/altertable_lakehouse/errors.py +35 -0
- altertable_lakehouse-0.2.0/src/altertable_lakehouse/models.py +78 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) Altertable
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: altertable-lakehouse
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Official Python SDK for Altertable Lakehouse API
|
|
5
|
+
License-File: LICENSE
|
|
6
|
+
Author: Altertable
|
|
7
|
+
Author-email: hello@altertable.ai
|
|
8
|
+
Requires-Python: >=3.9,<4.0
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
11
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Dist: dataclasses-json (>=0.6.7,<0.7.0)
|
|
17
|
+
Requires-Dist: httpx (>=0.28.1,<0.29.0)
|
|
18
|
+
Requires-Dist: pydantic (>=2.12.5,<3.0.0)
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
|
|
21
|
+
# Altertable Lakehouse Python SDK
|
|
22
|
+
|
|
23
|
+
Official Python SDK for the Altertable Lakehouse API.
|
|
24
|
+
|
|
25
|
+
## Installation
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
pip install altertable-lakehouse
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## Usage
|
|
32
|
+
|
|
33
|
+
### Initialization
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from altertable_lakehouse import Client
|
|
37
|
+
|
|
38
|
+
client = Client(username="your_username", password="your_password")
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
### Querying
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
from altertable_lakehouse.models import QueryRequest
|
|
45
|
+
|
|
46
|
+
# Stream rows (good for large datasets)
|
|
47
|
+
req = QueryRequest(statement="SELECT * FROM my_table")
|
|
48
|
+
metadata, row_iterator = client.query(req)
|
|
49
|
+
for row in row_iterator:
|
|
50
|
+
print(row)
|
|
51
|
+
|
|
52
|
+
# Accumulate all rows in memory
|
|
53
|
+
result = client.query_all(req)
|
|
54
|
+
print(result.rows)
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Append
|
|
58
|
+
|
|
59
|
+
```python
|
|
60
|
+
res = client.append(catalog="my_cat", schema="my_schema", table="my_table", data={"col1": "val1"})
|
|
61
|
+
print(res.ok)
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### Upload
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from altertable_lakehouse.models import UploadFormat, UploadMode
|
|
68
|
+
|
|
69
|
+
with open("data.csv", "rb") as f:
|
|
70
|
+
client.upload(
|
|
71
|
+
catalog="my_cat",
|
|
72
|
+
schema="my_schema",
|
|
73
|
+
table="my_table",
|
|
74
|
+
format=UploadFormat.CSV,
|
|
75
|
+
mode=UploadMode.APPEND,
|
|
76
|
+
content=f.read()
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
### Validate Query
|
|
81
|
+
|
|
82
|
+
```python
|
|
83
|
+
res = client.validate("SELECT * FROM non_existent")
|
|
84
|
+
print(res.valid)
|
|
85
|
+
print(res.connections_errors)
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### Query Log & Cancellation
|
|
89
|
+
|
|
90
|
+
```python
|
|
91
|
+
# Get query status
|
|
92
|
+
log_res = client.get_query("query_uuid_here")
|
|
93
|
+
print(log_res.progress)
|
|
94
|
+
|
|
95
|
+
# Cancel a query
|
|
96
|
+
cancel_res = client.cancel_query("query_uuid_here", "session_id_here")
|
|
97
|
+
print(cancel_res.cancelled)
|
|
98
|
+
```
|
|
99
|
+
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
# Altertable Lakehouse Python SDK
|
|
2
|
+
|
|
3
|
+
Official Python SDK for the Altertable Lakehouse API.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pip install altertable-lakehouse
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Usage
|
|
12
|
+
|
|
13
|
+
### Initialization
|
|
14
|
+
|
|
15
|
+
```python
|
|
16
|
+
from altertable_lakehouse import Client
|
|
17
|
+
|
|
18
|
+
client = Client(username="your_username", password="your_password")
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
### Querying
|
|
22
|
+
|
|
23
|
+
```python
|
|
24
|
+
from altertable_lakehouse.models import QueryRequest
|
|
25
|
+
|
|
26
|
+
# Stream rows (good for large datasets)
|
|
27
|
+
req = QueryRequest(statement="SELECT * FROM my_table")
|
|
28
|
+
metadata, row_iterator = client.query(req)
|
|
29
|
+
for row in row_iterator:
|
|
30
|
+
print(row)
|
|
31
|
+
|
|
32
|
+
# Accumulate all rows in memory
|
|
33
|
+
result = client.query_all(req)
|
|
34
|
+
print(result.rows)
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Append
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
res = client.append(catalog="my_cat", schema="my_schema", table="my_table", data={"col1": "val1"})
|
|
41
|
+
print(res.ok)
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### Upload
|
|
45
|
+
|
|
46
|
+
```python
|
|
47
|
+
from altertable_lakehouse.models import UploadFormat, UploadMode
|
|
48
|
+
|
|
49
|
+
with open("data.csv", "rb") as f:
|
|
50
|
+
client.upload(
|
|
51
|
+
catalog="my_cat",
|
|
52
|
+
schema="my_schema",
|
|
53
|
+
table="my_table",
|
|
54
|
+
format=UploadFormat.CSV,
|
|
55
|
+
mode=UploadMode.APPEND,
|
|
56
|
+
content=f.read()
|
|
57
|
+
)
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
### Validate Query
|
|
61
|
+
|
|
62
|
+
```python
|
|
63
|
+
res = client.validate("SELECT * FROM non_existent")
|
|
64
|
+
print(res.valid)
|
|
65
|
+
print(res.connections_errors)
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
### Query Log & Cancellation
|
|
69
|
+
|
|
70
|
+
```python
|
|
71
|
+
# Get query status
|
|
72
|
+
log_res = client.get_query("query_uuid_here")
|
|
73
|
+
print(log_res.progress)
|
|
74
|
+
|
|
75
|
+
# Cancel a query
|
|
76
|
+
cancel_res = client.cancel_query("query_uuid_here", "session_id_here")
|
|
77
|
+
print(cancel_res.cancelled)
|
|
78
|
+
```
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
[tool.poetry]
|
|
2
|
+
name = "altertable-lakehouse"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Official Python SDK for Altertable Lakehouse API"
|
|
5
|
+
authors = ["Altertable <hello@altertable.ai>"]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
packages = [{include = "altertable_lakehouse", from = "src"}]
|
|
8
|
+
|
|
9
|
+
[tool.poetry.dependencies]
|
|
10
|
+
python = "^3.9"
|
|
11
|
+
httpx = "^0.28.1"
|
|
12
|
+
dataclasses-json = "^0.6.7"
|
|
13
|
+
pydantic = "^2.12.5"
|
|
14
|
+
|
|
15
|
+
[tool.poetry.group.dev.dependencies]
|
|
16
|
+
pytest = "^8.0.0"
|
|
17
|
+
ruff = "^0.3.0"
|
|
18
|
+
mypy = "^1.8.0"
|
|
19
|
+
testcontainers = "<4.0"
|
|
20
|
+
|
|
21
|
+
[build-system]
|
|
22
|
+
requires = ["poetry-core"]
|
|
23
|
+
build-backend = "poetry.core.masonry.api"
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import json
|
|
3
|
+
import base64
|
|
4
|
+
import httpx
|
|
5
|
+
from typing import Any, Dict, Iterator, Optional, Union, Tuple, NoReturn
|
|
6
|
+
from .models import (
|
|
7
|
+
AppendRequestSingle, AppendRequestBatch, AppendResponse,
|
|
8
|
+
QueryRequest, QueryLogResponse, CancelQueryResponse,
|
|
9
|
+
ValidateResponse, UploadFormat, UploadMode,
|
|
10
|
+
QueryMetadata, QueryResult
|
|
11
|
+
)
|
|
12
|
+
from .errors import (
|
|
13
|
+
AuthError, BadRequestError, NetworkError, TimeoutError,
|
|
14
|
+
ParseError, ApiError, ConfigurationError,
|
|
15
|
+
AltertableLakehouseError
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
class Client:
|
|
19
|
+
def __init__(
|
|
20
|
+
self,
|
|
21
|
+
base_url: str = "https://api.altertable.ai",
|
|
22
|
+
username: Optional[str] = None,
|
|
23
|
+
password: Optional[str] = None,
|
|
24
|
+
token: Optional[str] = None,
|
|
25
|
+
timeout: float = 30.0,
|
|
26
|
+
user_agent_suffix: Optional[str] = None
|
|
27
|
+
):
|
|
28
|
+
self.base_url = base_url.rstrip("/")
|
|
29
|
+
self.timeout = timeout
|
|
30
|
+
|
|
31
|
+
auth_token = None
|
|
32
|
+
if token:
|
|
33
|
+
auth_token = token
|
|
34
|
+
elif username and password:
|
|
35
|
+
auth_token = base64.b64encode(f"{username}:{password}".encode()).decode()
|
|
36
|
+
elif "ALTERTABLE_BASIC_AUTH_TOKEN" in os.environ:
|
|
37
|
+
auth_token = os.environ["ALTERTABLE_BASIC_AUTH_TOKEN"]
|
|
38
|
+
elif "ALTERTABLE_USERNAME" in os.environ and "ALTERTABLE_PASSWORD" in os.environ:
|
|
39
|
+
u = os.environ["ALTERTABLE_USERNAME"]
|
|
40
|
+
p = os.environ["ALTERTABLE_PASSWORD"]
|
|
41
|
+
auth_token = base64.b64encode(f"{u}:{p}".encode()).decode()
|
|
42
|
+
|
|
43
|
+
if not auth_token:
|
|
44
|
+
raise ConfigurationError("No credentials provided.")
|
|
45
|
+
|
|
46
|
+
ua = "altertable-lakehouse-python/0.1.0"
|
|
47
|
+
if user_agent_suffix:
|
|
48
|
+
ua += f" {user_agent_suffix}"
|
|
49
|
+
|
|
50
|
+
self._client = httpx.Client(
|
|
51
|
+
base_url=self.base_url,
|
|
52
|
+
timeout=self.timeout,
|
|
53
|
+
headers={
|
|
54
|
+
"Authorization": f"Basic {auth_token}",
|
|
55
|
+
"User-Agent": ua
|
|
56
|
+
}
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
def _handle_error(self, e: Exception) -> NoReturn:
|
|
60
|
+
if isinstance(e, httpx.TimeoutException):
|
|
61
|
+
raise TimeoutError("Request timed out", e)
|
|
62
|
+
if isinstance(e, httpx.RequestError):
|
|
63
|
+
raise NetworkError(f"Network error: {str(e)}", e)
|
|
64
|
+
raise AltertableLakehouseError(f"Unexpected error: {str(e)}", e)
|
|
65
|
+
|
|
66
|
+
def _check_response(self, response: httpx.Response) -> None:
|
|
67
|
+
if response.is_success:
|
|
68
|
+
return
|
|
69
|
+
if response.status_code == 401:
|
|
70
|
+
raise AuthError("Unauthorized", response.status_code)
|
|
71
|
+
if response.status_code == 400:
|
|
72
|
+
raise BadRequestError(response.text, response.status_code)
|
|
73
|
+
raise ApiError(response.text, response.status_code)
|
|
74
|
+
|
|
75
|
+
def append(self, catalog: str, schema: str, table: str, data: Union[AppendRequestSingle, AppendRequestBatch]) -> AppendResponse:
|
|
76
|
+
try:
|
|
77
|
+
payload = data.model_dump() if hasattr(data, "model_dump") else data
|
|
78
|
+
res = self._client.post(
|
|
79
|
+
"/append",
|
|
80
|
+
params={"catalog": catalog, "schema": schema, "table": table},
|
|
81
|
+
json=payload
|
|
82
|
+
)
|
|
83
|
+
self._check_response(res)
|
|
84
|
+
return AppendResponse(**res.json())
|
|
85
|
+
except httpx.RequestError as e:
|
|
86
|
+
self._handle_error(e)
|
|
87
|
+
|
|
88
|
+
def upload(self, catalog: str, schema: str, table: str, format: UploadFormat, mode: UploadMode, content: bytes, primary_key: Optional[str] = None) -> None:
|
|
89
|
+
params = {
|
|
90
|
+
"catalog": catalog,
|
|
91
|
+
"schema": schema,
|
|
92
|
+
"table": table,
|
|
93
|
+
"format": format.value,
|
|
94
|
+
"mode": mode.value
|
|
95
|
+
}
|
|
96
|
+
if primary_key:
|
|
97
|
+
params["primary_key"] = primary_key
|
|
98
|
+
try:
|
|
99
|
+
res = self._client.post(
|
|
100
|
+
"/upload",
|
|
101
|
+
params=params,
|
|
102
|
+
content=content,
|
|
103
|
+
headers={"Content-Type": "application/octet-stream"}
|
|
104
|
+
)
|
|
105
|
+
self._check_response(res)
|
|
106
|
+
except httpx.RequestError as e:
|
|
107
|
+
self._handle_error(e)
|
|
108
|
+
|
|
109
|
+
def get_query(self, query_id: str) -> QueryLogResponse:
|
|
110
|
+
try:
|
|
111
|
+
res = self._client.get(f"/query/{query_id}")
|
|
112
|
+
self._check_response(res)
|
|
113
|
+
return QueryLogResponse(**res.json())
|
|
114
|
+
except httpx.RequestError as e:
|
|
115
|
+
self._handle_error(e)
|
|
116
|
+
|
|
117
|
+
def cancel_query(self, query_id: str, session_id: str) -> CancelQueryResponse:
|
|
118
|
+
try:
|
|
119
|
+
res = self._client.delete(f"/query/{query_id}", params={"session_id": session_id})
|
|
120
|
+
self._check_response(res)
|
|
121
|
+
return CancelQueryResponse(**res.json())
|
|
122
|
+
except httpx.RequestError as e:
|
|
123
|
+
self._handle_error(e)
|
|
124
|
+
|
|
125
|
+
def validate(self, statement: str) -> ValidateResponse:
|
|
126
|
+
try:
|
|
127
|
+
res = self._client.post("/validate", json={"statement": statement})
|
|
128
|
+
self._check_response(res)
|
|
129
|
+
return ValidateResponse(**res.json())
|
|
130
|
+
except httpx.RequestError as e:
|
|
131
|
+
self._handle_error(e)
|
|
132
|
+
|
|
133
|
+
def query(self, request: QueryRequest) -> Tuple[QueryMetadata, Iterator[Dict[str, Any]]]:
|
|
134
|
+
payload = request.model_dump(exclude_none=True)
|
|
135
|
+
try:
|
|
136
|
+
req = self._client.build_request(
|
|
137
|
+
"POST",
|
|
138
|
+
"/query",
|
|
139
|
+
json=payload,
|
|
140
|
+
headers={"Accept": "application/x-ndjson"}
|
|
141
|
+
)
|
|
142
|
+
res = self._client.send(req, stream=True)
|
|
143
|
+
self._check_response(res)
|
|
144
|
+
|
|
145
|
+
def parse_stream() -> Iterator[Dict[str, Any]]:
|
|
146
|
+
line_index = 0
|
|
147
|
+
for line in res.iter_lines():
|
|
148
|
+
if not line.strip():
|
|
149
|
+
continue
|
|
150
|
+
try:
|
|
151
|
+
yield json.loads(line)
|
|
152
|
+
except json.JSONDecodeError as exc:
|
|
153
|
+
raise ParseError("Failed to parse NDJSON line", line_index, line) from exc
|
|
154
|
+
line_index += 1
|
|
155
|
+
|
|
156
|
+
iterator = parse_stream()
|
|
157
|
+
metadata = QueryMetadata()
|
|
158
|
+
|
|
159
|
+
first = next(iterator, None)
|
|
160
|
+
if first and "metadata" in first:
|
|
161
|
+
metadata.stats = first.get("metadata", {})
|
|
162
|
+
second = next(iterator, None)
|
|
163
|
+
if second and "columns" in second:
|
|
164
|
+
metadata.columns = second.get("columns", [])
|
|
165
|
+
else:
|
|
166
|
+
def row_generator1(first_row: Any, iter_obj: Iterator[Any]) -> Iterator[Any]:
|
|
167
|
+
if first_row:
|
|
168
|
+
yield first_row
|
|
169
|
+
yield from iter_obj
|
|
170
|
+
return metadata, row_generator1(second, iterator)
|
|
171
|
+
else:
|
|
172
|
+
def row_generator2(first_row: Any, iter_obj: Iterator[Any]) -> Iterator[Any]:
|
|
173
|
+
if first_row:
|
|
174
|
+
yield first_row
|
|
175
|
+
yield from iter_obj
|
|
176
|
+
return metadata, row_generator2(first, iterator)
|
|
177
|
+
|
|
178
|
+
return metadata, iterator
|
|
179
|
+
|
|
180
|
+
except httpx.RequestError as e:
|
|
181
|
+
self._handle_error(e)
|
|
182
|
+
|
|
183
|
+
def query_all(self, request: QueryRequest) -> QueryResult:
|
|
184
|
+
metadata, iterator = self.query(request)
|
|
185
|
+
rows = list(iterator)
|
|
186
|
+
return QueryResult(metadata=metadata, rows=rows)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
3
|
+
class AltertableLakehouseError(Exception):
|
|
4
|
+
def __init__(self, message: str, cause: Optional[Exception] = None):
|
|
5
|
+
super().__init__(message)
|
|
6
|
+
self.cause = cause
|
|
7
|
+
|
|
8
|
+
class NetworkError(AltertableLakehouseError):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
class TimeoutError(AltertableLakehouseError):
|
|
12
|
+
pass
|
|
13
|
+
|
|
14
|
+
class SerializationError(AltertableLakehouseError):
|
|
15
|
+
pass
|
|
16
|
+
|
|
17
|
+
class ParseError(AltertableLakehouseError):
|
|
18
|
+
def __init__(self, message: str, line_index: int, raw_content: str):
|
|
19
|
+
super().__init__(f"{message} at line {line_index}: {raw_content}")
|
|
20
|
+
self.line_index = line_index
|
|
21
|
+
self.raw_content = raw_content
|
|
22
|
+
|
|
23
|
+
class ApiError(AltertableLakehouseError):
|
|
24
|
+
def __init__(self, message: str, status_code: int):
|
|
25
|
+
super().__init__(f"{status_code}: {message}")
|
|
26
|
+
self.status_code = status_code
|
|
27
|
+
|
|
28
|
+
class AuthError(ApiError):
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
class BadRequestError(ApiError):
|
|
32
|
+
pass
|
|
33
|
+
|
|
34
|
+
class ConfigurationError(AltertableLakehouseError):
|
|
35
|
+
pass
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
from typing import Any, Dict, List, Optional, Literal, Union
|
|
2
|
+
from enum import Enum
|
|
3
|
+
from pydantic import BaseModel, Field
|
|
4
|
+
|
|
5
|
+
class ComputeSize(str, Enum):
|
|
6
|
+
S = "S"
|
|
7
|
+
M = "M"
|
|
8
|
+
L = "L"
|
|
9
|
+
|
|
10
|
+
class UploadFormat(str, Enum):
|
|
11
|
+
CSV = "csv"
|
|
12
|
+
JSON = "json"
|
|
13
|
+
PARQUET = "parquet"
|
|
14
|
+
|
|
15
|
+
class UploadMode(str, Enum):
|
|
16
|
+
CREATE = "create"
|
|
17
|
+
APPEND = "append"
|
|
18
|
+
UPSERT = "upsert"
|
|
19
|
+
OVERWRITE = "overwrite"
|
|
20
|
+
|
|
21
|
+
AppendRequestSingle = Dict[str, Any]
|
|
22
|
+
AppendRequestBatch = List[Dict[str, Any]]
|
|
23
|
+
AppendRequest = Union[AppendRequestSingle, AppendRequestBatch]
|
|
24
|
+
|
|
25
|
+
class AppendResponse(BaseModel):
|
|
26
|
+
ok: bool
|
|
27
|
+
error_code: Optional[Literal["invalid-data"]] = None
|
|
28
|
+
|
|
29
|
+
class QueryRequest(BaseModel):
|
|
30
|
+
statement: str
|
|
31
|
+
catalog: Optional[str] = None
|
|
32
|
+
schema_: Optional[str] = Field(default=None, alias="schema")
|
|
33
|
+
session_id: Optional[str] = None
|
|
34
|
+
compute_size: Optional[ComputeSize] = None
|
|
35
|
+
sanitize: Optional[bool] = None
|
|
36
|
+
limit: Optional[int] = None
|
|
37
|
+
offset: Optional[int] = None
|
|
38
|
+
timezone: Optional[str] = None
|
|
39
|
+
ephemeral: Optional[bool] = None
|
|
40
|
+
visible: Optional[bool] = None
|
|
41
|
+
requested_by: Optional[str] = None
|
|
42
|
+
query_id: Optional[str] = None
|
|
43
|
+
|
|
44
|
+
class QueryLogResponse(BaseModel):
|
|
45
|
+
uuid: str
|
|
46
|
+
start_time: str
|
|
47
|
+
end_time: str
|
|
48
|
+
duration_ms: int
|
|
49
|
+
query: str
|
|
50
|
+
session_id: Optional[str] = None
|
|
51
|
+
client_interface: str
|
|
52
|
+
error: Optional[str] = None
|
|
53
|
+
stats: Dict[str, Any]
|
|
54
|
+
progress: int
|
|
55
|
+
visible: bool
|
|
56
|
+
requested_by: str
|
|
57
|
+
user_agent: str
|
|
58
|
+
|
|
59
|
+
class CancelQueryResponse(BaseModel):
|
|
60
|
+
cancelled: bool
|
|
61
|
+
message: str
|
|
62
|
+
|
|
63
|
+
class ValidateRequest(BaseModel):
|
|
64
|
+
statement: str
|
|
65
|
+
|
|
66
|
+
class ValidateResponse(BaseModel):
|
|
67
|
+
valid: bool
|
|
68
|
+
statement: str
|
|
69
|
+
connections_errors: Any
|
|
70
|
+
error: Optional[str] = None
|
|
71
|
+
|
|
72
|
+
class QueryMetadata(BaseModel):
|
|
73
|
+
columns: List[Dict[str, Any]] = Field(default_factory=list)
|
|
74
|
+
stats: Dict[str, Any] = Field(default_factory=dict)
|
|
75
|
+
|
|
76
|
+
class QueryResult(BaseModel):
|
|
77
|
+
metadata: QueryMetadata
|
|
78
|
+
rows: List[Any]
|