tinybird 4.6.20__tar.gz → 4.6.20.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 (130) hide show
  1. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/PKG-INFO +1 -16
  2. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/ch_utils/constants.py +0 -1
  3. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/ch_utils/engine.py +32 -49
  4. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/client.py +0 -41
  5. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datafile/common.py +1 -9
  6. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datatypes.py +12 -34
  7. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/feedback_manager.py +0 -5
  8. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/service_datasources.py +8 -7
  9. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/sql.py +6 -0
  10. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/sql_template.py +2 -8
  11. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/sql_toolset.py +0 -59
  12. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/__cli__.py +2 -2
  13. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/client.py +0 -41
  14. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/common.py +77 -15
  15. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/copy.py +1 -33
  16. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build.py +0 -1
  17. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_pipe.py +0 -4
  18. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/playground.py +0 -1
  19. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/feedback_manager.py +0 -5
  20. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/infra.py +2 -1
  21. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/logs.py +2 -1
  22. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/materialization.py +1 -1
  23. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/project.py +4 -4
  24. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +4 -4
  25. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/token.py +5 -2
  26. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/watch.py +1 -1
  27. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/branch.py +2 -1
  28. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/cicd.py +0 -2
  29. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/cli.py +9 -5
  30. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/common.py +94 -27
  31. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/config.py +2 -1
  32. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
  33. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/pipe.py +34 -49
  34. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
  35. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/workspace.py +13 -11
  36. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tornado_template.py +17 -216
  37. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird.egg-info/PKG-INFO +1 -16
  38. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird.egg-info/SOURCES.txt +1 -4
  39. tinybird-4.6.20/pyproject.toml +0 -239
  40. tinybird-4.6.20/tinybird/utils/__init__.py +0 -0
  41. tinybird-4.6.20/tinybird/utils/bools.py +0 -19
  42. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/setup.cfg +0 -0
  43. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/__cli__.py +0 -0
  44. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/check_pypi.py +0 -0
  45. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/config.py +0 -0
  46. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/context.py +0 -0
  47. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datafile/exceptions.py +0 -0
  48. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_connection.py +0 -0
  49. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  50. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  51. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/git_settings.py +0 -0
  52. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/iterating/__init__.py +0 -0
  53. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
  54. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/prompts.py +0 -0
  55. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/sql_template_fmt.py +0 -0
  56. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/syncasync.py +0 -0
  57. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/check_pypi.py +0 -0
  58. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/cli.py +0 -0
  59. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/config.py +0 -0
  60. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/branch.py +0 -0
  61. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/build.py +0 -0
  62. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/build_common.py +0 -0
  63. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/cicd.py +0 -0
  64. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/cli.py +0 -0
  65. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/config.py +0 -0
  66. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection.py +0 -0
  67. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  68. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_kafka.py +0 -0
  69. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/connection_s3.py +0 -0
  70. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/create.py +0 -0
  71. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  72. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  73. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/build_pipe.py +0 -0
  74. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/diff.py +0 -0
  75. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  76. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_common.py +0 -0
  77. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_connection.py +0 -0
  78. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/format_datasource.py +0 -0
  79. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +0 -0
  80. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
  81. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/datasource.py +0 -0
  82. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deployment.py +0 -0
  83. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deployment_common.py +0 -0
  84. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  85. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  86. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/exceptions.py +0 -0
  87. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/fmt.py +0 -0
  88. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/info.py +0 -0
  89. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/job.py +0 -0
  90. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/job_common.py +0 -0
  91. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/llm.py +0 -0
  92. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  93. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local.py +0 -0
  94. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local_common.py +0 -0
  95. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  96. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/login.py +0 -0
  97. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/login_common.py +0 -0
  98. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/logout.py +0 -0
  99. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/open.py +0 -0
  100. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/pipe.py +0 -0
  101. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/preview.py +0 -0
  102. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  103. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/py_project.py +0 -0
  104. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/query_output.py +0 -0
  105. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/regions.py +0 -0
  106. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/secret.py +0 -0
  107. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  108. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/sink.py +0 -0
  109. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/table.py +0 -0
  110. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  111. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/test.py +0 -0
  112. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/test_common.py +0 -0
  113. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  114. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/workspace.py +0 -0
  115. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  116. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli.py +0 -0
  117. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  118. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  119. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  120. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  121. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  122. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  123. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  124. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  125. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  126. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  127. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  128. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  129. {tinybird-4.6.20 → tinybird-4.6.20.dev0}/tinybird.egg-info/requires.txt +0 -0
  130. {tinybird-4.6.20 → tinybird-4.6.20.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
3
+ Version: 4.6.20.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,16 +52,6 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
- 4.6.20
56
- ********
57
-
58
- - `Added` Copy pipes accept ``ON_DEMAND_COMPUTE true`` so scheduled runs use on-demand compute. Manual ``tb copy run`` still requires ``--on-demand-compute``, and asks for confirmation when the local datafile does not enable it (skip it with ``--yes``).
59
-
60
- 4.6.19
61
- ********
62
-
63
- - `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
64
-
65
55
  4.6.18
66
56
  ********
67
57
 
@@ -72,11 +62,6 @@ Changelog
72
62
 
73
63
  - `Added` ``tb job retry <job_id>`` to retry eligible import and S3/GCS sync jobs in ``error`` or ``cancelled`` status.
74
64
 
75
- 4.6.18
76
- ********
77
-
78
- - `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
79
-
80
65
  4.6.16
81
66
  ********
82
67
 
@@ -263,5 +263,4 @@ VALID_QUERY_FORMATS = (
263
263
  "JSONCompact",
264
264
  "JSONStringsEachRowWithProgress",
265
265
  "ODBCDriver2",
266
- "Markdown",
267
266
  )
@@ -440,64 +440,35 @@ def get_engine_config(engine: str):
440
440
  )
441
441
 
442
442
 
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
-
443
+ def engine_params(columns, params: List[EngineParam], args: Dict):
444
+ params_values = []
456
445
  for p in params:
457
- if p.required and p.name not in engine_settings:
446
+ if p.required and p.name not in args:
458
447
  raise ValueError(f"Missing required parameter '{p.name}'")
459
- param_value = engine_settings.get(p.name) or p.default_value
448
+ param_value = args.get(p.name, None) or p.default_value
460
449
  if not param_value:
461
450
  continue
462
451
  if p.is_valid:
463
452
  check_is_valid(
464
- valid_check=p.is_valid,
465
- check_type="parameter",
466
- columns=schema_columns,
467
- tb_param=p.tb_param,
468
- value=param_value,
453
+ valid_check=p.is_valid, check_type="parameter", columns=columns, tb_param=p.tb_param, value=param_value
469
454
  )
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
491
455
  params_values.append(param_value)
492
456
  return params_values
493
457
 
494
458
 
495
- def engine_options(options: List[EngineOption], args: Dict) -> List[str]:
459
+ def engine_options(columns, options: List[EngineOption], args: Dict):
496
460
  options_values = []
497
461
  engine_settings = ""
498
462
 
499
463
  for o in options:
464
+ if o.required and o.name not in args:
465
+ raise ValueError(f"Missing required option '{o.name}'")
500
466
  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
+
501
472
  if option_value:
502
473
  if o.sql.lower() == "settings":
503
474
  engine_settings = f"{o.sql} {option_value}"
@@ -519,7 +490,7 @@ def check_is_valid(
519
490
  columns: List[Dict[str, Any]],
520
491
  tb_param: str,
521
492
  value: str,
522
- ) -> None:
493
+ ):
523
494
  """
524
495
  >>> check_is_valid(sorting_key_is_valid, 'option', ['column-name'], 'sorting_key', 'date')
525
496
 
@@ -536,13 +507,15 @@ def check_is_valid(
536
507
  raise ValueError(f"Invalid value '{value}' for {check_type} '{tb_param}', reason: {e}")
537
508
 
538
509
 
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()
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()
541
514
 
542
515
 
543
516
  def engine_full_from_dict(
544
517
  engine: str, args: dict, schema: Optional[str] = None, columns: Optional[List[Dict[str, Any]]] = None
545
- ) -> str:
518
+ ):
546
519
  """
547
520
  >>> schema = ''
548
521
  >>> engine_full_from_dict('wadus', {}, schema=schema)
@@ -633,10 +606,20 @@ def engine_full_from_dict(
633
606
  'ReplacingMergeTree(updated_at) PARTITION BY (tuple()) ORDER BY (project_id) SETTINGS index_granularity = 32'
634
607
  """
635
608
 
636
- validate_engine_config(engine, args, schema=schema, columns=columns)
637
- name, (params, options) = get_engine_config(engine)
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
+
638
616
  engine_settings = {key.replace("engine_", ""): value for key, value in args.items()}
639
- return build_engine(name, params, options, engine_settings)
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)
640
623
 
641
624
 
642
625
  def engine_params_from_engine_full(engine_full: str) -> Dict[str, Any]:
@@ -408,16 +408,6 @@ 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
-
421
411
  params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
422
412
 
423
413
  if sql_condition:
@@ -434,33 +424,6 @@ class TinyB:
434
424
 
435
425
  return await self.wait_for_job(res["id"], status_callback, backoff_multiplier=1.5, maximum_backoff_seconds=20)
436
426
 
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
-
464
427
  async def datasource_delete(self, datasource_name: str, force: bool = False, dry_run: bool = False):
465
428
  params = {"force": "true" if force else "false", "dry_run": "true" if dry_run else "false"}
466
429
  return await self._req(f"/v0/datasources/{datasource_name}?{urlencode(params)}", method="DELETE")
@@ -691,7 +654,6 @@ class TinyB:
691
654
  target_datasource: Optional[str] = None,
692
655
  schedule_cron: Optional[str] = None,
693
656
  mode: Optional[str] = None,
694
- on_demand_compute: Optional[bool] = None,
695
657
  ):
696
658
  data = {"schedule_cron": schedule_cron}
697
659
 
@@ -701,9 +663,6 @@ class TinyB:
701
663
  if mode:
702
664
  data["mode"] = mode
703
665
 
704
- if on_demand_compute is not None:
705
- data["on_demand_compute"] = "true" if on_demand_compute else "false"
706
-
707
666
  return await self._req(f"/v0/pipes/{pipe_name_or_id}/nodes/{node_id}/copy", method="PUT", data=data)
708
667
 
709
668
  async def pipe_remove_copy(self, pipe_name_or_id: str, node_id: str):
@@ -28,7 +28,6 @@ from tinybird.ch_utils.engine import VALID_ENGINE_PARAMS, EngineParam
28
28
  from tinybird.datafile.exceptions import IncludeFileNotFoundException, ParseException, ValidationException
29
29
  from tinybird.tb.modules.exceptions import CLIPipeException
30
30
  from tinybird.tb.modules.feedback_manager import FeedbackManager
31
- from tinybird.utils.bools import parse_optional_bool
32
31
 
33
32
  # Code from sql.py has been duplicated so I can change it without breaking absolutely everything in the app
34
33
  # I'll try not to make logic changes, just error reporting changes
@@ -180,10 +179,9 @@ class CopyParameters(Parameters):
180
179
  COPY_SCHEDULE = "copy_schedule"
181
180
  COPY_MODE = "copy_mode"
182
181
  COPY_MODE_ALIAS = "mode" # we need this because bot MODE and COPY_MODE go to `mode` variable inside the node
183
- ON_DEMAND_COMPUTE = "on_demand_compute"
184
182
  MANDATORY_ATTRIBUTES = PipeParameters.MANDATORY_ATTRIBUTES.union({TARGET_DATASOURCE})
185
183
  ACCEPTED_ATTRIBUTES = PipeParameters.ACCEPTED_ATTRIBUTES.union(MANDATORY_ATTRIBUTES).union(
186
- {COPY_SCHEDULE, COPY_MODE_ALIAS, ON_DEMAND_COMPUTE}
184
+ {COPY_SCHEDULE, COPY_MODE_ALIAS}
187
185
  )
188
186
 
189
187
 
@@ -418,11 +416,6 @@ class Datafile:
418
416
  )
419
417
  if not croniter.is_valid(copy_schedule):
420
418
  raise DatafileValidationError("COPY node schedule must be @on-demand or a valid cron expression.")
421
- if CopyParameters.ON_DEMAND_COMPUTE in node:
422
- try:
423
- node[CopyParameters.ON_DEMAND_COMPUTE] = parse_optional_bool(node[CopyParameters.ON_DEMAND_COMPUTE])
424
- except ValueError:
425
- raise DatafileValidationError("ON_DEMAND_COMPUTE must be true or false")
426
419
  for key in node.keys():
427
420
  if key not in CopyParameters.valid_params():
428
421
  raise DatafileValidationError(
@@ -2104,7 +2097,6 @@ def parse(
2104
2097
  "copy_schedule": assign_node_var(CopyParameters.COPY_SCHEDULE),
2105
2098
  "copy_mode": assign_node_var("mode"),
2106
2099
  "mode": assign_node_var("mode"),
2107
- "on_demand_compute": assign_node_var(CopyParameters.ON_DEMAND_COMPUTE),
2108
2100
  "filter": assign_node_var("filter"),
2109
2101
  "token": add_token,
2110
2102
  "include": include,
@@ -120,74 +120,52 @@ def datetime_test(x: str) -> bool:
120
120
  return any(p.match(x) for p in datetime_patterns)
121
121
 
122
122
 
123
- def bounded_integer(x: str, pattern: re.Pattern[str]) -> Optional[int]:
124
- if pattern.match(x) is None:
125
- return None
126
- digits = x.lstrip("+-").lstrip("0")
127
- # 256-bit unsigned integers have at most 78 decimal digits.
128
- if len(digits) > 78:
129
- return None
130
- return int(x) if len(x) <= 78 else int(digits or "0") * (-1 if x.startswith("-") else 1)
131
-
132
-
133
123
  def int_8_test(x: str) -> bool:
134
- value = bounded_integer(x, intx_re)
135
- return value is not None and -int_8_max <= value < int_8_max
124
+ return intx_re.match(x) is not None and -int_8_max <= int(x) < int_8_max
136
125
 
137
126
 
138
127
  def int16_test(x: str) -> bool:
139
- value = bounded_integer(x, intx_re)
140
- return value is not None and -int16_max <= value < int16_max
128
+ return intx_re.match(x) is not None and -int16_max <= int(x) < int16_max
141
129
 
142
130
 
143
131
  def int32_test(x: str) -> bool:
144
- value = bounded_integer(x, intx_re)
145
- return value is not None and -int32_max <= value < int32_max
132
+ return intx_re.match(x) is not None and -int32_max <= int(x) < int32_max
146
133
 
147
134
 
148
135
  def int64_test(x: str) -> bool:
149
- value = bounded_integer(x, intx_re)
150
- return value is not None and -int64_max <= value < int64_max
136
+ return intx_re.match(x) is not None and -int64_max <= int(x) < int64_max
151
137
 
152
138
 
153
139
  def int128_test(x: str) -> bool:
154
- value = bounded_integer(x, intx_re)
155
- return value is not None and -int128_max <= value < int128_max
140
+ return intx_re.match(x) is not None and -int128_max <= int(x) < int128_max
156
141
 
157
142
 
158
143
  def int256_test(x: str) -> bool:
159
- value = bounded_integer(x, intx_re)
160
- return value is not None and -int256_max <= value < int256_max
144
+ return intx_re.match(x) is not None and -int256_max <= int(x) < int256_max
161
145
 
162
146
 
163
147
  def uint_8_test(x: str) -> bool:
164
- value = bounded_integer(x, uintx_re)
165
- return value is not None and 0 <= value < uint_8_max
148
+ return uintx_re.match(x) is not None and 0 <= int(x) < uint_8_max
166
149
 
167
150
 
168
151
  def uint16_test(x: str) -> bool:
169
- value = bounded_integer(x, uintx_re)
170
- return value is not None and 0 <= value < uint16_max
152
+ return uintx_re.match(x) is not None and 0 <= int(x) < uint16_max
171
153
 
172
154
 
173
155
  def uint32_test(x: str) -> bool:
174
- value = bounded_integer(x, uintx_re)
175
- return value is not None and 0 <= value < uint32_max
156
+ return uintx_re.match(x) is not None and 0 <= int(x) < uint32_max
176
157
 
177
158
 
178
159
  def uint64_test(x: str) -> bool:
179
- value = bounded_integer(x, uintx_re)
180
- return value is not None and 0 <= value < uint64_max
160
+ return uintx_re.match(x) is not None and 0 <= int(x) < uint64_max
181
161
 
182
162
 
183
163
  def uint128_test(x: str) -> bool:
184
- value = bounded_integer(x, intx_re)
185
- return value is not None and 0 <= value < uint128_max
164
+ return intx_re.match(x) is not None and 0 <= int(x) < uint128_max
186
165
 
187
166
 
188
167
  def uint256_test(x: str) -> bool:
189
- value = bounded_integer(x, intx_re)
190
- return value is not None and 0 <= value < uint256_max
168
+ return intx_re.match(x) is not None and 0 <= int(x) < uint256_max
191
169
 
192
170
 
193
171
  def float_test(x: str) -> bool:
@@ -572,11 +572,6 @@ class FeedbackManager:
572
572
  "You can read more about the pricing at https://www.tinybird.co/docs/classic/work-with-data/process-and-copy/materialized-views#compute-compute-separation-for-populates\n\n"
573
573
  "Do you want to proceed with on-demand compute?"
574
574
  )
575
- warning_confirm_on_demand_compute_differs_from_datafile = warning_message(
576
- "The local datafile of pipe '{pipe}' does not set ON_DEMAND_COMPUTE true, but --on-demand-compute was passed.\n"
577
- "On-demand compute uses dedicated resources that are billed separately from your regular usage.\n\n"
578
- "Do you want to proceed?"
579
- )
580
575
 
581
576
  warning_confirm_delete_token = prompt_message("Do you want to delete Token {token}?")
582
577
  warning_confirm_refresh_token = prompt_message("Do you want to refresh Token {token}?")
@@ -39,7 +39,6 @@ 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)"},
43
42
  {"name": "status_code", "type": "Int32"},
44
43
  {"name": "request_id", "type": "String"},
45
44
  {"name": "parameters", "type": "Map(String, String)"},
@@ -444,21 +443,25 @@ def get_tinybird_service_datasources() -> List[Dict[str, Any]]:
444
443
  "dateColumn": "run_validation",
445
444
  "engine": {
446
445
  "engine": "MergeTree",
447
- "sorting_key": "workspace, host, query_hash, run_validation",
446
+ "sorting_key": "database, host, run_validation, query_hash",
448
447
  "partition_key": "toYYYYMM(run_validation)",
449
448
  },
450
449
  "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": "resource_type", "type": "LowCardinality(String)"},
456
- {"name": "resource_name", "type": "String"},
455
+ {"name": "region", "type": "String"},
456
+ {"name": "workspace", "type": "String"},
457
+ {"name": "database", "type": "String"},
458
+ {"name": "pipe_name", "type": "String"},
457
459
  {"name": "query_id", "type": "String"},
458
460
  {"name": "query", "type": "String"},
459
461
  {"name": "error_code", "type": "Int16"},
460
462
  {"name": "error", "type": "String"},
461
463
  {"name": "fix_suggestion", "type": "String"},
464
+ {"name": "fix_suggestion_ai", "type": "String"},
462
465
  {"name": "run_validation", "type": "DateTime"},
463
466
  ],
464
467
  },
@@ -677,7 +680,6 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
677
680
  {"name": "result_rows", "type": "UInt64"},
678
681
  {"name": "url", "type": "Nullable(String)"},
679
682
  {"name": "error", "type": "UInt8"},
680
- {"name": "error_message", "type": "Nullable(String)"},
681
683
  {"name": "status_code", "type": "Int32"},
682
684
  {"name": "request_id", "type": "String"},
683
685
  {"name": "parameters", "type": "Map(String, String)"},
@@ -782,7 +784,7 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
782
784
  },
783
785
  {
784
786
  "name": "organization.metrics_logs",
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.",
787
+ "description": "Metrics of your organization's dedicated clusters",
786
788
  "dateColumn": "timestamp",
787
789
  "engine": {
788
790
  "engine": "MergeTree",
@@ -796,7 +798,6 @@ def get_organization_service_datasources() -> List[Dict[str, Any]]:
796
798
  {"name": "metric", "type": "LowCardinality(String)"},
797
799
  {"name": "value", "type": "String"},
798
800
  {"name": "description", "type": "LowCardinality(String)"},
799
- {"name": "organization_id", "type": "String"},
800
801
  ],
801
802
  },
802
803
  {
@@ -129,9 +129,15 @@ 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
+
132
135
  def materialize_projection_sql(self):
133
136
  return f"MATERIALIZE PROJECTION IF EXISTS {self.name}"
134
137
 
138
+ def clear_projection_sql(self):
139
+ return f"CLEAR PROJECTION IF EXISTS {self.name}"
140
+
135
141
 
136
142
  def as_subquery(sql: str) -> str:
137
143
  return f"""(\n{sql}\n)"""
@@ -18,7 +18,7 @@ from tinybird.context import (
18
18
  )
19
19
 
20
20
  from .datatypes import testers
21
- from .tornado_template import SAFE_BUILTINS, VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Template
21
+ from .tornado_template import VALID_CUSTOM_FUNCTION_NAMES, SecurityException, Template
22
22
 
23
23
  VALID_ACTIVATE_FEATURES = frozenset(["analyzer", "parallel_replicas", "optimize_aggregation_in_order"])
24
24
 
@@ -84,7 +84,6 @@ 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",
88
87
  "__tb__semver",
89
88
  "debug_source_tables",
90
89
  "debug",
@@ -1375,7 +1374,7 @@ _namespace = {
1375
1374
  # they raise a pretty non understandable error but if someone
1376
1375
  # is using them they know what they are trying to do
1377
1376
  # read https://anee.me/escaping-python-jails-849c65cf306e on how to escape from python jails
1378
- "__builtins__": SAFE_BUILTINS,
1377
+ "__buildins__": {},
1379
1378
  "__import__": {},
1380
1379
  "__debug__": {},
1381
1380
  "__doc__": {},
@@ -1385,9 +1384,6 @@ _namespace = {
1385
1384
  "close": None,
1386
1385
  "print": None,
1387
1386
  "input": None,
1388
- # Keep customer parameters named `type`/`dir` usable without restoring the unsafe builtins.
1389
- "type": None,
1390
- "dir": None,
1391
1387
  }
1392
1388
 
1393
1389
 
@@ -1481,8 +1477,6 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1481
1477
  # the generated source code.
1482
1478
  "__name__": self.name.replace(".", "_"),
1483
1479
  "__loader__": ObjectDict(get_source=lambda name: self.code),
1484
- # Set again after kwargs so request parameters can't override it
1485
- "__builtins__": SAFE_BUILTINS,
1486
1480
  "max_threads": set_max_threads,
1487
1481
  "tb_secret": set_tb_secret,
1488
1482
  "tb_var": set_tb_secret,
@@ -699,62 +699,3 @@ 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'
8
- __revision__ = '3a75e0ef'
7
+ __version__ = '4.6.20.dev0'
8
+ __revision__ = 'fee8cae'
@@ -432,16 +432,6 @@ 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
-
445
435
  params = {"name": table_name, "url": url, "mode": mode, "debug": "blocks_block_log", "format": format}
446
436
 
447
437
  if sql_condition:
@@ -458,33 +448,6 @@ class TinyB:
458
448
 
459
449
  return self.wait_for_job(res["id"], status_callback, backoff_multiplier=1.5, maximum_backoff_seconds=20)
460
450
 
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
-
488
451
  def datasource_delete(self, datasource_name: str, force: bool = False, dry_run: bool = False):
489
452
  params = {"force": "true" if force else "false", "dry_run": "true" if dry_run else "false"}
490
453
  return self._req(f"/v0/datasources/{datasource_name}?{urlencode(params)}", method="DELETE")
@@ -743,7 +706,6 @@ class TinyB:
743
706
  target_datasource: Optional[str] = None,
744
707
  schedule_cron: Optional[str] = None,
745
708
  mode: Optional[str] = None,
746
- on_demand_compute: Optional[bool] = None,
747
709
  ):
748
710
  data = {"schedule_cron": schedule_cron}
749
711
 
@@ -753,9 +715,6 @@ class TinyB:
753
715
  if mode:
754
716
  data["mode"] = mode
755
717
 
756
- if on_demand_compute is not None:
757
- data["on_demand_compute"] = "true" if on_demand_compute else "false"
758
-
759
718
  return self._req(f"/v0/pipes/{pipe_name_or_id}/nodes/{node_id}/copy", method="PUT", data=data)
760
719
 
761
720
  def pipe_remove_copy(self, pipe_name_or_id: str, node_id: str):