tinybird 4.6.20.dev0__tar.gz → 4.6.21.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-4.6.20.dev0 → tinybird-4.6.21.dev0}/PKG-INFO +11 -1
- tinybird-4.6.21.dev0/pyproject.toml +228 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/ch_utils/constants.py +1 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/ch_utils/engine.py +49 -32
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/client.py +37 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/service_datasources.py +7 -8
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql.py +0 -6
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_template.py +1 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_toolset.py +59 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/__cli__.py +2 -2
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/client.py +37 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/common.py +15 -77
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/infra.py +1 -2
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/logs.py +1 -2
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/materialization.py +1 -1
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/project.py +4 -4
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +4 -4
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/token.py +2 -5
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/watch.py +1 -1
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/branch.py +1 -2
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/cicd.py +2 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/cli.py +5 -9
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/common.py +27 -94
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/config.py +1 -2
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/pipe.py +9 -13
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/workspace.py +11 -13
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tornado_template.py +12 -17
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/PKG-INFO +11 -1
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/SOURCES.txt +1 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/setup.cfg +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/__cli__.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/check_pypi.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/config.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/context.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/exceptions.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_connection.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_datasource.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_pipe.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datatypes.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/feedback_manager.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/git_settings.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/iterating/__init__.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/prompts.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/syncasync.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/check_pypi.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/cli.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/config.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/branch.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/build.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/build_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/cli.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/config.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/copy.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/create.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/playground.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datasource.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deployment.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deprecations.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/info.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/job.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/job_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local_logs.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/login.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/login_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/logout.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/open.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/preview.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/project_commands.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/py_project.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/query_output.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/secret.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/secret_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/sink.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/table.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/test.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/test_common.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/ts_project.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.2
|
|
2
2
|
Name: tinybird
|
|
3
|
-
Version: 4.6.
|
|
3
|
+
Version: 4.6.21.dev0
|
|
4
4
|
Summary: Tinybird Command Line Tool
|
|
5
5
|
Home-page: https://www.tinybird.co/docs/forward/commands
|
|
6
6
|
Author: Tinybird
|
|
@@ -52,6 +52,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
52
52
|
Changelog
|
|
53
53
|
----------
|
|
54
54
|
|
|
55
|
+
4.6.19
|
|
56
|
+
********
|
|
57
|
+
|
|
58
|
+
- `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
|
|
59
|
+
|
|
55
60
|
4.6.18
|
|
56
61
|
********
|
|
57
62
|
|
|
@@ -62,6 +67,11 @@ Changelog
|
|
|
62
67
|
|
|
63
68
|
- `Added` ``tb job retry <job_id>`` to retry eligible import and S3/GCS sync jobs in ``error`` or ``cancelled`` status.
|
|
64
69
|
|
|
70
|
+
4.6.18
|
|
71
|
+
********
|
|
72
|
+
|
|
73
|
+
- `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
|
|
74
|
+
|
|
65
75
|
4.6.16
|
|
66
76
|
********
|
|
67
77
|
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = [
|
|
3
|
+
"setuptools>=61.0",
|
|
4
|
+
"wheel==0.37.1",
|
|
5
|
+
"cffi==1.17.1",
|
|
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==14.0.0",
|
|
22
|
+
"aiofiles==24.1.0",
|
|
23
|
+
"aiohttp==3.11.2",
|
|
24
|
+
"anthropic==0.55.0",
|
|
25
|
+
"aws-msk-iam-sasl-signer-python==1.0.2",
|
|
26
|
+
"cachetools==5.5.0",
|
|
27
|
+
"cffi==1.17.1",
|
|
28
|
+
"chardet==4.0.0",
|
|
29
|
+
"ciso8601==2.2.0",
|
|
30
|
+
"click==8.1.8",
|
|
31
|
+
"clickhouse-cityhash==1.0.2.5",
|
|
32
|
+
"clickhouse-driver==0.2.9",
|
|
33
|
+
"confluent-kafka[avro,json,schemaregistry]==2.12.2",
|
|
34
|
+
"contextvars==2.4",
|
|
35
|
+
"cramjam==2.11.0",
|
|
36
|
+
"croniter==6.2.2",
|
|
37
|
+
"cryptography==3.4.8",
|
|
38
|
+
"datasketch==1.6.5",
|
|
39
|
+
"dnspython==2.8.0",
|
|
40
|
+
"docker==7.1.0",
|
|
41
|
+
"fastavro==1.12.2",
|
|
42
|
+
"fastmcp==2.12.2",
|
|
43
|
+
"google-cloud-aiplatform==1.148.1",
|
|
44
|
+
"google-cloud-compute==1.48.0",
|
|
45
|
+
"google-cloud-scheduler==2.20.0",
|
|
46
|
+
"google-cloud-secret-manager==2.29.0",
|
|
47
|
+
"google-cloud-storage==3.4.1",
|
|
48
|
+
"google_api_core==2.25.2",
|
|
49
|
+
"googleapis_common_protos==1.66.0",
|
|
50
|
+
"grpcio_status==1.67.1",
|
|
51
|
+
"humanfriendly==10.0",
|
|
52
|
+
"isal==1.8.0",
|
|
53
|
+
"jinja2",
|
|
54
|
+
"jsondiff==2.2.1",
|
|
55
|
+
"jsonschema==4.23.0",
|
|
56
|
+
"litellm==1.80.11",
|
|
57
|
+
"logfire-api==4.2.0",
|
|
58
|
+
"lru-dict==1.3.0",
|
|
59
|
+
"lz4==4.4.5",
|
|
60
|
+
"memray==1.20.0",
|
|
61
|
+
"msgspec==0.21.1",
|
|
62
|
+
"multidict==6.6.4",
|
|
63
|
+
"networkx==3.4.2",
|
|
64
|
+
"numpy==1.26.4",
|
|
65
|
+
"objgraph==3.6.2",
|
|
66
|
+
"opentelemetry-api==1.38.0",
|
|
67
|
+
"opentelemetry-exporter-otlp-proto-http==1.38.0",
|
|
68
|
+
"opentelemetry-sdk==1.38.0",
|
|
69
|
+
"orb-billing==2.17.0",
|
|
70
|
+
"orjson==3.11.5",
|
|
71
|
+
"packaging==23.2",
|
|
72
|
+
"passlib==1.7.1",
|
|
73
|
+
"posthog==7.28.0",
|
|
74
|
+
"pre-commit==4.6.2",
|
|
75
|
+
"prometheus_client==0.26.0",
|
|
76
|
+
"prompt_toolkit==3.0.48",
|
|
77
|
+
"propcache==0.3.2",
|
|
78
|
+
"proto_plus==1.25.0",
|
|
79
|
+
"protobuf==5.29.5",
|
|
80
|
+
"psutil==7.2.2",
|
|
81
|
+
"psycopg2-binary==2.9.12",
|
|
82
|
+
"py-spy==0.4.2",
|
|
83
|
+
"pyarrow==23.0.1",
|
|
84
|
+
"pycurl==7.45.7",
|
|
85
|
+
"pydantic-ai-slim[anthropic]==0.5.0",
|
|
86
|
+
"pydantic-ai-slim[google]==0.5.0",
|
|
87
|
+
"pydantic-ai-slim[retries]==0.5.0",
|
|
88
|
+
"pydantic==2.12.5",
|
|
89
|
+
"pyjwt[crypto]==2.9.0",
|
|
90
|
+
"pympler==1.1",
|
|
91
|
+
"pynacl==1.5.0",
|
|
92
|
+
"pyperclip==1.9.0",
|
|
93
|
+
"python-dateutil==2.9.0.post0",
|
|
94
|
+
"python-dotenv==1.1.0",
|
|
95
|
+
"python-ulid==2.7.0",
|
|
96
|
+
"pyyaml==6.0.1",
|
|
97
|
+
"redis[hiredis]==5.3.1",
|
|
98
|
+
"requests==2.32.4",
|
|
99
|
+
"sentry-sdk==2.69.1",
|
|
100
|
+
"setproctitle==1.3.3",
|
|
101
|
+
"shandy-sqlfmt==0.11.1",
|
|
102
|
+
"shandy-sqlfmt[jinjafmt]==0.11.1",
|
|
103
|
+
"sqlparse==0.4.3",
|
|
104
|
+
"starlette==1.0.1",
|
|
105
|
+
"streaming-form-data==1.20.0",
|
|
106
|
+
"stripe==3.1.0",
|
|
107
|
+
"tabulate==0.8.10",
|
|
108
|
+
"thefuzz==0.22.1",
|
|
109
|
+
"tinybird-python-sdk[ai]==0.3.7",
|
|
110
|
+
"tinybird-toolset==2.6.2",
|
|
111
|
+
"toposort==1.10",
|
|
112
|
+
"tornado==6.5.5",
|
|
113
|
+
"urllib3==1.26.20",
|
|
114
|
+
"uvicorn[standard]==0.49.0",
|
|
115
|
+
"virtualenv==20.21.1",
|
|
116
|
+
"watchdog==6.0.0",
|
|
117
|
+
"wheel==0.37.1",
|
|
118
|
+
"zlib-ng==1.0.0",
|
|
119
|
+
"zstandard==0.25.0",
|
|
120
|
+
]
|
|
121
|
+
|
|
122
|
+
[dependency-groups]
|
|
123
|
+
deploy = [
|
|
124
|
+
"GitPython==3.1.44",
|
|
125
|
+
"ansible==8.5.0",
|
|
126
|
+
"ara==1.6.1",
|
|
127
|
+
"boto3==1.36.21",
|
|
128
|
+
"fabric==3.2.2",
|
|
129
|
+
"google-auth==2.47.0",
|
|
130
|
+
"jsonnet",
|
|
131
|
+
"prettytable==3.8.0",
|
|
132
|
+
"requests==2.32.4",
|
|
133
|
+
"requests[security]",
|
|
134
|
+
"xmltodict",
|
|
135
|
+
]
|
|
136
|
+
devtools = [
|
|
137
|
+
"flameprof",
|
|
138
|
+
"matplotlib",
|
|
139
|
+
"memory_profiler",
|
|
140
|
+
"rope",
|
|
141
|
+
]
|
|
142
|
+
doc = [
|
|
143
|
+
"Pallets-Sphinx-Themes==2.3.0",
|
|
144
|
+
"pyOpenSSL<24.0.0",
|
|
145
|
+
"sphinx-autobuild==2024.2.4",
|
|
146
|
+
"sphinx-sitemap",
|
|
147
|
+
"sphinx==7.3.7",
|
|
148
|
+
"sphinxcontrib-httpdomain",
|
|
149
|
+
"sphinxcontrib-jquery==4.1",
|
|
150
|
+
]
|
|
151
|
+
lint = [
|
|
152
|
+
"botocore-stubs==1.36.23",
|
|
153
|
+
"mypy==2.3.1",
|
|
154
|
+
"ruff==0.16.8",
|
|
155
|
+
"types-Markdown",
|
|
156
|
+
"types-PyYAML",
|
|
157
|
+
"types-aiofiles==24.1.0.20241221",
|
|
158
|
+
"types-boto3-lite==1.36.21",
|
|
159
|
+
"types-cachetools",
|
|
160
|
+
"types-chardet",
|
|
161
|
+
"types-paramiko",
|
|
162
|
+
"types-pycurl",
|
|
163
|
+
"types-python-dateutil",
|
|
164
|
+
"types-pytz",
|
|
165
|
+
"types-requests",
|
|
166
|
+
"types-tabulate",
|
|
167
|
+
"types_croniter",
|
|
168
|
+
]
|
|
169
|
+
test = [
|
|
170
|
+
"coverage==7.10.6",
|
|
171
|
+
"freezegun==1.5.5",
|
|
172
|
+
"google-auth==2.47.0",
|
|
173
|
+
"gsutil==4.58",
|
|
174
|
+
"hypothesis==6.156.1",
|
|
175
|
+
"oauth2client==3.0.0",
|
|
176
|
+
"py==1.11.0",
|
|
177
|
+
"pytest-asyncio==0.23.8",
|
|
178
|
+
"pytest-cov==4.1.0",
|
|
179
|
+
"pytest-env==1.1.3",
|
|
180
|
+
"pytest-flakefinder==1.1.0",
|
|
181
|
+
"pytest-timeout==2.3.1",
|
|
182
|
+
"pytest-xdist==3.6.1",
|
|
183
|
+
"pytest==8.1.2",
|
|
184
|
+
"rangehttpserver==1.4.0",
|
|
185
|
+
"yappi==1.7.6",
|
|
186
|
+
]
|
|
187
|
+
|
|
188
|
+
[project.scripts]
|
|
189
|
+
tinybird_server = "tinybird.app:run"
|
|
190
|
+
tinybird_tool = "tinybird.tinybird_tool.main:cli"
|
|
191
|
+
tb_old = "tinybird.tb_cli:cli"
|
|
192
|
+
tb = "tinybird.tb.cli:cli"
|
|
193
|
+
colibri = "tinybird.colibri.cli:cli"
|
|
194
|
+
|
|
195
|
+
[tool.uv.workspace]
|
|
196
|
+
members = ["data_connector"]
|
|
197
|
+
|
|
198
|
+
[tool.setuptools]
|
|
199
|
+
include-package-data = true
|
|
200
|
+
|
|
201
|
+
[tool.setuptools.packages.find]
|
|
202
|
+
namespaces = false
|
|
203
|
+
include = ["tinybird", "tinybird.*", "tinybird_shared", "tinybird_shared.*", "scripts"]
|
|
204
|
+
exclude = ["tests*"]
|
|
205
|
+
|
|
206
|
+
[tool.setuptools.package-data]
|
|
207
|
+
tinybird = [
|
|
208
|
+
"templates/**/*",
|
|
209
|
+
"static/**/*",
|
|
210
|
+
"default_pipes/**/*",
|
|
211
|
+
"sql/**/*",
|
|
212
|
+
"monitors/**/*.sql",
|
|
213
|
+
"scripts/**/*",
|
|
214
|
+
"data_projects/**/*",
|
|
215
|
+
"data_projects/**/.*",
|
|
216
|
+
"raw_events/**/*",
|
|
217
|
+
"query_booster/**/*",
|
|
218
|
+
"deployments/**/*",
|
|
219
|
+
]
|
|
220
|
+
tinybird_shared = [
|
|
221
|
+
"redis_client/**/*",
|
|
222
|
+
"retry/**/*",
|
|
223
|
+
"metrics/**/*",
|
|
224
|
+
"gatherer_settings/**/*",
|
|
225
|
+
"clickhouse/**/*",
|
|
226
|
+
"gatherer_backpressure/**/*",
|
|
227
|
+
"data_connector_circuit_breaker/**/*",
|
|
228
|
+
]
|
|
@@ -440,35 +440,64 @@ def get_engine_config(engine: str):
|
|
|
440
440
|
)
|
|
441
441
|
|
|
442
442
|
|
|
443
|
-
def
|
|
444
|
-
|
|
443
|
+
def validate_engine_config(
|
|
444
|
+
engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
|
|
445
|
+
) -> None:
|
|
446
|
+
if schema is not None and columns is not None:
|
|
447
|
+
raise ValueError("You can not use 'schema' and 'columns' at the same time")
|
|
448
|
+
_, (params, options) = get_engine_config(engine)
|
|
449
|
+
schema_columns = parse_table_structure(schema) if schema is not None else (columns or [])
|
|
450
|
+
engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
|
|
451
|
+
|
|
452
|
+
for arg in engine_settings:
|
|
453
|
+
if not hasattr(TableDetails, arg):
|
|
454
|
+
raise ValueError(f"engine_{arg} is not a valid option")
|
|
455
|
+
|
|
445
456
|
for p in params:
|
|
446
|
-
if p.required and p.name not in
|
|
457
|
+
if p.required and p.name not in engine_settings:
|
|
447
458
|
raise ValueError(f"Missing required parameter '{p.name}'")
|
|
448
|
-
param_value =
|
|
459
|
+
param_value = engine_settings.get(p.name) or p.default_value
|
|
449
460
|
if not param_value:
|
|
450
461
|
continue
|
|
451
462
|
if p.is_valid:
|
|
452
463
|
check_is_valid(
|
|
453
|
-
valid_check=p.is_valid,
|
|
464
|
+
valid_check=p.is_valid,
|
|
465
|
+
check_type="parameter",
|
|
466
|
+
columns=schema_columns,
|
|
467
|
+
tb_param=p.tb_param,
|
|
468
|
+
value=param_value,
|
|
454
469
|
)
|
|
470
|
+
|
|
471
|
+
for o in options:
|
|
472
|
+
if o.required and o.name not in engine_settings:
|
|
473
|
+
raise ValueError(f"Missing required option '{o.name}'")
|
|
474
|
+
option_value = engine_settings.get(o.name) or o.default_value
|
|
475
|
+
if o.is_valid:
|
|
476
|
+
check_is_valid(
|
|
477
|
+
valid_check=o.is_valid,
|
|
478
|
+
check_type="option",
|
|
479
|
+
columns=schema_columns,
|
|
480
|
+
tb_param=o.tb_param,
|
|
481
|
+
value=option_value,
|
|
482
|
+
)
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
def engine_params(params: List[EngineParam], args: Dict) -> List[str]:
|
|
486
|
+
params_values = []
|
|
487
|
+
for p in params:
|
|
488
|
+
param_value = args.get(p.name, None) or p.default_value
|
|
489
|
+
if not param_value:
|
|
490
|
+
continue
|
|
455
491
|
params_values.append(param_value)
|
|
456
492
|
return params_values
|
|
457
493
|
|
|
458
494
|
|
|
459
|
-
def engine_options(
|
|
495
|
+
def engine_options(options: List[EngineOption], args: Dict) -> List[str]:
|
|
460
496
|
options_values = []
|
|
461
497
|
engine_settings = ""
|
|
462
498
|
|
|
463
499
|
for o in options:
|
|
464
|
-
if o.required and o.name not in args:
|
|
465
|
-
raise ValueError(f"Missing required option '{o.name}'")
|
|
466
500
|
option_value = args.get(o.name) or o.default_value
|
|
467
|
-
if o.is_valid:
|
|
468
|
-
check_is_valid(
|
|
469
|
-
valid_check=o.is_valid, check_type="option", columns=columns, tb_param=o.tb_param, value=option_value
|
|
470
|
-
)
|
|
471
|
-
|
|
472
501
|
if option_value:
|
|
473
502
|
if o.sql.lower() == "settings":
|
|
474
503
|
engine_settings = f"{o.sql} {option_value}"
|
|
@@ -490,7 +519,7 @@ def check_is_valid(
|
|
|
490
519
|
columns: List[Dict[str, Any]],
|
|
491
520
|
tb_param: str,
|
|
492
521
|
value: str,
|
|
493
|
-
):
|
|
522
|
+
) -> None:
|
|
494
523
|
"""
|
|
495
524
|
>>> check_is_valid(sorting_key_is_valid, 'option', ['column-name'], 'sorting_key', 'date')
|
|
496
525
|
|
|
@@ -507,15 +536,13 @@ def check_is_valid(
|
|
|
507
536
|
raise ValueError(f"Invalid value '{value}' for {check_type} '{tb_param}', reason: {e}")
|
|
508
537
|
|
|
509
538
|
|
|
510
|
-
def build_engine(
|
|
511
|
-
engine
|
|
512
|
-
):
|
|
513
|
-
return f"{engine}({', '.join(engine_params(columns, params, args))}) {' '.join(engine_options(columns, options, args))}".strip()
|
|
539
|
+
def build_engine(engine: str, params: List[EngineParam], options: List[EngineOption], args: Dict) -> str:
|
|
540
|
+
return f"{engine}({', '.join(engine_params(params, args))}) {' '.join(engine_options(options, args))}".strip()
|
|
514
541
|
|
|
515
542
|
|
|
516
543
|
def engine_full_from_dict(
|
|
517
544
|
engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
|
|
518
|
-
):
|
|
545
|
+
) -> str:
|
|
519
546
|
"""
|
|
520
547
|
>>> schema = ''
|
|
521
548
|
>>> engine_full_from_dict('wadus', {}, schema=schema)
|
|
@@ -606,20 +633,10 @@ def engine_full_from_dict(
|
|
|
606
633
|
'ReplacingMergeTree(updated_at) PARTITION BY (tuple()) ORDER BY (project_id) SETTINGS index_granularity = 32'
|
|
607
634
|
"""
|
|
608
635
|
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
engine_config = get_engine_config(engine)
|
|
612
|
-
name, (params, options) = engine_config
|
|
613
|
-
if columns is None and schema is not None:
|
|
614
|
-
columns = parse_table_structure(schema)
|
|
615
|
-
|
|
636
|
+
validate_engine_config(engine, args, schema=schema, columns=columns)
|
|
637
|
+
name, (params, options) = get_engine_config(engine)
|
|
616
638
|
engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
|
|
617
|
-
|
|
618
|
-
for arg in engine_settings:
|
|
619
|
-
if not hasattr(TableDetails, arg):
|
|
620
|
-
raise ValueError(f"engine_{arg} is not a valid option")
|
|
621
|
-
|
|
622
|
-
return build_engine(name, columns, params, options, engine_settings)
|
|
639
|
+
return build_engine(name, params, options, engine_settings)
|
|
623
640
|
|
|
624
641
|
|
|
625
642
|
def engine_params_from_engine_full(engine_full: str) -> Dict[str, Any]:
|
|
@@ -408,6 +408,16 @@ class TinyB:
|
|
|
408
408
|
format: str = "csv",
|
|
409
409
|
replace_options: Optional[Set[str]] = None,
|
|
410
410
|
) -> Dict[str, Any]:
|
|
411
|
+
if mode in ("append", "replace"):
|
|
412
|
+
return await self.datasource_append_url(
|
|
413
|
+
table_name,
|
|
414
|
+
url,
|
|
415
|
+
mode=mode,
|
|
416
|
+
sql_condition=sql_condition,
|
|
417
|
+
format=format,
|
|
418
|
+
replace_options=replace_options,
|
|
419
|
+
)
|
|
420
|
+
|
|
411
421
|
params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
|
|
412
422
|
|
|
413
423
|
if sql_condition:
|
|
@@ -424,6 +434,33 @@ class TinyB:
|
|
|
424
434
|
|
|
425
435
|
return await self.wait_for_job(res["id"], status_callback, backoff_multiplier=1.5, maximum_backoff_seconds=20)
|
|
426
436
|
|
|
437
|
+
async def datasource_append_url(
|
|
438
|
+
self,
|
|
439
|
+
datasource_name: str,
|
|
440
|
+
url: str,
|
|
441
|
+
mode: str = "append",
|
|
442
|
+
sql_condition: Optional[str] = None,
|
|
443
|
+
format: str = "csv",
|
|
444
|
+
replace_options: Optional[Set[str]] = None,
|
|
445
|
+
) -> Dict[str, Any]:
|
|
446
|
+
params = {"format": format, "url": url}
|
|
447
|
+
if sql_condition:
|
|
448
|
+
params["replace_condition"] = sql_condition
|
|
449
|
+
if replace_options:
|
|
450
|
+
for option in replace_options:
|
|
451
|
+
params[option] = "true"
|
|
452
|
+
|
|
453
|
+
res = await self._req(
|
|
454
|
+
f"/v1/datasources/{quote(datasource_name, safe='')}/{mode}?{urlencode(params, safe='')}",
|
|
455
|
+
method="POST",
|
|
456
|
+
data=b"",
|
|
457
|
+
)
|
|
458
|
+
if not isinstance(res, dict):
|
|
459
|
+
raise RuntimeError("We couldn't confirm that your import started. Please try again.")
|
|
460
|
+
if "error" in res:
|
|
461
|
+
raise Exception(res["error"])
|
|
462
|
+
return res
|
|
463
|
+
|
|
427
464
|
async def datasource_delete(self, datasource_name: str, force: bool = False, dry_run: bool = False):
|
|
428
465
|
params = {"force": "true" if force else "false", "dry_run": "true" if dry_run else "false"}
|
|
429
466
|
return await self._req(f"/v0/datasources/{datasource_name}?{urlencode(params)}", method="DELETE")
|
|
@@ -39,6 +39,7 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
39
39
|
{"name": "result_rows", "type": "UInt64"},
|
|
40
40
|
{"name": "url", "type": "Nullable(String)"},
|
|
41
41
|
{"name": "error", "type": "UInt8"},
|
|
42
|
+
{"name": "error_message", "type": "Nullable(String)"},
|
|
42
43
|
{"name": "status_code", "type": "Int32"},
|
|
43
44
|
{"name": "request_id", "type": "String"},
|
|
44
45
|
{"name": "parameters", "type": "Map(String, String)"},
|
|
@@ -443,25 +444,21 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
443
444
|
"dateColumn": "run_validation",
|
|
444
445
|
"engine": {
|
|
445
446
|
"engine": "MergeTree",
|
|
446
|
-
"sorting_key": "
|
|
447
|
+
"sorting_key": "workspace, host, query_hash, run_validation",
|
|
447
448
|
"partition_key": "toYYYYMM(run_validation)",
|
|
448
449
|
},
|
|
449
450
|
"columns": [
|
|
450
|
-
{"name": "host", "type": "LowCardinality(String)"},
|
|
451
451
|
{"name": "version", "type": "LowCardinality(String)"},
|
|
452
452
|
{"name": "stable_version", "type": "LowCardinality(String)"},
|
|
453
453
|
{"name": "query_hash", "type": "UInt64"},
|
|
454
454
|
{"name": "query_last_execution", "type": "DateTime"},
|
|
455
|
-
{"name": "
|
|
456
|
-
{"name": "
|
|
457
|
-
{"name": "database", "type": "String"},
|
|
458
|
-
{"name": "pipe_name", "type": "String"},
|
|
455
|
+
{"name": "resource_type", "type": "LowCardinality(String)"},
|
|
456
|
+
{"name": "resource_name", "type": "String"},
|
|
459
457
|
{"name": "query_id", "type": "String"},
|
|
460
458
|
{"name": "query", "type": "String"},
|
|
461
459
|
{"name": "error_code", "type": "Int16"},
|
|
462
460
|
{"name": "error", "type": "String"},
|
|
463
461
|
{"name": "fix_suggestion", "type": "String"},
|
|
464
|
-
{"name": "fix_suggestion_ai", "type": "String"},
|
|
465
462
|
{"name": "run_validation", "type": "DateTime"},
|
|
466
463
|
],
|
|
467
464
|
},
|
|
@@ -680,6 +677,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
680
677
|
{"name": "result_rows", "type": "UInt64"},
|
|
681
678
|
{"name": "url", "type": "Nullable(String)"},
|
|
682
679
|
{"name": "error", "type": "UInt8"},
|
|
680
|
+
{"name": "error_message", "type": "Nullable(String)"},
|
|
683
681
|
{"name": "status_code", "type": "Int32"},
|
|
684
682
|
{"name": "request_id", "type": "String"},
|
|
685
683
|
{"name": "parameters", "type": "Map(String, String)"},
|
|
@@ -784,7 +782,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
784
782
|
},
|
|
785
783
|
{
|
|
786
784
|
"name": "organization.metrics_logs",
|
|
787
|
-
"description": "Metrics of your organization's dedicated clusters",
|
|
785
|
+
"description": "Metrics of your organization's dedicated ClickHouse clusters, sampled roughly every 10 seconds. One row is one metric for one host at one point in time, so filter by metric and group by host or cluster. value is a String: cast it before doing arithmetic, for example toFloat64(value). description carries ClickHouse's own explanation of the metric. The metrics available cover queries and query concurrency, CPU and load, memory, replication lag and queues, MergeTree storage size and part counts, background merges and mutations, connections, caches, and disk space. The exact set depends on the ClickHouse version and the host platform, so list it with SELECT DISTINCT metric FROM organization.metrics_logs. host is an obfuscated identifier of the form cluster_shard, not the real hostname. This data source has a TTL of 30 days.",
|
|
788
786
|
"dateColumn": "timestamp",
|
|
789
787
|
"engine": {
|
|
790
788
|
"engine": "MergeTree",
|
|
@@ -798,6 +796,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
798
796
|
{"name": "metric", "type": "LowCardinality(String)"},
|
|
799
797
|
{"name": "value", "type": "String"},
|
|
800
798
|
{"name": "description", "type": "LowCardinality(String)"},
|
|
799
|
+
{"name": "organization_id", "type": "String"},
|
|
801
800
|
],
|
|
802
801
|
},
|
|
803
802
|
{
|
|
@@ -129,15 +129,9 @@ class TableProjection:
|
|
|
129
129
|
def add_projection_sql(self):
|
|
130
130
|
return f"ADD {self.to_sql()}"
|
|
131
131
|
|
|
132
|
-
def drop_projection_sql(self):
|
|
133
|
-
return f"DROP PROJECTION IF EXISTS {self.name}"
|
|
134
|
-
|
|
135
132
|
def materialize_projection_sql(self):
|
|
136
133
|
return f"MATERIALIZE PROJECTION IF EXISTS {self.name}"
|
|
137
134
|
|
|
138
|
-
def clear_projection_sql(self):
|
|
139
|
-
return f"CLEAR PROJECTION IF EXISTS {self.name}"
|
|
140
|
-
|
|
141
135
|
|
|
142
136
|
def as_subquery(sql: str) -> str:
|
|
143
137
|
return f"""(\n{sql}\n)"""
|
|
@@ -699,3 +699,62 @@ def is_invalid_resource_from_current_workspace(
|
|
|
699
699
|
valid_tables: Optional[Set[Tuple[str, str]]],
|
|
700
700
|
) -> bool:
|
|
701
701
|
return bool(database == default_database and valid_tables and r not in valid_tables and r not in _replaced_with)
|
|
702
|
+
|
|
703
|
+
|
|
704
|
+
def validate_ttl_expression(
|
|
705
|
+
columns: list[dict[str, str]],
|
|
706
|
+
ttl: Optional[str],
|
|
707
|
+
sorting_key: str,
|
|
708
|
+
primary_key: Optional[str] = None,
|
|
709
|
+
partition_key: Optional[str] = None,
|
|
710
|
+
) -> Optional[str]:
|
|
711
|
+
if not ttl:
|
|
712
|
+
return None
|
|
713
|
+
|
|
714
|
+
kwargs = {"partition_key": partition_key} if partition_key else {}
|
|
715
|
+
error = chquery.validate_ttl(
|
|
716
|
+
ttl=ttl,
|
|
717
|
+
columns=columns,
|
|
718
|
+
sorting_key=sorting_key,
|
|
719
|
+
primary_key=primary_key or sorting_key,
|
|
720
|
+
**kwargs,
|
|
721
|
+
)
|
|
722
|
+
if error is None:
|
|
723
|
+
return None
|
|
724
|
+
return error.removeprefix("DB::Exception: ")
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
TTL_ONLY_DROP_PARTS_SETTING = "ttl_only_drop_parts"
|
|
728
|
+
|
|
729
|
+
|
|
730
|
+
def should_default_ttl_only_drop_parts(
|
|
731
|
+
columns: list[dict[str, str]],
|
|
732
|
+
ttl: Optional[str],
|
|
733
|
+
sorting_key: str,
|
|
734
|
+
partition_key: Optional[str],
|
|
735
|
+
primary_key: Optional[str] = None,
|
|
736
|
+
) -> bool:
|
|
737
|
+
"""Whether ``ttl_only_drop_parts = 1`` should be defaulted for this table."""
|
|
738
|
+
if not ttl or not partition_key:
|
|
739
|
+
return False
|
|
740
|
+
result = chquery.check_ttl_partition_compatibility(
|
|
741
|
+
columns=columns,
|
|
742
|
+
ttl=ttl,
|
|
743
|
+
sorting_key=sorting_key,
|
|
744
|
+
primary_key=primary_key or sorting_key,
|
|
745
|
+
partition_key=partition_key,
|
|
746
|
+
)
|
|
747
|
+
return result["checked"] and result["error"] is None
|
|
748
|
+
|
|
749
|
+
|
|
750
|
+
def validate_partition_key_expression(
|
|
751
|
+
columns: list[dict[str, str]],
|
|
752
|
+
partition_key: Optional[str],
|
|
753
|
+
) -> Optional[str]:
|
|
754
|
+
if not partition_key:
|
|
755
|
+
return None
|
|
756
|
+
|
|
757
|
+
error = chquery.validate_partition_key(columns=columns, partition_key=partition_key)
|
|
758
|
+
if error is None:
|
|
759
|
+
return None
|
|
760
|
+
return error.removeprefix("DB::Exception: ")
|
|
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
|
|
|
4
4
|
__url__ = 'https://www.tinybird.co/docs/forward/commands'
|
|
5
5
|
__author__ = 'Tinybird'
|
|
6
6
|
__author_email__ = 'support@tinybird.co'
|
|
7
|
-
__version__ = '4.6.
|
|
8
|
-
__revision__ = '
|
|
7
|
+
__version__ = '4.6.21.dev0'
|
|
8
|
+
__revision__ = '965c47ce'
|
|
@@ -432,6 +432,16 @@ class TinyB:
|
|
|
432
432
|
format: str = "csv",
|
|
433
433
|
replace_options: Optional[Set[str]] = None,
|
|
434
434
|
) -> Dict[str, Any]:
|
|
435
|
+
if mode in ("append", "replace"):
|
|
436
|
+
return self.datasource_append_url(
|
|
437
|
+
table_name,
|
|
438
|
+
url,
|
|
439
|
+
mode=mode,
|
|
440
|
+
sql_condition=sql_condition,
|
|
441
|
+
format=format,
|
|
442
|
+
replace_options=replace_options,
|
|
443
|
+
)
|
|
444
|
+
|
|
435
445
|
params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
|
|
436
446
|
|
|
437
447
|
if sql_condition:
|
|
@@ -448,6 +458,33 @@ class TinyB:
|
|
|
448
458
|
|
|
449
459
|
return self.wait_for_job(res["id"], status_callback, backoff_multiplier=1.5, maximum_backoff_seconds=20)
|
|
450
460
|
|
|
461
|
+
def datasource_append_url(
|
|
462
|
+
self,
|
|
463
|
+
datasource_name: str,
|
|
464
|
+
url: str,
|
|
465
|
+
mode: str = "append",
|
|
466
|
+
sql_condition: Optional[str] = None,
|
|
467
|
+
format: str = "csv",
|
|
468
|
+
replace_options: Optional[Set[str]] = None,
|
|
469
|
+
) -> Dict[str, Any]:
|
|
470
|
+
params = {"format": format, "url": url}
|
|
471
|
+
if sql_condition:
|
|
472
|
+
params["replace_condition"] = sql_condition
|
|
473
|
+
if replace_options:
|
|
474
|
+
for option in replace_options:
|
|
475
|
+
params[option] = "true"
|
|
476
|
+
|
|
477
|
+
res = self._req(
|
|
478
|
+
f"/v1/datasources/{quote(datasource_name, safe='')}/{mode}?{urlencode(params, safe='')}",
|
|
479
|
+
method="POST",
|
|
480
|
+
data=b"",
|
|
481
|
+
)
|
|
482
|
+
if not isinstance(res, dict):
|
|
483
|
+
raise RuntimeError("We couldn't confirm that your import started. Please try again.")
|
|
484
|
+
if "error" in res:
|
|
485
|
+
raise Exception(res["error"])
|
|
486
|
+
return res
|
|
487
|
+
|
|
451
488
|
def datasource_delete(self, datasource_name: str, force: bool = False, dry_run: bool = False):
|
|
452
489
|
params = {"force": "true" if force else "false", "dry_run": "true" if dry_run else "false"}
|
|
453
490
|
return self._req(f"/v0/datasources/{datasource_name}?{urlencode(params)}", method="DELETE")
|