tinybird 4.6.19__tar.gz → 4.6.19.dev0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (128) hide show
  1. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/PKG-INFO +1 -16
  2. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/ch_utils/constants.py +0 -1
  3. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/ch_utils/engine.py +32 -49
  4. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/client.py +0 -37
  5. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/service_datasources.py +8 -7
  6. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/sql.py +10 -3
  7. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/sql_template.py +71 -53
  8. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/sql_template_fmt.py +13 -8
  9. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/sql_toolset.py +5 -62
  10. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/syncasync.py +11 -9
  11. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/__cli__.py +2 -2
  12. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/client.py +23 -51
  13. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/cicd.py +0 -1
  14. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/cli.py +2 -4
  15. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/common.py +89 -23
  16. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/config.py +2 -1
  17. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/connection_kafka.py +2 -1
  18. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/connection_s3.py +18 -13
  19. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/create.py +6 -3
  20. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/build.py +3 -2
  21. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/build_pipe.py +4 -1
  22. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/diff.py +1 -2
  23. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/format_common.py +10 -9
  24. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/format_connection.py +2 -1
  25. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/format_datasource.py +2 -1
  26. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/format_pipe.py +2 -1
  27. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/pipe_checker.py +7 -6
  28. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/playground.py +4 -3
  29. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datasource.py +8 -7
  30. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/deployment_common.py +5 -3
  31. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/infra.py +2 -1
  32. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/logs.py +2 -1
  33. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/materialization.py +1 -1
  34. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/project.py +4 -4
  35. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/tinyunit/tinyunit.py +4 -4
  36. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/token.py +5 -2
  37. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/watch.py +1 -1
  38. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/branch.py +2 -1
  39. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/cicd.py +0 -2
  40. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/cli.py +9 -5
  41. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/common.py +94 -27
  42. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/config.py +2 -1
  43. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/datasource.py +1 -1
  44. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/pipe.py +13 -9
  45. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/tinyunit/tinyunit.py +4 -4
  46. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/workspace.py +13 -11
  47. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tornado_template.py +17 -12
  48. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird.egg-info/PKG-INFO +1 -16
  49. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird.egg-info/SOURCES.txt +0 -1
  50. tinybird-4.6.19/pyproject.toml +0 -228
  51. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/setup.cfg +0 -0
  52. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/__cli__.py +0 -0
  53. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/check_pypi.py +0 -0
  54. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/config.py +0 -0
  55. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/context.py +0 -0
  56. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datafile/common.py +0 -0
  57. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datafile/exceptions.py +0 -0
  58. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datafile/parse_connection.py +0 -0
  59. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datafile/parse_datasource.py +0 -0
  60. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datafile/parse_pipe.py +0 -0
  61. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/datatypes.py +0 -0
  62. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/feedback_manager.py +0 -0
  63. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/git_settings.py +0 -0
  64. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/iterating/__init__.py +0 -0
  65. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/iterating/data_branch_modes.py +0 -0
  66. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/prompts.py +0 -0
  67. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/check_pypi.py +0 -0
  68. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/cli.py +0 -0
  69. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/config.py +0 -0
  70. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/branch.py +0 -0
  71. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/build.py +0 -0
  72. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/build_common.py +0 -0
  73. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/connection.py +0 -0
  74. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/connection_dynamodb.py +0 -0
  75. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/copy.py +0 -0
  76. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/build_common.py +0 -0
  77. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/build_datasource.py +0 -0
  78. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/fixture.py +0 -0
  79. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/datafile/pull.py +0 -0
  80. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/deployment.py +0 -0
  81. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/deprecations.py +0 -0
  82. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/endpoint.py +0 -0
  83. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/exceptions.py +0 -0
  84. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/feedback_manager.py +0 -0
  85. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/fmt.py +0 -0
  86. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/info.py +0 -0
  87. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/job.py +0 -0
  88. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/job_common.py +0 -0
  89. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/llm.py +0 -0
  90. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/llm_utils.py +0 -0
  91. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/local.py +0 -0
  92. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/local_common.py +0 -0
  93. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/local_logs.py +0 -0
  94. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/login.py +0 -0
  95. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/login_common.py +0 -0
  96. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/logout.py +0 -0
  97. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/open.py +0 -0
  98. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/pipe.py +0 -0
  99. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/preview.py +0 -0
  100. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/project_commands.py +0 -0
  101. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/py_project.py +0 -0
  102. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/query_output.py +0 -0
  103. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/regions.py +0 -0
  104. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/secret.py +0 -0
  105. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/secret_common.py +0 -0
  106. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/sink.py +0 -0
  107. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/table.py +0 -0
  108. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/telemetry.py +0 -0
  109. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/test.py +0 -0
  110. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/test_common.py +0 -0
  111. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/ts_project.py +0 -0
  112. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/workspace.py +0 -0
  113. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb/modules/workspace_members.py +0 -0
  114. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli.py +0 -0
  115. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/auth.py +0 -0
  116. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/connection.py +0 -0
  117. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/exceptions.py +0 -0
  118. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/fmt.py +0 -0
  119. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/job.py +0 -0
  120. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/regions.py +0 -0
  121. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/tag.py +0 -0
  122. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/telemetry.py +0 -0
  123. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/test.py +0 -0
  124. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird/tb_cli_modules/workspace_members.py +0 -0
  125. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird.egg-info/dependency_links.txt +0 -0
  126. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird.egg-info/entry_points.txt +0 -0
  127. {tinybird-4.6.19 → tinybird-4.6.19.dev0}/tinybird.egg-info/requires.txt +0 -0
  128. {tinybird-4.6.19 → tinybird-4.6.19.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.19
3
+ Version: 4.6.19.dev0
4
4
  Summary: Tinybird Command Line Tool
5
5
  Home-page: https://www.tinybird.co/docs/forward/commands
6
6
  Author: Tinybird
@@ -52,26 +52,11 @@ The Tinybird command-line tool allows you to use all the Tinybird functionality
52
52
  Changelog
53
53
  ----------
54
54
 
55
- 4.6.19
56
- ********
57
-
58
- - `Changed` ``tb datasource append`` and ``tb datasource replace`` submit remote CSV, NDJSON, and Parquet URLs through the v1 import endpoint by default.
59
-
60
- 4.6.18
61
- ********
62
-
63
- - `Fixed` CLI commands (for example ``tb migrate-to-forward``) no longer abort on startup when the best-effort refresh of the cached workspace id/name fails for a reason other than authentication.
64
-
65
55
  4.6.17
66
56
  ********
67
57
 
68
58
  - `Added` ``tb job retry <job_id>`` to retry eligible import and S3/GCS sync jobs in ``error`` or ``cancelled`` status.
69
59
 
70
- 4.6.18
71
- ********
72
-
73
- - `Added` ``Markdown`` is now a valid ``FORMAT`` for SQL queries (for example ``tb sql "SELECT 1 FORMAT Markdown"``), returned as a Markdown table.
74
-
75
60
  4.6.16
76
61
  ********
77
62
 
@@ -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")
@@ -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)"""
@@ -669,9 +675,10 @@ def _parse_table_structure(schema: str) -> List[Dict[str, Any]]:
669
675
  )
670
676
  advance_single_char()
671
677
  return schema[begin:i]
672
- # backticked name
673
- advance_single_char()
674
- return get_backticked()
678
+ else:
679
+ # backticked name
680
+ advance_single_char()
681
+ return get_backticked()
675
682
 
676
683
  def parse_expr(lookup: Iterable[SyntaxExpr]) -> str:
677
684
  nonlocal i, line, pos
@@ -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",
@@ -143,7 +142,8 @@ def transform_type(
143
142
  if x is None:
144
143
  if defined:
145
144
  raise SQLTemplateException(REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html")
146
- return None
145
+ else:
146
+ return None
147
147
  if tester == "String":
148
148
  if x is not None:
149
149
  return transform(x)
@@ -167,11 +167,12 @@ def _and(*args, **kwargs):
167
167
  tk = k.rsplit("__", 1)
168
168
  if len(tk) == 1:
169
169
  return "="
170
- if tk[1] in operands:
171
- return operands[tk[1]]
172
- raise SQLTemplateException(
173
- f"operand {tk[1]} not supported", documentation="/cli/advanced-templates.html#sql_and"
174
- )
170
+ else:
171
+ if tk[1] in operands:
172
+ return operands[tk[1]]
173
+ raise SQLTemplateException(
174
+ f"operand {tk[1]} not supported", documentation="/cli/advanced-templates.html#sql_and"
175
+ )
175
176
 
176
177
  return Expression(
177
178
  " and ".join([f"{_name(k)} {_op(k)} {expression_wrapper(v, k)}" for k, v in kwargs.items() if v is not None])
@@ -276,7 +277,8 @@ def columns(x, default=None, fn=None):
276
277
 
277
278
  if fn:
278
279
  return Expression(",".join(f"{fn}({str(column(c, c))}) as {c}" for c in _columns))
279
- return Expression(",".join(str(column(c, c)) for c in _columns))
280
+ else:
281
+ return Expression(",".join(str(column(c, c)) for c in _columns))
280
282
 
281
283
 
282
284
  def column(x, default=None):
@@ -366,10 +368,11 @@ def boolean(x, default=None):
366
368
  if default is None:
367
369
  return 0
368
370
  return boolean(default)
369
- if isinstance(x, Placeholder):
371
+ elif isinstance(x, Placeholder):
370
372
  return boolean(default)
371
- if isinstance(x, str) and (x == "0" or x.lower() == "false"):
372
- return 0
373
+ elif isinstance(x, str):
374
+ if x == "0" or x.lower() == "false":
375
+ return 0
373
376
 
374
377
  return int(bool(x))
375
378
 
@@ -400,7 +403,8 @@ def array_type(types):
400
403
  raise SQLTemplateException(
401
404
  REQUIRED_PARAM_NOT_DEFINED, documentation="/cli/advanced-templates.html"
402
405
  )
403
- return None
406
+ else:
407
+ return None
404
408
  values = []
405
409
  list_values = x if type(x) == list else x.split(",") # noqa: E721
406
410
  for i, t in enumerate(list_values):
@@ -740,11 +744,12 @@ def __date_diff(
740
744
 
741
745
  if unit == "days":
742
746
  return int(diff / 86400)
743
- if unit == "hours":
747
+ elif unit == "hours":
744
748
  return int(diff / 3600)
745
- if unit == "minutes":
749
+ elif unit == "minutes":
746
750
  return int(diff / 60)
747
- return int(diff)
751
+ else:
752
+ return int(diff)
748
753
  except Exception:
749
754
  if none_if_error:
750
755
  return None
@@ -1343,9 +1348,9 @@ def escape_single_quote_str(s):
1343
1348
  def expression_wrapper(x, name, escape_arrays: bool = False):
1344
1349
  if type(x) in (unicode_type, bytes, str):
1345
1350
  return "'" + sqlescape_for_string_expression(x) + "'"
1346
- if isinstance(x, Placeholder):
1351
+ elif isinstance(x, Placeholder):
1347
1352
  return "'__no_value__'"
1348
- if isinstance(x, Comment):
1353
+ elif isinstance(x, Comment):
1349
1354
  return "-- {x} \n"
1350
1355
  if x is None:
1351
1356
  truncated_name = name[:20] + "..." if len(name) > 20 else name
@@ -1356,7 +1361,10 @@ def expression_wrapper(x, name, escape_arrays: bool = False):
1356
1361
  logging.warning(f"expression_wrapper -> list :{x}:")
1357
1362
 
1358
1363
  try:
1359
- return f"[{','.join(escape_single_quote_str(item) if isinstance(item, str) else str(item) for item in x)}]"
1364
+ result = (
1365
+ f"[{','.join(escape_single_quote_str(item) if isinstance(item, str) else str(item) for item in x)}]"
1366
+ )
1367
+ return result
1360
1368
  except Exception as e:
1361
1369
  logging.error(f"Error escaping array: {e}")
1362
1370
  return x
@@ -1417,18 +1425,20 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1417
1425
  # secret available: Always use workspace secret regardless of test mode
1418
1426
  template_execution_results.add_ch_param(x)
1419
1427
  return Symbol("{" + sqlescape(x) + ": String}")
1420
- # secret not available: Check test mode and defaults
1421
- is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
1422
- if default is not None:
1423
- # Use provided default value
1424
- return default
1425
- if is_test_mode:
1426
- # In test mode without default - return placeholder
1427
- return Symbol("{" + sqlescape(x) + ": String}")
1428
- # Not in test mode, no secret, no default - raise error
1429
- raise SQLTemplateException(
1430
- f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
1431
- )
1428
+ else:
1429
+ # secret not available: Check test mode and defaults
1430
+ is_test_mode = TB_SECRET_IN_TEST_MODE in template_execution_results
1431
+ if default is not None:
1432
+ # Use provided default value
1433
+ return default
1434
+ elif is_test_mode:
1435
+ # In test mode without default - return placeholder
1436
+ return Symbol("{" + sqlescape(x) + ": String}")
1437
+ else:
1438
+ # Not in test mode, no secret, no default - raise error
1439
+ raise SQLTemplateException(
1440
+ f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
1441
+ )
1432
1442
  except Exception:
1433
1443
  raise SQLTemplateException(
1434
1444
  f"Cannot access secret '{x}'. Check the secret exists in the Workspace and the token has the required scope."
@@ -1512,12 +1522,14 @@ def generate(self, **kwargs) -> Tuple[str, TemplateExecutionResults]:
1512
1522
 
1513
1523
  if line:
1514
1524
  raise SQLTemplateException(f"{message.strip()} line {line[0]}")
1515
- raise SQLTemplateException(f"{message.strip()}")
1525
+ else:
1526
+ raise SQLTemplateException(f"{message.strip()}")
1516
1527
  except Exception as e:
1517
1528
  if isinstance(e, SQLTemplateException):
1518
1529
  raise e
1519
- logging.exception(f"Error on unbound local error: {e}")
1520
- raise ValueError(str(e))
1530
+ else:
1531
+ logging.exception(f"Error on unbound local error: {e}")
1532
+ raise ValueError(str(e))
1521
1533
  except TypeError as e:
1522
1534
  error = str(e)
1523
1535
  if "not supported between instances of 'Placeholder' and " in str(e):
@@ -1948,27 +1960,29 @@ def get_var_data(content, node_id=None):
1948
1960
  # ast.Constant nodes, so .value holds str/bytes/int/float/bool/None.
1949
1961
  if type(x) == ast.Constant: # noqa: E721
1950
1962
  return x.value
1951
- if type(x) == ast.Name: # noqa: E721
1963
+ elif type(x) == ast.Name: # noqa: E721
1952
1964
  return x.id
1953
- if type(x) == ast.List: # noqa: E721
1965
+ elif type(x) == ast.List: # noqa: E721
1954
1966
  # List can hold different types
1955
1967
  return _get_list_var_data(x)
1956
- if type(x) == ast.BinOp: # noqa: E721
1968
+ elif type(x) == ast.BinOp: # noqa: E721
1957
1969
  # in this case there could be several variables
1958
1970
  # if that's the case the left one is the main
1959
1971
  r = node_to_value(x.left)
1960
1972
  if not r:
1961
1973
  r = node_to_value(x.right)
1962
1974
  return r
1963
- if type(x) == ast.UnaryOp and type(x.operand) == ast.Constant: # noqa: E721
1975
+ elif type(x) == ast.UnaryOp and type(x.operand) == ast.Constant: # noqa: E721
1964
1976
  if type(x.op) == ast.USub: # noqa: E721
1965
1977
  return x.operand.value * -1
1966
- return x.operand.value
1967
- try:
1968
- return x.id
1969
- except Exception:
1970
- # don't let this ruin the parsing
1971
- pass
1978
+ else:
1979
+ return x.operand.value
1980
+ else:
1981
+ try:
1982
+ return x.id
1983
+ except Exception:
1984
+ # don't let this ruin the parsing
1985
+ pass
1972
1986
  return None
1973
1987
 
1974
1988
  def _get_list_var_data(x):
@@ -1980,7 +1994,7 @@ def get_var_data(content, node_id=None):
1980
1994
  # ast.Bytes/ast.Str/ast.Num/ast.NameConstant (removed in 3.14).
1981
1995
  if type(first_elem) == ast.Constant: # noqa: E721
1982
1996
  return [elem.value for elem in x.elts]
1983
- if type(first_elem) == ast.Name: # noqa: E721
1997
+ elif type(first_elem) == ast.Name: # noqa: E721
1984
1998
  return [elem.id for elem in x.elts]
1985
1999
 
1986
2000
  return []
@@ -1999,7 +2013,8 @@ def get_var_data(content, node_id=None):
1999
2013
  # to just `ast.parse(content)` once we confirm all environments use patched Python versions.
2000
2014
  def parse_content(content, retries=0):
2001
2015
  try:
2002
- return ast.parse(content)
2016
+ parsed = ast.parse(content)
2017
+ return parsed
2003
2018
  except Exception as e:
2004
2019
  if "AST constructor recursion depth mismatch" not in str(e):
2005
2020
  raise e
@@ -2027,7 +2042,8 @@ def get_var_data(content, node_id=None):
2027
2042
  if type(x) == ast.Call: # noqa: E721
2028
2043
  # Nested calls are traversed via `ast.iter_child_nodes` below.
2029
2044
  continue
2030
- args.append(node_to_value(x))
2045
+ else:
2046
+ args.append(node_to_value(x))
2031
2047
 
2032
2048
  kwargs = {}
2033
2049
  for x in node.keywords:
@@ -2515,7 +2531,8 @@ def format_SQLTemplateException_message(e: SQLTemplateException, vars_and_types:
2515
2531
  f"{REQUIRED_PARAM_NOT_DEFINED}. Check the parameters {join_with_different_last_separator(vars_with_default_none)}. Please provide a value or set a default value in the pipe code.",
2516
2532
  e.documentation,
2517
2533
  )
2518
- raise e
2534
+ else:
2535
+ raise e
2519
2536
 
2520
2537
 
2521
2538
  def render_sql_template(
@@ -2896,12 +2913,12 @@ def render_sql_template(
2896
2913
  # https://gitlab.com/tinybird/analytics/-/issues/943
2897
2914
  if "length" in v and not v["length"]:
2898
2915
  raise SQLTemplateException("length cannot be used as a variable name or as a function inside of a template")
2899
- if "missing 1 required positional argument" in str(e):
2916
+ elif "missing 1 required positional argument" in str(e):
2900
2917
  raise SQLTemplateException(
2901
2918
  "one of the transform type functions is missing an argument",
2902
2919
  documentation="/cli/advanced-templates.html#transform-types-functions",
2903
2920
  )
2904
- if "not callable" in str(e) or "unhashable type" in str(e):
2921
+ elif "not callable" in str(e) or "unhashable type" in str(e):
2905
2922
  raise SQLTemplateException(
2906
2923
  "wrong syntax, you might be using a not valid function inside a control block",
2907
2924
  documentation="/cli/advanced-templates.html",
@@ -3046,7 +3063,7 @@ def render_template_with_secrets(
3046
3063
  )
3047
3064
  return '""'
3048
3065
  return value
3049
- if default is not None:
3066
+ elif default is not None:
3050
3067
  if isinstance(default, str) and len(default) == 0:
3051
3068
  if empty_secret_raises:
3052
3069
  raise SQLTemplateException(
@@ -3054,9 +3071,10 @@ def render_template_with_secrets(
3054
3071
  )
3055
3072
  return '""'
3056
3073
  return default
3057
- raise SQLTemplateException(
3058
- f"Cannot access secret '{secret_name}'. Check the secret exists in the Workspace and the token has the required scope."
3059
- )
3074
+ else:
3075
+ raise SQLTemplateException(
3076
+ f"Cannot access secret '{secret_name}'. Check the secret exists in the Workspace and the token has the required scope."
3077
+ )
3060
3078
 
3061
3079
  # Create the template
3062
3080
  t = Template(content, name=name, autoescape=None)
@@ -33,7 +33,8 @@ class TBLine(Line):
33
33
  Tinybird => This is overriden from the base Line because we want SQL inside a template to be indented
34
34
  https://github.com/tconbeer/sqlfmt/blob/c11775b92d8a45f0e91d871b81a88a894d620bec/src/sqlfmt/line.py#L92
35
35
  """
36
- return INDENT * (self.depth[0] + self.depth[1])
36
+ prefix = INDENT * (self.depth[0] + self.depth[1])
37
+ return prefix
37
38
 
38
39
 
39
40
  def from_nodes(
@@ -105,7 +106,8 @@ def _format_jinja_node(self, node: Node, max_length: int) -> bool:
105
106
 
106
107
  return tag.is_blackened
107
108
 
108
- return False
109
+ else:
110
+ return False
109
111
 
110
112
 
111
113
  # Some monkey patching
@@ -156,7 +158,8 @@ class TinybirdNodeManager(NodeManager):
156
158
  TokenType.SET_OPERATOR,
157
159
  ):
158
160
  return " ".join(token.token.split())
159
- return token.token
161
+ else:
162
+ return token.token
160
163
 
161
164
 
162
165
  class TinybirdDialect(ClickHouse):
@@ -227,11 +230,12 @@ class TinybirdDialect(ClickHouse):
227
230
  Creates and returns an analyzer that uses the Dialect's rules for lexing
228
231
  Custom NodeManager for Tinybird
229
232
  """
230
- return Analyzer(
233
+ analyzer = Analyzer(
231
234
  line_length=line_length,
232
235
  rules=self.get_rules(),
233
236
  node_manager=TinybirdNodeManager(self.case_sensitive_names, self.lower_keywords),
234
237
  )
238
+ return analyzer
235
239
 
236
240
 
237
241
  @dataclass
@@ -249,10 +253,11 @@ class TBMode(Mode):
249
253
  def _calc_str(self) -> str:
250
254
  if self.is_multiline:
251
255
  return self.token.token + "\n"
252
- marker, comment_text = self._comment_parts()
253
- if comment_text == "":
254
- return marker + "\n"
255
- return marker + " " + comment_text + "\n"
256
+ else:
257
+ marker, comment_text = self._comment_parts()
258
+ if comment_text == "":
259
+ return marker + "\n"
260
+ return marker + " " + comment_text + "\n"
256
261
 
257
262
 
258
263
  Comment._calc_str = property(_calc_str)