tinybird-toolset 2.2.2__tar.gz → 2.4.0__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.
- {tinybird_toolset-2.2.2/src/tinybird_toolset.egg-info → tinybird_toolset-2.4.0}/PKG-INFO +1 -1
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/README.md +37 -8
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/setup.py +1 -1
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/chtoolset/query.py +1 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0/src/tinybird_toolset.egg-info}/PKG-INFO +1 -1
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/SOURCES.txt +1 -0
- tinybird_toolset-2.4.0/tests/test_normalize_table_column.py +431 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_replace_tables.py +19 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_tables.py +17 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/LICENSE +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/MANIFEST.in +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/conf.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/setup.cfg +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/chtoolset/__init__.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/dependency_links.txt +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/requires.txt +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/top_level.txt +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_check_compatible_types.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_check_write_query.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_chquery.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_convert_to_row_binary.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_explain_ast.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_get_columns_from_create_query.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_get_left_table.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_internal_cache.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_normalize_query_keep_names.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_parse_create_materialized_view_target_table.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_replace_tables_backward_compat.py +0 -0
- {tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_rewrite_aggregation_states.py +0 -0
|
@@ -60,6 +60,15 @@ The build is a multi-stage process orchestrated by `conf.py`:
|
|
|
60
60
|
2. **Toolset Build** - Compiles `functions/*.cpp` into `libCHToolset.a`
|
|
61
61
|
3. **Extension Build** - Compiles `src/query.cpp`, links everything into `_query.so`
|
|
62
62
|
|
|
63
|
+
On MacOS, you'll need the gnu version of multiple tools. You can get them with:
|
|
64
|
+
```
|
|
65
|
+
brew install grep findutils gpatch
|
|
66
|
+
```
|
|
67
|
+
And you'll have to add their locations to your PATH. Homebrew will output which command to run for each.
|
|
68
|
+
|
|
69
|
+
You'll also need `objcopy` or `llvm-objcopy` in your path. The latter is more practical and will be
|
|
70
|
+
in the bin path of your homebrew llvm installation, so you'll have to do something like: `export PATH="/opt/homebrew/opt/llvm@19/bin:$PATH"`
|
|
71
|
+
|
|
63
72
|
```bash
|
|
64
73
|
make build-3.11 # Build for Python 3.11
|
|
65
74
|
make test-3.11 # Build and test
|
|
@@ -148,9 +157,10 @@ Then, you will compile the dependencies and the module itself. You need a modern
|
|
|
148
157
|
If your system Clang is not the correct version, set the CC and CXX environment variables before building:
|
|
149
158
|
|
|
150
159
|
```bash
|
|
151
|
-
# Example for macOS with Homebrew llvm@
|
|
152
|
-
export
|
|
153
|
-
export
|
|
160
|
+
# Example for macOS with Homebrew llvm@21
|
|
161
|
+
export PATH=/opt/homebrew/opt/llvm@21/bin:$PATH
|
|
162
|
+
export CC=/opt/homebrew/opt/llvm@21/bin/clang
|
|
163
|
+
export CXX=/opt/homebrew/opt/llvm@21/bin/clang++
|
|
154
164
|
```
|
|
155
165
|
|
|
156
166
|
The best option is to use the Makefile targets which will use virtualenv to install dependencies, build the packages, install them too and run tests:
|
|
@@ -171,13 +181,30 @@ You need to be able to compile ClickHouse for MacOS so we follow [their guide](h
|
|
|
171
181
|
* Install Xcode and Command Line Tools
|
|
172
182
|
* Install the necessary tools (cmake ninja libtool gettext llvm gcc ccache findutils grep). For the LLVM version, check the [ClickHouse build documentation](https://clickhouse.com/docs/en/development/build) for the minimum required Clang version.
|
|
173
183
|
* Make sure your local clang is pointing to the llvm installation and not the default from OSX / Xcode. You can check it by running `clang --version` - it should show "Homebrew clang" and the correct version.
|
|
174
|
-
*
|
|
184
|
+
* Add Homebrew LLVM and the GNU userland tools to your `PATH` before building. ClickHouse's build on macOS expects tools such as `llvm-objcopy`, `llvm-strip`, `find`, and `grep` to resolve to the Homebrew/GNU versions instead of the default BSD/macOS tools.
|
|
175
185
|
```bash
|
|
176
|
-
|
|
177
|
-
export CC=/opt/homebrew/opt/llvm@
|
|
178
|
-
export CXX=/opt/homebrew/opt/llvm@
|
|
186
|
+
export PATH="/opt/homebrew/opt/llvm@21/bin:/opt/homebrew/opt/findutils/libexec/gnubin:/opt/homebrew/opt/grep/libexec/gnubin:$PATH"
|
|
187
|
+
export CC=/opt/homebrew/opt/llvm@21/bin/clang
|
|
188
|
+
export CXX=/opt/homebrew/opt/llvm@21/bin/clang++
|
|
189
|
+
```
|
|
190
|
+
* You can persist those settings in `~/.zshrc`:
|
|
191
|
+
```bash
|
|
192
|
+
echo '' >> ~/.zshrc
|
|
193
|
+
echo '# tinybird/clickhouse-toolset build environment' >> ~/.zshrc
|
|
194
|
+
echo 'export PATH="/opt/homebrew/opt/llvm@21/bin:/opt/homebrew/opt/findutils/libexec/gnubin:/opt/homebrew/opt/grep/libexec/gnubin:$PATH"' >> ~/.zshrc
|
|
195
|
+
echo 'export CC="/opt/homebrew/opt/llvm@21/bin/clang"' >> ~/.zshrc
|
|
196
|
+
echo 'export CXX="/opt/homebrew/opt/llvm@21/bin/clang++"' >> ~/.zshrc
|
|
197
|
+
source ~/.zshrc
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
* Follow the normal build. When validating a new machine or toolchain, prefer building a single Python version first:
|
|
201
|
+
```bash
|
|
202
|
+
make build-3.11
|
|
203
|
+
```
|
|
204
|
+
Then run the full build only after that succeeds:
|
|
205
|
+
```bash
|
|
206
|
+
make build
|
|
179
207
|
```
|
|
180
|
-
* Follow the normal build (`make build`)
|
|
181
208
|
|
|
182
209
|
#### Clean environment if you already did some compilation before
|
|
183
210
|
To clean your environment you need to make sure that the repository is clean and updated with the expected values. To do that you can use:
|
|
@@ -187,6 +214,8 @@ make distclean
|
|
|
187
214
|
git clean -fdx
|
|
188
215
|
```
|
|
189
216
|
|
|
217
|
+
If you manually configured `ch_build` or `ts_build` with a different CMake generator earlier, remove them before rebuilding. The project configures the toolset with `Ninja`, and stale caches created with `Unix Makefiles` will fail with a generator mismatch.
|
|
218
|
+
|
|
190
219
|
#### Make sure you have the expeted version from the submodules
|
|
191
220
|
```bash
|
|
192
221
|
git submodule sync && git submodule update --init --recursive
|
|
@@ -19,6 +19,7 @@ from chtoolset._query import replace_tables, \
|
|
|
19
19
|
set_row_binary_encoder_buffer_config, \
|
|
20
20
|
set_row_binary_encoder_thread_buffer_size, \
|
|
21
21
|
get_columns_from_create_query, \
|
|
22
|
+
normalize_table_column, \
|
|
22
23
|
parse_create_materialized_view_target_table as _parse_create_materialized_view_target_table
|
|
23
24
|
|
|
24
25
|
from typing import Optional, TypedDict
|
|
@@ -19,6 +19,7 @@ tests/test_get_columns_from_create_query.py
|
|
|
19
19
|
tests/test_get_left_table.py
|
|
20
20
|
tests/test_internal_cache.py
|
|
21
21
|
tests/test_normalize_query_keep_names.py
|
|
22
|
+
tests/test_normalize_table_column.py
|
|
22
23
|
tests/test_parse_create_materialized_view_target_table.py
|
|
23
24
|
tests/test_replace_tables.py
|
|
24
25
|
tests/test_replace_tables_backward_compat.py
|
|
@@ -0,0 +1,431 @@
|
|
|
1
|
+
import unittest
|
|
2
|
+
|
|
3
|
+
from chtoolset import query as chquery
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class TestNormalizeTableColumn(unittest.TestCase):
|
|
7
|
+
def test_normalizes_low_cardinality_with_nullable_flag(self):
|
|
8
|
+
column = chquery.normalize_table_column(
|
|
9
|
+
column={
|
|
10
|
+
"name": "status",
|
|
11
|
+
"type": "LowCardinality(String)",
|
|
12
|
+
"nullable": True,
|
|
13
|
+
},
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
self.assertEqual(column["name"], "status")
|
|
17
|
+
self.assertEqual(column["type"], "LowCardinality(String)")
|
|
18
|
+
self.assertTrue(column["nullable"])
|
|
19
|
+
|
|
20
|
+
def test_uses_normalized_name_alias(self):
|
|
21
|
+
column = chquery.normalize_table_column(
|
|
22
|
+
column={"name": "Name", "normalized_name": "name", "type": "String"},
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
self.assertEqual(column["name"], "name")
|
|
26
|
+
self.assertEqual(column["type"], "String")
|
|
27
|
+
self.assertFalse(column["nullable"])
|
|
28
|
+
|
|
29
|
+
def test_detects_nullable_simple_aggregate_function(self):
|
|
30
|
+
column = chquery.normalize_table_column(
|
|
31
|
+
column={
|
|
32
|
+
"name": "metric",
|
|
33
|
+
"type": "SimpleAggregateFunction(sum, Nullable(Int64))",
|
|
34
|
+
},
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
self.assertEqual(
|
|
38
|
+
column["type"], "SimpleAggregateFunction(sum, Nullable(Int64))"
|
|
39
|
+
)
|
|
40
|
+
self.assertTrue(column["nullable"])
|
|
41
|
+
|
|
42
|
+
def test_plain_nullable_wrapper_does_not_force_nullable_flag(self):
|
|
43
|
+
column = chquery.normalize_table_column(
|
|
44
|
+
column={"name": "discount", "type": "Nullable(Decimal64(4))"},
|
|
45
|
+
)
|
|
46
|
+
|
|
47
|
+
self.assertEqual(column["type"], "Nullable(Decimal64(4))")
|
|
48
|
+
self.assertTrue(column["nullable"])
|
|
49
|
+
|
|
50
|
+
def test_normalizes_decimal_spacing(self):
|
|
51
|
+
column = chquery.normalize_table_column(
|
|
52
|
+
column={
|
|
53
|
+
"name": "amount",
|
|
54
|
+
"normalized_name": "amount",
|
|
55
|
+
"type": "Decimal(18,4)",
|
|
56
|
+
"nullable": False,
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
self.assertEqual(column["type"], "Decimal(18, 4)")
|
|
61
|
+
self.assertFalse(column["nullable"])
|
|
62
|
+
|
|
63
|
+
def test_normalizes_decimal_spacing_with_nullable_flag(self):
|
|
64
|
+
column = chquery.normalize_table_column(
|
|
65
|
+
column={
|
|
66
|
+
"name": "tax",
|
|
67
|
+
"normalized_name": "tax",
|
|
68
|
+
"type": "Decimal(10,2)",
|
|
69
|
+
"nullable": True,
|
|
70
|
+
},
|
|
71
|
+
)
|
|
72
|
+
|
|
73
|
+
self.assertEqual(column["type"], "Decimal(10, 2)")
|
|
74
|
+
self.assertTrue(column["nullable"])
|
|
75
|
+
|
|
76
|
+
def test_preserves_decimal64_alias(self):
|
|
77
|
+
column = chquery.normalize_table_column(
|
|
78
|
+
column={
|
|
79
|
+
"name": "discount",
|
|
80
|
+
"normalized_name": "discount",
|
|
81
|
+
"type": "Nullable(Decimal64(4))",
|
|
82
|
+
"nullable": False,
|
|
83
|
+
},
|
|
84
|
+
)
|
|
85
|
+
|
|
86
|
+
self.assertEqual(column["type"], "Nullable(Decimal64(4))")
|
|
87
|
+
self.assertTrue(column["nullable"])
|
|
88
|
+
|
|
89
|
+
def test_preserves_datetime64_precision(self):
|
|
90
|
+
column = chquery.normalize_table_column(
|
|
91
|
+
column={
|
|
92
|
+
"name": "created_at",
|
|
93
|
+
"normalized_name": "created_at",
|
|
94
|
+
"type": "DateTime64(3)",
|
|
95
|
+
"nullable": False,
|
|
96
|
+
},
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
self.assertEqual(column["type"], "DateTime64(3)")
|
|
100
|
+
self.assertFalse(column["nullable"])
|
|
101
|
+
|
|
102
|
+
def test_normalizes_json_dynamic_param_spacing(self):
|
|
103
|
+
column = chquery.normalize_table_column(
|
|
104
|
+
column={
|
|
105
|
+
"name": "json",
|
|
106
|
+
"normalized_name": "json",
|
|
107
|
+
"type": "JSON(max_dynamic_types=2, max_dynamic_paths=16)",
|
|
108
|
+
"nullable": False,
|
|
109
|
+
},
|
|
110
|
+
)
|
|
111
|
+
|
|
112
|
+
self.assertEqual(
|
|
113
|
+
column["type"], "JSON(max_dynamic_types = 2, max_dynamic_paths = 16)"
|
|
114
|
+
)
|
|
115
|
+
self.assertFalse(column["nullable"])
|
|
116
|
+
|
|
117
|
+
def test_normalizes_nested_decimal_spacing(self):
|
|
118
|
+
column = chquery.normalize_table_column(
|
|
119
|
+
column={
|
|
120
|
+
"name": "agg",
|
|
121
|
+
"normalized_name": "agg",
|
|
122
|
+
"type": "AggregateFunction(sum, Decimal(10,2))",
|
|
123
|
+
"nullable": False,
|
|
124
|
+
},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
self.assertEqual(column["type"], "AggregateFunction(sum, Decimal(10, 2))")
|
|
128
|
+
self.assertFalse(column["nullable"])
|
|
129
|
+
|
|
130
|
+
def test_normalizes_multiline_complex_type_spacing(self):
|
|
131
|
+
column = chquery.normalize_table_column(
|
|
132
|
+
column={
|
|
133
|
+
"name": "agg",
|
|
134
|
+
"normalized_name": "agg",
|
|
135
|
+
"type": """AggregateFunction(
|
|
136
|
+
argMax, Nullable(UUID), Tuple(UInt8, DateTime64(3), DateTime64(3), DateTime64(3))
|
|
137
|
+
)""",
|
|
138
|
+
"nullable": False,
|
|
139
|
+
},
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
self.assertEqual(
|
|
143
|
+
column["type"],
|
|
144
|
+
"AggregateFunction(argMax, Nullable(UUID), Tuple(UInt8, DateTime64(3), DateTime64(3), DateTime64(3)))",
|
|
145
|
+
)
|
|
146
|
+
self.assertFalse(column["nullable"])
|
|
147
|
+
|
|
148
|
+
def test_preserves_space_before_timezone_string(self):
|
|
149
|
+
column = chquery.normalize_table_column(
|
|
150
|
+
column={
|
|
151
|
+
"name": "ts",
|
|
152
|
+
"normalized_name": "ts",
|
|
153
|
+
"type": "DateTime64(3, 'Europe/Vienna')",
|
|
154
|
+
"nullable": False,
|
|
155
|
+
},
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
self.assertEqual(column["type"], "DateTime64(3, 'Europe/Vienna')")
|
|
159
|
+
self.assertFalse(column["nullable"])
|
|
160
|
+
|
|
161
|
+
def test_nullable_flag_preserves_raw_type(self):
|
|
162
|
+
column = chquery.normalize_table_column(
|
|
163
|
+
column={
|
|
164
|
+
"name": "reason",
|
|
165
|
+
"normalized_name": "reason",
|
|
166
|
+
"type": "String",
|
|
167
|
+
"nullable": True,
|
|
168
|
+
},
|
|
169
|
+
)
|
|
170
|
+
|
|
171
|
+
self.assertEqual(column["type"], "String")
|
|
172
|
+
self.assertTrue(column["nullable"])
|
|
173
|
+
|
|
174
|
+
def test_nullable_flag_preserves_low_cardinality_type(self):
|
|
175
|
+
column = chquery.normalize_table_column(
|
|
176
|
+
column={
|
|
177
|
+
"name": "status",
|
|
178
|
+
"normalized_name": "status",
|
|
179
|
+
"type": "LowCardinality(String)",
|
|
180
|
+
"nullable": True,
|
|
181
|
+
},
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
self.assertEqual(column["type"], "LowCardinality(String)")
|
|
185
|
+
self.assertTrue(column["nullable"])
|
|
186
|
+
|
|
187
|
+
def test_preserves_existing_nullable_low_cardinality_wrapper(self):
|
|
188
|
+
column = chquery.normalize_table_column(
|
|
189
|
+
column={
|
|
190
|
+
"name": "status",
|
|
191
|
+
"normalized_name": "status",
|
|
192
|
+
"type": "LowCardinality(Nullable(String))",
|
|
193
|
+
"nullable": False,
|
|
194
|
+
},
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
self.assertEqual(column["type"], "LowCardinality(Nullable(String))")
|
|
198
|
+
self.assertTrue(column["nullable"])
|
|
199
|
+
|
|
200
|
+
def test_normalizes_default_value_into_specifier_and_expression(self):
|
|
201
|
+
column = chquery.normalize_table_column(
|
|
202
|
+
column={
|
|
203
|
+
"name": "created_at",
|
|
204
|
+
"normalized_name": "created_at",
|
|
205
|
+
"type": "DateTime64(3)",
|
|
206
|
+
"nullable": False,
|
|
207
|
+
"default_value": "DEFAULT now()",
|
|
208
|
+
},
|
|
209
|
+
)
|
|
210
|
+
|
|
211
|
+
self.assertEqual(column["type"], "DateTime64(3)")
|
|
212
|
+
self.assertEqual(column["default_specifier"], "DEFAULT")
|
|
213
|
+
self.assertEqual(column["default_expression"], "now()")
|
|
214
|
+
|
|
215
|
+
def test_preserves_split_default_specifier_and_expression(self):
|
|
216
|
+
column = chquery.normalize_table_column(
|
|
217
|
+
column={
|
|
218
|
+
"name": "country",
|
|
219
|
+
"normalized_name": "country",
|
|
220
|
+
"type": "String",
|
|
221
|
+
"nullable": False,
|
|
222
|
+
"default_value": "DEFAULT 'Unknown'",
|
|
223
|
+
},
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
self.assertEqual(column["default_specifier"], "DEFAULT")
|
|
227
|
+
self.assertEqual(column["default_expression"], "'Unknown'")
|
|
228
|
+
|
|
229
|
+
def test_normalizes_comment_codec_ttl_and_primary_key(self):
|
|
230
|
+
column = chquery.normalize_table_column(
|
|
231
|
+
column={
|
|
232
|
+
"name": "ts",
|
|
233
|
+
"normalized_name": "ts",
|
|
234
|
+
"type": "DateTime64(3)",
|
|
235
|
+
"nullable": False,
|
|
236
|
+
"comment": "'event time'",
|
|
237
|
+
"codec": "CODEC(ZSTD(1))",
|
|
238
|
+
"ttl": "ts + toIntervalDay(30)",
|
|
239
|
+
"is_primary_key": True,
|
|
240
|
+
},
|
|
241
|
+
)
|
|
242
|
+
|
|
243
|
+
self.assertEqual(column["type"], "DateTime64(3)")
|
|
244
|
+
self.assertEqual(column["comment"], "'event time'")
|
|
245
|
+
self.assertEqual(column["codec"], "CODEC(ZSTD(1))")
|
|
246
|
+
self.assertEqual(column["ttl"], "ts + toIntervalDay(30)")
|
|
247
|
+
self.assertTrue(column["is_primary_key"])
|
|
248
|
+
|
|
249
|
+
def test_preserves_normalized_name_for_output_name(self):
|
|
250
|
+
column = chquery.normalize_table_column(
|
|
251
|
+
column={
|
|
252
|
+
"name": "CamelCase",
|
|
253
|
+
"normalized_name": "camel_case",
|
|
254
|
+
"type": "String",
|
|
255
|
+
},
|
|
256
|
+
)
|
|
257
|
+
|
|
258
|
+
self.assertEqual(column["name"], "camel_case")
|
|
259
|
+
self.assertEqual(column["type"], "String")
|
|
260
|
+
|
|
261
|
+
def test_normalizes_string_scalar(self):
|
|
262
|
+
column = chquery.normalize_table_column(
|
|
263
|
+
column={
|
|
264
|
+
"name": "id",
|
|
265
|
+
"normalized_name": "id",
|
|
266
|
+
"type": "String",
|
|
267
|
+
"nullable": False,
|
|
268
|
+
},
|
|
269
|
+
)
|
|
270
|
+
|
|
271
|
+
self.assertEqual(column["type"], "String")
|
|
272
|
+
|
|
273
|
+
def test_normalizes_bool_scalar(self):
|
|
274
|
+
column = chquery.normalize_table_column(
|
|
275
|
+
column={
|
|
276
|
+
"name": "is_active",
|
|
277
|
+
"normalized_name": "is_active",
|
|
278
|
+
"type": "Bool",
|
|
279
|
+
"nullable": False,
|
|
280
|
+
},
|
|
281
|
+
)
|
|
282
|
+
|
|
283
|
+
self.assertEqual(column["type"], "Bool")
|
|
284
|
+
|
|
285
|
+
def test_normalizes_uint64_scalar(self):
|
|
286
|
+
column = chquery.normalize_table_column(
|
|
287
|
+
column={
|
|
288
|
+
"name": "count",
|
|
289
|
+
"normalized_name": "count",
|
|
290
|
+
"type": "UInt64",
|
|
291
|
+
"nullable": False,
|
|
292
|
+
},
|
|
293
|
+
)
|
|
294
|
+
|
|
295
|
+
self.assertEqual(column["type"], "UInt64")
|
|
296
|
+
|
|
297
|
+
def test_normalizes_array_payload(self):
|
|
298
|
+
column = chquery.normalize_table_column(
|
|
299
|
+
column={
|
|
300
|
+
"name": "tags",
|
|
301
|
+
"normalized_name": "tags",
|
|
302
|
+
"type": "Array(String)",
|
|
303
|
+
"nullable": False,
|
|
304
|
+
},
|
|
305
|
+
)
|
|
306
|
+
|
|
307
|
+
self.assertEqual(column["type"], "Array(String)")
|
|
308
|
+
|
|
309
|
+
def test_normalizes_map_payload(self):
|
|
310
|
+
column = chquery.normalize_table_column(
|
|
311
|
+
column={
|
|
312
|
+
"name": "metadata",
|
|
313
|
+
"normalized_name": "metadata",
|
|
314
|
+
"type": "Map(String, String)",
|
|
315
|
+
"nullable": False,
|
|
316
|
+
},
|
|
317
|
+
)
|
|
318
|
+
|
|
319
|
+
self.assertEqual(column["type"], "Map(String, String)")
|
|
320
|
+
|
|
321
|
+
def test_normalizes_low_cardinality_payload(self):
|
|
322
|
+
column = chquery.normalize_table_column(
|
|
323
|
+
column={
|
|
324
|
+
"name": "category",
|
|
325
|
+
"normalized_name": "category",
|
|
326
|
+
"type": "LowCardinality(String)",
|
|
327
|
+
"nullable": False,
|
|
328
|
+
},
|
|
329
|
+
)
|
|
330
|
+
|
|
331
|
+
self.assertEqual(column["type"], "LowCardinality(String)")
|
|
332
|
+
|
|
333
|
+
def test_normalizes_nullable_string_field(self):
|
|
334
|
+
column = chquery.normalize_table_column(
|
|
335
|
+
column={
|
|
336
|
+
"name": "phone",
|
|
337
|
+
"normalized_name": "phone",
|
|
338
|
+
"type": "Nullable(String)",
|
|
339
|
+
"nullable": False,
|
|
340
|
+
},
|
|
341
|
+
)
|
|
342
|
+
|
|
343
|
+
self.assertEqual(column["type"], "Nullable(String)")
|
|
344
|
+
|
|
345
|
+
def test_normalizes_nullable_datetime64_field(self):
|
|
346
|
+
column = chquery.normalize_table_column(
|
|
347
|
+
column={
|
|
348
|
+
"name": "deleted_at",
|
|
349
|
+
"normalized_name": "deleted_at",
|
|
350
|
+
"type": "Nullable(DateTime64(3))",
|
|
351
|
+
"nullable": False,
|
|
352
|
+
},
|
|
353
|
+
)
|
|
354
|
+
|
|
355
|
+
self.assertEqual(column["type"], "Nullable(DateTime64(3))")
|
|
356
|
+
|
|
357
|
+
def test_normalizes_decimal_business_field(self):
|
|
358
|
+
column = chquery.normalize_table_column(
|
|
359
|
+
column={
|
|
360
|
+
"name": "price",
|
|
361
|
+
"normalized_name": "price",
|
|
362
|
+
"type": "Decimal(12,2)",
|
|
363
|
+
"nullable": False,
|
|
364
|
+
},
|
|
365
|
+
)
|
|
366
|
+
|
|
367
|
+
self.assertEqual(column["type"], "Decimal(12, 2)")
|
|
368
|
+
|
|
369
|
+
def test_preserves_decimal64_business_field(self):
|
|
370
|
+
column = chquery.normalize_table_column(
|
|
371
|
+
column={
|
|
372
|
+
"name": "amount",
|
|
373
|
+
"normalized_name": "amount",
|
|
374
|
+
"type": "Decimal64(4)",
|
|
375
|
+
"nullable": False,
|
|
376
|
+
},
|
|
377
|
+
)
|
|
378
|
+
|
|
379
|
+
self.assertEqual(column["type"], "Decimal64(4)")
|
|
380
|
+
|
|
381
|
+
def test_normalizes_float64_business_field(self):
|
|
382
|
+
column = chquery.normalize_table_column(
|
|
383
|
+
column={
|
|
384
|
+
"name": "score",
|
|
385
|
+
"normalized_name": "score",
|
|
386
|
+
"type": "Float64",
|
|
387
|
+
"nullable": False,
|
|
388
|
+
},
|
|
389
|
+
)
|
|
390
|
+
|
|
391
|
+
self.assertEqual(column["type"], "Float64")
|
|
392
|
+
|
|
393
|
+
def test_default_now_for_created_at(self):
|
|
394
|
+
column = chquery.normalize_table_column(
|
|
395
|
+
column={
|
|
396
|
+
"name": "created_at",
|
|
397
|
+
"normalized_name": "created_at",
|
|
398
|
+
"type": "DateTime64(3)",
|
|
399
|
+
"nullable": False,
|
|
400
|
+
"default_value": "DEFAULT now()",
|
|
401
|
+
},
|
|
402
|
+
)
|
|
403
|
+
|
|
404
|
+
self.assertEqual(column["default_specifier"], "DEFAULT")
|
|
405
|
+
self.assertEqual(column["default_expression"], "now()")
|
|
406
|
+
|
|
407
|
+
def test_default_true_for_bool(self):
|
|
408
|
+
column = chquery.normalize_table_column(
|
|
409
|
+
column={
|
|
410
|
+
"name": "is_active",
|
|
411
|
+
"normalized_name": "is_active",
|
|
412
|
+
"type": "Bool",
|
|
413
|
+
"nullable": False,
|
|
414
|
+
"default_value": "DEFAULT true",
|
|
415
|
+
},
|
|
416
|
+
)
|
|
417
|
+
|
|
418
|
+
self.assertEqual(column["default_expression"], "true")
|
|
419
|
+
|
|
420
|
+
def test_default_zero_for_retry_count(self):
|
|
421
|
+
column = chquery.normalize_table_column(
|
|
422
|
+
column={
|
|
423
|
+
"name": "retry_count",
|
|
424
|
+
"normalized_name": "retry_count",
|
|
425
|
+
"type": "UInt32",
|
|
426
|
+
"nullable": False,
|
|
427
|
+
"default_value": "DEFAULT 0",
|
|
428
|
+
},
|
|
429
|
+
)
|
|
430
|
+
|
|
431
|
+
self.assertEqual(column["default_expression"], "0")
|
|
@@ -1144,6 +1144,25 @@ class TestDisabledFunctions(unittest.TestCase):
|
|
|
1144
1144
|
expected = "SELECT * FROM (SELECT count() from system.numbers LIMIT 2) AS a, (SELECT count() from system.numbers LIMIT 2) AS a SETTINGS join_use_nulls = 1"
|
|
1145
1145
|
self.assertEqual(chquery.format(expected), chquery.format(replaced))
|
|
1146
1146
|
|
|
1147
|
+
def test_allow_analytics_query_settings(self):
|
|
1148
|
+
sql = """SELECT * FROM a SETTINGS
|
|
1149
|
+
max_threads = 1,
|
|
1150
|
+
max_memory_usage = 1000000,
|
|
1151
|
+
max_execution_time = 60,
|
|
1152
|
+
use_query_condition_cache = 0
|
|
1153
|
+
"""
|
|
1154
|
+
replacement = {("default", "a"): ("", "(SELECT count() FROM system.numbers LIMIT 1)")}
|
|
1155
|
+
replaced = chquery.replace_tables(sql, replacement, default_database="default")
|
|
1156
|
+
out = chquery.format(replaced)
|
|
1157
|
+
self.assertIn("system.numbers", out)
|
|
1158
|
+
for key in (
|
|
1159
|
+
"max_threads",
|
|
1160
|
+
"max_memory_usage",
|
|
1161
|
+
"max_execution_time",
|
|
1162
|
+
"use_query_condition_cache",
|
|
1163
|
+
):
|
|
1164
|
+
self.assertIn(key, out)
|
|
1165
|
+
|
|
1147
1166
|
def test_disallow_enabled_settings(self):
|
|
1148
1167
|
sql = "SELECT * FROM a, a SETTINGS join_use_nulls=1"
|
|
1149
1168
|
replacement = {
|
|
@@ -2025,6 +2025,23 @@ SELECT *
|
|
|
2025
2025
|
self.assertEqual(chquery.tables("SELECT count() from system.numbers SETTINGS aggregate_functions_null_for_empty=1"),
|
|
2026
2026
|
[('system', 'numbers', '')])
|
|
2027
2027
|
|
|
2028
|
+
def test_allow_analytics_query_settings(self):
|
|
2029
|
+
sql = """SELECT count() FROM system.numbers LIMIT 1 SETTINGS
|
|
2030
|
+
max_threads = 1,
|
|
2031
|
+
max_memory_usage = 1000000,
|
|
2032
|
+
max_execution_time = 60,
|
|
2033
|
+
optimize_aggregation_in_order = 1,
|
|
2034
|
+
use_skip_indexes = 1,
|
|
2035
|
+
use_skip_indexes_if_final = 0,
|
|
2036
|
+
optimize_move_to_prewhere = 1,
|
|
2037
|
+
query_plan_optimize_lazy_materialization = 0,
|
|
2038
|
+
min_bytes_to_use_direct_io = 0,
|
|
2039
|
+
enable_filesystem_cache = 1,
|
|
2040
|
+
use_query_cache = 0,
|
|
2041
|
+
use_query_condition_cache = 0
|
|
2042
|
+
"""
|
|
2043
|
+
self.assertEqual(chquery.tables(sql), [('system', 'numbers', '')])
|
|
2044
|
+
|
|
2028
2045
|
def test_disallow_blocked_settings(self):
|
|
2029
2046
|
with self.assertRaisesRegex(ValueError, """DB::Exception: Usage of setting 'aggregate_functions_null_for_empty' is restricted. Contact support@tinybird.co if you require access to this feature"""):
|
|
2030
2047
|
chquery.tables("SELECT count() from system.numbers SETTINGS aggregate_functions_null_for_empty=1",
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
{tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/requires.txt
RENAMED
|
File without changes
|
{tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/src/tinybird_toolset.egg-info/top_level.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_get_columns_from_create_query.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{tinybird_toolset-2.2.2 → tinybird_toolset-2.4.0}/tests/test_replace_tables_backward_compat.py
RENAMED
|
File without changes
|
|
File without changes
|