Applies to VAST Cluster 5.5.
Functions › Numeric and math functions › Hyperbolic functions. Returns the inverse hyperbolic cosine of a number: the value of 0 or more whose cosh is that number. The argument should be 1 or more. A value below 1 doesn't raise an error: the result is nan.
Syntax
acosh(x)
| Argument | Type | Description |
|---|---|---|
x |
Any number (DOUBLE, REAL, an integer or a DECIMAL) |
A value of 1 or more, including inf. |
Returns: DOUBLE, 0 or more; nan for an argument below 1.
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_numeric_acosh. 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 six ratios. Row 3 has no value, and row 6 is below 1, outside the range acosh is defined for:
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_numeric_acosh"'
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}.ratios"
run(f"CREATE SCHEMA IF NOT EXISTS {SCHEMA}")
run(f"CREATE TABLE IF NOT EXISTS {TABLE} (id BIGINT, x DOUBLE)")
run(f"INSERT INTO {TABLE} VALUES "
"(1, 2.0), (2, 1.0), (3, NULL), (4, 1.5), (5, 10.0), (6, 0.5)")
[]
[]
[(6,)]
What it does
acosh(x) is the inverse of cosh for results of 0 or more: it returns the value y >= 0 for which cosh(y) = x. It's 0 at 1 and grows like ln(2x) for large x, so a value as large as 1e308 gives a result of only about 710.
| Call | Result |
|---|---|
acosh(1) |
0.0 |
acosh(2) |
1.3169578969248166 |
acosh(1e308) |
709.889355822726 |
acosh(0.5) |
nan: below 1 |
The result is a DOUBLE for every numeric argument type: an integer, a REAL, a DECIMAL or a DOUBLE.
run("SELECT acosh(1), acosh(2), acosh(1e308), acosh(0.5)")
run("SELECT typeof(acosh(1::INTEGER)), typeof(acosh(0.5::FLOAT)), typeof(acosh(0.5))")
[(0.0, 1.3169578969248166, 709.889355822726, nan)]
[('DOUBLE', 'DOUBLE', 'DOUBLE')]
NULLs and edge cases
| Input | Result |
|---|---|
NULL |
NULL |
'inf'::DOUBLE |
inf |
'nan'::DOUBLE |
nan |
Any value below 1, such as 0.9999999999, 0, -0.0::DOUBLE, -1 or '-inf'::DOUBLE |
nan, not an error |
The smallest positive DOUBLE (5e-324) |
nan |
'12' (a string literal holding a number) |
Converted to DOUBLE: 3.176313180591656 |
'abc' (a string that isn't a number) |
Error: Could not convert string 'abc' to DOUBLE |
A BLOB, date, boolean, list or interval |
Error: No function matches the given name and argument types, for example 'acosh(DATE)' |
The same cases, as the cluster returns them. They use the helpers from Set up the examples:
run("SELECT acosh(NULL), acosh('inf'::DOUBLE), acosh('nan'::DOUBLE)")
run("SELECT acosh(0.9999999999), acosh(0), acosh(-0.0::DOUBLE), acosh(-1), acosh('-inf'::DOUBLE)")
run("SELECT acosh(5e-324::DOUBLE), acosh('12')")
run("SELECT acosh('abc')")
run("SELECT acosh(DATE '2026-10-16')")
[(None, inf, nan)]
[(nan, nan, nan, nan, nan)]
[(nan, 3.176313180591656)]
OSError: Could not convert string 'abc' to DOUBLE
InternalError: Binder Error: No function matches the given name and argument types 'acosh(DATE)'. You might need to add explicit type casts.
Examples
The inverse hyperbolic cosine of each ratio. Row 6 is below 1, so its result is nan; isnan finds those rows:
run(f"SELECT id, x, acosh(x), isnan(acosh(x)) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, 2.0, 1.3169578969248166, False), (2, 1.0, 0.0, False), (3, None, None, None), (4, 1.5, 0.9624236501192069, False), (5, 10.0, 2.993222846126381, False), (6, 0.5, nan, True)]
Filter on the result, and on the result with the rows below 1 left out first. Row 6 (nan) passes the first condition, because a nan result is greater than 1 in a comparison; the second leaves it out:
run(f"SELECT id, x FROM {TABLE} WHERE acosh(x) > 1 ORDER BY id LIMIT 10")
run(f"SELECT id, x FROM {TABLE} WHERE x >= 1 AND acosh(x) > 1 ORDER BY id LIMIT 10")
[(1, 2.0), (5, 10.0), (6, 0.5)]
[(1, 2.0), (5, 10.0)]
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). VAST DataBase can evaluate a comparison of a column with a constant, but not a function of a column, so acosh() is always computed by the Query Engine:
- In the
SELECTlist, the scan reads the column and the Query Engine computesacosh()for each row it returns. - In
WHERE, the scan sends the column for every row, and aFILTERstep in the Query Engine computesacosh()and keeps the rows that match. No rows are skipped in storage.
The plan of the first WHERE example shows the FILTER step above the scan:
plan(f"SELECT id FROM {TABLE} WHERE acosh(x) > 1")
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ (acosh(x) > 1.0) │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ x │
│ id │
│ │
└───────────────────────────┘
Make it faster
- Compare the raw column with the inverse of the constant. For values of 1 or more,
acosh()grows as its argument grows, soacosh(x) > 1is the same condition asx > cosh(1), which also leaves out the values below 1. A function on constants is computed once, before the scan, so the second form is a comparison of the column with a constant, which is pushed down to VAST DataBase. Its plan has noFILTERstep:
plan(f"SELECT id FROM {TABLE} WHERE x > cosh(1)")
run(f"SELECT id, x FROM {TABLE} WHERE x > cosh(1) ORDER BY id LIMIT 10")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
[(1, 2.0), (5, 10.0)]
- Store the result if you filter on it often. Compute it when you write the rows and keep it in its own column: a condition on that column is pushed down, a condition on
acosh()isn't.
Limitations and errors
- Below 1 gives
nan, not an error. Unlike acos, asin and atanh, which fail outside their range,acosh()returnsnanfor any argument below 1, so the query succeeds. Leave those rows out with a pushed-downx >= 1, or find them withisnan(acosh(x)). - Numbers only. A
BLOB, a date, a boolean, a list or an interval fails withNo function matches the given name and argument types; the error lists the one formacoshhas,acosh(DOUBLE) -> DOUBLE. A string literal is converted toDOUBLEif it holds a number, and otherwise fails withCould not convert string 'abc' to DOUBLE. - One argument.
acosh(1, 2)fails withNo function matches the given name and argument types 'acosh(INTEGER_LITERAL, INTEGER_LITERAL)'.
Similar functions
| Function | Returns | Below its range |
|---|---|---|
acosh(x) |
the inverse hyperbolic cosine, for 1 or more | acosh(0.5) is nan |
| cosh | the hyperbolic cosine: the inverse of acosh |
acosh(cosh(1)) is 1.0 |
| asinh | the inverse hyperbolic sine, for any number | (no range limit) |
| atanh | the inverse hyperbolic tangent, for -1 to 1 | atanh(2) fails: ATANH is undefined outside [-1,1] |
| acos | the inverse cosine, an angle, for -1 to 1 | acos(2) fails: ACOS is undefined outside [-1,1] |
run("SELECT acosh(0.5), acosh(cosh(1)), asinh(0.5)")
run("SELECT atanh(2)")
run("SELECT acos(2)")
[(nan, 1.0, 0.48121182505960347)]
OSError: ATANH is undefined outside [-1,1]
OSError: ACOS is undefined outside [-1,1]
Clean up:
run(f"DROP TABLE IF EXISTS {TABLE}")
run(f"DROP SCHEMA IF EXISTS {SCHEMA}")
conn.close()
[]
[]
See also
- Numeric and math functions: all the numeric functions.
- Where filters run and pushdowns.
- EXPLAIN: how to read a plan.