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.

gamma

Prev Next

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 SELECT list, the scan reads the column and the Query Engine computes gamma() for each row it returns.
  • In WHERE, the scan sends the column for every row, and a FILTER step in the Query Engine computes gamma() 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 x of 2 or more, the gamma function grows with x, so on such values gamma(x) > 24 keeps the same rows as x > 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 for x > 5 has no FILTER step:
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) in WHERE isn't.
  • Combine it with a pushed-down condition. In id > 100 AND gamma(x) > 10, id > 100 is still evaluated in VAST DataBase, so gamma() runs only on the rows that pass it.

Limitations and errors

  • Zero is an error. gamma(0) and gamma(-0.0::DOUBLE) fail with cannot 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 or NULL. Test for it with isnan.
  • Large arguments overflow to inf: gamma(172) is inf. Use lgamma to work with the logarithm instead.
  • Floating point, not exact. gamma(8) is 5039.999999999999. For exact factorials use factorial, which takes an INTEGER.
  • Numbers only. A BLOB, a date, a boolean, a list or an interval fails with No function matches the given name and argument types. A string is converted to DOUBLE, so a string that isn't a number fails with Could 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