tinybird 2.0.1.dev0__tar.gz → 3.0.1__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 (156) hide show
  1. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/PKG-INFO +9 -3
  2. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/ch_utils/engine.py +2 -77
  3. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/config.py +0 -4
  4. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datafile/common.py +79 -27
  5. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/feedback_manager.py +0 -25
  6. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/sql.py +6 -5
  7. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/sql_template.py +389 -8
  8. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/sql_toolset.py +3 -6
  9. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/__cli__.py +2 -2
  10. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/utils.py +5 -3
  11. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/build.py +30 -3
  12. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/build_common.py +7 -1
  13. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/common.py +7 -3
  14. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/create.py +13 -8
  15. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/build_datasource.py +0 -6
  16. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/local_common.py +10 -3
  17. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/project.py +21 -10
  18. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/common.py +5 -2
  19. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tornado_template.py +8 -3
  20. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/PKG-INFO +9 -3
  21. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/setup.cfg +0 -0
  22. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/__cli__.py +0 -0
  23. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/ch_utils/constants.py +0 -0
  24. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/check_pypi.py +0 -0
  25. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/client.py +0 -0
  26. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/context.py +0 -0
  27. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datafile/exceptions.py +0 -0
  28. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datafile/parse_connection.py +0 -0
  29. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datafile/parse_datasource.py +0 -0
  30. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datafile/parse_pipe.py +0 -0
  31. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/datatypes.py +0 -0
  32. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/git_settings.py +0 -0
  33. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/prompts.py +0 -0
  34. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/service_datasources.py +0 -0
  35. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/sql_template_fmt.py +0 -0
  36. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/syncasync.py +0 -0
  37. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/check_pypi.py +0 -0
  38. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/cli.py +0 -0
  39. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/client.py +0 -0
  40. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/config.py +0 -0
  41. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/__init__.py +0 -0
  42. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/agent.py +0 -0
  43. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/animations.py +0 -0
  44. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/banner.py +0 -0
  45. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/command_agent.py +0 -0
  46. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/compactor.py +0 -0
  47. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/explore_agent.py +0 -0
  48. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/file_agent.py +0 -0
  49. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/memory.py +0 -0
  50. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/mock_agent.py +0 -0
  51. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/models.py +0 -0
  52. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/prompts.py +0 -0
  53. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/testing_agent.py +0 -0
  54. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/__init__.py +0 -0
  55. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/analyze.py +0 -0
  56. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/append.py +0 -0
  57. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/build.py +0 -0
  58. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/datafile.py +0 -0
  59. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/deploy.py +0 -0
  60. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/deploy_check.py +0 -0
  61. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/diff_resource.py +0 -0
  62. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/execute_query.py +0 -0
  63. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/file.py +0 -0
  64. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/get_endpoint_stats.py +0 -0
  65. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/get_openapi_definition.py +0 -0
  66. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/mock.py +0 -0
  67. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/plan.py +0 -0
  68. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/request_endpoint.py +0 -0
  69. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/run_command.py +0 -0
  70. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/secret.py +0 -0
  71. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/agent/tools/test.py +0 -0
  72. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/branch.py +0 -0
  73. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/cicd.py +0 -0
  74. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/cli.py +0 -0
  75. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/config.py +0 -0
  76. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/connection.py +0 -0
  77. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/connection_kafka.py +0 -0
  78. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/connection_s3.py +0 -0
  79. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/copy.py +0 -0
  80. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/build.py +0 -0
  81. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/build_common.py +0 -0
  82. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  83. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/diff.py +0 -0
  84. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/fixture.py +0 -0
  85. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/format_common.py +0 -0
  86. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  87. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  88. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/format_pipe.py +0 -0
  89. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  90. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/playground.py +0 -0
  91. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datafile/pull.py +0 -0
  92. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/datasource.py +0 -0
  93. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/deployment.py +0 -0
  94. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/deployment_common.py +0 -0
  95. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/deprecations.py +0 -0
  96. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/dev_server.py +0 -0
  97. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/endpoint.py +0 -0
  98. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/exceptions.py +0 -0
  99. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/feedback_manager.py +0 -0
  100. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/fmt.py +0 -0
  101. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/info.py +0 -0
  102. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/infra.py +0 -0
  103. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/job.py +0 -0
  104. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/job_common.py +0 -0
  105. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/llm.py +0 -0
  106. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/llm_utils.py +0 -0
  107. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/local.py +0 -0
  108. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/local_logs.py +0 -0
  109. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/login.py +0 -0
  110. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/login_common.py +0 -0
  111. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/logout.py +0 -0
  112. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/materialization.py +0 -0
  113. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/mock.py +0 -0
  114. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/mock_common.py +0 -0
  115. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/open.py +0 -0
  116. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/pipe.py +0 -0
  117. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/regions.py +0 -0
  118. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/secret.py +0 -0
  119. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/secret_common.py +0 -0
  120. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/shell.py +0 -0
  121. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/sink.py +0 -0
  122. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/table.py +0 -0
  123. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/telemetry.py +0 -0
  124. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/test.py +0 -0
  125. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/test_common.py +0 -0
  126. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/tinyunit/tinyunit.py +0 -0
  127. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/tinyunit/tinyunit_lib.py +0 -0
  128. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/token.py +0 -0
  129. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/watch.py +0 -0
  130. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/workspace.py +0 -0
  131. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb/modules/workspace_members.py +0 -0
  132. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli.py +0 -0
  133. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/auth.py +0 -0
  134. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/branch.py +0 -0
  135. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/cicd.py +0 -0
  136. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/cli.py +0 -0
  137. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/config.py +0 -0
  138. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/connection.py +0 -0
  139. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/datasource.py +0 -0
  140. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/exceptions.py +0 -0
  141. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/fmt.py +0 -0
  142. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/job.py +0 -0
  143. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/pipe.py +0 -0
  144. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/regions.py +0 -0
  145. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/tag.py +0 -0
  146. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/telemetry.py +0 -0
  147. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/test.py +0 -0
  148. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  149. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  150. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/workspace.py +0 -0
  151. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  152. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/SOURCES.txt +0 -0
  153. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/dependency_links.txt +0 -0
  154. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/entry_points.txt +0 -0
  155. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/requires.txt +0 -0
  156. {tinybird-2.0.1.dev0 → tinybird-3.0.1}/tinybird.egg-info/top_level.txt +0 -0
@@ -1,11 +1,11 @@
1
1
  Metadata-Version: 2.2
2
2
  Name: tinybird
3
- Version: 2.0.1.dev0
3
+ Version: 3.0.1
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
7
7
  Author-email: support@tinybird.co
8
- Requires-Python: >=3.9, <3.14
8
+ Requires-Python: >=3.10, <3.14
9
9
  Description-Content-Type: text/x-rst
10
10
  Requires-Dist: aiofiles==24.1.0
11
11
  Requires-Dist: anthropic==0.55.0
@@ -57,9 +57,15 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
57
57
  Changelog
58
58
  ----------
59
59
 
60
- 2.0.0
60
+ 3.0.1
61
61
  *******
62
62
 
63
+ - `Experimental` Added hidden `--with-connections` flag to `tb build` and `tb dev` commands for enabling connection datasources (S3, Kafka, GCS) in branches. This is an experimental feature and is not yet publicly documented.
64
+
65
+ 3.0.0
66
+ *******
67
+
68
+ - `Removed` Python 3.9 support. The minimum supported Python version is now 3.10.
63
69
  - `Removed` BigQuery and Snowflake connector support from Forward CLI build and deployment commands
64
70
 
65
71
  1.1.14
@@ -14,7 +14,6 @@ from ..sql import (
14
14
  )
15
15
 
16
16
  DEFAULT_EMPTY_PARAMETERS = ["ttl", "partition_key", "sorting_key"]
17
- DEFAULT_JOIN_EMPTY_PARAMETERS = ["join_strictness", "join_type", "key_columns"]
18
17
 
19
18
  # Currently we only support the simplest TTLs
20
19
  # f(X) + toIntervalZ(N)
@@ -62,25 +61,6 @@ class TableDetails:
62
61
  >>> ed.to_datafile()
63
62
  'ENGINE "MergeTree"\\nENGINE_PARTITION_KEY "toYear(timestamp)"\\nENGINE_SORTING_KEY "timestamp, cityHash64(location)"\\nENGINE_SAMPLING_KEY "cityHash64(location)"\\nENGINE_SETTINGS "index_granularity = 32, index_granularity_bytes = 2048"\\nENGINE_TTL "toDate(timestamp) + INTERVAL 1 DAY"'
64
63
 
65
- >>> ed = TableDetails({"engine_full": "Join(ANY, LEFT, id)", "engine": "Join", "partition_key": "", "sorting_key": "", "primary_key": "", "sampling_key": ""})
66
- >>> ed.engine_full
67
- 'Join(ANY, LEFT, id)'
68
- >>> ed.engine
69
- 'Join'
70
- >>> ed.to_json()
71
- {'engine_full': 'Join(ANY, LEFT, id)', 'engine': 'Join', 'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'id'}
72
- >>> ed.to_datafile()
73
- 'ENGINE "Join"\\nENGINE_JOIN_STRICTNESS "ANY"\\nENGINE_JOIN_TYPE "LEFT"\\nENGINE_KEY_COLUMNS "id"'
74
-
75
- >>> ed = TableDetails({"database": "d_01", "name": "t_01", "engine": "Join", "join_strictness": "ANY", "join_type": "LEFT", "key_columns": "id"})
76
- >>> ed.engine_full == None
77
- True
78
- >>> ed.engine
79
- 'Join'
80
- >>> ed.to_json()
81
- {'engine_full': None, 'engine': 'Join', 'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'id'}
82
- >>> ed.to_datafile()
83
- 'ENGINE "Join"\\nENGINE_JOIN_STRICTNESS "ANY"\\nENGINE_JOIN_TYPE "LEFT"\\nENGINE_KEY_COLUMNS "id"'
84
64
  >>> ed = TableDetails({ "engine_full": "MergeTree() PARTITION BY toYear(timestamp) ORDER BY (timestamp, cityHash64(location)) SAMPLE BY cityHash64(location) SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 1800 TTL toDate(timestamp) + INTERVAL 1 DAY"})
85
65
  >>> ed.engine_full
86
66
  'MergeTree() PARTITION BY toYear(timestamp) ORDER BY (timestamp, cityHash64(location)) SAMPLE BY cityHash64(location) SETTINGS index_granularity = 8192, ttl_only_drop_parts = 1, merge_with_ttl_timeout = 1800 TTL toDate(timestamp) + INTERVAL 1 DAY'
@@ -222,21 +202,6 @@ class TableDetails:
222
202
  _sign = self.details.get("sign", None)
223
203
  return _sign
224
204
 
225
- @property
226
- def join_strictness(self):
227
- _join_strictness = self.details.get("join_strictness", None)
228
- return _join_strictness
229
-
230
- @property
231
- def join_type(self):
232
- _join_type = self.details.get("join_type", None)
233
- return _join_type
234
-
235
- @property
236
- def key_columns(self):
237
- _key_columns = self.details.get("key_columns", None)
238
- return _key_columns
239
-
240
205
  @property
241
206
  def statistics(self) -> Dict[str, Any]:
242
207
  return {
@@ -268,12 +233,6 @@ class TableDetails:
268
233
  d["sampling_key"] = self.sampling_key
269
234
  if self.settings:
270
235
  d["settings"] = self.settings
271
- if self.join_strictness:
272
- d["join_strictness"] = self.join_strictness
273
- if self.join_type:
274
- d["join_type"] = self.join_type
275
- if self.key_columns:
276
- d["key_columns"] = self.key_columns
277
236
  if self.ver:
278
237
  d["ver"] = self.ver
279
238
  if self.is_deleted:
@@ -292,10 +251,7 @@ class TableDetails:
292
251
  d = {**d, **engine_params}
293
252
 
294
253
  if include_empty_details:
295
- if self.engine and self.engine.lower() == "join":
296
- d = set_empty_details(d, DEFAULT_JOIN_EMPTY_PARAMETERS)
297
- else:
298
- d = set_empty_details(d, DEFAULT_EMPTY_PARAMETERS)
254
+ d = set_empty_details(d, DEFAULT_EMPTY_PARAMETERS)
299
255
 
300
256
  if exclude:
301
257
  for attr in exclude:
@@ -480,20 +436,6 @@ ENABLED_ENGINES = [
480
436
  ],
481
437
  MERGETREE_OPTIONS,
482
438
  ),
483
- # Join(join_strictness, join_type, k1[, k2, ...])
484
- engine_config(
485
- "Join",
486
- [
487
- # https://github.com/ClickHouse/ClickHouse/blob/fa8e4e4735b932f08b6beffcb2d069b72de34401/src/Storages/StorageJoin.cpp
488
- EngineParam(
489
- name="join_strictness", required=True, is_valid=case_insensitive_check(["ANY", "ALL", "SEMI", "ANTI"])
490
- ),
491
- EngineParam(
492
- name="join_type", required=True, is_valid=case_insensitive_check(["LEFT", "INNER", "RIGHT", "FULL"])
493
- ),
494
- EngineParam(name="key_columns", required=True, is_valid=columns_are_valid),
495
- ],
496
- ),
497
439
  # Null()
498
440
  engine_config("Null"),
499
441
  ]
@@ -599,7 +541,7 @@ def engine_full_from_dict(
599
541
  >>> engine_full_from_dict('wadus', {}, schema=schema)
600
542
  Traceback (most recent call last):
601
543
  ...
602
- ValueError: Engine wadus is not supported, supported engines include: MergeTree, ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree, CollapsingMergeTree, VersionedCollapsingMergeTree, Join, Null
544
+ ValueError: Engine wadus is not supported, supported engines include: MergeTree, ReplacingMergeTree, SummingMergeTree, AggregatingMergeTree, CollapsingMergeTree, VersionedCollapsingMergeTree, Null
603
545
  >>> schema = ''
604
546
  >>> engine_full_from_dict('null', {}, schema=schema)
605
547
  'Null()'
@@ -607,19 +549,6 @@ def engine_full_from_dict(
607
549
  >>> engine_full_from_dict('null', {}, columns=[])
608
550
  'Null()'
609
551
 
610
- >>> schema = 'cid Int32'
611
- >>> engine_full_from_dict('Join', {'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'cid'}, schema=schema)
612
- 'Join(ANY, LEFT, cid)'
613
- >>> engine_full_from_dict('Join', {'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'cid'}, columns=[{'name': 'cid', 'type': 'Int32', 'codec': None, 'default_value': None, 'nullable': False, 'normalized_name': 'cid'}])
614
- 'Join(ANY, LEFT, cid)'
615
- >>> schema = 'cid1 Int32, cid2 Int8'
616
- >>> engine_full_from_dict('Join', {'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'cid1, cid2'}, schema=schema)
617
- 'Join(ANY, LEFT, cid1, cid2)'
618
- >>> engine_full_from_dict('Join', {'join_strictness': 'ANY', 'join_type': 'OUTER', 'key_columns': 'cid'}, schema=schema)
619
- Traceback (most recent call last):
620
- ...
621
- ValueError: Invalid value 'OUTER' for parameter 'engine_join_type', reason: valid values are LEFT, INNER, RIGHT, FULL
622
-
623
552
  >>> schema = ''
624
553
  >>> engine_full_from_dict('MergeTree', {}, schema=schema)
625
554
  'MergeTree() ORDER BY (tuple())'
@@ -715,10 +644,6 @@ def engine_params_from_engine_full(engine_full: str) -> Dict[str, Any]:
715
644
  """
716
645
  >>> engine_params_from_engine_full("Null()")
717
646
  {}
718
- >>> engine_params_from_engine_full("Join(ANY, LEFT, id)")
719
- {'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'id'}
720
- >>> engine_params_from_engine_full("Join(ANY, LEFT, k1, k2)")
721
- {'join_strictness': 'ANY', 'join_type': 'LEFT', 'key_columns': 'k1, k2'}
722
647
  >>> engine_params_from_engine_full("AggregatingMergeTree('/clickhouse/tables/{layer}-{shard}/d_f837aa.sales_by_country_rt__v0_staging_t_00c3091e7530472caebda05e97288a1d', '{replica}') PARTITION BY toYYYYMM(date) ORDER BY (purchase_location, cod_device, date) SETTINGS index_granularity = 8192")
723
648
  {}
724
649
  >>> engine_params_from_engine_full("ReplicatedSummingMergeTree('/clickhouse/tables/{layer}-{shard}/d_abcf3e.t_69f9da31f4524995b8911e1b24c80ab4', '{replica}') PARTITION BY toYYYYMM(date) ORDER BY (date, purchase_location, sku_rank_lc) SETTINGS index_granularity = 8192")
@@ -137,7 +137,3 @@ class FeatureFlags:
137
137
  @classmethod
138
138
  def is_localhost(cls) -> bool:
139
139
  return "SET_LOCALHOST" in environ
140
-
141
- @classmethod
142
- def enable_snowflake_connector_command(cls) -> bool:
143
- return "ENABLE_SNOWFLAKE_CONNECTOR_COMMAND" in environ
@@ -33,6 +33,13 @@ from tinybird.tb.modules.feedback_manager import FeedbackManager
33
33
  # I'll try not to make logic changes, just error reporting changes
34
34
  # from tinybird.sql import parse_indexes_structure, parse_table_structure, schema_to_sql_columns
35
35
 
36
+ # Pre-compiled regex patterns
37
+ _PATTERN_INDEX_ENTRY = re.compile(
38
+ r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?"
39
+ )
40
+ _PATTERN_SIMPLE_AGG_FUNC = re.compile(r"SimpleAggregateFunction\((\w+),\s*(?!(?:Nullable))([\w,.()]+)\)")
41
+ _PATTERN_VERSION_NUMBER = re.compile(r"[0-9]+$")
42
+
36
43
 
37
44
  class DatafileValidationError(Exception): ...
38
45
 
@@ -271,6 +278,74 @@ VALID_BLOB_STORAGE_CRON_VALUES = {
271
278
  }
272
279
 
273
280
 
281
+ def extract_column_names_from_sorting_key_part(part: str) -> List[str]:
282
+ """
283
+ Extract actual column names from a sorting key part (which might be an expression).
284
+
285
+ Examples:
286
+ - "shop" -> ["shop"]
287
+ - "`column_name`" -> ["column_name"]
288
+ - "ifNull(ad_id, '')" -> ["ad_id"]
289
+ - "toDate(timestamp)" -> ["timestamp"]
290
+ - "concat(first_name, last_name)" -> ["first_name", "last_name"]
291
+ """
292
+ columns = []
293
+
294
+ if "(" in part and part.endswith(")"):
295
+ # Function expression - extract column names from inside parentheses
296
+ func_start = part.find("(")
297
+ inner_content = part[func_start + 1 : -1].strip()
298
+ for inner_part in inner_content.split(","):
299
+ inner_part = inner_part.strip().strip("`")
300
+ if inner_part and inner_part.isidentifier():
301
+ columns.append(inner_part)
302
+ elif part:
303
+ # Simple column name
304
+ column_name = part.strip("`")
305
+ if column_name:
306
+ columns.append(column_name)
307
+
308
+ return columns
309
+
310
+
311
+ def parse_sorting_key_column_names(sorting_key: str) -> List[str]:
312
+ """
313
+ Extract all column names from a sorting key expression.
314
+
315
+ Examples:
316
+ - "shop, event_date, channel" -> ["shop", "event_date", "channel"]
317
+ - "shop, event_date, ifNull(ad_id, ''), event_id" -> ["shop", "event_date", "ad_id", "event_id"]
318
+ - "tuple(shop, toDate(timestamp))" -> ["shop", "timestamp"]
319
+ """
320
+ # Remove tuple() wrapper if present
321
+ column_str = sorting_key
322
+ if column_str.startswith("tuple(") and column_str.endswith(")"):
323
+ column_str = column_str[6:-1]
324
+
325
+ sorting_key_columns = []
326
+
327
+ # Use regex to find all sorting key parts, respecting parentheses and quotes
328
+ # Pattern matches: function calls with args, or simple identifiers
329
+ # This handles cases like: ifNull(col, ''), toDate(timestamp), simple_col
330
+ pattern = r"""
331
+ (?: # Non-capturing group for the whole part
332
+ \w+\s*\([^)]*\) # Function call: word + ( + anything except ) + )
333
+ | # OR
334
+ `[^`]+` # Backtick-quoted identifier
335
+ | # OR
336
+ \w+ # Simple word/identifier
337
+ )
338
+ """
339
+
340
+ for match in re.finditer(pattern, column_str, re.VERBOSE):
341
+ part = match.group(0).strip()
342
+ # Extract column names from the part (handles both simple columns and function expressions)
343
+ extracted_columns = extract_column_names_from_sorting_key_part(part)
344
+ sorting_key_columns.extend(extracted_columns)
345
+
346
+ return sorting_key_columns
347
+
348
+
274
349
  class Datafile:
275
350
  def __init__(self) -> None:
276
351
  self.maintainer: Optional[str] = None
@@ -559,7 +634,7 @@ class Datafile:
559
634
  )
560
635
 
561
636
  # Extract column names from the part
562
- extracted_columns = self._extract_column_names_from_part(part)
637
+ extracted_columns = extract_column_names_from_sorting_key_part(part)
563
638
  sorting_key_columns.extend(extracted_columns)
564
639
 
565
640
  return sorting_key_columns
@@ -595,26 +670,6 @@ class Datafile:
595
670
 
596
671
  return func_name in aggregate_function_names
597
672
 
598
- def _extract_column_names_from_part(self, part: str) -> List[str]:
599
- """Extract column names from a sorting key part."""
600
- columns = []
601
-
602
- if "(" in part and part.endswith(")"):
603
- # Function expression - extract column names from inside parentheses
604
- func_start = part.find("(")
605
- inner_content = part[func_start + 1 : -1].strip()
606
- for inner_part in inner_content.split(","):
607
- inner_part = inner_part.strip().strip("`")
608
- if inner_part and inner_part.isidentifier():
609
- columns.append(inner_part)
610
- elif part:
611
- # Simple column name
612
- column_name = part.strip("`")
613
- if column_name:
614
- columns.append(column_name)
615
-
616
- return columns
617
-
618
673
  def _validate_columns_against_schema(
619
674
  self, sorting_key_columns: List[str], schema_columns: Dict[str, Dict[str, Any]]
620
675
  ) -> None:
@@ -783,10 +838,7 @@ def parse_indexes_structure(indexes: Optional[List[str]]) -> List[TableIndex]:
783
838
  pos=leading_whitespaces + 1,
784
839
  )
785
840
 
786
- match = re.match(
787
- r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?",
788
- index,
789
- )
841
+ match = _PATTERN_INDEX_ENTRY.match(index)
790
842
  if match:
791
843
  index_name, a, index_type, value, granularity = match.groups()
792
844
  index_expr = f"{index_type}({value})" if value else index_type
@@ -1323,7 +1375,7 @@ def try_to_fix_nullable_in_simple_aggregating_function(t: str) -> Optional[str]:
1323
1375
  # as it is done with other aggregate functions.
1324
1376
  # If not, the aggregation could return incorrect results.
1325
1377
  result = None
1326
- if match := re.search(r"SimpleAggregateFunction\((\w+),\s*(?!(?:Nullable))([\w,.()]+)\)", t):
1378
+ if match := _PATTERN_SIMPLE_AGG_FUNC.search(t):
1327
1379
  fn = match.group(1)
1328
1380
  inner_type = match.group(2)
1329
1381
  result = f"SimpleAggregateFunction({fn}, Nullable({inner_type}))"
@@ -2468,7 +2520,7 @@ def get_name_version(ds: str) -> Dict[str, Any]:
2468
2520
  return {"name": tk[0], "version": None}
2469
2521
  elif len(tk) == 2:
2470
2522
  if len(tk[1]):
2471
- if tk[1][0] == "v" and re.match("[0-9]+$", tk[1][1:]):
2523
+ if tk[1][0] == "v" and _PATTERN_VERSION_NUMBER.match(tk[1][1:]):
2472
2524
  return {"name": tk[0], "version": int(tk[1][1:])}
2473
2525
  else:
2474
2526
  return {"name": tk[0] + "__" + tk[1], "version": None}
@@ -305,12 +305,6 @@ class FeedbackManager:
305
305
  error_unknown_kafka_connection = error_message(
306
306
  "Unknown Kafka connection in Data Source '{datasource}'. Hint: you can create it with the 'tb connection create kafka' command.\n** See https://www.tinybird.co/docs/ingest/kafka.html?highlight=kafka#using-include-to-store-connection-settings to learn more."
307
307
  )
308
- error_unknown_bq_connection = error_message(
309
- "Unknown BigQuery connection in Data Source '{datasource}'. Hint: you can create it with the 'tb connection create bigquery' command."
310
- )
311
- error_unknown_snowflake_connection = error_message(
312
- "Unknown Snowflake connection in Data Source '{datasource}'. Hint: you can create it with the 'tb connection create snowflake' command."
313
- )
314
308
  error_unknown_connection_service = error_message("Unknown connection service '{service}'")
315
309
  error_missing_connection_name = error_message("Missing IMPORT_CONNECTION_NAME in '{datasource}'.")
316
310
  error_missing_external_datasource = error_message(
@@ -417,9 +411,6 @@ class FeedbackManager:
417
411
  error_updating_tag = error_message("Error updating tag: {error}")
418
412
  error_tag_generic = error_message("There was an issue updating tags. {error}")
419
413
  error_tag_not_found = error_message("Tag {tag_name} not found.")
420
- error_snowflake_connector_not_enabled = error_message(
421
- "🚨🚨🚨 [DEPRECATED] Snowflake connector is deprecated. Can't create the connector. **"
422
- )
423
414
 
424
415
  info_incl_relative_path = info_message("** Relative path {path} does not exist, skipping.")
425
416
  info_ignoring_incl_file = info_message(
@@ -468,13 +459,6 @@ class FeedbackManager:
468
459
  prompt_ws_name = prompt_message("❓ Workspace name:")
469
460
  prompt_ws_template = prompt_message("❓ Starter template:")
470
461
 
471
- prompt_bigquery_account = prompt_message(
472
- """** Log into your Google Cloud Platform Console as a project editor and go to https://console.cloud.google.com/iam-admin/iam
473
- ** Grant access to this principal: {service_account}
474
- ** Assign it the role "BigQuery Data Viewer"
475
- Ready? """
476
- )
477
-
478
462
  prompt_s3_iamrole_connection_login_aws = prompt_message("""[1] Log into your AWS Console\n\n""")
479
463
  prompt_s3_iamrole_connection_policy = prompt_message(
480
464
  """\n[2] Go to IAM > Policies. Create a new policy with the following permissions. Please, replace {replacements}:\n\n{access_policy}\n\n(The policy has been copied to your clipboard)\n\n"""
@@ -509,12 +493,6 @@ Ready? """
509
493
  error_bump_release = error_message(
510
494
  "Error: Bump the --semver flag, example 'tb --semver 0.0.2 {command}'. If you are in CI, bump the VERSION envvar in .tinyenv\n"
511
495
  )
512
- error_bigquery_improper_permissions = error_message(
513
- "Error: no access detected. It might take a minute to detect the new permissions.\n"
514
- )
515
- error_snowflake_improper_permissions = error_message(
516
- "Snowflake connection is not valid. Please check your credentials and try again.\n"
517
- )
518
496
  error_connection_improper_permissions = error_message(
519
497
  "Connection is not valid. Please check your credentials and try again.\n"
520
498
  )
@@ -646,9 +624,6 @@ Ready? """
646
624
  warning_tag_remove = prompt_message(
647
625
  "Tag {tag_name} is used by {resources_len} resources. Do you want to remove it?"
648
626
  )
649
- warning_bigquery_connector_deprecated = warning_message(
650
- "🚨🚨🚨 [DEPRECATED] BigQuery connector is deprecated. Use ingestion from GCS instead. https://www.tinybird.co/docs/get-data-in/guides/ingest-from-bigquery-using-google-cloud-storage **"
651
- )
652
627
  warning_s3_access_key_secret_deprecated = warning_message(
653
628
  "🚨🚨🚨 [DEPRECATED] S3 (Access Key + Secret) connector is deprecated. Use S3 IAM role instead. https://www.tinybird.co/docs/get-data-in/connectors/s3 **"
654
629
  )
@@ -39,6 +39,7 @@ _RE_INDEX_ENTRY = re.compile(
39
39
  r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?"
40
40
  )
41
41
  _RE_REPLICATED_MT = re.compile(r"Replicated(.*)MergeTree\(([^\)]*)\)(.*)")
42
+ _RE_REPLICATED_ENGINE_SPLIT = re.compile(r"(Replicated.*MergeTree\(')([^']*)('.*)")
42
43
 
43
44
 
44
45
  @dataclass
@@ -836,7 +837,7 @@ def engine_can_be_replicated(engine: Optional[str]) -> bool:
836
837
  """
837
838
  >>> engine_can_be_replicated('MergeTree() order by tuple()')
838
839
  True
839
- >>> engine_can_be_replicated('JOIN(ANY, LEFT, foo)')
840
+ >>> engine_can_be_replicated('Null')
840
841
  False
841
842
  >>> engine_can_be_replicated('ReplicatingMergeTree() order by tuple()')
842
843
  True
@@ -855,7 +856,7 @@ def engine_supports_delete(engine: Optional[str]) -> bool:
855
856
  """
856
857
  >>> engine_supports_delete('MergeTree() order by tuple()')
857
858
  True
858
- >>> engine_supports_delete('JOIN(ANY, LEFT, foo)')
859
+ >>> engine_supports_delete('Null')
859
860
  False
860
861
  >>> engine_supports_delete('ReplicatingMergeTree() order by tuple()')
861
862
  True
@@ -873,8 +874,8 @@ def engine_replicated_to_local(engine: str) -> str:
873
874
  'MergeTree() order by (test)'
874
875
  >>> engine_replicated_to_local("ReplicatedReplacingMergeTree('/clickhouse/tables/{layer}-{shard}/test.foo', '{replica}', timestamp) order by (test)")
875
876
  'ReplacingMergeTree(timestamp) order by (test)'
876
- >>> engine_replicated_to_local("Join(ANY, LEFT, test)")
877
- 'Join(ANY, LEFT, test)'
877
+ >>> engine_replicated_to_local("Null")
878
+ 'Null'
878
879
  >>> engine_replicated_to_local("ReplicatedVersionedCollapsingMergeTree('/clickhouse/tables/{layer}-{shard}/test.foo', '{replica}', sign,version) ORDER BY pk TTL toDate(local_timeplaced) + toIntervalDay(3) SETTINGS index_granularity = 8192")
879
880
  'VersionedCollapsingMergeTree(sign, version) ORDER BY pk TTL toDate(local_timeplaced) + toIntervalDay(3) SETTINGS index_granularity = 8192'
880
881
  """
@@ -911,7 +912,7 @@ def engine_patch_replicated_engine(engine: str, engine_full: Optional[str], new_
911
912
  if not engine_full:
912
913
  return None
913
914
  if engine.lower().startswith("Replicated".lower()):
914
- parts = re.split(r"(Replicated.*MergeTree\(')([^']*)('.*)", engine_full)
915
+ parts = _RE_REPLICATED_ENGINE_SPLIT.split(engine_full)
915
916
  paths = parts[2].split("/")
916
917
  paths[-1] = new_table_name
917
918
  zoo_path = "/".join(paths)