tinybird-cli 5.9.1.dev2__tar.gz → 5.9.1.dev3__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 (48) hide show
  1. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/PKG-INFO +6 -1
  2. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/ch_utils/engine.py +21 -1
  4. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/datafile.py +4 -4
  5. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/sql.py +31 -3
  6. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/sql_template.py +22 -5
  7. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/common.py +4 -4
  8. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/PKG-INFO +6 -1
  9. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/setup.cfg +0 -0
  10. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/ch_utils/constants.py +0 -0
  11. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/check_pypi.py +0 -0
  12. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/client.py +0 -0
  13. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/config.py +0 -0
  14. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/connectors.py +0 -0
  15. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/context.py +0 -0
  16. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/datatypes.py +0 -0
  17. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/feedback_manager.py +0 -0
  18. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/git_settings.py +0 -0
  19. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/sql_template_fmt.py +0 -0
  20. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/sql_toolset.py +0 -0
  21. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/syncasync.py +0 -0
  22. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli.py +0 -0
  23. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/auth.py +0 -0
  24. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/branch.py +0 -0
  25. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/cicd.py +0 -0
  26. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/cli.py +0 -0
  27. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/config.py +0 -0
  28. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/connection.py +0 -0
  29. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/datasource.py +0 -0
  30. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/exceptions.py +0 -0
  31. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/fmt.py +0 -0
  32. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/job.py +0 -0
  33. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/pipe.py +0 -0
  34. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/regions.py +0 -0
  35. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/tag.py +0 -0
  36. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/telemetry.py +0 -0
  37. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/test.py +0 -0
  38. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  39. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  40. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/token.py +0 -0
  41. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/workspace.py +0 -0
  42. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  43. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird/tornado_template.py +0 -0
  44. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  45. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  46. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/entry_points.txt +0 -0
  47. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/requires.txt +0 -0
  48. {tinybird-cli-5.9.1.dev2 → tinybird-cli-5.9.1.dev3}/tinybird_cli.egg-info/top_level.txt +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.9.1.dev2
3
+ Version: 5.9.1.dev3
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,6 +18,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.9.1.dev3
22
+ ***********
23
+
24
+ - `Fixed` Correctly parse lambda expressions in indexes
25
+
21
26
  5.9.1.dev2
22
27
  ***********
23
28
 
@@ -4,5 +4,5 @@ __description__ = 'Tinybird Command Line Tool'
4
4
  __url__ = 'https://www.tinybird.co/docs/cli/introduction.html'
5
5
  __author__ = 'Tinybird'
6
6
  __author_email__ = 'support@tinybird.co'
7
- __version__ = '5.9.1.dev2'
8
- __revision__ = '4f458c3'
7
+ __version__ = '5.9.1.dev3'
8
+ __revision__ = '6e69bc3'
@@ -4,7 +4,14 @@ from collections import defaultdict
4
4
  from dataclasses import asdict
5
5
  from typing import Any, Callable, Dict, Iterable, List, Optional
6
6
 
7
- from ..sql import TableIndex, col_name, engine_replicated_to_local, parse_indexes_structure, parse_table_structure
7
+ from ..sql import (
8
+ TableIndex,
9
+ TableProjection,
10
+ col_name,
11
+ engine_replicated_to_local,
12
+ parse_indexes_structure,
13
+ parse_table_structure,
14
+ )
8
15
 
9
16
  DEFAULT_EMPTY_PARAMETERS = ["ttl", "partition_key", "sorting_key"]
10
17
  DEFAULT_JOIN_EMPTY_PARAMETERS = ["join_strictness", "join_type", "key_columns"]
@@ -237,6 +244,10 @@ class TableDetails:
237
244
  def indexes(self) -> List[TableIndex]:
238
245
  return _parse_indexes(str(self.details.get("create_table_query", "")))
239
246
 
247
+ @property
248
+ def projections(self) -> List[TableProjection]:
249
+ return _parse_projections(self.details.get("create_table_query", ""))
250
+
240
251
  def to_json(self, exclude: Optional[List[str]] = None, include_empty_details: bool = False):
241
252
  # name, database are not exported since they are not part of the engine
242
253
  d: Dict[str, Any] = {
@@ -833,3 +844,12 @@ def _parse_indexes(create_table_query_expr: str) -> List[TableIndex]:
833
844
  return []
834
845
 
835
846
  return parse_indexes_structure(indexes)
847
+
848
+
849
+ def _parse_projections(create_table_query_expr: str) -> List[TableProjection]:
850
+ return [
851
+ TableProjection(name, expr)
852
+ for name, expr in re.findall(
853
+ r"PROJECTION\s+(\w+)\s*\(((?:[^()]|\((?:[^()]|\([^()]*\))*\))*)\)", create_table_query_expr
854
+ )
855
+ ]
@@ -586,7 +586,7 @@ class Deployment:
586
586
  self.cli_git_release.validate_local_for_release(self.current_release, check_outdated=check_outdated)
587
587
  click.echo(FeedbackManager.info_deployment_detecting_changes_header())
588
588
  commit = self.cli_git_release.get_main_branch_commit() if use_main else self.current_release["commit"]
589
- diffs = self.cli_git_release.diff_datafiles(commit)
589
+ diffs = self.cli_git_release.diff_datafiles(commit) # type: ignore
590
590
  click.echo(
591
591
  FeedbackManager.info_git_release_diffs(
592
592
  workspace=self.current_ws["name"],
@@ -2118,7 +2118,7 @@ class PipeCheckerRunnerResponse:
2118
2118
 
2119
2119
 
2120
2120
  class PipeCheckerRunner:
2121
- checker_stream_result_class = unittest.runner._WritelnDecorator # type: ignore
2121
+ checker_stream_result_class = unittest.runner._WritelnDecorator
2122
2122
 
2123
2123
  def __init__(self, pipe_name: str, host: str):
2124
2124
  self.pipe_name = pipe_name
@@ -2281,7 +2281,7 @@ class PipeCheckerRunner:
2281
2281
  )
2282
2282
 
2283
2283
  result = PipeCheckerTextTestResult(
2284
- self.checker_stream_result_class(sys.stdout), descriptions=True, verbosity=2, custom_output=custom_output
2284
+ self.checker_stream_result_class(sys.stdout), descriptions=True, verbosity=2, custom_output=custom_output # type: ignore
2285
2285
  )
2286
2286
  result.failfast = failfast
2287
2287
  suite.run(result)
@@ -3989,7 +3989,7 @@ async def build_graph(
3989
3989
  mapped_workspaces.append(
3990
3990
  workspace_map.get(shared_with)
3991
3991
  if workspace_map.get(shared_with, None) is not None
3992
- else shared_with
3992
+ else shared_with # type: ignore
3993
3993
  )
3994
3994
  r["shared_with"] = mapped_workspaces
3995
3995
 
@@ -38,11 +38,37 @@ class TableIndex:
38
38
  return f"CLEAR INDEX IF EXISTS {self.name}"
39
39
 
40
40
 
41
+ @dataclass
42
+ class TableProjection:
43
+ """Defines a CH table PROJECTION"""
44
+
45
+ name: str
46
+ expr: str
47
+
48
+ def to_datafile(self):
49
+ return f"{self.name} ({self.expr})"
50
+
51
+ def to_sql(self):
52
+ return f"PROJECTION {self.to_datafile()}"
53
+
54
+ def add_index_sql(self):
55
+ return f"ADD {self.to_sql()}"
56
+
57
+ def drop_index_sql(self):
58
+ return f"DROP PROJECTION IF EXISTS {self.name}"
59
+
60
+ def materialize_index_sql(self):
61
+ return f"MATERIALIZE PROJECTION IF EXISTS {self.name}"
62
+
63
+ def clear_index_sql(self):
64
+ return f"CLEAR PROJECTION IF EXISTS {self.name}"
65
+
66
+
41
67
  def as_subquery(sql: str) -> str:
42
68
  return f"""(\n{sql}\n)"""
43
69
 
44
70
 
45
- def get_format(sql: str) -> str:
71
+ def get_format(sql: str) -> Optional[str]:
46
72
  """
47
73
  retrieves FORMAT from CH sql
48
74
  >>> get_format('select * from test')
@@ -211,7 +237,7 @@ def format_parse_error(
211
237
  return message
212
238
 
213
239
 
214
- def parse_indexes_structure(indexes: List[str]) -> List[TableIndex]:
240
+ def parse_indexes_structure(indexes: Optional[List[str]]) -> List[TableIndex]:
215
241
  """
216
242
  >>> parse_indexes_structure(["index_name a TYPE set(100) GRANULARITY 100", "index_name_bf mapValues(d) TYPE bloom_filter(0.001) GRANULARITY 16"])
217
243
  [TableIndex(name='index_name', expr='a', type_full='set(100)', granularity='100'), TableIndex(name='index_name_bf', expr='mapValues(d)', type_full='bloom_filter(0.001)', granularity='16')]
@@ -239,6 +265,8 @@ def parse_indexes_structure(indexes: List[str]) -> List[TableIndex]:
239
265
  ValueError: invalid INDEX format. Usage: `name expr TYPE type_full GRANULARITY granularity`
240
266
  >>> parse_indexes_structure(["my_index m['key'] TYPE ngrambf_v1(1, 1024, 1, 42) GRANULARITY 1"])
241
267
  [TableIndex(name='my_index', expr="m['key']", type_full='ngrambf_v1(1, 1024, 1, 42)', granularity='1')]
268
+ >>> parse_indexes_structure(["my_index_lambda arrayMap(x -> tupleElement(x,'message'), column_name) TYPE ngrambf_v1(1, 1024, 1, 42) GRANULARITY 1"])
269
+ [TableIndex(name='my_index_lambda', expr="arrayMap(x -> tupleElement(x,'message'), column_name)", type_full='ngrambf_v1(1, 1024, 1, 42)', granularity='1')]
242
270
  >>> parse_indexes_structure(["ip_range_minmax_idx (toIPv6(ip_range_start), toIPv6(ip_range_end)) TYPE minmax GRANULARITY 1"])
243
271
  [TableIndex(name='ip_range_minmax_idx', expr='(toIPv6(ip_range_start), toIPv6(ip_range_end))', type_full='minmax', granularity='1')]
244
272
  """
@@ -253,7 +281,7 @@ def parse_indexes_structure(indexes: List[str]) -> List[TableIndex]:
253
281
  raise ValueError("invalid INDEX format. Usage: `name expr TYPE type_full GRANULARITY granularity`")
254
282
 
255
283
  match = re.match(
256
- r"(\w+)\s+([\w\s*\[\]\*\(\),\'\".]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?",
284
+ r"(\w+)\s+([\w\s*\[\]\*\(\),\'\"-><.]+)\s+TYPE\s+(\w+)(?:\(([\w\s*.,]+)\))?(?:\s+GRANULARITY\s+(\d+))?",
257
285
  index,
258
286
  )
259
287
  if match:
@@ -1619,8 +1619,10 @@ def get_var_data(content, node_id=None): # noqa: C901
1619
1619
 
1620
1620
  kwargs = {}
1621
1621
  for x in node.keywords:
1622
- kwargs[x.arg] = node_to_value(x.value)
1623
-
1622
+ value = node_to_value(x.value)
1623
+ kwargs[x.arg] = value
1624
+ if x.arg == "default":
1625
+ kwargs["default"] = check_default_value(value)
1624
1626
  if func in VALID_CUSTOM_FUNCTION_NAMES:
1625
1627
  # Type definition here is set to 'String' because it comes from a
1626
1628
  # `defined(variable)` expression that does not contain any type hint.
@@ -1628,11 +1630,15 @@ def get_var_data(content, node_id=None): # noqa: C901
1628
1630
  # args[0] check is used to avoid adding unnamed parameters found in
1629
1631
  # templates like: `split_to_array('')`
1630
1632
  if len(args) > 0 and args[0] not in vars and args[0]:
1631
- vars[args[0]] = {"type": "String", "default": None, "used_in": "function_call"}
1633
+ vars[args[0]] = {
1634
+ "type": "String",
1635
+ "default": None,
1636
+ "used_in": "function_call",
1637
+ }
1632
1638
  elif func == "Array":
1633
1639
  if "default" not in kwargs:
1634
1640
  default = kwargs.get("default", args[2] if len(args) > 2 and args[2] else None)
1635
- kwargs["default"] = default
1641
+ kwargs["default"] = check_default_value(default)
1636
1642
  if len(args):
1637
1643
  vars[args[0]] = {
1638
1644
  "type": f"Array({args[1]})" if len(args) > 1 else "Array(String)",
@@ -1644,9 +1650,11 @@ def get_var_data(content, node_id=None): # noqa: C901
1644
1650
  # if this is a cast use the function name to get the type
1645
1651
  if "default" not in kwargs:
1646
1652
  default = kwargs.get("default", args[1] if len(args) > 1 else None)
1647
- kwargs["default"] = default
1653
+ kwargs["default"] = check_default_value(default)
1648
1654
  try:
1649
1655
  vars[args[0]] = {"type": func, **kwargs}
1656
+ if "default" in kwargs:
1657
+ kwargs["default"] = check_default_value(kwargs["default"])
1650
1658
  except TypeError as e:
1651
1659
  logging.exception(f"pipe parsing problem {content} (node '{node_id}'): {e}")
1652
1660
  except Exception as e:
@@ -1665,6 +1673,13 @@ def get_var_data(content, node_id=None): # noqa: C901
1665
1673
 
1666
1674
  return vars
1667
1675
 
1676
+ def check_default_value(value):
1677
+ if isinstance(value, int):
1678
+ MAX_SAFE_INTEGER = 9007199254740991
1679
+ if value > MAX_SAFE_INTEGER:
1680
+ return str(value)
1681
+ return value
1682
+
1668
1683
  def parse_content(content, retries=0):
1669
1684
  try:
1670
1685
  parsed = ast.parse(content)
@@ -1744,6 +1759,8 @@ def get_var_names_and_types(t, node_id=None): # noqa: C901
1744
1759
  [{'name': 'symbol_id', 'type': 'Int128', 'description': 'Symbol Id', 'required': True, 'default': 11111}, {'name': 'user_id', 'type': 'Int256', 'description': 'User Id', 'default': 3555}]
1745
1760
  >>> get_var_names_and_types(Template("SELECT now() > {{DateTime64(timestamp, '2020-09-09 10:10:10.000')}}"))
1746
1761
  [{'name': 'timestamp', 'type': 'DateTime64', 'default': '2020-09-09 10:10:10.000'}]
1762
+ >>> get_var_names_and_types(Template("SELECT * FROM filter_value WHERE symbol = {{Int64(symbol_id, 9223372036854775807)}}"))
1763
+ [{'name': 'symbol_id', 'type': 'Int64', 'default': '9223372036854775807'}]
1747
1764
  """
1748
1765
  try:
1749
1766
 
@@ -1214,12 +1214,12 @@ def autocomplete_topics(ctx: Context, args, incomplete):
1214
1214
 
1215
1215
 
1216
1216
  def validate_datasource_name(name):
1217
- if not isinstance(name, str) or str == "":
1217
+ if not isinstance(name, str) or name == "":
1218
1218
  raise CLIException(FeedbackManager.error_datasource_name())
1219
1219
 
1220
1220
 
1221
1221
  def validate_connection_id(connection_id):
1222
- if not isinstance(connection_id, str) or str == "":
1222
+ if not isinstance(connection_id, str) or connection_id == "":
1223
1223
  raise CLIException(FeedbackManager.error_datasource_connection_id())
1224
1224
 
1225
1225
 
@@ -1235,7 +1235,7 @@ def validate_kafka_group(group):
1235
1235
 
1236
1236
  def validate_kafka_auto_offset_reset(auto_offset_reset):
1237
1237
  valid_values = {"latest", "earliest", "none"}
1238
- if not (auto_offset_reset in valid_values):
1238
+ if auto_offset_reset not in valid_values:
1239
1239
  raise CLIException(FeedbackManager.error_kafka_auto_offset_reset())
1240
1240
 
1241
1241
 
@@ -1588,7 +1588,7 @@ async def try_update_config_with_remote(
1588
1588
  def ask_for_admin_token_interactively(ui_host: str, default_token: Optional[str]) -> str:
1589
1589
  return (
1590
1590
  click.prompt(
1591
- f"\nCopy the \"admin your@email\" token from {ui_host}/tokens and paste it here { f'OR press enter to use the token from .tinyb file' if default_token else ''}",
1591
+ f"\nCopy the \"admin your@email\" token from {ui_host}/tokens and paste it here { 'OR press enter to use the token from .tinyb file' if default_token else ''}",
1592
1592
  hide_input=True,
1593
1593
  show_default=False,
1594
1594
  default=default_token,
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.9.1.dev2
3
+ Version: 5.9.1.dev3
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,6 +18,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
+ 5.9.1.dev3
22
+ ***********
23
+
24
+ - `Fixed` Correctly parse lambda expressions in indexes
25
+
21
26
  5.9.1.dev2
22
27
  ***********
23
28