A Quick Look at DuckDB’s BIT_OR() Function

In DuckDB, the bit_or() function returns the bitwise OR of all bits in a given expression.

Let’s see how the bit_or() function works in DuckDB with some basic examples.

Syntax

The syntax goes like this:

bit_or(arg)

Where arg is the target column or expression that the function operates on.

It’s also possible to provide an over clause to make the function execute as a window function.

Example

Suppose we create the following table and insert data:

CREATE TABLE flag_data (
    ref INTEGER,
    flag INTEGER
);

INSERT INTO flag_data VALUES
    (1, 15),  -- 1111 in binary
    (1, 13),  -- 1101 in binary
    (1, 14),  -- 1110 in binary
    (2, 7),   -- 0111 in binary
    (2, 6);   -- 0110 in binary

Here, the flag column contains integer values. The comments show the 4-bit binary equivalent for the respective value.

Now let’s use bit_or() to return the bitwise OR of all bits in that column:

SELECT 
    bit_or(flag)
FROM flag_data;

Result:

+--------------+
| bit_or(flag) |
+--------------+
| 15 |
+--------------+

We can pass bit_or() to the bin() function in order to get the binary representation:

SELECT 
    bit_or(flag),
    bin(bit_or(flag)) as binary_representation
FROM flag_data;

Result:

+--------------+-----------------------+
| bit_or(flag) | binary_representation |
+--------------+-----------------------+
| 15 | 1111 |
+--------------+-----------------------+

Grouped Data

Here it is grouped by the ref column:

SELECT ref, 
       bit_or(flag) as bitwise_or,
       bin(bit_or(flag)) as binary_representation
FROM flag_data
GROUP BY ref;

Result:

+-----+------------+-----------------------+
| ref | bitwise_or | binary_representation |
+-----+------------+-----------------------+
| 1 | 15 | 1111 |
| 2 | 7 | 111 |
+-----+------------+-----------------------+

Window Function

We can provide an OVER clause in order to make bit_or() operate as a window function.

For this example, let’s create another table:

-- Create a table to store user permissions by department
CREATE TABLE employee_permissions (
    employee_id INTEGER,
    department_id INTEGER,
    hire_date DATE,
    -- Using integer to store permission flags where:
    -- 1 (001): Read
    -- 2 (010): Write
    -- 4 (100): Admin
    permission_flags INTEGER
);

-- Populate with sample data
INSERT INTO employee_permissions VALUES
    (1, 1, '2023-01-01', 1),  -- Read only
    (2, 1, '2023-02-15', 3),  -- Read + Write
    (3, 1, '2023-03-10', 7),  -- Read + Write + Admin
    (4, 2, '2023-01-05', 1),  -- Read only
    (5, 2, '2023-04-20', 2),  -- Write only
    (6, 2, '2023-06-01', 4),  -- Admin only
    (7, 3, '2023-07-01', 3),  -- Read + Write
    (8, 3, '2023-08-15', 5);  -- Read + Admin

We can use the following query to get the cumulative permissions within each department over time:

SELECT 
    employee_id,
    department_id,
    hire_date,
    permission_flags,
    bit_or(permission_flags) OVER (
        PARTITION BY department_id 
        ORDER BY hire_date
        ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW
    ) as cumulative_dept_permissions
FROM employee_permissions
ORDER BY department_id, hire_date;

Result:

+-------------+---------------+------------+------------------+-----------------------------+
| employee_id | department_id | hire_date | permission_flags | cumulative_dept_permissions |
+-------------+---------------+------------+------------------+-----------------------------+
| 1 | 1 | 2023-01-01 | 1 | 1 |
| 2 | 1 | 2023-02-15 | 3 | 3 |
| 3 | 1 | 2023-03-10 | 7 | 7 |
| 4 | 2 | 2023-01-05 | 1 | 1 |
| 5 | 2 | 2023-04-20 | 2 | 3 |
| 6 | 2 | 2023-06-01 | 4 | 7 |
| 7 | 3 | 2023-07-01 | 3 | 3 |
| 8 | 3 | 2023-08-15 | 5 | 7 |
+-------------+---------------+------------+------------------+-----------------------------+