tinybird-cli 5.0.0.dev5__tar.gz → 5.0.1.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 (46) hide show
  1. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/PKG-INFO +9 -31
  2. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/__cli__.py +2 -2
  3. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/ch_utils/constants.py +1 -0
  4. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/client.py +17 -7
  5. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/datafile.py +2 -1
  6. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/sql_toolset.py +69 -16
  7. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird_cli.egg-info/PKG-INFO +9 -31
  8. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/setup.cfg +0 -0
  9. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/ch_utils/engine.py +0 -0
  10. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/check_pypi.py +0 -0
  11. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/config.py +0 -0
  12. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/connectors.py +0 -0
  13. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/context.py +0 -0
  14. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/datatypes.py +0 -0
  15. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/feedback_manager.py +0 -0
  16. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/git_settings.py +0 -0
  17. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/sql.py +0 -0
  18. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/sql_template.py +0 -0
  19. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/sql_template_fmt.py +0 -0
  20. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/syncasync.py +0 -0
  21. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli.py +0 -0
  22. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  23. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/branch.py +0 -0
  24. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/cicd.py +0 -0
  25. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/cli.py +0 -0
  26. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/common.py +0 -0
  27. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/config.py +0 -0
  28. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  29. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/datasource.py +0 -0
  30. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  31. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  32. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/pipe.py +0 -0
  33. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  34. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  35. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  36. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +0 -0
  37. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit_lib.py +0 -0
  38. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/token.py +0 -0
  39. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/workspace.py +0 -0
  40. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  41. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird/tornado_template.py +0 -0
  42. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird_cli.egg-info/SOURCES.txt +0 -0
  43. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird_cli.egg-info/dependency_links.txt +0 -0
  44. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird_cli.egg-info/entry_points.txt +0 -0
  45. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/tinybird_cli.egg-info/requires.txt +0 -0
  46. {tinybird-cli-5.0.0.dev5 → tinybird-cli-5.0.1.dev0}/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.0.0.dev5
3
+ Version: 5.0.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,42 +18,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.0.0.dev5
22
- ************
23
-
24
- - `Added` parameter to `tb workspace members add` --role
25
-
26
- 5.0.0.dev4
27
- ************
21
+ 5.0.0
22
+ ******
28
23
 
29
- - `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
24
+ - `Breaking change` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
30
25
 
31
- 5.0.0.dev3
32
- ************
26
+ Detailed info at https://www.tinybird.co/docs/changelog/2024-06-17-insertion_date-deprecation
33
27
 
28
+ - `Added` parameter to `tb pipe regression-test` --relative-change
29
+ - `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
30
+ - `Added` --role parameter to `tb workspace members add`
34
31
  - `Changed` Point region hosts to new `app.tinybird.co` domain
35
-
36
- 5.0.0.dev2
37
- ************
38
-
32
+ - `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
39
33
  - `Improved` syntax error messages when parsing endpoints.
40
34
 
41
- 5.0.0.dev1
42
- ************
43
-
44
- - `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
45
-
46
- 5.0.0.dev0
47
- ************
48
-
49
- - `Changed` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
50
-
51
- 4.1.2.dev0
52
- ************
53
-
54
- - `Added` parameter to `tb pipe regression-test` --relative-change
55
-
56
-
57
35
  4.1.1
58
36
  ************
59
37
 
@@ -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.0.0.dev5'
8
- __revision__ = 'e2df9f1'
7
+ __version__ = '5.0.1.dev0'
8
+ __revision__ = '2af0ad5'
@@ -4,6 +4,7 @@ LIVE_WS_NAME = "live"
4
4
  SNAPSHOT_WS_NAME = "snapshot"
5
5
 
6
6
  ENABLED_TABLE_FUNCTIONS = {"generateRandom", "null", "numbers", "numbers_mt", "values", "zeros", "zeros_mt"}
7
+ COPY_ENABLED_TABLE_FUNCTIONS = frozenset(["postgresql"])
7
8
 
8
9
  ENABLED_SYSTEM_TABLES = {
9
10
  "functions",
@@ -11,6 +11,7 @@ import requests.adapters
11
11
  from requests import Response
12
12
  from urllib3 import Retry
13
13
 
14
+ from tinybird.ch_utils.constants import COPY_ENABLED_TABLE_FUNCTIONS
14
15
  from tinybird.syncasync import sync_to_async
15
16
  from tinybird.tb_cli_modules.regions import fill_with_public_regions
16
17
  from tinybird.tb_cli_modules.telemetry import add_telemetry_event
@@ -1035,23 +1036,32 @@ class TinyB(object):
1035
1036
  return result["q"]
1036
1037
 
1037
1038
  @staticmethod
1038
- def _sql_get_used_tables_local(sql: str, raising: bool = False) -> List[str]:
1039
+ def _sql_get_used_tables_local(sql: str, raising: bool = False, is_copy: Optional[bool] = False) -> List[str]:
1039
1040
  from tinybird.sql_toolset import sql_get_used_tables
1040
1041
 
1041
- tables = sql_get_used_tables(sql, raising, table_functions=False)
1042
+ tables = sql_get_used_tables(
1043
+ sql, raising, table_functions=False, function_allow_list=COPY_ENABLED_TABLE_FUNCTIONS if is_copy else None
1044
+ )
1042
1045
  return [t[1] if t[0] == "" else f"{t[0]}.{t[1]}" for t in tables]
1043
1046
 
1044
- async def _sql_get_used_tables_remote(self, sql: str, raising: bool = False) -> List[str]:
1045
- params = {"q": sql, "raising": "true" if raising else "false", "table_functions": "false"}
1047
+ async def _sql_get_used_tables_remote(
1048
+ self, sql: str, raising: bool = False, is_copy: Optional[bool] = False
1049
+ ) -> List[str]:
1050
+ params = {
1051
+ "q": sql,
1052
+ "raising": "true" if raising else "false",
1053
+ "table_functions": "false",
1054
+ "is_copy": "true" if is_copy else "false",
1055
+ }
1046
1056
  result = await self._req("/v0/sql_tables", data=params, method="POST")
1047
1057
  return [t[1] if t[0] == "" else f"{t[0]}.{t[1]}" for t in result["tables"]]
1048
1058
 
1049
1059
  # Get used tables from a query. Does not include table functions
1050
- async def sql_get_used_tables(self, sql: str, raising: bool = False) -> List[str]:
1060
+ async def sql_get_used_tables(self, sql: str, raising: bool = False, is_copy: Optional[bool] = False) -> List[str]:
1051
1061
  try:
1052
- return self._sql_get_used_tables_local(sql, raising)
1062
+ return self._sql_get_used_tables_local(sql, raising, is_copy)
1053
1063
  except ModuleNotFoundError:
1054
- return await self._sql_get_used_tables_remote(sql, raising)
1064
+ return await self._sql_get_used_tables_remote(sql, raising, is_copy)
1055
1065
 
1056
1066
  @staticmethod
1057
1067
  def _replace_tables_local(q: str, replacements):
@@ -1476,6 +1476,7 @@ async def process_file(
1476
1476
  deps = []
1477
1477
  nodes: List[Dict[str, Any]] = []
1478
1478
 
1479
+ is_copy = any([node for node in doc.nodes if node.get("type", "standard").lower() == PipeNodeTypes.COPY])
1479
1480
  for node in doc.nodes:
1480
1481
  sql = node["sql"]
1481
1482
  node_type = node.get("type", "standard").lower()
@@ -1504,7 +1505,7 @@ async def process_file(
1504
1505
  sql_rendered = sql
1505
1506
 
1506
1507
  try:
1507
- dependencies = await tb_client.sql_get_used_tables(sql_rendered, raising=True)
1508
+ dependencies = await tb_client.sql_get_used_tables(sql_rendered, raising=True, is_copy=is_copy)
1508
1509
  deps += [t for t in dependencies if t not in [n["name"] for n in doc.nodes]]
1509
1510
 
1510
1511
  except Exception as e:
@@ -3,20 +3,26 @@ import logging
3
3
  from collections import defaultdict
4
4
  from datetime import datetime
5
5
  from functools import lru_cache
6
- from typing import Any, List, Optional, Set, Tuple
6
+ from typing import Any, FrozenSet, List, Optional, Set, Tuple
7
7
 
8
8
  from chtoolset import query as chquery
9
9
  from toposort import toposort
10
10
 
11
- from tinybird.ch_utils.constants import ENABLED_TABLE_FUNCTIONS
11
+ from tinybird.ch_utils.constants import COPY_ENABLED_TABLE_FUNCTIONS, ENABLED_TABLE_FUNCTIONS
12
12
 
13
13
  VALID_REMOTE = "VALID_REMOTE"
14
14
 
15
15
 
16
16
  class InvalidFunction(ValueError):
17
17
  def __init__(self, msg: str = "", table_function_names: str = ""):
18
+ if any([fn for fn in COPY_ENABLED_TABLE_FUNCTIONS if fn in msg]):
19
+ msg = msg.replace("is restricted", "is restricted to Copy Pipes")
20
+
18
21
  if table_function_names:
19
- self.msg = f"The query uses disabled table functions: '{table_function_names}'"
22
+ if "postgresql" in table_function_names:
23
+ self.msg = "The postgresql table function is only allowed in Copy Pipes"
24
+ else:
25
+ self.msg = f"The query uses disabled table functions: '{table_function_names}'"
20
26
  else:
21
27
  self.msg = msg
22
28
  super().__init__(self.msg)
@@ -58,7 +64,11 @@ def format_where_for_mutation_command(where_clause: str) -> str:
58
64
 
59
65
  @lru_cache(maxsize=2**13)
60
66
  def sql_get_used_tables_cached(
61
- sql: str, raising: bool = False, default_database: str = "", table_functions: bool = True
67
+ sql: str,
68
+ raising: bool = False,
69
+ default_database: str = "",
70
+ table_functions: bool = True,
71
+ function_allow_list: Optional[FrozenSet[str]] = None,
62
72
  ) -> List[Tuple[str, str, str]]:
63
73
  """
64
74
  >>> sql_get_used_tables("SELECT 1 FROM the_table")
@@ -79,7 +89,11 @@ def sql_get_used_tables_cached(
79
89
  [('d_d3926a', 't_976af08ec4b547419e729c63e754b17b', '')]
80
90
  """
81
91
  try:
82
- tables: List[Tuple[str, str, str]] = chquery.tables(sql, default_database=default_database)
92
+ _function_allow_list = list() if function_allow_list is None else list(function_allow_list)
93
+
94
+ tables: List[Tuple[str, str, str]] = chquery.tables(
95
+ sql, default_database=default_database, function_allow_list=_function_allow_list
96
+ )
83
97
  if not table_functions:
84
98
  return [(t[0], t[1], "") for t in tables if t[0] or t[1]]
85
99
  return tables
@@ -93,10 +107,17 @@ def sql_get_used_tables_cached(
93
107
 
94
108
 
95
109
  def sql_get_used_tables(
96
- sql: str, raising: bool = False, default_database: str = "", table_functions: bool = True
110
+ sql: str,
111
+ raising: bool = False,
112
+ default_database: str = "",
113
+ table_functions: bool = True,
114
+ function_allow_list: Optional[FrozenSet[str]] = None,
97
115
  ) -> List[Tuple[str, str, str]]:
98
- # We return a copy here since the callers might (and replace_tables does) edit the resulting object
99
- return copy.copy(sql_get_used_tables_cached(sql, raising, default_database, table_functions))
116
+ hashable_list = frozenset() if function_allow_list is None else function_allow_list
117
+
118
+ return copy.copy(
119
+ sql_get_used_tables_cached(sql, raising, default_database, table_functions, function_allow_list=hashable_list)
120
+ )
100
121
 
101
122
 
102
123
  class ReplacementsDict(dict):
@@ -150,9 +171,18 @@ def replace_tables_chquery_cached(
150
171
  default_database: str = "",
151
172
  output_one_line: bool = False,
152
173
  timestamp: Optional[datetime] = None,
174
+ function_allow_list: Optional[FrozenSet[str]] = None,
153
175
  ) -> str:
154
176
  replacements = dict(sorted_replacements) if sorted_replacements else {}
155
- return chquery.replace_tables(sql, replacements, default_database=default_database, one_line=output_one_line)
177
+ _function_allow_list = list() if function_allow_list is None else list(function_allow_list)
178
+
179
+ return chquery.replace_tables(
180
+ sql,
181
+ replacements,
182
+ default_database=default_database,
183
+ one_line=output_one_line,
184
+ function_allow_list=_function_allow_list,
185
+ )
156
186
 
157
187
 
158
188
  def replace_tables(
@@ -164,15 +194,19 @@ def replace_tables(
164
194
  valid_tables: Optional[Set[Tuple[str, str]]] = None,
165
195
  output_one_line: bool = False,
166
196
  timestamp: Optional[datetime] = None,
197
+ function_allow_list: Optional[FrozenSet[str]] = None,
167
198
  ) -> str:
168
199
  """
169
200
  Given a query and a list of table replacements, returns the query after applying the table replacements.
170
201
  It takes into account dependencies between replacement subqueries (if any)
171
202
  It also validates the sql to verify it's valid and doesn't use unknown or prohibited functions
172
203
  """
204
+ hashable_list = frozenset() if function_allow_list is None else function_allow_list
173
205
  if not replacements:
174
206
  # Always call replace_tables to do validation and formatting
175
- return replace_tables_chquery_cached(sql, None, output_one_line=output_one_line, timestamp=timestamp)
207
+ return replace_tables_chquery_cached(
208
+ sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
209
+ )
176
210
 
177
211
  _replaced_with = set()
178
212
  _replacements = ReplacementsDict()
@@ -182,14 +216,24 @@ def replace_tables(
182
216
  _replaced_with.add(r)
183
217
 
184
218
  deps: defaultdict = defaultdict(set)
185
- _tables = sql_get_used_tables(sql, default_database=default_database, raising=True, table_functions=check_functions)
219
+ _tables = sql_get_used_tables(
220
+ sql,
221
+ default_database=default_database,
222
+ raising=True,
223
+ table_functions=check_functions,
224
+ function_allow_list=function_allow_list,
225
+ )
186
226
  seen_tables = set()
187
227
  table: Tuple[str, str] | Tuple[str, str, str]
228
+ if function_allow_list is None:
229
+ _enabled_table_functions = ENABLED_TABLE_FUNCTIONS
230
+ else:
231
+ _enabled_table_functions = ENABLED_TABLE_FUNCTIONS.union(set(function_allow_list))
188
232
  while _tables:
189
233
  table = _tables.pop()
190
234
  if len(table) == 3:
191
235
  first_table, second_table, last_table = table
192
- if last_table and last_table not in ENABLED_TABLE_FUNCTIONS:
236
+ if last_table and last_table not in _enabled_table_functions:
193
237
  raise InvalidFunction(table_function_names=last_table)
194
238
  if first_table or second_table:
195
239
  table = (first_table, second_table)
@@ -204,7 +248,7 @@ def replace_tables(
204
248
  if len(dependent_table) == 3:
205
249
  if (
206
250
  dependent_table[2]
207
- and dependent_table[2] not in ENABLED_TABLE_FUNCTIONS
251
+ and dependent_table[2] not in _enabled_table_functions
208
252
  and not (dependent_table[2] in ["cluster"] and replacement[0] == VALID_REMOTE)
209
253
  ):
210
254
  raise InvalidFunction(table_function_names=dependent_table[2])
@@ -219,7 +263,9 @@ def replace_tables(
219
263
  deps_sorted = list(reversed(list(toposort(deps))))
220
264
 
221
265
  if not deps_sorted:
222
- return replace_tables_chquery_cached(sql, None, output_one_line=output_one_line, timestamp=timestamp)
266
+ return replace_tables_chquery_cached(
267
+ sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
268
+ )
223
269
 
224
270
  for current_deps in deps_sorted:
225
271
  current_replacements = {}
@@ -251,10 +297,17 @@ def replace_tables(
251
297
  # We need to transform the dictionary into something cacheable, so a sorted tuple of tuples it is
252
298
  r = tuple(sorted([(k, v) for k, v in current_replacements.items()]))
253
299
  sql = replace_tables_chquery_cached(
254
- sql, r, default_database=default_database, output_one_line=output_one_line, timestamp=timestamp
300
+ sql,
301
+ r,
302
+ default_database=default_database,
303
+ output_one_line=output_one_line,
304
+ timestamp=timestamp,
305
+ function_allow_list=hashable_list,
255
306
  )
256
307
  else:
257
- sql = replace_tables_chquery_cached(sql, None, output_one_line=output_one_line, timestamp=timestamp)
308
+ sql = replace_tables_chquery_cached(
309
+ sql, None, output_one_line=output_one_line, timestamp=timestamp, function_allow_list=hashable_list
310
+ )
258
311
 
259
312
  return sql
260
313
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: tinybird-cli
3
- Version: 5.0.0.dev5
3
+ Version: 5.0.1.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/cli/introduction.html
6
6
  Author: Tinybird
@@ -18,42 +18,20 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
18
18
  Changelog
19
19
  ----------
20
20
 
21
- 5.0.0.dev5
22
- ************
23
-
24
- - `Added` parameter to `tb workspace members add` --role
25
-
26
- 5.0.0.dev4
27
- ************
21
+ 5.0.0
22
+ ******
28
23
 
29
- - `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
24
+ - `Breaking change` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
30
25
 
31
- 5.0.0.dev3
32
- ************
26
+ Detailed info at https://www.tinybird.co/docs/changelog/2024-06-17-insertion_date-deprecation
33
27
 
28
+ - `Added` parameter to `tb pipe regression-test` --relative-change
29
+ - `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
30
+ - `Added` --role parameter to `tb workspace members add`
34
31
  - `Changed` Point region hosts to new `app.tinybird.co` domain
35
-
36
- 5.0.0.dev2
37
- ************
38
-
32
+ - `Improved` help message of `tb workspace members set-role` now displays the 3 valid roles: viewer|guest|admin.
39
33
  - `Improved` syntax error messages when parsing endpoints.
40
34
 
41
- 5.0.0.dev1
42
- ************
43
-
44
- - `Added` `tb push` displays warnings when using a reserved parameter in a pipe.
45
-
46
- 5.0.0.dev0
47
- ************
48
-
49
- - `Changed` Make `insertion_date` column explicit. This column is no longer inferred, it must be present in the Data Source file.
50
-
51
- 4.1.2.dev0
52
- ************
53
-
54
- - `Added` parameter to `tb pipe regression-test` --relative-change
55
-
56
-
57
35
  4.1.1
58
36
  ************
59
37