tinybird-cli 6.5.7.dev0__tar.gz → 7.0.0.dev0__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.
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/PKG-INFO +15 -2
- tinybird_cli-7.0.0.dev0/pyproject.toml +246 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/__cli__.py +2 -2
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/ch_utils/constants.py +1 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/ch_utils/engine.py +90 -54
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/client.py +58 -46
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/config.py +0 -4
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/datafile_common.py +67 -44
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/datatypes.py +34 -12
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/feedback_manager.py +5 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/git_settings.py +1 -3
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/sql.py +3 -10
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/sql_template.py +121 -98
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/sql_template_fmt.py +10 -15
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/sql_toolset.py +63 -11
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/syncasync.py +9 -54
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/branch.py +4 -25
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/cicd.py +2 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/cli.py +5 -9
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/common.py +30 -97
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/config.py +1 -2
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/datasource.py +2 -2
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/pipe.py +49 -34
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/token.py +1 -2
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/workspace.py +11 -13
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tornado_template.py +227 -83
- tinybird_cli-7.0.0.dev0/tinybird/utils/__init__.py +0 -0
- tinybird_cli-7.0.0.dev0/tinybird/utils/bools.py +19 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/PKG-INFO +15 -2
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/SOURCES.txt +3 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/setup.cfg +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/check_pypi.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/context.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/entry_points.txt +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/requires.txt +0 -0
- {tinybird_cli-6.5.7.dev0 → tinybird_cli-7.0.0.dev0}/tinybird_cli.egg-info/top_level.txt +0 -0
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: tinybird_cli
|
|
3
|
-
Version:
|
|
3
|
+
Version: 7.0.0.dev0
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/cli
|
|
6
6
|
Author: Tinybird
|
|
7
7
|
Author-email: support@tinybird.co
|
|
8
|
-
Requires-Python: >=3.
|
|
8
|
+
Requires-Python: >=3.11, <3.14
|
|
9
9
|
Description-Content-Type: text/x-rst
|
|
10
10
|
Requires-Dist: aiofiles==24.1.0
|
|
11
11
|
Requires-Dist: clickhouse-toolset==0.34.dev0
|
|
@@ -44,6 +44,19 @@ Changelog
|
|
|
44
44
|
----------
|
|
45
45
|
|
|
46
46
|
|
|
47
|
+
7.0.0
|
|
48
|
+
***********
|
|
49
|
+
|
|
50
|
+
- `Removed` Python 3.10 support. The minimum supported Python version is now 3.11.
|
|
51
|
+
|
|
52
|
+
6.5.7
|
|
53
|
+
***********
|
|
54
|
+
|
|
55
|
+
- `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
|
|
56
|
+
- `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
|
|
57
|
+
- `Added` Copy pipes accept ``ON_DEMAND_COMPUTE true`` so scheduled runs use on-demand compute. Manual ``tb pipe copy run`` still requires ``--on-demand-compute``, and asks for confirmation when the local datafile does not enable it (skip it with ``--yes``).
|
|
58
|
+
- `Removed` The ``--main`` flag from ``tb branch regression-tests``. It ran the comparison against the main Branch for a custom CI workflow.
|
|
59
|
+
|
|
47
60
|
6.5.6
|
|
48
61
|
***********
|
|
49
62
|
|
|
@@ -0,0 +1,246 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools>=61.0",
|
|
4
|
+
"wheel==0.37.1",
|
|
5
|
+
"cffi~=2.0",
|
|
6
|
+
]
|
|
7
|
+
build-backend = "setuptools.build_meta"
|
|
8
|
+
|
|
9
|
+
[project]
|
|
10
|
+
name = "tinybird"
|
|
11
|
+
version = "1.0"
|
|
12
|
+
description = "tinybird"
|
|
13
|
+
readme = { text = "tinybird analytics", content-type = "text/markdown" }
|
|
14
|
+
requires-python = ">=3.13,<3.15"
|
|
15
|
+
authors = [
|
|
16
|
+
{ name = "tinybird.co" },
|
|
17
|
+
]
|
|
18
|
+
dependencies = [
|
|
19
|
+
"GitPython==3.1.44",
|
|
20
|
+
"Markdown==3.5.1",
|
|
21
|
+
"aioboto3~=15.0",
|
|
22
|
+
"aiofiles==24.1.0",
|
|
23
|
+
"aiohttp~=3.11",
|
|
24
|
+
"anthropic~=1.9",
|
|
25
|
+
"async-lru==2.3.0",
|
|
26
|
+
"aws-msk-iam-sasl-signer-python==1.0.2",
|
|
27
|
+
"cachetools~=6.0",
|
|
28
|
+
"cffi~=2.0",
|
|
29
|
+
"chardet==4.0.0",
|
|
30
|
+
"ciso8601==2.2.0",
|
|
31
|
+
"click~=8.5",
|
|
32
|
+
"clickhouse-cityhash==1.0.2.5",
|
|
33
|
+
"clickhouse-driver==0.2.9",
|
|
34
|
+
"confluent-kafka[avro,json,schemaregistry]==2.12.2",
|
|
35
|
+
"contextvars==2.4",
|
|
36
|
+
"cramjam==2.11.0",
|
|
37
|
+
"croniter==6.2.2",
|
|
38
|
+
"cryptography~=50.0",
|
|
39
|
+
"datasketch==1.6.5",
|
|
40
|
+
"dnspython==2.8.0",
|
|
41
|
+
"docker==7.1.0",
|
|
42
|
+
"fastavro==1.12.2",
|
|
43
|
+
"fastmcp==2.12.2",
|
|
44
|
+
"google-cloud-aiplatform~=1.148",
|
|
45
|
+
"google-cloud-compute==1.48.0",
|
|
46
|
+
"google-cloud-scheduler==2.20.0",
|
|
47
|
+
"google-cloud-secret-manager==2.29.0",
|
|
48
|
+
"google-cloud-storage~=3.4",
|
|
49
|
+
"google_api_core~=2.25",
|
|
50
|
+
"googleapis_common_protos==1.66.0",
|
|
51
|
+
"grpcio_status==1.67.1",
|
|
52
|
+
"humanfriendly==10.0",
|
|
53
|
+
"isal==1.8.0",
|
|
54
|
+
"jinja2",
|
|
55
|
+
"jsondiff==2.2.1",
|
|
56
|
+
"jsonschema==4.23.0",
|
|
57
|
+
"litellm~=1.97",
|
|
58
|
+
"logfire-api==4.2.0",
|
|
59
|
+
"lru-dict==1.3.0",
|
|
60
|
+
"lz4==4.4.5",
|
|
61
|
+
"memray==1.20.0",
|
|
62
|
+
"msgspec==0.21.1",
|
|
63
|
+
"multidict==6.9.1",
|
|
64
|
+
"networkx==3.4.2",
|
|
65
|
+
"numpy==1.26.4",
|
|
66
|
+
"objgraph==3.6.2",
|
|
67
|
+
"opentelemetry-api~=1.38",
|
|
68
|
+
"opentelemetry-exporter-otlp-proto-http~=1.38",
|
|
69
|
+
"opentelemetry-sdk~=1.38",
|
|
70
|
+
"orb-billing==4.80.1",
|
|
71
|
+
"orjson==3.11.9",
|
|
72
|
+
"packaging~=26.3",
|
|
73
|
+
"passlib==1.7.1",
|
|
74
|
+
"posthog==7.28.0",
|
|
75
|
+
"pre-commit==4.6.2",
|
|
76
|
+
"prometheus_client==0.26.0",
|
|
77
|
+
"prompt_toolkit==3.0.48",
|
|
78
|
+
"propcache==0.3.2",
|
|
79
|
+
"proto_plus==1.25.0",
|
|
80
|
+
"protobuf==5.29.5",
|
|
81
|
+
"psutil==7.2.2",
|
|
82
|
+
"psycopg2-binary==2.9.12",
|
|
83
|
+
"py-spy==0.4.2",
|
|
84
|
+
"pyarrow==23.0.1",
|
|
85
|
+
"pycurl==7.45.7",
|
|
86
|
+
"pydantic-ai-slim[anthropic]~=2.0",
|
|
87
|
+
"pydantic-ai-slim[google]~=2.0",
|
|
88
|
+
"pydantic-ai-slim[retries]~=2.0",
|
|
89
|
+
"pydantic~=2.12",
|
|
90
|
+
"pyjwt[crypto]==2.9.0",
|
|
91
|
+
"pympler==1.1",
|
|
92
|
+
"pynacl==1.5.0",
|
|
93
|
+
"pyperclip==1.9.0",
|
|
94
|
+
"python-dateutil==2.9.0.post0",
|
|
95
|
+
"python-dotenv==1.1.0",
|
|
96
|
+
"python-ulid==2.7.0",
|
|
97
|
+
"pyyaml~=6.0",
|
|
98
|
+
"redis[hiredis]==5.3.1",
|
|
99
|
+
"requests~=2.32.5",
|
|
100
|
+
"sentry-sdk==2.69.2",
|
|
101
|
+
"setproctitle==1.3.3",
|
|
102
|
+
"shandy-sqlfmt==0.11.1",
|
|
103
|
+
"shandy-sqlfmt[jinjafmt]==0.11.1",
|
|
104
|
+
"sqlparse==0.4.3",
|
|
105
|
+
"starlette==1.0.1",
|
|
106
|
+
"streaming-form-data==1.20.0",
|
|
107
|
+
"stripe==3.1.0",
|
|
108
|
+
"tabulate==0.8.10",
|
|
109
|
+
"thefuzz==0.22.1",
|
|
110
|
+
"tinybird-python-sdk[ai]==0.3.7",
|
|
111
|
+
"tinybird-toolset==2.7.1",
|
|
112
|
+
"toposort==1.10",
|
|
113
|
+
"tornado==6.5.5",
|
|
114
|
+
"urllib3~=2.8",
|
|
115
|
+
"uvicorn[standard]~=0.53.0",
|
|
116
|
+
"virtualenv==20.21.1",
|
|
117
|
+
"watchdog==6.0.0",
|
|
118
|
+
"wheel==0.37.1",
|
|
119
|
+
"zlib-ng==1.0.0",
|
|
120
|
+
"zstandard==0.25.0",
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
[dependency-groups]
|
|
124
|
+
deploy = [
|
|
125
|
+
"GitPython==3.1.44",
|
|
126
|
+
"ansible==8.5.0",
|
|
127
|
+
"ara==1.6.1",
|
|
128
|
+
"boto3~=1.40",
|
|
129
|
+
"fabric==3.2.2",
|
|
130
|
+
"google-auth~=2.47",
|
|
131
|
+
"jsonnet",
|
|
132
|
+
"prettytable==3.8.0",
|
|
133
|
+
"requests~=2.32.5",
|
|
134
|
+
"requests[security]",
|
|
135
|
+
"xmltodict",
|
|
136
|
+
]
|
|
137
|
+
devtools = [
|
|
138
|
+
"flameprof",
|
|
139
|
+
"matplotlib",
|
|
140
|
+
"memory_profiler",
|
|
141
|
+
"rope",
|
|
142
|
+
]
|
|
143
|
+
doc = [
|
|
144
|
+
"Pallets-Sphinx-Themes==2.3.0",
|
|
145
|
+
"pyOpenSSL~=26.4",
|
|
146
|
+
"sphinx-autobuild==2024.2.4",
|
|
147
|
+
"sphinx-sitemap",
|
|
148
|
+
"sphinx==7.3.7",
|
|
149
|
+
"sphinxcontrib-httpdomain",
|
|
150
|
+
"sphinxcontrib-jquery==4.1",
|
|
151
|
+
]
|
|
152
|
+
lint = [
|
|
153
|
+
"botocore-stubs~=1.40",
|
|
154
|
+
"celery-types~=0.20.0",
|
|
155
|
+
"flake8~=7.4",
|
|
156
|
+
"flake8-async~=26.8",
|
|
157
|
+
"mypy==2.3.1",
|
|
158
|
+
"ruff==0.16.10",
|
|
159
|
+
"types-Markdown",
|
|
160
|
+
"types-PyYAML",
|
|
161
|
+
"types-aiofiles==24.1.0.20241221",
|
|
162
|
+
"types-boto3-lite~=1.40",
|
|
163
|
+
"types-cachetools",
|
|
164
|
+
"types-chardet",
|
|
165
|
+
"types-paramiko",
|
|
166
|
+
"types-pycurl",
|
|
167
|
+
"types-python-dateutil",
|
|
168
|
+
"types-pytz",
|
|
169
|
+
"types-requests",
|
|
170
|
+
"types-tabulate",
|
|
171
|
+
"types_croniter",
|
|
172
|
+
]
|
|
173
|
+
test = [
|
|
174
|
+
"coverage~=7.16",
|
|
175
|
+
"freezegun==1.5.5",
|
|
176
|
+
"google-auth~=2.47",
|
|
177
|
+
"gsutil==4.58",
|
|
178
|
+
"httpx2~=2.13",
|
|
179
|
+
"hypothesis==6.156.9",
|
|
180
|
+
"oauth2client==3.0.0",
|
|
181
|
+
"py==1.11.0",
|
|
182
|
+
"pytest-asyncio~=1.4",
|
|
183
|
+
"pytest-cov~=7.0",
|
|
184
|
+
"pytest-env==1.1.3",
|
|
185
|
+
"pytest-flakefinder==1.1.0",
|
|
186
|
+
"pytest-timeout==2.3.1",
|
|
187
|
+
"pytest-xdist==3.6.1",
|
|
188
|
+
"pytest~=8.1",
|
|
189
|
+
"rangehttpserver==1.4.0",
|
|
190
|
+
"yappi==1.7.6",
|
|
191
|
+
]
|
|
192
|
+
# Synced automatically by plain `uv run`/`uv sync`, no `--group` needed.
|
|
193
|
+
dev = [
|
|
194
|
+
{ include-group = "test" },
|
|
195
|
+
{ include-group = "deploy" },
|
|
196
|
+
{ include-group = "lint" },
|
|
197
|
+
]
|
|
198
|
+
|
|
199
|
+
[project.scripts]
|
|
200
|
+
tinybird_server = "tinybird.app:run"
|
|
201
|
+
tinybird_tool = "tinybird.tinybird_tool.main:cli"
|
|
202
|
+
tb_old = "tinybird.tb_cli:cli"
|
|
203
|
+
tb = "tinybird.tb.cli:cli"
|
|
204
|
+
colibri = "tinybird.colibri.cli:cli"
|
|
205
|
+
|
|
206
|
+
[tool.uv.workspace]
|
|
207
|
+
members = [
|
|
208
|
+
"chbackups",
|
|
209
|
+
"clickhouse",
|
|
210
|
+
"data_connector",
|
|
211
|
+
"gatherer",
|
|
212
|
+
"platform-api",
|
|
213
|
+
"tinybird_orchestrator",
|
|
214
|
+
]
|
|
215
|
+
|
|
216
|
+
[tool.setuptools]
|
|
217
|
+
include-package-data = true
|
|
218
|
+
|
|
219
|
+
[tool.setuptools.packages.find]
|
|
220
|
+
namespaces = false
|
|
221
|
+
include = ["tinybird", "tinybird.*", "tinybird_shared", "tinybird_shared.*", "scripts"]
|
|
222
|
+
exclude = ["tests*"]
|
|
223
|
+
|
|
224
|
+
[tool.setuptools.package-data]
|
|
225
|
+
tinybird = [
|
|
226
|
+
"templates/**/*",
|
|
227
|
+
"static/**/*",
|
|
228
|
+
"default_pipes/**/*",
|
|
229
|
+
"sql/**/*",
|
|
230
|
+
"monitors/**/*.sql",
|
|
231
|
+
"scripts/**/*",
|
|
232
|
+
"data_projects/**/*",
|
|
233
|
+
"data_projects/**/.*",
|
|
234
|
+
"raw_events/**/*",
|
|
235
|
+
"query_booster/**/*",
|
|
236
|
+
"deployments/**/*",
|
|
237
|
+
]
|
|
238
|
+
tinybird_shared = [
|
|
239
|
+
"redis_client/**/*",
|
|
240
|
+
"retry/**/*",
|
|
241
|
+
"metrics/**/*",
|
|
242
|
+
"gatherer_settings/**/*",
|
|
243
|
+
"clickhouse/**/*",
|
|
244
|
+
"gatherer_backpressure/**/*",
|
|
245
|
+
"data_connector_circuit_breaker/**/*",
|
|
246
|
+
]
|
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/cli'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '7.0.0.dev0'
|
|
8
|
+
__revision__ = '3fcfdc16'
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import functools
|
|
1
2
|
import logging
|
|
2
3
|
import re
|
|
3
4
|
from collections import defaultdict
|
|
@@ -25,6 +26,9 @@ DEFAULT_EMPTY_PARAMETERS = ["ttl", "partition_key", "sorting_key"]
|
|
|
25
26
|
# * `N` is a number
|
|
26
27
|
SIMPLE_TTL_DEFINITION = re.compile(r"""^([a-zA-Z0-9_\-\.\(\)\ \/\*]*) \+ (toInterval[a-zA-Z]*\([0-9]+\))$""")
|
|
27
28
|
|
|
29
|
+
# ClickHouse 25.8 quotes the engine name `Null`, as `null` is a keyword.
|
|
30
|
+
_BACKQUOTED_ENGINE_NAME = re.compile(r"^`(\w+)`")
|
|
31
|
+
|
|
28
32
|
|
|
29
33
|
class TableDetails:
|
|
30
34
|
"""
|
|
@@ -78,6 +82,16 @@ class TableDetails:
|
|
|
78
82
|
|
|
79
83
|
@property
|
|
80
84
|
def engine_full(self) -> Optional[str]:
|
|
85
|
+
"""
|
|
86
|
+
>>> TableDetails({"engine_full": "`Null`", "engine": "Null"}).engine_full
|
|
87
|
+
'Null'
|
|
88
|
+
>>> TableDetails({"engine_full": "`Null`()", "engine": "Null"}).engine_full
|
|
89
|
+
'Null()'
|
|
90
|
+
>>> TableDetails({"engine_full": "Null", "engine": "Null"}).to_json()
|
|
91
|
+
{'engine_full': 'Null', 'engine': 'Null'}
|
|
92
|
+
>>> TableDetails({"engine_full": "`Null`", "engine": "Null"}).to_json()
|
|
93
|
+
{'engine_full': 'Null', 'engine': 'Null'}
|
|
94
|
+
"""
|
|
81
95
|
_engine_full: Optional[str] = self.details.get("engine_full", None)
|
|
82
96
|
if not _engine_full:
|
|
83
97
|
return None
|
|
@@ -86,7 +100,7 @@ class TableDetails:
|
|
|
86
100
|
# We cannot remove index_granularity = 8192 blindly because it might be followed by other settings
|
|
87
101
|
if settings and settings.strip().lower() == "index_granularity = 8192":
|
|
88
102
|
_engine_full = _engine_full.replace(" SETTINGS index_granularity = 8192", "")
|
|
89
|
-
return engine_replicated_to_local(_engine_full)
|
|
103
|
+
return engine_replicated_to_local(_BACKQUOTED_ENGINE_NAME.sub(r"\1", _engine_full))
|
|
90
104
|
|
|
91
105
|
@property
|
|
92
106
|
def original_engine_full(self) -> Optional[str]:
|
|
@@ -111,8 +125,7 @@ class TableDetails:
|
|
|
111
125
|
|
|
112
126
|
@property
|
|
113
127
|
def version(self):
|
|
114
|
-
|
|
115
|
-
return _version
|
|
128
|
+
return self.details.get("version", None)
|
|
116
129
|
|
|
117
130
|
def is_replicated(self) -> bool:
|
|
118
131
|
engine: Optional[str] = self.details.get("engine", None)
|
|
@@ -183,6 +196,7 @@ class TableDetails:
|
|
|
183
196
|
settings = self.details.get("settings", None)
|
|
184
197
|
if settings and settings.strip().lower() != "index_granularity = 8192":
|
|
185
198
|
return settings
|
|
199
|
+
return None
|
|
186
200
|
|
|
187
201
|
@property
|
|
188
202
|
def ttl(self):
|
|
@@ -190,23 +204,19 @@ class TableDetails:
|
|
|
190
204
|
|
|
191
205
|
@property
|
|
192
206
|
def ver(self):
|
|
193
|
-
|
|
194
|
-
return _ver
|
|
207
|
+
return self.details.get("ver", None)
|
|
195
208
|
|
|
196
209
|
@property
|
|
197
210
|
def is_deleted(self):
|
|
198
|
-
|
|
199
|
-
return _is_deleted
|
|
211
|
+
return self.details.get("is_deleted", None)
|
|
200
212
|
|
|
201
213
|
@property
|
|
202
214
|
def columns(self):
|
|
203
|
-
|
|
204
|
-
return _columns
|
|
215
|
+
return self.details.get("columns", None)
|
|
205
216
|
|
|
206
217
|
@property
|
|
207
218
|
def sign(self):
|
|
208
|
-
|
|
209
|
-
return _sign
|
|
219
|
+
return self.details.get("sign", None)
|
|
210
220
|
|
|
211
221
|
@property
|
|
212
222
|
def statistics(self) -> Dict[str, Any]:
|
|
@@ -444,38 +454,70 @@ def get_engine_config(engine: str):
|
|
|
444
454
|
)
|
|
445
455
|
|
|
446
456
|
|
|
447
|
-
def
|
|
448
|
-
|
|
457
|
+
def validate_engine_config(
|
|
458
|
+
engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
|
|
459
|
+
) -> None:
|
|
460
|
+
if schema is not None and columns is not None:
|
|
461
|
+
raise ValueError("You can not use 'schema' and 'columns' at the same time")
|
|
462
|
+
_, (params, options) = get_engine_config(engine)
|
|
463
|
+
schema_columns = parse_table_structure(schema) if schema is not None else (columns or [])
|
|
464
|
+
engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
|
|
465
|
+
|
|
466
|
+
for arg in engine_settings:
|
|
467
|
+
if not hasattr(TableDetails, arg):
|
|
468
|
+
raise ValueError(f"engine_{arg} is not a valid option")
|
|
469
|
+
|
|
449
470
|
for p in params:
|
|
450
|
-
if p.required and p.name not in
|
|
471
|
+
if p.required and p.name not in engine_settings:
|
|
451
472
|
raise ValueError(f"Missing required parameter '{p.name}'")
|
|
452
|
-
param_value =
|
|
473
|
+
param_value = engine_settings.get(p.name) or p.default_value
|
|
453
474
|
if not param_value:
|
|
454
475
|
continue
|
|
455
476
|
if p.is_valid:
|
|
456
477
|
check_is_valid(
|
|
457
|
-
valid_check=p.is_valid,
|
|
478
|
+
valid_check=p.is_valid,
|
|
479
|
+
check_type="parameter",
|
|
480
|
+
columns=schema_columns,
|
|
481
|
+
tb_param=p.tb_param,
|
|
482
|
+
value=param_value,
|
|
458
483
|
)
|
|
484
|
+
|
|
485
|
+
for o in options:
|
|
486
|
+
if o.required and o.name not in engine_settings:
|
|
487
|
+
raise ValueError(f"Missing required option '{o.name}'")
|
|
488
|
+
option_value = engine_settings.get(o.name) or o.default_value
|
|
489
|
+
if o.is_valid:
|
|
490
|
+
check_is_valid(
|
|
491
|
+
valid_check=o.is_valid,
|
|
492
|
+
check_type="option",
|
|
493
|
+
columns=schema_columns,
|
|
494
|
+
tb_param=o.tb_param,
|
|
495
|
+
value=option_value,
|
|
496
|
+
)
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
def engine_params(params: List[EngineParam], args: Dict) -> List[str]:
|
|
500
|
+
params_values = []
|
|
501
|
+
for p in params:
|
|
502
|
+
param_value = args.get(p.name, None) or p.default_value
|
|
503
|
+
if not param_value:
|
|
504
|
+
continue
|
|
459
505
|
params_values.append(param_value)
|
|
460
506
|
return params_values
|
|
461
507
|
|
|
462
508
|
|
|
463
|
-
def engine_options(
|
|
509
|
+
def engine_options(options: List[EngineOption], args: Dict) -> List[str]:
|
|
464
510
|
options_values = []
|
|
465
511
|
engine_settings = ""
|
|
466
512
|
|
|
467
513
|
for o in options:
|
|
468
|
-
if o.required and o.name not in args:
|
|
469
|
-
raise ValueError(f"Missing required option '{o.name}'")
|
|
470
514
|
option_value = args.get(o.name) or o.default_value
|
|
471
|
-
if o.is_valid:
|
|
472
|
-
check_is_valid(
|
|
473
|
-
valid_check=o.is_valid, check_type="option", columns=columns, tb_param=o.tb_param, value=option_value
|
|
474
|
-
)
|
|
475
|
-
|
|
476
515
|
if option_value:
|
|
477
516
|
if o.sql.lower() == "settings":
|
|
478
517
|
engine_settings = f"{o.sql} {option_value}"
|
|
518
|
+
# Conditional TTLs fail if we wrap the value in parentheses
|
|
519
|
+
elif o.sql.lower() == "ttl":
|
|
520
|
+
options_values.append(f"{o.sql} {option_value}")
|
|
479
521
|
else:
|
|
480
522
|
options_values.append(f"{o.sql} ({option_value})")
|
|
481
523
|
|
|
@@ -491,7 +533,7 @@ def check_is_valid(
|
|
|
491
533
|
columns: List[Dict[str, Any]],
|
|
492
534
|
tb_param: str,
|
|
493
535
|
value: str,
|
|
494
|
-
):
|
|
536
|
+
) -> None:
|
|
495
537
|
"""
|
|
496
538
|
>>> check_is_valid(sorting_key_is_valid, 'option', ['column-name'], 'sorting_key', 'date')
|
|
497
539
|
|
|
@@ -508,15 +550,13 @@ def check_is_valid(
|
|
|
508
550
|
raise ValueError(f"Invalid value '{value}' for {check_type} '{tb_param}', reason: {e}")
|
|
509
551
|
|
|
510
552
|
|
|
511
|
-
def build_engine(
|
|
512
|
-
engine
|
|
513
|
-
):
|
|
514
|
-
return f"{engine}({', '.join(engine_params(columns, params, args))}) {' '.join(engine_options(columns, options, args))}".strip()
|
|
553
|
+
def build_engine(engine: str, params: List[EngineParam], options: List[EngineOption], args: Dict) -> str:
|
|
554
|
+
return f"{engine}({', '.join(engine_params(params, args))}) {' '.join(engine_options(options, args))}".strip()
|
|
515
555
|
|
|
516
556
|
|
|
517
557
|
def engine_full_from_dict(
|
|
518
558
|
engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
|
|
519
|
-
):
|
|
559
|
+
) -> str:
|
|
520
560
|
"""
|
|
521
561
|
>>> schema = ''
|
|
522
562
|
>>> engine_full_from_dict('wadus', {}, schema=schema)
|
|
@@ -536,7 +576,9 @@ def engine_full_from_dict(
|
|
|
536
576
|
>>> engine_full_from_dict('MergeTree', {'sorting_key': 'local_date, cod_store'}, schema=schema)
|
|
537
577
|
'MergeTree() ORDER BY (local_date, cod_store)'
|
|
538
578
|
>>> engine_full_from_dict('MergeTree', {'partition_key': 'toDate(timestamp)', 'sorting_key': 'local_date, cod_store', 'settings': 'index_granularity = 32, index_granularity_bytes = 2048', 'ttl': 'toDate(local_date) + INTERVAL 1 DAY'}, schema=schema)
|
|
539
|
-
'MergeTree() PARTITION BY (toDate(timestamp)) ORDER BY (local_date, cod_store) TTL
|
|
579
|
+
'MergeTree() PARTITION BY (toDate(timestamp)) ORDER BY (local_date, cod_store) TTL toDate(local_date) + INTERVAL 1 DAY SETTINGS index_granularity = 32, index_granularity_bytes = 2048'
|
|
580
|
+
>>> engine_full_from_dict('MergeTree', {'sorting_key': 'timestamp', 'ttl': 'timestamp + INTERVAL 1 DAY DELETE WHERE is_deleted = 1'}, schema=schema)
|
|
581
|
+
'MergeTree() ORDER BY (timestamp) TTL timestamp + INTERVAL 1 DAY DELETE WHERE is_deleted = 1'
|
|
540
582
|
|
|
541
583
|
>>> schema = ''
|
|
542
584
|
>>> engine_full_from_dict('CollapsingMergeTree', {'sign': 'sign_column'}, schema=schema)
|
|
@@ -605,20 +647,10 @@ def engine_full_from_dict(
|
|
|
605
647
|
'ReplacingMergeTree(updated_at) PARTITION BY (tuple()) ORDER BY (project_id) SETTINGS index_granularity = 32'
|
|
606
648
|
"""
|
|
607
649
|
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
engine_config = get_engine_config(engine)
|
|
611
|
-
name, (params, options) = engine_config
|
|
612
|
-
if columns is None and schema is not None:
|
|
613
|
-
columns = parse_table_structure(schema)
|
|
614
|
-
|
|
650
|
+
validate_engine_config(engine, args, schema=schema, columns=columns)
|
|
651
|
+
name, (params, options) = get_engine_config(engine)
|
|
615
652
|
engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
|
|
616
|
-
|
|
617
|
-
for arg in engine_settings:
|
|
618
|
-
if not hasattr(TableDetails, arg):
|
|
619
|
-
raise ValueError(f"engine_{arg} is not a valid option")
|
|
620
|
-
|
|
621
|
-
return build_engine(name, columns, params, options, engine_settings)
|
|
653
|
+
return build_engine(name, params, options, engine_settings)
|
|
622
654
|
|
|
623
655
|
|
|
624
656
|
def engine_params_from_engine_full(engine_full: str) -> Dict[str, Any]:
|
|
@@ -694,8 +726,7 @@ def ttl_from_engine(engine: str) -> Optional[str]:
|
|
|
694
726
|
return None
|
|
695
727
|
settings_array = engine.split(" SETTINGS ")
|
|
696
728
|
settings = " SETTINGS " + settings_array[1] if len(settings_array) > 1 else None
|
|
697
|
-
|
|
698
|
-
return ttl
|
|
729
|
+
return ttl_array[1][: -(len(settings))] if settings else ttl_array[1]
|
|
699
730
|
|
|
700
731
|
|
|
701
732
|
def ttl_condition_from_engine_full(engine_full: Optional[str]) -> Optional[str]:
|
|
@@ -759,17 +790,22 @@ def ttl_condition_from_engine_full(engine_full: Optional[str]) -> Optional[str]:
|
|
|
759
790
|
return None
|
|
760
791
|
|
|
761
792
|
|
|
793
|
+
@functools.lru_cache(maxsize=1024)
|
|
794
|
+
def _index_lines(create_table_query_expr: str) -> tuple[str, ...]:
|
|
795
|
+
from tinybird.sql_toolset import format_sql
|
|
796
|
+
|
|
797
|
+
return tuple(
|
|
798
|
+
line.strip() for line in format_sql(create_table_query_expr).splitlines() if line.strip().startswith("INDEX")
|
|
799
|
+
)
|
|
800
|
+
|
|
801
|
+
|
|
762
802
|
def _parse_indexes(create_table_query_expr: str) -> List[TableIndex]:
|
|
763
|
-
|
|
803
|
+
# format_sql is CPU-heavy and runs on the event loop. ClickHouse writes the
|
|
804
|
+
# INDEX keyword in uppercase in create_table_query.
|
|
805
|
+
if "INDEX" not in create_table_query_expr:
|
|
764
806
|
return []
|
|
765
807
|
try:
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
indexes = [
|
|
769
|
-
line.strip()
|
|
770
|
-
for line in format_sql(create_table_query_expr).splitlines()
|
|
771
|
-
if line.strip().startswith("INDEX")
|
|
772
|
-
]
|
|
808
|
+
indexes = list(_index_lines(create_table_query_expr))
|
|
773
809
|
except ModuleNotFoundError:
|
|
774
810
|
# this is not needed from CLI
|
|
775
811
|
return []
|