blob: 5479e57e1932c38fe179b7f36d07096e9f7de19b [file]
<!DOCTYPE html>
<html lang="en" data-content_root="../" >
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" /><meta name="viewport" content="width=device-width, initial-scale=1" />
<title>Scalar UDFs &#8212; Apache DataFusion Java documentation</title>
<script data-cfasync="false">
document.documentElement.dataset.mode = localStorage.getItem("mode") || "";
document.documentElement.dataset.theme = localStorage.getItem("theme") || "";
</script>
<!--
this give us a css class that will be invisible only if js is disabled
-->
<noscript>
<style>
.pst-js-only { display: none !important; }
</style>
</noscript>
<!-- Loaded before other Sphinx assets -->
<link href="../_static/styles/theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
<link href="../_static/styles/pydata-sphinx-theme.css?digest=8878045cc6db502f8baf" rel="stylesheet" />
<link rel="stylesheet" type="text/css" href="../_static/pygments.css?v=8f2a1f02" />
<!-- So that users can add custom icons -->
<script src="../_static/scripts/fontawesome.js?digest=8878045cc6db502f8baf"></script>
<!-- Pre-loaded scripts that we'll load fully later -->
<link rel="preload" as="script" href="../_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf" />
<link rel="preload" as="script" href="../_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf" />
<script src="../_static/documentation_options.js?v=5929fcd5"></script>
<script src="../_static/doctools.js?v=9a2dae69"></script>
<script src="../_static/sphinx_highlight.js?v=dc90522c"></script>
<script>DOCUMENTATION_OPTIONS.pagename = 'user-guide/scalar-udf';</script>
<link rel="index" title="Index" href="../genindex.html" />
<link rel="search" title="Search" href="../search.html" />
<link rel="next" title="Java table providers" href="table-provider.html" />
<link rel="prev" title="Logical plans via datafusion-proto" href="proto-plans.html" />
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<meta name="docsearch:language" content="en"/>
<meta name="docsearch:version" content="" />
</head>
<body data-bs-spy="scroll" data-bs-target=".bd-toc-nav" data-offset="180" data-bs-root-margin="0px 0px -60%" data-default-mode="">
<div id="pst-skip-link" class="skip-link d-print-none"><a href="#main-content">Skip to main content</a></div>
<div id="pst-scroll-pixel-helper"></div>
<button type="button" class="btn rounded-pill" id="pst-back-to-top">
<i class="fa-solid fa-arrow-up"></i>Back to top</button>
<dialog id="pst-search-dialog">
<form class="bd-search d-flex align-items-center"
action="../search.html"
method="get">
<i class="fa-solid fa-magnifying-glass"></i>
<input type="search"
class="form-control"
name="q"
placeholder="Search the docs ..."
aria-label="Search the docs ..."
autocomplete="off"
autocorrect="off"
autocapitalize="off"
spellcheck="false"/>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd>K</kbd></span>
</form>
</dialog>
<div class="pst-async-banner-revealer d-none">
<aside id="bd-header-version-warning" class="d-none d-print-none" aria-label="Version warning"></aside>
</div>
<header class="bd-header navbar navbar-expand-lg bd-navbar d-print-none">
<div class="bd-header__inner bd-page-width">
<button class="pst-navbar-icon sidebar-toggle primary-toggle" aria-label="Site navigation">
<span class="fa-solid fa-bars"></span>
</button>
<div class="col-lg-3 navbar-header-items__start">
<div class="navbar-item">
<a class="navbar-brand logo" href="../index.html">
<p class="title logo__title">Apache DataFusion Java documentation</p>
</a></div>
</div>
<div class="col-lg-9 navbar-header-items">
<div class="me-auto navbar-header-items__center">
<div class="navbar-item">
<nav>
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion-java">
GitHub Repository
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion-java/issues">
Issue Tracker
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://datafusion.apache.org/">
Apache DataFusion
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion/blob/main/CODE_OF_CONDUCT.md">
Code of Conduct
</a>
</li>
<li class="nav-item current active">
<a class="nav-link nav-internal" href="index.html">
User Guide
</a>
</li>
<li class="nav-item dropdown">
<button class="btn dropdown-toggle nav-item" type="button"
data-bs-toggle="dropdown" aria-expanded="false"
aria-controls="pst-nav-more-links">
More
</button>
<ul id="pst-nav-more-links" class="dropdown-menu">
<li class=" ">
<a class="nav-link dropdown-item nav-internal" href="../contributor-guide/index.html">
Contributor Guide
</a>
</li>
</ul>
</li>
</ul>
</nav></div>
</div>
<div class="navbar-header-items__end">
<div class="navbar-item navbar-persistent--container">
<button class="btn search-button-field search-button__button pst-js-only" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
</div>
<div class="navbar-item">
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="Color mode" data-bs-title="Color mode" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="Light"></i>
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="Dark"></i>
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="System Settings"></i>
</button></div>
</div>
</div>
<div class="navbar-persistent--mobile">
<button class="btn search-button-field search-button__button pst-js-only" title="Search" aria-label="Search" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="fa-solid fa-magnifying-glass"></i>
<span class="search-button__default-text">Search</span>
<span class="search-button__kbd-shortcut"><kbd class="kbd-shortcut__modifier">Ctrl</kbd>+<kbd class="kbd-shortcut__modifier">K</kbd></span>
</button>
</div>
<button class="pst-navbar-icon sidebar-toggle secondary-toggle" aria-label="On this page">
<span class="fa-solid fa-outdent"></span>
</button>
</div>
</header>
<div class="bd-container">
<div class="bd-container__inner bd-page-width">
<dialog id="pst-primary-sidebar-modal"></dialog>
<div id="pst-primary-sidebar" class="bd-sidebar-primary bd-sidebar">
<div class="sidebar-header-items sidebar-primary__section">
<div class="sidebar-header-items__center">
<div class="navbar-item">
<nav>
<ul class="bd-navbar-elements navbar-nav">
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion-java">
GitHub Repository
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion-java/issues">
Issue Tracker
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://datafusion.apache.org/">
Apache DataFusion
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-external" href="https://github.com/apache/datafusion/blob/main/CODE_OF_CONDUCT.md">
Code of Conduct
</a>
</li>
<li class="nav-item current active">
<a class="nav-link nav-internal" href="index.html">
User Guide
</a>
</li>
<li class="nav-item ">
<a class="nav-link nav-internal" href="../contributor-guide/index.html">
Contributor Guide
</a>
</li>
</ul>
</nav></div>
</div>
<div class="sidebar-header-items__end">
<div class="navbar-item">
<button class="btn btn-sm nav-link pst-navbar-icon theme-switch-button pst-js-only" aria-label="Color mode" data-bs-title="Color mode" data-bs-placement="bottom" data-bs-toggle="tooltip">
<i class="theme-switch fa-solid fa-sun fa-lg" data-mode="light" title="Light"></i>
<i class="theme-switch fa-solid fa-moon fa-lg" data-mode="dark" title="Dark"></i>
<i class="theme-switch fa-solid fa-circle-half-stroke fa-lg" data-mode="auto" title="System Settings"></i>
</button></div>
</div>
</div>
<div class="sidebar-primary-items__start sidebar-primary__section">
<div class="sidebar-primary-item">
<nav class="bd-docs-nav bd-links"
aria-label="Section Navigation">
<p class="bd-links__title" role="heading" aria-level="1">Section Navigation</p>
<div class="bd-toc-item navbar-nav"><ul class="current nav bd-sidenav">
<li class="toctree-l1"><a class="reference internal" href="installation.html">Installation</a></li>
<li class="toctree-l1"><a class="reference internal" href="quickstart.html">Quickstart</a></li>
<li class="toctree-l1"><a class="reference internal" href="sessioncontext.html">SessionContext</a></li>
<li class="toctree-l1"><a class="reference internal" href="dataframe.html">DataFrame and SQL</a></li>
<li class="toctree-l1"><a class="reference internal" href="parquet.html">Parquet</a></li>
<li class="toctree-l1"><a class="reference internal" href="proto-plans.html">Logical plans via datafusion-proto</a></li>
<li class="toctree-l1 current active"><a class="current reference internal" href="#">Scalar UDFs</a></li>
<li class="toctree-l1"><a class="reference internal" href="table-provider.html">Java table providers</a></li>
<li class="toctree-l1"><a class="reference internal" href="api-reference.html">API Reference</a></li>
</ul>
</div>
</nav></div>
</div>
<div class="sidebar-primary-items__end sidebar-primary__section">
<div class="sidebar-primary-item">
<div id="ethical-ad-placement"
class="flat"
data-ea-publisher="readthedocs"
data-ea-type="readthedocs-sidebar"
data-ea-manual="true">
</div></div>
</div>
</div>
<main id="main-content" class="bd-main" role="main">
<div class="bd-content">
<div class="bd-article-container">
<div class="bd-header-article d-print-none">
<div class="header-article-items header-article__inner">
<div class="header-article-items__start">
<div class="header-article-item">
<nav aria-label="Breadcrumb" class="d-print-none">
<ul class="bd-breadcrumbs">
<li class="breadcrumb-item breadcrumb-home">
<a href="../index.html" class="nav-link" aria-label="Home">
<i class="fa-solid fa-home"></i>
</a>
</li>
<li class="breadcrumb-item"><a href="index.html" class="nav-link">User Guide</a></li>
<li class="breadcrumb-item active" aria-current="page"><span class="ellipsis">Scalar UDFs</span></li>
</ul>
</nav>
</div>
</div>
</div>
</div>
<div id="searchbox"></div>
<article class="bd-article">
<!--
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing,
software distributed under the License is distributed on an
"AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied. See the License for the
specific language governing permissions and limitations
under the License.
-->
<section id="scalar-udfs">
<h1>Scalar UDFs<a class="headerlink" href="#scalar-udfs" title="Link to this heading">#</a></h1>
<p>A scalar UDF is a Java-implemented SQL function that operates one row at a time,
expressed in vectorised form: each invocation receives a batch of input columns
and returns either a per-row output column of the same length (<code class="docutils literal notranslate"><span class="pre">Array</span></code>) or a
single value broadcast to every row (<code class="docutils literal notranslate"><span class="pre">Scalar</span></code>).</p>
<section id="implement">
<h2>Implement<a class="headerlink" href="#implement" title="Link to this heading">#</a></h2>
<p>Implement the <code class="docutils literal notranslate"><span class="pre">ScalarFunction</span></code> interface. The implementation declares its own
SQL name, argument fields, return field, and volatility, and supplies the
per-batch <code class="docutils literal notranslate"><span class="pre">evaluate</span></code> body:</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span><span class="w"> </span><span class="nn">java.util.List</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.arrow.memory.BufferAllocator</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.arrow.vector.IntVector</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.arrow.vector.types.pojo.ArrowType</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.arrow.vector.types.pojo.Field</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.datafusion.ColumnarValue</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.datafusion.ScalarFunction</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.datafusion.ScalarFunctionArgs</span><span class="p">;</span>
<span class="kn">import</span><span class="w"> </span><span class="nn">org.apache.datafusion.Volatility</span><span class="p">;</span>
<span class="kd">public</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kd">class</span> <span class="nc">AddOne</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">ScalarFunction</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">ArrowType</span><span class="w"> </span><span class="n">INT32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">Int</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">);</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">name</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;add_one&quot;</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Field</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">argFields</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;x&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">INT32</span><span class="p">));</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Field</span><span class="w"> </span><span class="nf">returnField</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;y&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">INT32</span><span class="p">);</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Volatility</span><span class="w"> </span><span class="nf">volatility</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Volatility</span><span class="p">.</span><span class="na">IMMUTABLE</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span>
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">ColumnarValue</span><span class="w"> </span><span class="nf">evaluate</span><span class="p">(</span><span class="n">BufferAllocator</span><span class="w"> </span><span class="n">allocator</span><span class="p">,</span><span class="w"> </span><span class="n">ScalarFunctionArgs</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">IntVector</span><span class="w"> </span><span class="n">in</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="p">(</span><span class="n">IntVector</span><span class="p">)</span><span class="w"> </span><span class="n">args</span><span class="p">.</span><span class="na">args</span><span class="p">().</span><span class="na">get</span><span class="p">(</span><span class="mi">0</span><span class="p">).</span><span class="na">vector</span><span class="p">();</span>
<span class="w"> </span><span class="n">IntVector</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">IntVector</span><span class="p">(</span><span class="s">&quot;add_one&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="p">);</span>
<span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">allocateNew</span><span class="p">(</span><span class="n">in</span><span class="p">.</span><span class="na">getValueCount</span><span class="p">());</span>
<span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="p">(</span><span class="kt">int</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="mi">0</span><span class="p">;</span><span class="w"> </span><span class="n">i</span><span class="w"> </span><span class="o">&lt;</span><span class="w"> </span><span class="n">in</span><span class="p">.</span><span class="na">getValueCount</span><span class="p">();</span><span class="w"> </span><span class="n">i</span><span class="o">++</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="k">if</span><span class="w"> </span><span class="p">(</span><span class="n">in</span><span class="p">.</span><span class="na">isNull</span><span class="p">(</span><span class="n">i</span><span class="p">))</span><span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">setNull</span><span class="p">(</span><span class="n">i</span><span class="p">);</span>
<span class="w"> </span><span class="k">else</span><span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">set</span><span class="p">(</span><span class="n">i</span><span class="p">,</span><span class="w"> </span><span class="n">in</span><span class="p">.</span><span class="na">get</span><span class="p">(</span><span class="n">i</span><span class="p">)</span><span class="w"> </span><span class="o">+</span><span class="w"> </span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">setValueCount</span><span class="p">(</span><span class="n">in</span><span class="p">.</span><span class="na">getValueCount</span><span class="p">());</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">ColumnarValue</span><span class="p">.</span><span class="na">array</span><span class="p">(</span><span class="n">out</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>Each entry in <code class="docutils literal notranslate"><span class="pre">args.args()</span></code> is a <code class="docutils literal notranslate"><span class="pre">ColumnarValue</span></code> — either <code class="docutils literal notranslate"><span class="pre">ColumnarValue.Array</span></code>
(a per-row vector of length <code class="docutils literal notranslate"><span class="pre">args.rowCount()</span></code>) or <code class="docutils literal notranslate"><span class="pre">ColumnarValue.Scalar</span></code> (a
length-1 vector representing a single literal or folded constant). Access the
underlying Arrow vector with <code class="docutils literal notranslate"><span class="pre">.vector()</span></code>.</p>
<p>Allocate any new vectors — including the result — from the supplied
<code class="docutils literal notranslate"><span class="pre">BufferAllocator</span></code>. The input vectors are read-only views; do not close them.
Ownership of the returned vector transfers to the framework on return.</p>
</section>
<section id="declaring-argument-and-return-fields">
<h2>Declaring argument and return fields<a class="headerlink" href="#declaring-argument-and-return-fields" title="Link to this heading">#</a></h2>
<p>Each argument and the return value are described as Arrow <code class="docutils literal notranslate"><span class="pre">Field</span></code>s. A <code class="docutils literal notranslate"><span class="pre">Field</span></code>
carries a name, a <code class="docutils literal notranslate"><span class="pre">FieldType</span></code> (Arrow type plus nullability and metadata), and a
list of child fields used by nested types.</p>
<p>For primitive types the <code class="docutils literal notranslate"><span class="pre">Field.nullable(name,</span> <span class="pre">arrowType)</span></code> and
<code class="docutils literal notranslate"><span class="pre">Field.notNullable(name,</span> <span class="pre">arrowType)</span></code> factories are the shortest form:</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="n">Field</span><span class="w"> </span><span class="n">x</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;x&quot;</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">Int</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">));</span>
<span class="n">Field</span><span class="w"> </span><span class="n">y</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">Field</span><span class="p">.</span><span class="na">notNullable</span><span class="p">(</span><span class="s">&quot;y&quot;</span><span class="p">,</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">Int</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">));</span>
</pre></div>
</div>
<p>Nested Arrow types — <code class="docutils literal notranslate"><span class="pre">List</span></code>, <code class="docutils literal notranslate"><span class="pre">Struct</span></code>, <code class="docutils literal notranslate"><span class="pre">Map</span></code>, <code class="docutils literal notranslate"><span class="pre">Union</span></code> — must declare their
element / member / key / value fields as children, because that information
does not live on the <code class="docutils literal notranslate"><span class="pre">ArrowType</span></code> itself. Use the
<code class="docutils literal notranslate"><span class="pre">new</span> <span class="pre">Field(name,</span> <span class="pre">FieldType,</span> <span class="pre">children)</span></code> constructor:</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="c1">// List&lt;Int32&gt;</span>
<span class="n">ArrowType</span><span class="w"> </span><span class="n">INT32</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">Int</span><span class="p">(</span><span class="mi">32</span><span class="p">,</span><span class="w"> </span><span class="kc">true</span><span class="p">);</span>
<span class="n">Field</span><span class="w"> </span><span class="n">listOfInt</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Field</span><span class="p">(</span>
<span class="w"> </span><span class="s">&quot;vals&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="n">FieldType</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">List</span><span class="p">()),</span>
<span class="w"> </span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;item&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">INT32</span><span class="p">)));</span>
<span class="c1">// Struct&lt;a: Int32, b: Int32&gt;</span>
<span class="n">Field</span><span class="w"> </span><span class="n">structAB</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">Field</span><span class="p">(</span>
<span class="w"> </span><span class="s">&quot;ab&quot;</span><span class="p">,</span>
<span class="w"> </span><span class="n">FieldType</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">Struct</span><span class="p">()),</span>
<span class="w"> </span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">(</span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;a&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">INT32</span><span class="p">),</span><span class="w"> </span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;b&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">INT32</span><span class="p">)));</span>
</pre></div>
</div>
<p>A UDF declared with <code class="docutils literal notranslate"><span class="pre">Field.nullable(...)</span></code> arguments is registered with an exact
nullability-bearing signature; calls whose argument types do not match exactly
are rejected. The declared <code class="docutils literal notranslate"><span class="pre">returnField</span></code>’s nullability is preserved end-to-end:
a non-nullable return field stays non-nullable in the result schema.</p>
</section>
<section id="returning-a-scalar">
<h2>Returning a Scalar<a class="headerlink" href="#returning-a-scalar" title="Link to this heading">#</a></h2>
<p>Functions that yield a single value (nullary constants like <code class="docutils literal notranslate"><span class="pre">pi()</span></code>, or any
function that wants the framework to broadcast a result across the batch) can
return <code class="docutils literal notranslate"><span class="pre">ColumnarValue.scalar(...)</span></code> over a length-1 vector:</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="kd">public</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="kd">class</span> <span class="nc">JavaPi</span><span class="w"> </span><span class="kd">implements</span><span class="w"> </span><span class="n">ScalarFunction</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="kd">private</span><span class="w"> </span><span class="kd">static</span><span class="w"> </span><span class="kd">final</span><span class="w"> </span><span class="n">ArrowType</span><span class="w"> </span><span class="n">FLOAT64</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">ArrowType</span><span class="p">.</span><span class="na">FloatingPoint</span><span class="p">(</span><span class="n">org</span><span class="p">.</span><span class="na">apache</span><span class="p">.</span><span class="na">arrow</span><span class="p">.</span><span class="na">vector</span><span class="p">.</span><span class="na">types</span><span class="p">.</span><span class="na">FloatingPointPrecision</span><span class="p">.</span><span class="na">DOUBLE</span><span class="p">);</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">String</span><span class="w"> </span><span class="nf">name</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="s">&quot;java_pi&quot;</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">List</span><span class="o">&lt;</span><span class="n">Field</span><span class="o">&gt;</span><span class="w"> </span><span class="nf">argFields</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">List</span><span class="p">.</span><span class="na">of</span><span class="p">();</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Field</span><span class="w"> </span><span class="nf">returnField</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Field</span><span class="p">.</span><span class="na">nullable</span><span class="p">(</span><span class="s">&quot;p&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">FLOAT64</span><span class="p">);</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span><span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">Volatility</span><span class="w"> </span><span class="nf">volatility</span><span class="p">()</span><span class="w"> </span><span class="p">{</span><span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">Volatility</span><span class="p">.</span><span class="na">VOLATILE</span><span class="p">;</span><span class="w"> </span><span class="p">}</span>
<span class="w"> </span><span class="nd">@Override</span>
<span class="w"> </span><span class="kd">public</span><span class="w"> </span><span class="n">ColumnarValue</span><span class="w"> </span><span class="nf">evaluate</span><span class="p">(</span><span class="n">BufferAllocator</span><span class="w"> </span><span class="n">allocator</span><span class="p">,</span><span class="w"> </span><span class="n">ScalarFunctionArgs</span><span class="w"> </span><span class="n">args</span><span class="p">)</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">org</span><span class="p">.</span><span class="na">apache</span><span class="p">.</span><span class="na">arrow</span><span class="p">.</span><span class="na">vector</span><span class="p">.</span><span class="na">Float8Vector</span><span class="w"> </span><span class="n">out</span><span class="w"> </span><span class="o">=</span>
<span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">org</span><span class="p">.</span><span class="na">apache</span><span class="p">.</span><span class="na">arrow</span><span class="p">.</span><span class="na">vector</span><span class="p">.</span><span class="na">Float8Vector</span><span class="p">(</span><span class="s">&quot;pi&quot;</span><span class="p">,</span><span class="w"> </span><span class="n">allocator</span><span class="p">);</span>
<span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">allocateNew</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">set</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span><span class="w"> </span><span class="n">Math</span><span class="p">.</span><span class="na">PI</span><span class="p">);</span>
<span class="w"> </span><span class="n">out</span><span class="p">.</span><span class="na">setValueCount</span><span class="p">(</span><span class="mi">1</span><span class="p">);</span>
<span class="w"> </span><span class="k">return</span><span class="w"> </span><span class="n">ColumnarValue</span><span class="p">.</span><span class="na">scalar</span><span class="p">(</span><span class="n">out</span><span class="p">);</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p>The framework expands the scalar across <code class="docutils literal notranslate"><span class="pre">args.rowCount()</span></code> rows automatically.</p>
</section>
<section id="register">
<h2>Register<a class="headerlink" href="#register" title="Link to this heading">#</a></h2>
<p>Wrap the implementation in a <code class="docutils literal notranslate"><span class="pre">ScalarUdf</span></code> and pass it to
<code class="docutils literal notranslate"><span class="pre">SessionContext.registerUdf</span></code>:</p>
<div class="highlight-java notranslate"><div class="highlight"><pre><span></span><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">SessionContext</span><span class="w"> </span><span class="n">ctx</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">SessionContext</span><span class="p">())</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="na">registerUdf</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">ScalarUdf</span><span class="p">(</span><span class="k">new</span><span class="w"> </span><span class="n">AddOne</span><span class="p">()));</span>
<span class="w"> </span><span class="k">try</span><span class="w"> </span><span class="p">(</span><span class="n">DataFrame</span><span class="w"> </span><span class="n">df</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">ctx</span><span class="p">.</span><span class="na">sql</span><span class="p">(</span><span class="s">&quot;SELECT add_one(x) FROM t&quot;</span><span class="p">);</span>
<span class="w"> </span><span class="n">ArrowReader</span><span class="w"> </span><span class="n">r</span><span class="w"> </span><span class="o">=</span><span class="w"> </span><span class="n">df</span><span class="p">.</span><span class="na">collect</span><span class="p">(</span><span class="n">allocator</span><span class="p">))</span><span class="w"> </span><span class="p">{</span>
<span class="w"> </span><span class="c1">// ...</span>
<span class="w"> </span><span class="p">}</span>
<span class="p">}</span>
</pre></div>
</div>
<p><code class="docutils literal notranslate"><span class="pre">ScalarUdf</span></code> mirrors DataFusion’s <code class="docutils literal notranslate"><span class="pre">ScalarUDF</span></code> struct; <code class="docutils literal notranslate"><span class="pre">ScalarFunction</span></code> mirrors
<code class="docutils literal notranslate"><span class="pre">ScalarUDFImpl</span></code>. Use <code class="docutils literal notranslate"><span class="pre">Volatility.IMMUTABLE</span></code> for pure functions, <code class="docutils literal notranslate"><span class="pre">STABLE</span></code> for
functions that are deterministic within a single query, and <code class="docutils literal notranslate"><span class="pre">VOLATILE</span></code> for
non-deterministic functions.</p>
</section>
<section id="errors">
<h2>Errors<a class="headerlink" href="#errors" title="Link to this heading">#</a></h2>
<p>If the UDF throws, the exception class and message surface in the
<code class="docutils literal notranslate"><span class="pre">RuntimeException</span></code> raised from <code class="docutils literal notranslate"><span class="pre">collect()</span></code>. If the returned <code class="docutils literal notranslate"><span class="pre">ColumnarValue</span></code> is
<code class="docutils literal notranslate"><span class="pre">null</span></code>, an Array result’s vector length does not equal <code class="docutils literal notranslate"><span class="pre">args.rowCount()</span></code>, or
the result’s Arrow type differs from the declared return field, the runtime
raises a <code class="docutils literal notranslate"><span class="pre">RuntimeException</span></code> with a descriptive message. A Scalar result whose
vector is not length-1 is rejected at the <code class="docutils literal notranslate"><span class="pre">ColumnarValue.scalar</span></code> factory.</p>
</section>
<section id="threading">
<h2>Threading<a class="headerlink" href="#threading" title="Link to this heading">#</a></h2>
<p>DataFusion may invoke a UDF concurrently from multiple worker threads. If the
implementation carries mutable state, the implementation must synchronize it.</p>
</section>
<section id="limitations-v1">
<h2>Limitations (v1)<a class="headerlink" href="#limitations-v1" title="Link to this heading">#</a></h2>
<ul class="simple">
<li><p>Scalar UDFs only — no aggregates, window functions, or table functions.</p></li>
<li><p>Exact-signature only — no variadic or polymorphic argument lists.</p></li>
<li><p>No nullable-argument short-circuiting; null inputs are passed through to the
UDF as nulls in the input vector.</p></li>
</ul>
</section>
</section>
</article>
<footer class="prev-next-footer d-print-none">
<div class="prev-next-area">
<a class="left-prev"
href="proto-plans.html"
title="previous page">
<i class="fa-solid fa-angle-left"></i>
<div class="prev-next-info">
<p class="prev-next-subtitle">previous</p>
<p class="prev-next-title">Logical plans via datafusion-proto</p>
</div>
</a>
<a class="right-next"
href="table-provider.html"
title="next page">
<div class="prev-next-info">
<p class="prev-next-subtitle">next</p>
<p class="prev-next-title">Java table providers</p>
</div>
<i class="fa-solid fa-angle-right"></i>
</a>
</div>
</footer>
</div>
<dialog id="pst-secondary-sidebar-modal"></dialog>
<div id="pst-secondary-sidebar" class="bd-sidebar-secondary bd-toc"><div class="sidebar-secondary-items sidebar-secondary__inner">
<div class="sidebar-secondary-item">
<div
id="pst-page-navigation-heading-2"
class="page-toc tocsection onthispage">
<i class="fa-solid fa-list"></i> On this page
</div>
<nav class="bd-toc-nav page-toc" aria-labelledby="pst-page-navigation-heading-2">
<ul class="visible nav section-nav flex-column">
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#implement">Implement</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#declaring-argument-and-return-fields">Declaring argument and return fields</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#returning-a-scalar">Returning a Scalar</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#register">Register</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#errors">Errors</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#threading">Threading</a></li>
<li class="toc-h2 nav-item toc-entry"><a class="reference internal nav-link" href="#limitations-v1">Limitations (v1)</a></li>
</ul>
</nav></div>
<div class="sidebar-secondary-item">
<div role="note" aria-label="source link">
<h3>This Page</h3>
<ul class="this-page-menu">
<li><a href="../_sources/user-guide/scalar-udf.md.txt"
rel="nofollow">Show Source</a></li>
</ul>
</div></div>
</div></div>
</div>
<footer class="bd-footer-content">
</footer>
</main>
</div>
</div>
<!-- Scripts loaded after <body> so the DOM is not blocked -->
<script defer src="../_static/scripts/bootstrap.js?digest=8878045cc6db502f8baf"></script>
<script defer src="../_static/scripts/pydata-sphinx-theme.js?digest=8878045cc6db502f8baf"></script>
<footer class="bd-footer">
<div class="bd-footer__inner bd-page-width">
<div class="footer-items__start">
<div class="footer-item">
<p class="copyright">
© Copyright 2026, Apache Software Foundation.
<br/>
</p>
</div>
<div class="footer-item">
<p class="sphinx-version">
Created using <a href="https://www.sphinx-doc.org/">Sphinx</a> 7.4.7.
<br/>
</p>
</div>
</div>
<div class="footer-items__end">
<div class="footer-item">
<p class="theme-version">
<!-- # L10n: Setting the PST URL as an argument as this does not need to be localized -->
Built with the <a href="https://pydata-sphinx-theme.readthedocs.io/en/stable/index.html">PyData Sphinx Theme</a> 0.16.1.
</p></div>
</div>
</div>
</footer>
</body>
</html>