Welcome to the new DOCS.vastdata.com. We've added new menus and a version selector, plus new persona and resource pages to help customers get started. Looking for a previous article? Use the docs search.

cosh

Prev Next

Applies to VAST Cluster 5.5.

Functions › Numeric and math functions › Hyperbolic functions. Returns the hyperbolic cosine of a number, (exp(x) + exp(-x)) / 2. It takes any number, and the result is always 1 or more. A result too large for a DOUBLE is infinity, not an error.

Syntax

cosh(x)
Argument Type Description
x Any number (DOUBLE, REAL, an integer or a DECIMAL) Any value, including inf and -inf.

Returns: DOUBLE, 1 or more (or nan for a nan argument).

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_cosh. 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 readings. Row 3 has no value, and row 6 is too large for its hyperbolic cosine to fit in a DOUBLE:

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_cosh"'

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}.readings"
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, 1.0), (2, -1.0), (3, NULL), (4, 0.0), (5, 2.5), (6, 800.0)")
[]
[]
[(6,)]

What it does

cosh(x) is the hyperbolic cosine, (exp(x) + exp(-x)) / 2. It's an even function (cosh(-x) is cosh(x)), it's 1 at 0, and it grows like exp(|x|) / 2 for large x. Its inverse, for arguments of 0 or more, is acosh.

Call Result
cosh(0) 1.0
cosh(1) 1.5430806348152437
cosh(-1) 1.5430806348152437
cosh(710) 1.1169973830808557e+308
cosh(711) inf: too large for a DOUBLE

The result is a DOUBLE for every numeric argument type: an integer, a REAL, a DECIMAL or a DOUBLE.

run("SELECT cosh(0), cosh(1), cosh(-1), cosh(710), cosh(711)")
run("SELECT typeof(cosh(1::INTEGER)), typeof(cosh(0.5::FLOAT)), typeof(cosh(0.5))")
[(1.0, 1.5430806348152437, 1.5430806348152437, 1.1169973830808557e+308, inf)]
[('DOUBLE', 'DOUBLE', 'DOUBLE')]

NULLs and edge cases

Input Result
NULL NULL
'inf'::DOUBLE inf
'-inf'::DOUBLE inf: both infinities give plus infinity
'nan'::DOUBLE nan
-0.0::DOUBLE 1.0
The smallest positive DOUBLE (5e-324) 1.0
'12' (a string literal holding a number) Converted to DOUBLE: 81377.39571257407
'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 'cosh(DATE)'

The same cases, as the cluster returns them. They use the helpers from Set up the examples:

run("SELECT cosh(NULL), cosh('inf'::DOUBLE), cosh('-inf'::DOUBLE), cosh('nan'::DOUBLE)")
run("SELECT cosh(-0.0::DOUBLE), cosh(5e-324::DOUBLE), cosh('12')")
run("SELECT cosh('abc')")
run("SELECT cosh(DATE '2026-10-16')")
[(None, inf, inf, nan)]
[(1.0, 1.0, 81377.39571257407)]
OSError: Could not convert string 'abc' to DOUBLE
InternalError: Binder Error: No function matches the given name and argument types 'cosh(DATE)'. You might need to add explicit type casts.

Examples

The hyperbolic cosine of each reading. Rows 1 and 2 give the same value, and row 6 overflows to inf while the query still returns every row:

run(f"SELECT id, x, cosh(x) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, 1.0, 1.5430806348152437), (2, -1.0, 1.5430806348152437), (3, None, None), (4, 0.0, 1.0), (5, 2.5, 6.132289479663686), (6, 800.0, inf)]

Keep only the rows whose hyperbolic cosine is above 10:

run(f"SELECT id, x FROM {TABLE} WHERE cosh(x) > 10 ORDER BY id LIMIT 10")
[(6, 800.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 cosh() is always computed by the Query Engine:

  • In the SELECT list, the scan reads the column and the Query Engine computes cosh() for each row it returns.
  • In WHERE, the scan sends the column for every row, and a FILTER step in the Query Engine computes cosh() and keeps the rows that match. No rows are skipped in storage.

The plan of the WHERE example shows the FILTER step above the scan:

plan(f"SELECT id FROM {TABLE} WHERE cosh(x) > 10")
┌───────────────────────────┐
│         PROJECTION        │
│    ────────────────────   │
│             #1            │
│                           │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│           FILTER          │
│    ────────────────────   │
│      (cosh(x) > 10.0)     │
│                           │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│         VAST_SCAN         │
│    ────────────────────   │
│    Function: VAST_SCAN    │
│                           │
│        Projections:       │
│             x             │
│             id            │
│                           │
└───────────────────────────┘

Make it faster

  • Compare the raw column with the inverse of the constant, on both sides of zero. cosh() grows with the distance from zero, so cosh(x) > 10 is the same condition as x > acosh(10) OR x < -acosh(10). A function on constants is computed once, before the scan, so each side is a comparison of the column with a constant, and the OR of them is pushed down to VAST DataBase. Its plan has no FILTER step:
plan(f"SELECT id FROM {TABLE} WHERE x > acosh(10) OR x < -acosh(10)")
run(f"SELECT id, x FROM {TABLE} WHERE x > acosh(10) OR x < -acosh(10) ORDER BY id LIMIT 10")
┌───────────────────────────┐
│         VAST_SCAN         │
│    ────────────────────   │
│    Function: VAST_SCAN    │
│      Projections: id      │
│                           │
└───────────────────────────┘
[(6, 800.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 cosh() isn't.

Limitations and errors

  • Overflow gives infinity, not an error. Above about 710 the result is inf (cosh(711) is inf). To find those rows, test the result with isinf:
run(f"SELECT id, x, isinf(cosh(x)) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, 1.0, False), (2, -1.0, False), (3, None, None), (4, 0.0, False), (5, 2.5, False), (6, 800.0, True)]
  • Numbers only. A BLOB, a date, a boolean, a list or an interval fails with No function matches the given name and argument types; the error lists the one form cosh has, cosh(DOUBLE) -> DOUBLE. A string literal is converted to DOUBLE if it holds a number, and otherwise fails with Could not convert string 'abc' to DOUBLE.
  • One argument. cosh(1, 2) fails with No function matches the given name and argument types 'cosh(INTEGER_LITERAL, INTEGER_LITERAL)'.

Similar functions

Function Returns Result for 1
cosh(x) the hyperbolic cosine 1.5430806348152437
sinh the hyperbolic sine, (exp(x) - exp(-x)) / 2 1.1752011936438014
tanh the hyperbolic tangent, sinh(x) / cosh(x) 0.7615941559557649
acosh the inverse of cosh, for arguments of 0 or more acosh(cosh(1)) is 1.0
exp e to the power x
cos the cosine of an angle in radians
run("SELECT cosh(1), sinh(1), tanh(1), acosh(cosh(1)), (exp(1) + exp(-1)) / 2, cos(1)")
[(1.5430806348152437, 1.1752011936438014, 0.7615941559557649, 1.0, 1.5430806348152437, 0.5403023058681398)]

Clean up:

run(f"DROP TABLE IF EXISTS {TABLE}")
run(f"DROP SCHEMA IF EXISTS {SCHEMA}")
conn.close()
[]
[]

See also