tinybird 4.6.13.dev0__tar.gz → 4.6.15__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.13.dev0 → tinybird-4.6.15}/PKG-INFO +25 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/ch_utils/engine.py +6 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/client.py +26 -23
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/config.py +0 -4
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datafile/common.py +22 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datafile/parse_pipe.py +30 -6
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/feedback_manager.py +7 -3
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/service_datasources.py +43 -19
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/sql_template.py +51 -2
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/sql_toolset.py +1 -6
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/__cli__.py +2 -2
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/client.py +39 -16
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/build_common.py +10 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/common.py +25 -3
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/config.py +1 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/build.py +1 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/pipe_checker.py +6 -2
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/playground.py +1 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/pull.py +1 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datasource.py +120 -36
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/deployment.py +5 -8
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/deployment_common.py +83 -13
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/feedback_manager.py +0 -3
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/common.py +66 -3
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/config.py +1 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/datasource.py +27 -3
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/workspace.py +10 -6
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tornado_template.py +5 -5
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird.egg-info/PKG-INFO +25 -1
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/setup.cfg +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/__cli__.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/ch_utils/constants.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/check_pypi.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/context.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datafile/exceptions.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datafile/parse_connection.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datafile/parse_datasource.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/datatypes.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/git_settings.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/iterating/__init__.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/iterating/data_branch_modes.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/prompts.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/sql.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/sql_template_fmt.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/syncasync.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/check_pypi.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/cli.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/config.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/branch.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/build.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/cicd.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/cli.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/connection.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/connection_dynamodb.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/connection_kafka.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/connection_s3.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/copy.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/create.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/build_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/diff.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/fixture.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/format_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/format_connection.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/deprecations.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/endpoint.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/exceptions.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/fmt.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/info.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/infra.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/job.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/job_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/llm.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/llm_utils.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/local.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/local_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/local_logs.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/login.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/login_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/logout.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/logs.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/materialization.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/open.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/pipe.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/preview.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/project.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/project_commands.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/py_project.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/query_output.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/regions.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/secret.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/secret_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/sink.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/table.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/telemetry.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/test.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/test_common.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/token.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/ts_project.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/watch.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/workspace.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb/modules/workspace_members.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/auth.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/branch.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/cicd.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/cli.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/connection.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/exceptions.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/fmt.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/job.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/pipe.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/regions.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/tag.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/telemetry.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/test.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird/tb_cli_modules/workspace_members.py +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird.egg-info/SOURCES.txt +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird.egg-info/dependency_links.txt +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird.egg-info/entry_points.txt +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/tinybird.egg-info/requires.txt +0 -0
- {tinybird-4.6.13.dev0 → tinybird-4.6.15}/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.15
|
|
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,28 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
|
|
|
52
52
|
Changelog
|
|
53
53
|
----------
|
|
54
54
|
|
|
55
|
+
4.6.15
|
|
56
|
+
********
|
|
57
|
+
|
|
58
|
+
- `Fixed` ``tb deploy`` waits for the old deployment to delete to avoid clashing with a new deployment submission.
|
|
59
|
+
|
|
60
|
+
4.6.14
|
|
61
|
+
********
|
|
62
|
+
|
|
63
|
+
- `Changed` ``tb datasource append`` and ``tb datasource replace`` submit local CSV, NDJSON, and Parquet files through the v1 import endpoint by default. ``tb datasource append --events`` also submits its request body through v1. These asynchronous imports print the queued job ID; pass ``--wait`` to wait for completion. Remote URL imports continue to use their existing route.
|
|
64
|
+
|
|
65
|
+
4.6.14
|
|
66
|
+
********
|
|
67
|
+
|
|
68
|
+
- `Fixed` `tb migrate-to-forward` correctly finds and offers to delete all branches, not just those created by the user running the migration.
|
|
69
|
+
|
|
70
|
+
4.6.13
|
|
71
|
+
********
|
|
72
|
+
|
|
73
|
+
- `Fixed` `tb deploy` and `tb build` now reject pipes that declare a parameter without a valid name (e.g. a numeric-literal cast like ``{{Float32(1682892000.0)}}``), instead of creating an unusable parameter.
|
|
74
|
+
- `Added` ``tb datasource append`` and ``tb datasource replace`` support the experimental ``--experimental=use_v1`` option for local CSV, NDJSON, and Parquet files. It submits the import through the v1 endpoint and prints the queued job ID; pass ``--wait`` to wait for completion.
|
|
75
|
+
- `Fixed` Forward builds branch with vendored files without incorrectly detecting drift.
|
|
76
|
+
|
|
55
77
|
4.6.12
|
|
56
78
|
********
|
|
57
79
|
|
|
@@ -69,6 +91,7 @@ Changelog
|
|
|
69
91
|
|
|
70
92
|
- `Removed` `tinybird.bi_stats_rt` from `tb logs` sources. The BI Connector `bi_stats` / `bi_stats_rt` service datasources are deprecated.
|
|
71
93
|
|
|
94
|
+
|
|
72
95
|
4.6.9
|
|
73
96
|
*******
|
|
74
97
|
|
|
@@ -77,6 +100,7 @@ Changelog
|
|
|
77
100
|
4.6.8
|
|
78
101
|
*******
|
|
79
102
|
|
|
103
|
+
- `Changed` Upgraded Tornado to 6.5.5.
|
|
80
104
|
- `Fixed` Tinybird Local CLI commands to respect Git branch local workspaces correctly.
|
|
81
105
|
|
|
82
106
|
4.6.7
|
|
@@ -476,6 +476,9 @@ def engine_options(columns, options: List[EngineOption], args: Dict):
|
|
|
476
476
|
if option_value:
|
|
477
477
|
if o.sql.lower() == "settings":
|
|
478
478
|
engine_settings = f"{o.sql} {option_value}"
|
|
479
|
+
# Conditional TTLs fail if we wrap the value in parentheses
|
|
480
|
+
elif o.sql.lower() == "ttl":
|
|
481
|
+
options_values.append(f"{o.sql} {option_value}")
|
|
479
482
|
else:
|
|
480
483
|
options_values.append(f"{o.sql} ({option_value})")
|
|
481
484
|
|
|
@@ -536,7 +539,9 @@ def engine_full_from_dict(
|
|
|
536
539
|
>>> engine_full_from_dict('MergeTree', {'sorting_key': 'local_date, cod_store'}, schema=schema)
|
|
537
540
|
'MergeTree() ORDER BY (local_date, cod_store)'
|
|
538
541
|
>>> 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
|
|
542
|
+
'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'
|
|
543
|
+
>>> engine_full_from_dict('MergeTree', {'sorting_key': 'timestamp', 'ttl': 'timestamp + INTERVAL 1 DAY DELETE WHERE is_deleted = 1'}, schema=schema)
|
|
544
|
+
'MergeTree() ORDER BY (timestamp) TTL timestamp + INTERVAL 1 DAY DELETE WHERE is_deleted = 1'
|
|
540
545
|
|
|
541
546
|
>>> schema = ''
|
|
542
547
|
>>> engine_full_from_dict('CollapsingMergeTree', {'sign': 'sign_column'}, schema=schema)
|
|
@@ -411,7 +411,7 @@ class TinyB:
|
|
|
411
411
|
sql_condition: Optional[str] = None,
|
|
412
412
|
format: str = "csv",
|
|
413
413
|
replace_options: Optional[Set[str]] = None,
|
|
414
|
-
):
|
|
414
|
+
) -> Dict[str, Any]:
|
|
415
415
|
params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
|
|
416
416
|
|
|
417
417
|
if sql_condition:
|
|
@@ -437,35 +437,38 @@ class TinyB:
|
|
|
437
437
|
datasource_name: str,
|
|
438
438
|
file: Union[str, Path],
|
|
439
439
|
mode: str = "append",
|
|
440
|
-
status_callback=None,
|
|
440
|
+
status_callback=None, # noqa: ARG002 - kept for v0 caller compatibility
|
|
441
441
|
sql_condition: Optional[str] = None,
|
|
442
442
|
format: str = "csv",
|
|
443
443
|
replace_options: Optional[Set[str]] = None,
|
|
444
444
|
):
|
|
445
|
-
|
|
446
|
-
|
|
445
|
+
# V1 returns the import job immediately, so waiting and progress reporting
|
|
446
|
+
# belong to the caller.
|
|
447
|
+
async with aiofiles.open(file, "rb") as content:
|
|
448
|
+
file_content = await content.read()
|
|
449
|
+
v1_params = {"format": format}
|
|
447
450
|
if sql_condition:
|
|
448
|
-
|
|
451
|
+
v1_params["replace_condition"] = sql_condition
|
|
449
452
|
if replace_options:
|
|
450
453
|
for option in list(replace_options):
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
454
|
+
v1_params[option] = "true"
|
|
455
|
+
content_types = {
|
|
456
|
+
"csv": "text/csv",
|
|
457
|
+
"ndjson": "application/x-ndjson",
|
|
458
|
+
"parquet": "application/vnd.apache.parquet",
|
|
459
|
+
}
|
|
460
|
+
headers = {"Content-Type": content_types[format]}
|
|
461
|
+
if str(file).endswith(".gz"):
|
|
462
|
+
headers["Content-Encoding"] = "gzip"
|
|
463
|
+
res = await self._req(
|
|
464
|
+
f"/v1/datasources/{quote(datasource_name, safe='')}/{mode}?{urlencode(v1_params, safe='')}",
|
|
465
|
+
data=file_content,
|
|
466
|
+
headers=headers,
|
|
467
|
+
method="POST",
|
|
468
|
+
)
|
|
469
|
+
if not isinstance(res, dict):
|
|
470
|
+
raise RuntimeError("We couldn't confirm that your import started. Please try again.")
|
|
471
|
+
return res
|
|
469
472
|
|
|
470
473
|
async def datasource_truncate(self, datasource_name: str):
|
|
471
474
|
return await self._req(f"/v0/datasources/{datasource_name}/truncate", method="POST", data="")
|
|
@@ -49,11 +49,7 @@ LEGACY_HOSTS = {
|
|
|
49
49
|
"https://ui.europe-west2.gcp.tinybird.co": "https://cloud.tinybird.co/gcp/europe-west2",
|
|
50
50
|
"https://ui.ap-east.aws.tinybird.co": "https://app.tinybird.co/aws/ap-east-1",
|
|
51
51
|
"https://ui.ap-east-1.aws.tinybird.co": "https://app.tinybird.co/aws/ap-east-1",
|
|
52
|
-
"https://ui.split.tinybird.co": "https://app.tinybird.co/aws/split-us-east",
|
|
53
|
-
"https://ui.split.us-west-2.aws.tinybird.co": "https://app.tinybird.co/aws/split-us-west-2",
|
|
54
52
|
"https://ui.northamerica-northeast2.gcp.tinybird.co": "https://app.tinybird.co/gcp/northamerica-northeast2",
|
|
55
|
-
"https://api.split.tinybird.co": "https://app.tinybird.co/aws/split-us-east",
|
|
56
|
-
"https://api.split.us-west-2.aws.tinybird.co": "https://app.tinybird.co/aws/split-us-west-2",
|
|
57
53
|
"https://ui.wadus1.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus1",
|
|
58
54
|
"https://ui.wadus2.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus2",
|
|
59
55
|
"https://ui.wadus3.gcp.tinybird.co": "https://app.wadus.tinybird.co/gcp/wadus3",
|
|
@@ -278,6 +278,12 @@ REQUIRED_DYNAMODB_PARAMS = {
|
|
|
278
278
|
}
|
|
279
279
|
DYNAMODB_PARAMS = REQUIRED_DYNAMODB_PARAMS
|
|
280
280
|
|
|
281
|
+
REQUIRED_POSTGRES_PARAMS = {
|
|
282
|
+
"pg_connection_name",
|
|
283
|
+
"pg_table",
|
|
284
|
+
}
|
|
285
|
+
POSTGRES_PARAMS = REQUIRED_POSTGRES_PARAMS
|
|
286
|
+
|
|
281
287
|
|
|
282
288
|
def extract_column_names_from_sorting_key_part(part: str) -> List[str]:
|
|
283
289
|
"""
|
|
@@ -602,6 +608,13 @@ class Datafile:
|
|
|
602
608
|
raise DatafileValidationError(
|
|
603
609
|
f"Invalid import schedule '{node['import_schedule']}'. Only {sorted(VALID_BLOB_STORAGE_CRON_VALUES)} values are allowed"
|
|
604
610
|
)
|
|
611
|
+
# Validate Postgres params
|
|
612
|
+
if any(param in node for param in POSTGRES_PARAMS) and (
|
|
613
|
+
missing := [param for param in REQUIRED_POSTGRES_PARAMS if param not in node]
|
|
614
|
+
):
|
|
615
|
+
raise DatafileValidationError(
|
|
616
|
+
f"Some Postgres params have been provided, but the following required ones are missing: {missing}"
|
|
617
|
+
)
|
|
605
618
|
|
|
606
619
|
else:
|
|
607
620
|
# We cannot validate a datafile whose kind is unknown
|
|
@@ -2068,6 +2081,8 @@ def parse(
|
|
|
2068
2081
|
"import_query": assign_var("import_query"), # Deprecated, BQ and SFK
|
|
2069
2082
|
"import_table_arn": assign_var("import_table_arn"), # Only for DynamoDB
|
|
2070
2083
|
"import_export_bucket": assign_var("import_export_bucket"), # For DynamoDB
|
|
2084
|
+
"pg_connection_name": assign_var("pg_connection_name"),
|
|
2085
|
+
"pg_table": assign_var("pg_table"),
|
|
2071
2086
|
"shared_with": shared_with,
|
|
2072
2087
|
"export_service": export_service, # Deprecated
|
|
2073
2088
|
"forward_query": forward_query,
|
|
@@ -2091,6 +2106,7 @@ def parse(
|
|
|
2091
2106
|
"sql": sql("sql"),
|
|
2092
2107
|
"version": version,
|
|
2093
2108
|
"deployment_method": assign_var("deployment_method", allowed_values={"alter"}),
|
|
2109
|
+
"export_service": export_service, # Deprecated
|
|
2094
2110
|
"export_connection_name": assign_var("export_connection_name"),
|
|
2095
2111
|
"export_schedule": assign_var("export_schedule"),
|
|
2096
2112
|
"export_bucket_uri": assign_var("export_bucket_uri"),
|
|
@@ -2127,6 +2143,12 @@ def parse(
|
|
|
2127
2143
|
"gcs_secret": assign_var("gcs_hmac_secret"),
|
|
2128
2144
|
"gcs_hmac_access_id": assign_var("gcs_hmac_access_id"),
|
|
2129
2145
|
"gcs_hmac_secret": assign_var("gcs_hmac_secret"),
|
|
2146
|
+
"pg_host": assign_var("pg_host"),
|
|
2147
|
+
"pg_port": assign_var("pg_port"),
|
|
2148
|
+
"pg_database": assign_var("pg_database"),
|
|
2149
|
+
"pg_user": assign_var("pg_user"),
|
|
2150
|
+
"pg_password": assign_var("pg_password"),
|
|
2151
|
+
"pg_ssl_mode": assign_var("pg_ssl_mode"),
|
|
2130
2152
|
"include": include,
|
|
2131
2153
|
},
|
|
2132
2154
|
}
|
|
@@ -11,7 +11,11 @@ from tinybird.datafile.common import (
|
|
|
11
11
|
parse,
|
|
12
12
|
)
|
|
13
13
|
from tinybird.datafile.exceptions import IncludeFileNotFoundException, ParseException
|
|
14
|
-
from tinybird.sql_template import
|
|
14
|
+
from tinybird.sql_template import (
|
|
15
|
+
get_template_and_variables,
|
|
16
|
+
render_sql_template,
|
|
17
|
+
validate_template_parameter_names,
|
|
18
|
+
)
|
|
15
19
|
from tinybird.tb.modules.feedback_manager import FeedbackManager
|
|
16
20
|
from tinybird.tornado_template import UnClosedIfError
|
|
17
21
|
|
|
@@ -52,20 +56,38 @@ def parse_pipe(
|
|
|
52
56
|
if "type" in node:
|
|
53
57
|
node["type"] = node["type"].lower()
|
|
54
58
|
sql = node.get("sql", "")
|
|
55
|
-
|
|
59
|
+
source_sql = sql.strip()
|
|
60
|
+
if not source_sql:
|
|
61
|
+
raise click.ClickException(
|
|
62
|
+
FeedbackManager.error_parsing_node(node=node["name"], pipe=filename, error="Empty query")
|
|
63
|
+
)
|
|
64
|
+
if source_sql.startswith("%"):
|
|
56
65
|
secrets_list: Optional[List[str]] = None
|
|
57
66
|
if secrets:
|
|
58
67
|
secrets_list = list(secrets.keys())
|
|
68
|
+
# Reject parameters declared without a usable name (e.g. a numeric
|
|
69
|
+
# literal cast like `{{Float32(1682892000.0)}}`) before rendering.
|
|
70
|
+
validate_template_parameter_names(sql[1:], node_id=node["name"])
|
|
59
71
|
# Setting test_mode=True to ignore errors on required parameters and
|
|
60
72
|
# secrets_in_test_mode=False to raise errors on missing secrets
|
|
61
73
|
sql, _, variable_warnings = render_sql_template(
|
|
62
|
-
|
|
74
|
+
source_sql[1:],
|
|
63
75
|
name=node["name"],
|
|
64
76
|
secrets=secrets_list,
|
|
65
77
|
test_mode=True,
|
|
66
78
|
secrets_in_test_mode=ignore_secrets,
|
|
67
79
|
)
|
|
68
80
|
doc.warnings = variable_warnings
|
|
81
|
+
if not sql.strip():
|
|
82
|
+
raise click.ClickException(
|
|
83
|
+
FeedbackManager.error_parsing_node(
|
|
84
|
+
node=node["name"],
|
|
85
|
+
pipe=filename,
|
|
86
|
+
error="Template renders to an empty query without parameters",
|
|
87
|
+
)
|
|
88
|
+
)
|
|
89
|
+
else:
|
|
90
|
+
sql = source_sql
|
|
69
91
|
# it'll fail with a ModuleNotFoundError when the toolset is not available but it returns the parsed doc
|
|
70
92
|
from tinybird.sql_toolset import format_sql as toolset_format_sql
|
|
71
93
|
|
|
@@ -79,13 +101,15 @@ def parse_pipe(
|
|
|
79
101
|
)
|
|
80
102
|
)
|
|
81
103
|
except ValueError as e:
|
|
82
|
-
|
|
104
|
+
source_sql = node.get("sql", "")
|
|
105
|
+
source_sql_stripped = source_sql.strip()
|
|
106
|
+
t, template_variables, _ = get_template_and_variables(source_sql, name=node["name"])
|
|
83
107
|
|
|
84
|
-
if
|
|
108
|
+
if not source_sql_stripped.startswith("%") and len(template_variables) > 0:
|
|
85
109
|
raise click.ClickException(FeedbackManager.error_template_start(filename=filename))
|
|
86
110
|
raise click.ClickException(
|
|
87
111
|
FeedbackManager.error_parsing_file(
|
|
88
|
-
filename=filename, lineno="", error=f"{str(e)} + SQL(value error): {
|
|
112
|
+
filename=filename, lineno="", error=f"{str(e)} + SQL(value error): {source_sql}"
|
|
89
113
|
)
|
|
90
114
|
)
|
|
91
115
|
except UnClosedIfError as e:
|
|
@@ -242,10 +242,11 @@ class FeedbackManager:
|
|
|
242
242
|
error_organization_index = error_message(
|
|
243
243
|
"Error selecting organization '{organization_index}'. Select a valid index or 0 to cancel"
|
|
244
244
|
)
|
|
245
|
-
warning_none_organization = warning_message(
|
|
246
|
-
"Tinybird is now based on organizations. Please, go to the UI ({ui_host}) to follow the migration process. \nYour workspace will be created any way."
|
|
247
|
-
)
|
|
248
245
|
error_while_fetching_orgs = error_message("Error while fetching organizations: {error}")
|
|
246
|
+
error_classic_workspace_creation_deprecated = error_message(
|
|
247
|
+
"The Classic experience is deprecated. New workspaces should be created in Forward. "
|
|
248
|
+
"Migration guide: https://www.tinybird.co/docs/forward/guides/migrate-from-classic"
|
|
249
|
+
)
|
|
249
250
|
error_deleted_include = error_message(
|
|
250
251
|
"Related include file {include_file} was deleted and it's used in {filename}. Delete or remove dependency from {filename}."
|
|
251
252
|
)
|
|
@@ -864,6 +865,9 @@ class FeedbackManager:
|
|
|
864
865
|
success_total_rows = success_message("** Total rows in {datasource}: {total_rows}")
|
|
865
866
|
success_appended_datasource = success_message("** Data appended to Data Source '{datasource}' successfully!")
|
|
866
867
|
success_replaced_datasource = success_message("** Data replaced in Data Source '{datasource}' successfully!")
|
|
868
|
+
success_import_job_queued = success_message("** {operation} import job queued: {job_id}")
|
|
869
|
+
success_import_job_completed = success_message("** {operation} import completed: {job_id}")
|
|
870
|
+
info_import_job_status = info_message("Check status: tb job details {job_id}")
|
|
867
871
|
success_auth = success_message(
|
|
868
872
|
"** Auth successful! \n** Configuration written to .tinyb file, consider adding it to .gitignore"
|
|
869
873
|
)
|
|
@@ -19,12 +19,12 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
19
19
|
return [
|
|
20
20
|
{
|
|
21
21
|
"name": "tinybird.pipe_stats_rt",
|
|
22
|
-
"description": "Contains information about all requests made to your API endpoints in real time. This data source has a TTL of
|
|
22
|
+
"description": "Contains information about all requests made to your API endpoints in real time. This data source has a TTL of 3 months. If you need to query data older than 3 months you must use the aggregated by day data available at tinybird.pipe_stats.",
|
|
23
23
|
"dateColumn": "start_datetime",
|
|
24
24
|
"engine": {
|
|
25
25
|
"engine": "MergeTree",
|
|
26
26
|
"sorting_key": "pipe_id, start_datetime",
|
|
27
|
-
"ttl": "start_datetime +
|
|
27
|
+
"ttl": "start_datetime + toIntervalMonth(3)",
|
|
28
28
|
},
|
|
29
29
|
"columns": [
|
|
30
30
|
{"name": "start_datetime", "type": "DateTime"},
|
|
@@ -48,6 +48,7 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
48
48
|
{"name": "client_ip", "type": "Nullable(String)"},
|
|
49
49
|
{"name": "resource_tags", "type": "Array(String)"},
|
|
50
50
|
{"name": "memory_usage", "type": "UInt64"},
|
|
51
|
+
{"name": "replica", "type": "String"},
|
|
51
52
|
],
|
|
52
53
|
},
|
|
53
54
|
{
|
|
@@ -333,6 +334,25 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
|
|
|
333
334
|
{"name": "job_metadata", "type": "String"},
|
|
334
335
|
],
|
|
335
336
|
},
|
|
337
|
+
{
|
|
338
|
+
"name": "tinybird.rate_limits_log",
|
|
339
|
+
"description": "Endpoint concurrency limiter transitions in your workspace.",
|
|
340
|
+
"dateColumn": "timestamp",
|
|
341
|
+
"engine": {
|
|
342
|
+
"engine": "MergeTree",
|
|
343
|
+
"sorting_key": "timestamp, pipe_name",
|
|
344
|
+
"partition_key": "toYYYYMM(timestamp)",
|
|
345
|
+
},
|
|
346
|
+
"columns": [
|
|
347
|
+
{"name": "timestamp", "type": "DateTime64(3)"},
|
|
348
|
+
{"name": "workspace_id", "type": "String"},
|
|
349
|
+
{"name": "workspace_name", "type": "String"},
|
|
350
|
+
{"name": "pipe_name", "type": "String"},
|
|
351
|
+
{"name": "rate_limit_type", "type": "LowCardinality(String)"},
|
|
352
|
+
{"name": "action", "type": "LowCardinality(String)"},
|
|
353
|
+
{"name": "limit_metadata", "type": "String"},
|
|
354
|
+
],
|
|
355
|
+
},
|
|
336
356
|
{
|
|
337
357
|
"name": "tinybird.hook_log",
|
|
338
358
|
"description": "Log of hook executions and their results.",
|
|
@@ -518,22 +538,6 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
518
538
|
{"name": "origin", "type": "String"},
|
|
519
539
|
],
|
|
520
540
|
},
|
|
521
|
-
{
|
|
522
|
-
"name": "organization.processed_data",
|
|
523
|
-
"description": "Information related to all processed data per day per workspace.",
|
|
524
|
-
"dateColumn": "date",
|
|
525
|
-
"engine": {
|
|
526
|
-
"engine": "SummingMergeTree",
|
|
527
|
-
"sorting_key": "database, date",
|
|
528
|
-
"partition_key": "toYYYYMM(date)",
|
|
529
|
-
},
|
|
530
|
-
"columns": [
|
|
531
|
-
{"name": "date", "type": "Date"},
|
|
532
|
-
{"name": "database", "type": "String"},
|
|
533
|
-
{"name": "read_bytes", "type": "UInt64"},
|
|
534
|
-
{"name": "written_bytes", "type": "UInt64"},
|
|
535
|
-
],
|
|
536
|
-
},
|
|
537
541
|
{
|
|
538
542
|
"name": "organization.datasources_storage",
|
|
539
543
|
"description": "Similar to tinybird.datasources_storage but with data for all Organization Workspaces.",
|
|
@@ -659,7 +663,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
659
663
|
"engine": {
|
|
660
664
|
"engine": "MergeTree",
|
|
661
665
|
"sorting_key": "workspace_id, pipe_id, start_datetime",
|
|
662
|
-
"ttl": "start_datetime +
|
|
666
|
+
"ttl": "start_datetime + toIntervalMonth(3)",
|
|
663
667
|
},
|
|
664
668
|
"columns": [
|
|
665
669
|
{"name": "workspace_id", "type": "String"},
|
|
@@ -683,6 +687,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
683
687
|
{"name": "user_agent", "type": "Nullable(String)"},
|
|
684
688
|
{"name": "resource_tags", "type": "Array(String)"},
|
|
685
689
|
{"name": "memory_usage", "type": "UInt64"},
|
|
690
|
+
{"name": "replica", "type": "String"},
|
|
686
691
|
],
|
|
687
692
|
},
|
|
688
693
|
{
|
|
@@ -726,6 +731,25 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
|
|
|
726
731
|
{"name": "job_metadata", "type": "String"},
|
|
727
732
|
],
|
|
728
733
|
},
|
|
734
|
+
{
|
|
735
|
+
"name": "organization.rate_limits_log",
|
|
736
|
+
"description": "Endpoint concurrency limiter transitions across the organization.",
|
|
737
|
+
"dateColumn": "timestamp",
|
|
738
|
+
"engine": {
|
|
739
|
+
"engine": "MergeTree",
|
|
740
|
+
"sorting_key": "workspace_id, timestamp, pipe_name",
|
|
741
|
+
"partition_key": "toYYYYMM(timestamp)",
|
|
742
|
+
},
|
|
743
|
+
"columns": [
|
|
744
|
+
{"name": "timestamp", "type": "DateTime64(3)"},
|
|
745
|
+
{"name": "workspace_id", "type": "String"},
|
|
746
|
+
{"name": "workspace_name", "type": "String"},
|
|
747
|
+
{"name": "pipe_name", "type": "String"},
|
|
748
|
+
{"name": "rate_limit_type", "type": "LowCardinality(String)"},
|
|
749
|
+
{"name": "action", "type": "LowCardinality(String)"},
|
|
750
|
+
{"name": "limit_metadata", "type": "String"},
|
|
751
|
+
],
|
|
752
|
+
},
|
|
729
753
|
{
|
|
730
754
|
"name": "organization.sinks_ops_log",
|
|
731
755
|
"description": "Historic Logs for all Sink job executions across the organization",
|
|
@@ -1396,7 +1396,7 @@ _namespace = {
|
|
|
1396
1396
|
}
|
|
1397
1397
|
|
|
1398
1398
|
|
|
1399
|
-
reserved_vars =
|
|
1399
|
+
reserved_vars = {"_tt_tmp", "_tt_append", "isinstance", "str", "error", "custom_error", *list(vars(builtins))}
|
|
1400
1400
|
for p in DEFAULT_PARAM_NAMES: # we handle these in an specific manner
|
|
1401
1401
|
reserved_vars.discard(p) # `format` is part of builtins
|
|
1402
1402
|
# Allow 'id' to be used as a template parameter - https://gitlab.com/tinybird/analytics/-/issues/19119
|
|
@@ -2263,7 +2263,12 @@ def get_var_names_and_types(t: Template, node_id: Optional[str] = None) -> List[
|
|
|
2263
2263
|
vars_out.extend(
|
|
2264
2264
|
parse_statement_code_if_new_vars(statement_code, skip_names=typed_names | statement_expr_names)
|
|
2265
2265
|
)
|
|
2266
|
-
|
|
2266
|
+
# Inside a {% %} statement a type function with a literal first arg
|
|
2267
|
+
# (e.g. {% set x = Float32(1.5) %}) is a plain value cast: the
|
|
2268
|
+
# assigned variable is the parameter, the literal is not. Drop
|
|
2269
|
+
# these artifacts so they don't surface as bogus params nor get
|
|
2270
|
+
# rejected by validate_template_parameter_names.
|
|
2271
|
+
return [vd for vd in vars_out if isinstance(vd["name"], str) and vd["name"]]
|
|
2267
2272
|
|
|
2268
2273
|
def _n(chunks: list, vars_out: list[dict[str, Any]]) -> None:
|
|
2269
2274
|
for x in chunks:
|
|
@@ -2310,6 +2315,43 @@ def get_var_names_and_types_cached(t: Template):
|
|
|
2310
2315
|
return get_var_names_and_types(t)
|
|
2311
2316
|
|
|
2312
2317
|
|
|
2318
|
+
def validate_template_parameter_names(sql: str, node_id: Optional[str] = None) -> None:
|
|
2319
|
+
"""Reject template parameters that don't have a usable name.
|
|
2320
|
+
|
|
2321
|
+
Type functions such as ``{{Float32(x)}}`` double as casts, so a literal
|
|
2322
|
+
first argument (e.g. ``{{Float32(1682892000.0)}}`` or ``{{String('')}}``)
|
|
2323
|
+
is parsed as a parameter whose name is a number or an empty string instead
|
|
2324
|
+
of an identifier. Such a "parameter" can never be supplied by a caller and
|
|
2325
|
+
breaks parameter metadata consumers, so it must be rejected at write time.
|
|
2326
|
+
|
|
2327
|
+
Raises ``ValueError`` with the same wording used elsewhere for invalid names.
|
|
2328
|
+
|
|
2329
|
+
Templates that fail to parse are left untouched: there are no parameters to
|
|
2330
|
+
validate, and reporting/deferring those errors is the job of the existing
|
|
2331
|
+
render/validation flow (some paths, e.g. ``ignore_sql_errors``, tolerate
|
|
2332
|
+
unparseable SQL on purpose). This keeps the check strictly additive. The
|
|
2333
|
+
only exception is the invalid-name ``ValueError`` that extraction itself
|
|
2334
|
+
raises (e.g. for a list literal used as first argument), which is exactly
|
|
2335
|
+
what this validator exists to report.
|
|
2336
|
+
"""
|
|
2337
|
+
try:
|
|
2338
|
+
params = get_var_names_and_types(Template(sql), node_id=node_id)
|
|
2339
|
+
except SQLTemplateException:
|
|
2340
|
+
# Template-level problem (e.g. a SecurityException converted by
|
|
2341
|
+
# get_var_names_and_types), not an invalid parameter name.
|
|
2342
|
+
return
|
|
2343
|
+
except ValueError:
|
|
2344
|
+
# Extraction itself rejects some invalid names (e.g. a list literal
|
|
2345
|
+
# as first argument) with the same ValueError this function raises.
|
|
2346
|
+
raise
|
|
2347
|
+
except Exception:
|
|
2348
|
+
return
|
|
2349
|
+
for param in params:
|
|
2350
|
+
name = param.get("name")
|
|
2351
|
+
if not isinstance(name, str) or not name:
|
|
2352
|
+
raise ValueError(f'"{name}" can not be used as a variable name')
|
|
2353
|
+
|
|
2354
|
+
|
|
2313
2355
|
def wrap_vars(t, escape_arrays: bool = False):
|
|
2314
2356
|
def _n(chunks, v):
|
|
2315
2357
|
for x in chunks:
|
|
@@ -2851,6 +2893,13 @@ def render_sql_template(
|
|
|
2851
2893
|
documentation="/cli/advanced-templates.html",
|
|
2852
2894
|
)
|
|
2853
2895
|
raise SQLTemplateException(str(e), documentation="/cli/advanced-templates.html")
|
|
2896
|
+
except KeyError as e:
|
|
2897
|
+
key = e.args[0] if e.args else "unknown"
|
|
2898
|
+
raise SQLTemplateException(
|
|
2899
|
+
f"Missing key '{key}' while rendering the template. "
|
|
2900
|
+
"Ensure every object accessed in the template has this key.",
|
|
2901
|
+
documentation="/cli/advanced-templates.html",
|
|
2902
|
+
)
|
|
2854
2903
|
except IndexError as e:
|
|
2855
2904
|
# This happens when trying to access string indices on empty strings
|
|
2856
2905
|
if "string index out of range" in str(e):
|
|
@@ -493,7 +493,6 @@ def replace_tables_chquery_cached(
|
|
|
493
493
|
sorted_replacements: Optional[tuple] = None,
|
|
494
494
|
default_database: str = "",
|
|
495
495
|
output_one_line: bool = False,
|
|
496
|
-
timestamp: Optional[datetime] = None,
|
|
497
496
|
function_allow_list: Optional[FrozenSet[str]] = None,
|
|
498
497
|
settings_allow_list: Optional[FrozenSet[str]] = None,
|
|
499
498
|
) -> str:
|
|
@@ -536,7 +535,6 @@ def replace_tables(
|
|
|
536
535
|
sql,
|
|
537
536
|
None,
|
|
538
537
|
output_one_line=output_one_line,
|
|
539
|
-
timestamp=timestamp,
|
|
540
538
|
function_allow_list=hashable_list,
|
|
541
539
|
settings_allow_list=hashable_settings_list,
|
|
542
540
|
)
|
|
@@ -610,7 +608,6 @@ def replace_tables(
|
|
|
610
608
|
sql,
|
|
611
609
|
None,
|
|
612
610
|
output_one_line=output_one_line,
|
|
613
|
-
timestamp=timestamp,
|
|
614
611
|
function_allow_list=hashable_list,
|
|
615
612
|
settings_allow_list=hashable_settings_list,
|
|
616
613
|
)
|
|
@@ -643,13 +640,12 @@ def replace_tables(
|
|
|
643
640
|
|
|
644
641
|
if current_replacements:
|
|
645
642
|
# We need to transform the dictionary into something cacheable, so a sorted tuple of tuples it is
|
|
646
|
-
r = tuple(sorted(
|
|
643
|
+
r = tuple(sorted(current_replacements.items()))
|
|
647
644
|
sql = replace_tables_chquery_cached(
|
|
648
645
|
sql,
|
|
649
646
|
r,
|
|
650
647
|
default_database=default_database,
|
|
651
648
|
output_one_line=output_one_line,
|
|
652
|
-
timestamp=timestamp,
|
|
653
649
|
function_allow_list=hashable_list,
|
|
654
650
|
settings_allow_list=hashable_settings_list,
|
|
655
651
|
)
|
|
@@ -658,7 +654,6 @@ def replace_tables(
|
|
|
658
654
|
sql,
|
|
659
655
|
None,
|
|
660
656
|
output_one_line=output_one_line,
|
|
661
|
-
timestamp=timestamp,
|
|
662
657
|
function_allow_list=hashable_list,
|
|
663
658
|
settings_allow_list=hashable_settings_list,
|
|
664
659
|
)
|
|
@@ -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.15'
|
|
8
|
+
__revision__ = '026318e'
|
|
@@ -435,7 +435,7 @@ class TinyB:
|
|
|
435
435
|
sql_condition: Optional[str] = None,
|
|
436
436
|
format: str = "csv",
|
|
437
437
|
replace_options: Optional[Set[str]] = None,
|
|
438
|
-
):
|
|
438
|
+
) -> Dict[str, Any]:
|
|
439
439
|
params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
|
|
440
440
|
|
|
441
441
|
if sql_condition:
|
|
@@ -466,23 +466,46 @@ class TinyB:
|
|
|
466
466
|
format: str = "csv",
|
|
467
467
|
replace_options: Optional[Set[str]] = None,
|
|
468
468
|
):
|
|
469
|
-
|
|
470
|
-
|
|
469
|
+
# Keep this argument for SDK compatibility; v1 returns before import completion.
|
|
470
|
+
del status_callback
|
|
471
|
+
with open(file, "rb") as content:
|
|
472
|
+
file_content = content.read()
|
|
473
|
+
content_types = {
|
|
474
|
+
"csv": "text/csv",
|
|
475
|
+
"ndjson": "application/x-ndjson",
|
|
476
|
+
"parquet": "application/vnd.apache.parquet",
|
|
477
|
+
}
|
|
478
|
+
headers = {"Content-Type": content_types[format]}
|
|
479
|
+
if str(file).endswith(".gz"):
|
|
480
|
+
headers["Content-Encoding"] = "gzip"
|
|
481
|
+
v1_params = {"format": format}
|
|
471
482
|
if sql_condition:
|
|
472
|
-
|
|
483
|
+
v1_params["replace_condition"] = sql_condition
|
|
473
484
|
if replace_options:
|
|
474
485
|
for option in list(replace_options):
|
|
475
|
-
|
|
486
|
+
v1_params[option] = "true"
|
|
487
|
+
res = self._req(
|
|
488
|
+
f"/v1/datasources/{quote(datasource_name, safe='')}/{mode}?{urlencode(v1_params, safe='')}",
|
|
489
|
+
data=file_content,
|
|
490
|
+
headers=headers,
|
|
491
|
+
method="POST",
|
|
492
|
+
)
|
|
493
|
+
if not isinstance(res, dict):
|
|
494
|
+
raise RuntimeError("We couldn't confirm that your import started. Please try again.")
|
|
495
|
+
if "error" in res:
|
|
496
|
+
raise Exception(res["error"])
|
|
497
|
+
return res
|
|
476
498
|
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
499
|
+
def datasource_append_events(self, datasource_name: str, events: str) -> dict:
|
|
500
|
+
"""Submit NDJSON event data through the asynchronous v1 append route."""
|
|
501
|
+
res = self._req(
|
|
502
|
+
f"/v1/datasources/{quote(datasource_name, safe='')}/append?format=ndjson",
|
|
503
|
+
data=events.encode(),
|
|
504
|
+
headers={"Content-Type": "application/x-ndjson"},
|
|
505
|
+
method="POST",
|
|
506
|
+
)
|
|
507
|
+
if "error" in res:
|
|
508
|
+
raise Exception(res["error"])
|
|
486
509
|
return res
|
|
487
510
|
|
|
488
511
|
def datasource_truncate(self, datasource_name: str):
|
|
@@ -810,8 +833,8 @@ class TinyB:
|
|
|
810
833
|
def user_workspace_branches(self, version: str = "v0"):
|
|
811
834
|
return self._req(f"/{version}/user/workspaces/?with_environments=true&only_environments=true")
|
|
812
835
|
|
|
813
|
-
def branches(self):
|
|
814
|
-
return self._req("/
|
|
836
|
+
def branches(self, version: str = "v1"):
|
|
837
|
+
return self._req(f"/{version}/environments")
|
|
815
838
|
|
|
816
839
|
def releases(self, workspace_id):
|
|
817
840
|
return self._req(f"/v0/workspaces/{workspace_id}/releases")
|