Applies to VAST Cluster 5.5.
Functions › List, struct and map functions › Aggregate a list. Tells whether any element of a BOOLEAN list is true: it returns true if at least one of its non-NULL elements is true, false if all are false, and NULL if it has none. It's the list form of the bool_or aggregate.
Syntax
list_bool_or(l)
| Argument | Type | Description |
|---|---|---|
l |
BOOLEAN[] |
The list to test. |
Returns: BOOLEAN.
Set up the examples
The examples connect with the ADBC driver, as in VAST Query Engine (ADBC), and work in a schema called docs_sql_fn_lists_list_bool_or. Set QE_ENDPOINT, AWS_ACCESS_KEY_ID, AWS_SECRET_ACCESS_KEY and VASTDB_BUCKET (SQL reference), and run the blocks in order in one Python session. Below each block is the output you should see.
This block connects, defines the run() and plan() helpers, and creates a table of five rows: a list of alert flags and a list of measurements. Row 2 has NULL elements, row 3 has empty lists and row 4 has no values:
import os
import re
import adbc_driver_manager
import adbc_driver_manager.dbapi as dbapi
import adbc_driver_vastdb
BUCKET = os.environ["VASTDB_BUCKET"]
SCHEMA = f'"{BUCKET}/docs_sql_fn_lists_list_bool_or"'
conn = dbapi.connect(
driver=adbc_driver_vastdb.get_driver_path(),
db_kwargs={
"vast.db.endpoint": os.environ["QE_ENDPOINT"],
"vast.db.access_key": os.environ["AWS_ACCESS_KEY_ID"],
"vast.db.secret_key": os.environ["AWS_SECRET_ACCESS_KEY"],
},
autocommit=True,
)
def server_message(e):
"""The server's message in a driver error, without the request ID or the echoed statement."""
text = re.sub(r"\[req=0x[0-9a-f]+\]: ", "", str(e))
found = (re.search(r'message: "(.*?)", request_id', text, re.S)
or re.search(r"description: (.*)\)", text, re.S))
message = found.group(1) if found else text.strip().splitlines()[-1]
message = re.sub(r"^Plan query: '.*?' failed with: ", "", message, flags=re.S)
message = re.split(r"\\n|\n", message)[0].replace('\\"', '"')
return message.replace(BUCKET, "<bucket>")
def run(sql):
"""Run one statement. Print the rows it returns, or the error."""
try:
with conn.cursor() as cur:
cur.execute(sql)
rows = cur.fetchall() if cur.description else []
except (adbc_driver_manager.Error, OSError) as e:
print(f"{type(e).__name__}: {server_message(e)}")
else:
print(rows)
def plan(sql):
"""Print the plan without its ~N Rows lines: the table's row count, which can still be 0 just after an insert."""
with conn.cursor() as cur:
cur.execute("EXPLAIN " + sql)
steps = cur.fetchall()[0][1]
print("\n".join(line for line in steps.splitlines() if "Rows" not in line))
TABLE = f"{SCHEMA}.samples"
run(f"CREATE SCHEMA IF NOT EXISTS {SCHEMA}")
run(f"CREATE TABLE IF NOT EXISTS {TABLE} (id BIGINT, alerts BOOLEAN[], vals DOUBLE[])")
run(f"INSERT INTO {TABLE} VALUES "
"(1, [true, false, true], [0.5, 2.5, -1.0]), "
"(2, [NULL, true], [2.0, NULL]), "
"(3, [], []), "
"(4, NULL, NULL), "
"(5, [false, false], [1e308, 1e308, -1e-308])")
[]
[]
[(5,)]
What it does
list_bool_or(l) combines the elements of l with OR, skipping NULL elements:
list_bool_or([true, false])istrue, andlist_bool_or([false, false])isfalse.- NULL elements are skipped:
list_bool_or([false, NULL])isfalse, not NULL. - A list with no non-NULL elements, empty or all NULL, returns NULL.
The elements must be BOOLEAN. A list of numbers isn't converted: list_bool_or([1, 0]) fails. To test whether any element of another list meets a condition, turn it into a BOOLEAN list with list_transform first: list_bool_or(list_transform(vals, x -> x > 1)) tells whether any measurement is above 1.
NULLs and edge cases
| Input | Result |
|---|---|
list_bool_or(NULL::BOOLEAN[]) |
NULL |
list_bool_or([]::BOOLEAN[]) |
NULL |
list_bool_or([NULL]::BOOLEAN[]) |
NULL |
list_bool_or([false, NULL]) |
false: NULL elements are skipped |
list_bool_or([false, false]) |
false |
list_bool_or([true, false]) |
true |
list_bool_or([1, 0]) |
Error: Binder Error: No matching aggregate function; the next line of the message names the element type, bool_or(INTEGER) |
| A number, string, date or other non-list | Error: Binder Error: No function matches the given name and argument types 'list_aggr(INTEGER_LITERAL, STRING_LITERAL)'. You might need to add explicit type casts. (the types named are those of the argument) |
The same cases, as the cluster returns them. They use the helpers from Set up the examples:
run("SELECT list_bool_or(NULL::BOOLEAN[]), list_bool_or([]::BOOLEAN[]), list_bool_or([NULL]::BOOLEAN[]), "
"list_bool_or([false, NULL]), list_bool_or([false, false]), list_bool_or([true, false])")
run("SELECT list_bool_or([1, 0])")
run(f"SELECT list_bool_or(123) FROM {TABLE} LIMIT 1")
[(None, None, None, False, False, True)]
InternalError: Binder Error: No matching aggregate function
InternalError: Binder Error: No function matches the given name and argument types 'list_aggr(INTEGER_LITERAL, STRING_LITERAL)'. You might need to add explicit type casts.
Examples
Tell whether any alert in each row is raised. Row 2's NULL is skipped, and rows 3 and 4 have nothing to test:
run(f"SELECT id, list_bool_or(alerts) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, True), (2, True), (3, None), (4, None), (5, False)]
Keep the rows where any measurement is above 1, by mapping the measurements to a BOOLEAN list first:
run(f"SELECT id, vals FROM {TABLE} WHERE list_bool_or(list_transform(vals, x -> x > 1)) ORDER BY id LIMIT 10")
[(1, [0.5, 2.5, -1.0]), (2, [2.0, None]), (5, [1e+308, 1e+308, -1e-308])]
How it runs in VAST
The Query Engine runs on the cluster's CNodes, and a query on a table starts with a scan in VAST DataBase (VAST_SCAN). The scan reads a list column whole, and list_bool_or() and any lambda inside it always run in the Query Engine:
- In the
SELECTlist, a projection step computes the result for each row after the scan. - In
WHERE, the scan sends the list for every row, and aFILTERstep above the scan computes the condition.
The plan of the WHERE example shows the FILTER step:
plan(f"SELECT id FROM {TABLE} WHERE list_bool_or(list_transform(vals, x -> x > 1))")
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ list_aggr(list_transform │
│ (vals), 'bool_or') │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ vals │
│ id │
│ │
└───────────────────────────┘
Make it faster
- Add a condition on a plain column. A comparison of a column with a constant, such as
id > 1, is evaluated in VAST DataBase, solist_bool_orruns only on the rows that pass it. The plan forid > 1alone has noFILTERstep:
plan(f"SELECT id FROM {TABLE} WHERE id > 1")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
Limitations and errors
- The elements must be
BOOLEAN. Numbers aren't converted, and fail withBinder Error: No matching aggregate function. Compare them to a value inside list_transform, as in Examples. - The argument must be a list. Anything else fails with
Binder Error: No function matches the given name and argument types 'list_aggr(...)'; the message nameslist_aggrbecause the function is a form of list_aggregate. ORDER BYneeds aLIMIT. Without one, the query fails withThe operator 'ORDER_BY' type=1 is currently not supported for this usage.
Similar functions
| Function | Result for [true, NULL, false] |
|---|---|
list_bool_or(l) |
true: at least one element is true |
| list_bool_and | false: not every non-NULL element is true |
list_aggregate(l, 'bool_or') |
true, the same as list_bool_or |
| bool_or | the same test over the rows of a table, instead of the elements of a list |
run("SELECT list_bool_or(l), list_bool_and(l), list_aggregate(l, 'bool_or') FROM (SELECT [true, NULL, false] AS l)")
[(True, False, True)]
Clean up:
run(f"DROP TABLE IF EXISTS {TABLE}")
run(f"DROP SCHEMA IF EXISTS {SCHEMA}")
conn.close()
[]
[]
See also
- List, struct and map functions: all the list aggregates.
- list_bool_and: whether every element is true.
- Where filters run and pushdowns.
- EXPLAIN: how to read a plan.