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.
Files changed (128) hide show
  1. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/PKG-INFO +11 -1
  2. tinybird-4.6.21.dev0/pyproject.toml +228 -0
  3. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/ch_utils/constants.py +1 -0
  4. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/ch_utils/engine.py +49 -32
  5. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/client.py +37 -0
  6. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/service_datasources.py +7 -8
  7. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql.py +0 -6
  8. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_template.py +1 -0
  9. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_toolset.py +59 -0
  10. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/__cli__.py +2 -2
  11. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/client.py +37 -0
  12. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/common.py +15 -77
  13. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/infra.py +1 -2
  14. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/logs.py +1 -2
  15. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/materialization.py +1 -1
  16. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/project.py +4 -4
  17. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +4 -4
  18. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/token.py +2 -5
  19. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/watch.py +1 -1
  20. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/branch.py +1 -2
  21. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/cicd.py +2 -0
  22. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/cli.py +5 -9
  23. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/common.py +27 -94
  24. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/config.py +1 -2
  25. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
  26. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/pipe.py +9 -13
  27. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
  28. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/workspace.py +11 -13
  29. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tornado_template.py +12 -17
  30. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/PKG-INFO +11 -1
  31. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/SOURCES.txt +1 -0
  32. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/setup.cfg +0 -0
  33. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/__cli__.py +0 -0
  34. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/check_pypi.py +0 -0
  35. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/config.py +0 -0
  36. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/context.py +0 -0
  37. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/common.py +0 -0
  38. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/exceptions.py +0 -0
  39. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_connection.py +0 -0
  40. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  41. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  42. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/datatypes.py +0 -0
  43. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/feedback_manager.py +0 -0
  44. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/git_settings.py +0 -0
  45. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/iterating/__init__.py +0 -0
  46. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
  47. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/prompts.py +0 -0
  48. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/sql_template_fmt.py +0 -0
  49. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/syncasync.py +0 -0
  50. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/check_pypi.py +0 -0
  51. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/cli.py +0 -0
  52. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/config.py +0 -0
  53. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/branch.py +0 -0
  54. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/build.py +0 -0
  55. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/build_common.py +0 -0
  56. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/cicd.py +0 -0
  57. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/cli.py +0 -0
  58. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/config.py +0 -0
  59. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection.py +0 -0
  60. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  61. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
  62. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
  63. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/copy.py +0 -0
  64. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/create.py +0 -0
  65. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build.py +0 -0
  66. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  67. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  68. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  69. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
  70. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  71. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
  72. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  73. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  74. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  75. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  76. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/playground.py +0 -0
  77. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
  78. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/datasource.py +0 -0
  79. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deployment.py +0 -0
  80. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
  81. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  82. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  83. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/exceptions.py +0 -0
  84. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
  85. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/fmt.py +0 -0
  86. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/info.py +0 -0
  87. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/job.py +0 -0
  88. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/job_common.py +0 -0
  89. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/llm.py +0 -0
  90. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  91. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local.py +0 -0
  92. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local_common.py +0 -0
  93. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  94. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/login.py +0 -0
  95. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/login_common.py +0 -0
  96. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/logout.py +0 -0
  97. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/open.py +0 -0
  98. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/pipe.py +0 -0
  99. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/preview.py +0 -0
  100. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  101. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/py_project.py +0 -0
  102. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/query_output.py +0 -0
  103. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/regions.py +0 -0
  104. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/secret.py +0 -0
  105. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  106. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/sink.py +0 -0
  107. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/table.py +0 -0
  108. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  109. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/test.py +0 -0
  110. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/test_common.py +0 -0
  111. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  112. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/workspace.py +0 -0
  113. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  114. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli.py +0 -0
  115. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  116. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  117. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  118. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  119. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  120. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  121. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  122. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  123. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  124. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  125. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  126. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  127. {tinybird-4.6.20.dev0 → tinybird-4.6.21.dev0}/tinybird.egg-info/requires.txt +0 -0
  128. {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.20.dev0
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
+ ]
@@ -263,4 +263,5 @@ VALID_QUERY_FORMATS = (
263
263
  "JSONCompact",
264
264
  "JSONStringsEachRowWithProgress",
265
265
  "ODBCDriver2",
266
+ "Markdown",
266
267
  )
@@ -440,35 +440,64 @@ def get_engine_config(engine: str):
440
440
  )
441
441
 
442
442
 
443
- def engine_params(columns, params: List[EngineParam], args: Dict):
444
- params_values = []
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 args:
457
+ if p.required and p.name not in engine_settings:
447
458
  raise ValueError(f"Missing required parameter '{p.name}'")
448
- param_value = args.get(p.name, None) or p.default_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, check_type="parameter", columns=columns, tb_param=p.tb_param, value=param_value
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(columns, options: List[EngineOption], args: Dict):
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: str, columns: Optional[List], params: List[EngineParam], options: List[EngineOption], args: Dict
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
- if schema is not None and columns is not None:
610
- raise ValueError("You can not use 'schema' and 'columns' at the same time")
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": "database, host, run_validation, query_hash",
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": "region", "type": "String"},
456
- {"name": "workspace", "type": "String"},
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)"""
@@ -84,6 +84,7 @@ class SQLTemplateException(ValueError):
84
84
  JOB_TIMESTAMP_PARAM = "job_timestamp"
85
85
  DEFAULT_PARAM_NAMES = ["format", "q"]
86
86
  RESERVED_PARAM_NAMES = [
87
+ "__tb__csv_header",
87
88
  "__tb__semver",
88
89
  "debug_source_tables",
89
90
  "debug",
@@ -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.20.dev0'
8
- __revision__ = 'fee8cae'
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")