From 373361e7d6a8ad2b775418c432027e83f43d3c47 Mon Sep 17 00:00:00 2001 From: Brian Schott Date: Wed, 10 Jun 2026 18:04:15 +0000 Subject: [PATCH] Add functions to emulate Snowflake's bitmap_bit_position and bitmap_bucket_number --- udfs/migration/snowflake/README.md | 31 ++++++++++ .../snowflake/bitmap_bit_position.sqlx | 24 ++++++++ .../snowflake/bitmap_bucket_number.sqlx | 24 ++++++++ udfs/migration/snowflake/test_cases.js | 61 ++++++++++++++++++- 4 files changed, 139 insertions(+), 1 deletion(-) create mode 100644 udfs/migration/snowflake/bitmap_bit_position.sqlx create mode 100644 udfs/migration/snowflake/bitmap_bucket_number.sqlx diff --git a/udfs/migration/snowflake/README.md b/udfs/migration/snowflake/README.md index d780d5c3b..1ac104acc 100644 --- a/udfs/migration/snowflake/README.md +++ b/udfs/migration/snowflake/README.md @@ -14,8 +14,12 @@ SELECT bqutil.sf.factorial(0) ## UDFs * [array_equal](#array_equal) +* [bitmap_bit_position](#bitmap_bit_position) +* [bitmap_bucket_number](#bitmap_bucket_number) * [factorial](#factorial) * [flatten](#flatten) +* [json_ilike](#json_ilike) +* [object_agg](#object_agg) ## Documentation @@ -38,6 +42,33 @@ SELECT bqutil.sf.array_equal([JSON '1', JSON '2'], [JSON '1', JSON '2']) as eq1, eq1|eq2|eq3 ---|---|--- true|true|false + +### [bitmap_bit_position(value INT64)](bitmap_bit_position.sqlx) +Emulates the `bitmap_bit_position` function present in Snowflake. [Snowflake docs](https://docs.snowflake.com/en/sql-reference/functions/bitmap_bit_position) + +```sql +SELECT bqutil.sf.bitmap_bit_position(1) as pos1, + bqutil.sf.bitmap_bit_position(32768) as pos2, + bqutil.sf.bitmap_bit_position(32769) as pos3; +``` + +pos1|pos2|pos3 +---|---|--- +0|32767|0 + +### [bitmap_bucket_number(value INT64)](bitmap_bucket_number.sqlx) +Emulates the `bitmap_bucket_number` function present in Snowflake. [Snowflake docs](https://docs.snowflake.com/en/sql-reference/functions/bitmap_bucket_number) + +```sql +SELECT bqutil.sf.bitmap_bucket_number(1) as bucket1, + bqutil.sf.bitmap_bucket_number(32768) as bucket2, + bqutil.sf.bitmap_bucket_number(32769) as bucket3; +``` + +bucket1|bucket2|bucket3 +---|---|--- +1|1|2 + ### [factorial(integer_expr INT64)](factorial.sqlx) Computes the factorial of its input. The input argument must be an integer expression in the range of `0` to `27`. Due to data type differences, the maximum input value in BigQuery is smaller than in Snowflake. [Snowflake docs](https://docs.snowflake.com/en/sql-reference/functions/factorial.html) ```sql diff --git a/udfs/migration/snowflake/bitmap_bit_position.sqlx b/udfs/migration/snowflake/bitmap_bit_position.sqlx new file mode 100644 index 000000000..b52b6e418 --- /dev/null +++ b/udfs/migration/snowflake/bitmap_bit_position.sqlx @@ -0,0 +1,24 @@ +config { hasOutput: true } +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CREATE OR REPLACE FUNCTION ${self()}(value INT64) +RETURNS INT64 +OPTIONS ( + description = "Emulates the 'bitmap_bit_position' function present in Snowflake." +) AS ( + if(value > 0, (value - 1) & 32767, 0) +); diff --git a/udfs/migration/snowflake/bitmap_bucket_number.sqlx b/udfs/migration/snowflake/bitmap_bucket_number.sqlx new file mode 100644 index 000000000..853339d80 --- /dev/null +++ b/udfs/migration/snowflake/bitmap_bucket_number.sqlx @@ -0,0 +1,24 @@ +config { hasOutput: true } +/* + * Copyright 2026 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +CREATE OR REPLACE FUNCTION ${self()}(value INT64) +RETURNS INT64 +OPTIONS ( + description = "Emulates the 'bitmap_bucket_number' function present in Snowflake." +) AS ( + if(value > 0, ((value - 1) >> 15) + 1, 0) +); diff --git a/udfs/migration/snowflake/test_cases.js b/udfs/migration/snowflake/test_cases.js index 008fbcad5..aac683d22 100644 --- a/udfs/migration/snowflake/test_cases.js +++ b/udfs/migration/snowflake/test_cases.js @@ -207,5 +207,64 @@ generate_udf_test("array_equal", [ } ]); +generate_udf_test("bitmap_bucket_number", [ + { + inputs: [`CAST(1 AS INT64)`], + expected_output: `CAST(1 AS INT64)` + }, + { + inputs: [`CAST(1000000 AS INT64)`], + expected_output: `CAST(31 AS INT64)` + }, + { + inputs: [`CAST(32767 AS INT64)`], + expected_output: `CAST(1 AS INT64)` + }, + { + inputs: [`CAST(32768 AS INT64)`], + expected_output: `CAST(1 AS INT64)` + }, + { + inputs: [`CAST(32769 AS INT64)`], + expected_output: `CAST(2 AS INT64)` + }, + { + inputs: [`CAST(400000 AS INT64)`], + expected_output: `CAST(13 AS INT64)` + }, + { + inputs: [`CAST(0 AS INT64)`], + expected_output: `CAST(0 AS INT64)` + } +]); - +generate_udf_test("bitmap_bit_position", [ + { + inputs: [`CAST(1 AS INT64)`], + expected_output: `CAST(0 AS INT64)` + }, + { + inputs: [`CAST(1000000 AS INT64)`], + expected_output: `CAST(16959 AS INT64)` + }, + { + inputs: [`CAST(32767 AS INT64)`], + expected_output: `CAST(32766 AS INT64)` + }, + { + inputs: [`CAST(32768 AS INT64)`], + expected_output: `CAST(32767 AS INT64)` + }, + { + inputs: [`CAST(32769 AS INT64)`], + expected_output: `CAST(0 AS INT64)` + }, + { + inputs: [`CAST(400000 AS INT64)`], + expected_output: `CAST(6783 AS INT64)` + }, + { + inputs: [`CAST(0 AS INT64)`], + expected_output: `CAST(0 AS INT64)` + } +]);