Applies to VAST Cluster 5.5.
Functions › Numeric and math functions › Powers and logarithms. Returns the gamma function of a number. For a positive whole number n, gamma(n) is the factorial of n - 1, computed in floating point: gamma(5) is 24.0, which is 4!. It also takes fractions and negative numbers. It has no aliases.
Syntax
gamma(x)
| Argument | Type | Description |
|---|---|---|
x |
DOUBLE (any integer, DECIMAL or FLOAT is converted) |
The number to take the gamma function of. It can't be 0. |
Returns: DOUBLE.
Aliases: none.
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_gamma. 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 four shape parameters, as used by gamma and beta distributions. The last row has no value:
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_gamma"'
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}.shapes"
run(f"CREATE SCHEMA IF NOT EXISTS {SCHEMA}")
run(f"CREATE TABLE IF NOT EXISTS {TABLE} (id BIGINT, name VARCHAR, x DOUBLE)")
run(f"INSERT INTO {TABLE} VALUES "
"(1, 'half', 0.5), "
"(2, 'five', 5), "
"(3, 'eight', 8), "
"(4, NULL, NULL)")
[]
[]
[(4,)]
What it does
gamma(x) extends the factorial to numbers that aren't whole: for a positive whole number n, gamma(n) is (n − 1)!, so gamma(1) is 1.0 and gamma(5) is 24.0. In between, it gives values such as gamma(0.5), 1.772453850905516 (the square root of π).
The argument and the result are DOUBLEs, so the result is rounded as floating-point numbers are, even for a whole number: gamma(8) returns 5039.999999999999, not 7! = 5040, and gamma('12') (a string of digits is converted) returns 39916799.99999999. For exact factorials of whole numbers, use factorial.
The result grows fast. gamma(171) is 7.257415615307999e+306, close to the largest DOUBLE, and gamma(172) is already inf. For large arguments, work with lgamma, the logarithm of the gamma function, instead.
Below 0, the gamma function alternates in sign between the negative whole numbers: gamma(-0.5) is -3.5449077018110318 and gamma(-1.5) is 2.363271801207355. At a negative whole number it isn't defined, and gamma() returns nan, not an error. At 0 it is an error.
NULLs and edge cases
| Input | Result |
|---|---|
NULL |
NULL |
1 |
1.0 |
0.5 |
1.772453850905516 |
171 |
7.257415615307999e+306 |
172 |
inf (too big for a DOUBLE) |
1e-300 |
9.999999999999999e+299 |
-0.5 |
-3.5449077018110318 |
-1, -2 (negative whole numbers) |
nan |
-1000.5 |
-0.0 (too small for a DOUBLE) |
'inf'::DOUBLE |
inf |
'-inf'::DOUBLE, 'nan'::DOUBLE |
nan |
'12' (a string of digits) |
39916799.99999999 |
0 or -0.0::DOUBLE |
Error: cannot take gamma of zero |
'abc' |
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 'gamma(DATE)' |
The same cases, as the cluster returns them. They use the helpers from Set up the examples:
run("SELECT gamma(NULL), gamma(1), gamma(0.5), gamma(171), gamma(172), gamma(1e-300)")
run("SELECT gamma(-0.5), gamma(-1), gamma(-2), gamma(-1000.5)")
run("SELECT gamma('inf'::DOUBLE), gamma('-inf'::DOUBLE), gamma('nan'::DOUBLE)")
run("SELECT gamma('12')")
run("SELECT gamma(0)")
run("SELECT gamma(-0.0::DOUBLE)")
run("SELECT gamma('abc')")
run("SELECT gamma('\\xAA'::BLOB)")
run("SELECT gamma(DATE '2026-10-16')")
run("SELECT gamma(true)")
run("SELECT gamma([1, 2])")
run("SELECT gamma(INTERVAL 1 DAY)")
[(None, 1.0, 1.772453850905516, 7.257415615307999e+306, inf, 9.999999999999999e+299)]
[(-3.5449077018110318, nan, nan, -0.0)]
[(inf, nan, nan)]
[(39916799.99999999,)]
OSError: cannot take gamma of zero
OSError: cannot take gamma of zero
OSError: Could not convert string 'abc' to DOUBLE
InternalError: Binder Error: No function matches the given name and argument types 'gamma(BLOB)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'gamma(DATE)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'gamma(BOOLEAN)'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'gamma(INTEGER[])'. You might need to add explicit type casts.
InternalError: Binder Error: No function matches the given name and argument types 'gamma(INTERVAL)'. You might need to add explicit type casts.
Examples
The gamma function of each shape parameter, next to its logarithm from lgamma:
run(f"SELECT id, name, x, gamma(x), lgamma(x) FROM {TABLE} ORDER BY id LIMIT 10")
[(1, 'half', 0.5, 1.772453850905516, 0.5723649429247001), (2, 'five', 5.0, 24.0, 3.1780538303479458), (3, 'eight', 8.0, 5039.999999999999, 8.525161361065413), (4, None, None, None, None)]
Keep only the rows where the gamma function is more than 10:
run(f"SELECT id, name FROM {TABLE} WHERE gamma(x) > 10 ORDER BY id LIMIT 10")
[(2, 'five'), (3, 'eight')]
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 gamma() is always computed by the Query Engine:
- In the
SELECTlist, the scan reads the column and the Query Engine computesgamma()for each row it returns. - In
WHERE, the scan sends the column for every row, and aFILTERstep in the Query Engine computesgamma()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 gamma(x) > 10")
┌───────────────────────────┐
│ PROJECTION │
│ ──────────────────── │
│ #1 │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ FILTER │
│ ──────────────────── │
│ (gamma(x) > 10.0) │
│ │
└─────────────┬─────────────┘
┌─────────────┴─────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ │
│ Projections: │
│ x │
│ id │
│ │
└───────────────────────────┘
Make it faster
- Compare the column where you can. For
xof 2 or more, the gamma function grows withx, so on such valuesgamma(x) > 24keeps the same rows asx > 5. A comparison of the raw column with a constant is pushed down to VAST DataBase, so rows that don't match never leave storage. The plan forx > 5has noFILTERstep:
plan(f"SELECT id FROM {TABLE} WHERE x > 5")
┌───────────────────────────┐
│ VAST_SCAN │
│ ──────────────────── │
│ Function: VAST_SCAN │
│ Projections: id │
│ │
└───────────────────────────┘
- Store the value if you filter on it often. Compute
gamma(x)when you write the rows and keep it in its own column: a comparison of that column is pushed down,gamma(x)inWHEREisn't. - Combine it with a pushed-down condition. In
id > 100 AND gamma(x) > 10,id > 100is still evaluated in VAST DataBase, sogamma()runs only on the rows that pass it.
Limitations and errors
- Zero is an error.
gamma(0)andgamma(-0.0::DOUBLE)fail withcannot take gamma of zero. On a table, one zero fails the whole query, not just that row. Keep zeros out of the column, or filter them out with a condition on the raw column. - Negative whole numbers give
nan, not an error orNULL. Test for it with isnan. - Large arguments overflow to
inf:gamma(172)isinf. Use lgamma to work with the logarithm instead. - Floating point, not exact.
gamma(8)is5039.999999999999. For exact factorials use factorial, which takes anINTEGER. - Numbers only. A
BLOB, a date, a boolean, a list or an interval fails withNo function matches the given name and argument types. A string is converted toDOUBLE, so a string that isn't a number fails withCould not convert string ... to DOUBLE.
run("SELECT gamma(8), factorial(7)")
[(5039.999999999999, Decimal('5040'))]
Similar functions
| Function | Computes | Result for 5! |
|---|---|---|
gamma(x) |
the gamma function, in floating point | gamma(6) = 120.0 |
factorial, also the postfix ! operator |
the exact factorial of an INTEGER |
factorial(5) = 120, (5)! = 120 |
| lgamma | the natural logarithm of the absolute value of the gamma function | exp(lgamma(6)) = 119.99999999999997 |
run("SELECT gamma(6), factorial(5), (5)!, exp(lgamma(6))")
[(120.0, Decimal('120'), Decimal('120'), 119.99999999999997)]
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 powers and logarithms.
- lgamma and factorial.
- Where filters run and pushdowns.
- EXPLAIN: how to read a plan.