mirror of
https://github.com/lancedb/lancedb.git
synced 2026-11-13 22:26:53 +00:00
Remote Python `drop_function` currently discards the dependent-view warnings returned by SQL DROP. Both the synchronous and coroutine APIs now emit those warnings through `warnings.warn` while preserving their boolean result and executing DROP once. The server support is already merged in lancedb/sophon#9820. The Rust decoder reads warnings across every result batch, including an empty first batch. Missing warning columns remain compatible with older servers; malformed or null warning lists are rejected. The hidden binding bridge carries the warnings to Python. The separate `drop_function_async` API and its `(bool, Job)` contract are unchanged. Python warning filters can raise after the catalog mutation has completed. Validation: a compiled PyO3/public-server reproduction failed before the fix and passed afterward; the public async path also returned `true` with the expected dependent-view warning. Final source passes two focused Rust tests, two Python wrapper tests, strict targeted Clippy, formatting, and Ruff. Three decoder tests cover batch/null/type and older-server behavior. The public runs preceded a two-file simplification of the completed Job projection; focused final-source checks cover that delta. Current-main integration is conflict-free, and its overlapping changes concern independent retry-policy APIs/tests. Documentation now covers warning filters, the post-removal warnings-as-errors behavior, and silent compatibility with older servers. The follow-up changes only prose and a Python docstring; the runtime validation above remains applicable. Refs ENT-3659. Whole-issue independent acceptance remains pending.
558 lines
23 KiB
Plaintext
558 lines
23 KiB
Plaintext
---
|
|
title: "Functions"
|
|
tag: "Early access"
|
|
sidebarTitle: "Function"
|
|
description: "Register a Python function once and apply it as a column, for work SQL cannot express."
|
|
icon: code
|
|
keywords: ["udf", "functions", "create_function", "embedding", "feature engineering"]
|
|
availability:
|
|
embedded: unavailable
|
|
enterprise: available
|
|
---
|
|
|
|
import {
|
|
PyFunctionDefine as FunctionDefine,
|
|
PyFunctionLocalCall as FunctionLocalCall,
|
|
PyFunctionCreateTable as FunctionCreateTable,
|
|
PyFunctionRegister as FunctionRegister,
|
|
PyFunctionApply as FunctionApply,
|
|
PyFunctionRefresh as FunctionRefresh,
|
|
PyFunctionInspect as FunctionInspect,
|
|
PyFunctionClassDefine as FunctionClassDefine,
|
|
PyFunctionClassRegister as FunctionClassRegister,
|
|
PyFunctionClassApply as FunctionClassApply,
|
|
} from '/snippets/function.mdx';
|
|
import { PyConnectEnterprisePlaceholders as FunctionConnect } from '/snippets/connection.mdx';
|
|
|
|
<Badge color="purple">Early access</Badge>
|
|
|
|
Some columns cannot be written as a SQL expression: an embedding, a model call,
|
|
an image decode. A function is a Python callable registered with the database,
|
|
versioned, and then applied as a column. The code runs where the data is
|
|
rather than pulling rows to your process.
|
|
|
|
Registering and running a function needs a LanceDB Enterprise deployment with
|
|
Functions enabled, which they are not by default. On a
|
|
LanceDB Embedded connection `create_function` raises `NotImplementedError`; to
|
|
derive a column there, declare a
|
|
[SQL computed column](/feature-engineering/add-computed-column) instead.
|
|
|
|
<Note>
|
|
In SQL, a function is registered with [`CREATE FUNCTION`](/sql/ddl/function#create) and listed with [`SHOW FUNCTIONS`](/sql/ddl/function#show).
|
|
</Note>
|
|
|
|
Functions are the way to fill columns with Python in LanceDB Enterprise. If you
|
|
already use [Geneva](/geneva), it remains available; see the Geneva docs for
|
|
how its concepts map onto these.
|
|
|
|
The steps below take a column of numbers to a filled column of their doubles.
|
|
|
|
## Define a function {#define}
|
|
|
|
`@function` packages a scalar Python callable. Input and output types are inferred
|
|
from annotations, so `double` takes one non-nullable `float64` parameter,
|
|
`value`, and returns a non-nullable `float64`:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionDefine}
|
|
</CodeBlock>
|
|
|
|
The decorated object stays callable. Calling it runs the original function in
|
|
your process, without the database, so unit tests stay ordinary tests:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionLocalCall}
|
|
</CodeBlock>
|
|
|
|
Where annotations cannot express the Arrow type precisely, pass `input_schema`
|
|
and `output_schema` together: one without the other is rejected. A scalar
|
|
output must be non-nullable, because a physical NULL is what marks a
|
|
computed-column row as unfilled. A named-struct output may declare nullable
|
|
fields: whether a row has a result at all is the struct's own validity, which
|
|
Enterprise keeps in a separate internal column when it expands the result into
|
|
sibling columns, so a null field is a value rather than a missing row.
|
|
|
|
## Connect and create a table {#table}
|
|
|
|
The remaining steps run against your Enterprise deployment. Replace the database
|
|
URI, API key, region and endpoint below with your deployment details. In application
|
|
code, load credentials from environment variables or a secrets manager:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionConnect}
|
|
</CodeBlock>
|
|
|
|
For an ordinary stored input column, the Arrow type must match the parameter.
|
|
A nullable column requires a nullable parameter, such as `Optional[float]`;
|
|
a non-nullable column can bind to either. This example uses `value: float`,
|
|
so the table declares `value` non-nullable.
|
|
|
|
Computed-column inputs depend on the Enterprise server version. Where supported,
|
|
they bind to non-nullable parameters even though the physical column is
|
|
nullable: refresh skips rows whose computed input is still null. Check your
|
|
deployment's support before using another computed column as an input.
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionCreateTable}
|
|
</CodeBlock>
|
|
|
|
## Register it {#register}
|
|
|
|
Registration returns a query-backed job; the immutable version exists once the
|
|
SQL command finishes and the job succeeds. Its `job_type` is `query`, and its
|
|
single `id` is the SQL query UUID. Repeated waits reuse the completed result.
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionRegister}
|
|
</CodeBlock>
|
|
|
|
`create_function` is the blocking equivalent. A registered version is fetched
|
|
with `db.get_function(name, version=...)`.
|
|
|
|
<Note>
|
|
The SQL command submits an internal `create_function` image-build job and
|
|
waits for publication. The SDK returns the SQL query handle. Query handles
|
|
have no registry history and cannot be reopened with `open_job()`.
|
|
</Note>
|
|
|
|
## List and describe {#list-describe}
|
|
|
|
`list_functions` pages through every published version in a namespace,
|
|
ordered by name then version; `get_function` opens one exact version by
|
|
name and `version`. Both take `namespace_path`, defaulting to the root
|
|
namespace, the same as `create_function`.
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
for version in db.list_functions():
|
|
print(version.name, version.version, version.created_at_millis)
|
|
|
|
version = db.get_function("double", version="1")
|
|
```
|
|
|
|
```rust
|
|
use futures::TryStreamExt;
|
|
|
|
# async fn list_and_get(connection: &lancedb::Connection) -> Result<(), Box<dyn std::error::Error>> {
|
|
let mut versions = connection.list_functions(&[], Default::default());
|
|
while let Some(version) = versions.try_next().await? {
|
|
println!("{} {} {}", version.name(), version.version(), version.created_at_millis());
|
|
}
|
|
|
|
let version = connection.get_function("double", "1", &[]).await?;
|
|
# Ok(())
|
|
# }
|
|
```
|
|
</CodeGroup>
|
|
|
|
<CodeGroup>
|
|
```sql SQL icon="database"
|
|
SHOW FUNCTIONS
|
|
SHOW FUNCTIONS IN "analytics" WITH (name = 'double', limit = 100)
|
|
|
|
DESCRIBE FUNCTION double WITH (version = '1')
|
|
```
|
|
</CodeGroup>
|
|
|
|
`list_functions` returns a lazy iterator: `page_limit` bounds each request,
|
|
not the total, and `page_token` resumes from a saved token. Child namespaces
|
|
are not included; list each namespace separately. A version's
|
|
`created_at_millis`, `metadata`, `disabled` flag, and `secret_bindings` are
|
|
all readable this way without applying or calling the function.
|
|
|
|
## Try it in SQL {#try-sql}
|
|
|
|
A registered function is callable in SQL by its name, the same as any other
|
|
scalar function. Before applying it as a column, call it directly over the
|
|
table to check the registration succeeded, without committing a schema
|
|
change:
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
reader = db.execute_query("SELECT value, double(value) AS doubled FROM function_demo")
|
|
print(reader.read_all().to_pylist())
|
|
# [{'value': 1.0, 'doubled': 2.0}, {'value': 2.0, 'doubled': 4.0}, {'value': 3.0, 'doubled': 6.0}]
|
|
```
|
|
</CodeGroup>
|
|
|
|
<Note>
|
|
Equivalent SQL: `SELECT value, double(value) AS doubled FROM function_demo`.
|
|
See [Querying with SQL](/basics/sql) for `execute_query` and other languages.
|
|
</Note>
|
|
|
|
## Apply it as a column {#apply}
|
|
|
|
Calling the registered version binds each parameter, by name, to a column with
|
|
`col(...)`. `add_columns` declares the result as a new column:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionApply}
|
|
</CodeBlock>
|
|
|
|
The column is declared empty; the function has not run yet. A mapping with one
|
|
function application keeps its scalar or named-struct result in the named
|
|
column. A bare named-struct application expands its result fields as one atomic
|
|
binding, with aliases from `rename(columns=...)`.
|
|
|
|
### Bind more than one input, or fill more than one column {#multi}
|
|
|
|
Parameters bind by name, so a function that takes several arguments binds
|
|
several columns in one call. `ratio` divides two existing columns, so give
|
|
the table a second one first:
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
table.add_columns({"count": "value + 1"})
|
|
|
|
@function
|
|
def ratio(numerator: float, denominator: float) -> float:
|
|
return numerator / denominator
|
|
|
|
version = db.create_function_async(ratio).wait()
|
|
table.add_columns({"ratio": version(numerator=col("value"), denominator=col("count"))})
|
|
```
|
|
</CodeGroup>
|
|
|
|
A named-struct output fills more than one column from a single application.
|
|
Declare the fields with `output_schema`, then alias each field to the column
|
|
it fills with `rename(columns=...)` and pass the application itself, not a
|
|
mapping, so both columns come from one atomic binding:
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
import pyarrow as pa
|
|
|
|
@function(
|
|
input_schema=pa.schema([pa.field("value", pa.float64(), nullable=False)]),
|
|
output_schema=pa.schema([
|
|
pa.field("half", pa.float64(), nullable=False),
|
|
pa.field("double", pa.float64(), nullable=False),
|
|
]),
|
|
)
|
|
def scale(value: float):
|
|
return {"half": value / 2, "double": value * 2}
|
|
|
|
version = db.create_function_async(scale).wait()
|
|
application = version(value=col("value")).rename(
|
|
columns={"half": "value_half", "double": "value_double"}
|
|
)
|
|
table.add_columns(application)
|
|
```
|
|
</CodeGroup>
|
|
|
|
<Note>
|
|
`input_schema` and `output_schema` must be given together on v0.41.0. A
|
|
version after it accepts either alone, inferring the other from the
|
|
annotations.
|
|
</Note>
|
|
|
|
A single refresh fills `value_half` and `value_double` together: there is no
|
|
state where one is filled and the other is still empty.
|
|
|
|
## Fill the column {#refresh}
|
|
|
|
A refresh runs the function on the cluster as a [job](/basics/jobs). `wait()`
|
|
blocks until the job finishes and returns its result, including how many rows
|
|
it filled:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionRefresh}
|
|
</CodeBlock>
|
|
|
|
The filled values are read like any other column:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionInspect}
|
|
</CodeBlock>
|
|
|
|
Filling then works as it does for a SQL computed column: a later refresh fills
|
|
rows added since the last one and recomputes rows whose inputs changed, and
|
|
leaves filled rows with unchanged inputs alone. See
|
|
[Refresh a column](/feature-engineering/refresh-column).
|
|
|
|
## Keep state in a class {#classes}
|
|
|
|
A function that loads a model or opens a client should not do it for every
|
|
row. Decorate a class instead: its `__init__` does the expensive setup once,
|
|
and `__call__` handles each row or batch.
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionClassDefine}
|
|
</CodeBlock>
|
|
|
|
Registering the class does not construct it. `@function` reads the signature of
|
|
`__init__` and packages the source, so the client never loads the model. On
|
|
the cluster, each instance of the function calls `__init__` once. It then calls
|
|
`__call__` for every row or batch it processes, and `close()`, if defined, once
|
|
when it retires. Import heavy packages inside `__init__`, as above, so only the
|
|
worker needs them installed, and declare them in `pip`.
|
|
|
|
State belongs to one instance. An instance reuses it across every batch it
|
|
serves, but instances do not share it, so each one holds its own copy of the
|
|
model. Plan memory, or a GPU, for one copy per instance.
|
|
|
|
Register it the same way as a plain function:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionClassRegister}
|
|
</CodeBlock>
|
|
|
|
The annotated parameters of `__init__` are the function's initialization
|
|
fields. Their values come from each binding rather than from the function, so
|
|
one registered version can run with different settings on different columns:
|
|
|
|
<CodeBlock filename="Python" language="Python" icon="python">
|
|
{FunctionClassApply}
|
|
</CodeBlock>
|
|
|
|
Pass the application to `add_columns` as [above](#apply). Initialization values
|
|
are fixed for every row of a binding and take booleans, integers, floats,
|
|
strings, and lists or structs of them. A parameter with a default may be
|
|
omitted. Initialization values are stored with the binding, so keep
|
|
credentials out of them: pass `secrets=[EnvVarSecret(...)]` to
|
|
`create_function` and read the variable in `__init__`.
|
|
|
|
Calling the decorated class in your own process constructs it, the same as an
|
|
undecorated class, so local tests can create an instance and call it directly.
|
|
|
|
## Blob v2 columns {#blobs}
|
|
|
|
A Function can bind a [Blob v2 column](/management/table#managed-blobs) as
|
|
input and return another. In row mode, a top-level blob parameter arrives as
|
|
Python `bytes`, or `None` for a nullable input, after the worker has read
|
|
the payload; annotate it `BlobReader` instead for seekable, incremental
|
|
reads that avoid materializing the whole payload first. Return bytes and let
|
|
the destination table store the Managed payload and descriptor --
|
|
`output_schema=lancedb.blob(...)` is what makes the result Blob v2 rather
|
|
than plain Arrow `binary`; a bare annotation persists as ordinary binary
|
|
instead, skipping the Managed storage path and `fetch_blobs`.
|
|
|
|
See [Read and produce blobs](/curation/create-function#blobs) for a full
|
|
example: extracting a frame from a stored video, chaining a second Function
|
|
onto its output, persisting both as columns or a materialized view, and
|
|
reading a blob input incrementally.
|
|
|
|
## Declare the environment {#environment}
|
|
|
|
The function runs on the Enterprise service, not in your application, so its
|
|
dependencies are part of its definition:
|
|
|
|
| | |
|
|
| --- | --- |
|
|
| `pip` | requirements for the function's execution environment |
|
|
| `conda`, `conda_channels` | conda packages instead of pip; channels in priority order |
|
|
| `env` | environment variables |
|
|
| `python_version` | Python major/minor for the execution environment, defaulting to yours |
|
|
| `gpu` | whether every execution requires a GPU |
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
@function(gpu=True)
|
|
def extract_frame(video: BlobReader, ts: float) -> bytes:
|
|
... # runs only on a GPU-capable worker
|
|
```
|
|
</CodeGroup>
|
|
|
|
These are part of the **immutable function version**. Changing a dependency
|
|
produces a new version rather than altering the one already registered, so a
|
|
column filled by version 1 is unaffected by version 2.
|
|
|
|
Declaring them is not optional bookkeeping. If the worker environment differs
|
|
from the one the function was written against, the failures are among the
|
|
hardest to diagnose in the system, because the error rarely names the cause.
|
|
|
|
### Symptoms of an environment mismatch {#mismatch-symptoms}
|
|
|
|
| Symptom | Likely cause |
|
|
| --- | --- |
|
|
| `TypeError: Enum.__new__() missing 1 required positional argument` | `attrs` version mismatch |
|
|
| `TypeError: Can't instantiate abstract class` | package structure differs between versions |
|
|
| `ArrowInvalid: cannot cast`, or serialization errors | NumPy 1.x against 2.x |
|
|
| `ModuleNotFoundError` on the worker | package installed locally but not declared |
|
|
| Model loading failures | PyTorch version mismatch |
|
|
| Permission denied | an API key present locally but absent from `env` |
|
|
|
|
The packages worth pinning explicitly, because a mismatch in them produces the
|
|
errors above rather than a clear one: **numpy, torch, pyarrow, attrs,
|
|
pydantic**.
|
|
|
|
<Note>
|
|
The client version defaults `python_version`, so a function registered from a
|
|
3.11 environment runs on 3.11 unless you say otherwise. That default is usually
|
|
right and occasionally surprising. Pin it when the function is sensitive to it.
|
|
</Note>
|
|
|
|
## Remote calls and credentials {#secrets}
|
|
|
|
A function that calls an external API needs a credential without putting it
|
|
in the function's source or its `env` map in plain text. A Secret is a
|
|
database-scoped named credential; `db.create_secret` stores the value once,
|
|
and an `EnvVarSecret` binds it to the environment variable the function
|
|
reads. Binding is a registration-time decision, passed to
|
|
`create_function_async`, not to `@function`. No API returns a Secret's value,
|
|
and it never appears in the function's registration: the function reads the
|
|
environment variable the way it always did, and the binding is what puts a
|
|
value there. A mistyped Secret name is caught at registration; a mistyped
|
|
environment variable name surfaces only on the first rows, since nothing
|
|
records which variables a function reads.
|
|
|
|
See [Vend a Secret to a Function](/curation/vend-secret#specify) for the
|
|
full worked example.
|
|
|
|
## Failure handling {#failures}
|
|
|
|
By default, a Function that raises fails the refresh job. Choose how the
|
|
refresh handles row errors with its error policy:
|
|
|
|
<CodeGroup>
|
|
```sql SQL icon="database"
|
|
REFRESH COLUMN embedding ON documents WITH (error_policy = 'skip')
|
|
```
|
|
</CodeGroup>
|
|
|
|
`skip` writes null outputs for failing rows and records their errors. Inspect
|
|
them with `SHOW ERRORS` or `table.function_errors()`. Failure limits still
|
|
apply, and initialization or infrastructure failures can still fail the job.
|
|
See the [SQL refresh options](/sql/dml/refresh-column#options). A Function
|
|
that calls a transiently failing external service can handle bounded retries
|
|
inside its body.
|
|
|
|
Letting the job fail is the other option: fix the cause and refresh again. The
|
|
next unfiltered refresh [skips rows that are filled and current](/feature-engineering/refresh-column#idempotent),
|
|
but how much of the failed job's work was kept is not guaranteed, so do not
|
|
count on the retry costing only the rows that did not complete. A refresh with
|
|
`WHERE` recomputes every matching live row, including already-filled outputs.
|
|
|
|
{/*
|
|
Draft for the Function retry policy release.
|
|
|
|
### Retry transient errors
|
|
|
|
Native retries require an SDK that exposes `FunctionRetryPolicy` and a
|
|
LanceDB Enterprise server that supports Function retry policies. Upgrading
|
|
the SDK alone does not enable retries on an older server. If a refresh
|
|
override reports that the Function image does not support retry policies,
|
|
re-register the Function on a supporting server.
|
|
|
|
Import `FunctionRetryPolicy` from `lancedb.functions` and pass a policy to
|
|
`@function` through `retry_policy`. For example,
|
|
`FunctionRetryPolicy(transient=True, max_attempts=3)` allows up to three
|
|
calls for matching transient failures. `max_attempts` includes the first
|
|
call; setting it to `1` disables user-error retries. A
|
|
[refresh override](/feature-engineering/refresh-column#resources) replaces
|
|
the registered policy for that refresh.
|
|
|
|
Enable retries only when repeating a failed call's partial work is safe.
|
|
An external request may have succeeded before the Function raised, so this
|
|
policy does not guarantee exactly-once external effects. Functions declared
|
|
with external side effects reject retry policies. Calls that return
|
|
successfully are not repeated by this policy; infrastructure recovery and
|
|
later refreshes have separate replay behavior.
|
|
|
|
Each failed callable invocation has one attempt budget. After exhaustion,
|
|
the refresh's `error_policy` applies. With an explicit retry policy, `skip`
|
|
on a batch Function writes nulls for every row in the failed call and records
|
|
their errors. It does not split that call into smaller retries with new
|
|
budgets. A row Function handles each row separately. Error records expose
|
|
`attempts` when the server records it; older records may omit the count.
|
|
|
|
Worker recovery uses a separate retry budget. Framework deadlines and
|
|
cancellation stop execution without consuming further user-error retries.
|
|
*/}
|
|
|
|
## What gets packaged {#packaging}
|
|
|
|
<Warning>
|
|
The artifact is a **snapshot**: the function source, plus exactly the
|
|
module-level names it references (modules as imports, importable classes and
|
|
functions as imports, literals inlined).
|
|
|
|
Code that reaches the module namespace another way is rejected where it can be
|
|
seen and unsupported where it cannot: `globals()`, `eval`, `sys.modules`,
|
|
`builtins`. Closures and a non-standard `__builtins__` are rejected outright.
|
|
</Warning>
|
|
|
|
In practice this means a function should be self-contained: take its inputs as
|
|
arguments, import what it needs at module level, and avoid capturing state from
|
|
the surrounding scope.
|
|
|
|
Pass `code=[module, ...]` to ship additional helper modules into the same
|
|
image, for logic the function imports but that does not belong inlined into
|
|
it -- a model-loading helper shared by several Functions, for example.
|
|
Continuing the [`caption` Function](/curation/create-function#chain), with
|
|
its model-loading code factored into a sibling module instead of inlined:
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
import caption_worker # a sibling module with the model-loading logic
|
|
|
|
@function(gpu=True, code=[caption_worker])
|
|
def caption(jpeg: bytes) -> str:
|
|
return caption_worker.run(jpeg)
|
|
```
|
|
</CodeGroup>
|
|
|
|
Each module in `code` is captured and shipped the same way the function
|
|
itself is: as source, snapshotted at registration. Only `.py` source files are
|
|
included; package data such as JSON, YAML, prompt, or tokenizer files is not
|
|
copied. `@function` warns before registration when it skips such files from a
|
|
package in `code`, naming the affected paths. To use a package that includes
|
|
the required resources, install it with `pip=` or `conda=` and remove that
|
|
package from `code`; otherwise the Python-only copied package shadows the
|
|
installed distribution.
|
|
|
|
## Drop it {#drop}
|
|
|
|
<CodeGroup>
|
|
```python Python icon="python"
|
|
db.drop_function("double", version="1")
|
|
```
|
|
|
|
```rust
|
|
# async fn drop_it(connection: &lancedb::Connection) -> Result<(), Box<dyn std::error::Error>> {
|
|
let dropped = connection.drop_function("double", "1", &[]).await?;
|
|
# Ok(())
|
|
# }
|
|
```
|
|
|
|
```sql SQL icon="database"
|
|
DROP FUNCTION double WITH (version = '1')
|
|
```
|
|
</CodeGroup>
|
|
|
|
`db.drop_function(name, version=...)` removes only that published revision from
|
|
lookup and listing, preserving other revisions and their grants. It returns
|
|
`False` for a repeated drop or a name that is already gone. A revision never
|
|
published under an existing name is an error. The last visible revision's drop
|
|
releases the name and schedules object cleanup, which can finish after the call
|
|
returns as a [background job](/basics/jobs#teardown).
|
|
`drop_function_async` returns `(dropped, job)`, where `job` is a query-backed
|
|
handle. Waiting confirms the revision's catalog removal; physical cleanup can
|
|
continue afterward.
|
|
|
|
When a deployment reports dependency warnings for a boolean drop,
|
|
`db.drop_function` emits each warning as a `UserWarning`. This also applies to
|
|
`await db.drop_function(...)` on an async connection. The warnings are emitted
|
|
after catalog removal, and standard Python warnings filters apply. If warnings
|
|
are configured as errors, the call can raise after the revision has been
|
|
removed; the drop is not rolled back. Servers whose DROP response omits the
|
|
warning field remain silent.
|
|
|
|
A deployment refuses the drop unless it also allows changes to a function's
|
|
lifecycle, which is a separate setting, off by default even where Functions are
|
|
enabled.
|
|
|
|
## What to read next {#next}
|
|
|
|
<CardGroup cols={2}>
|
|
<Card title="Create a Function" icon="code" href="/curation/create-function">
|
|
Scalar, batch, stateful, GPU and remote-service examples for common curation tasks.
|
|
</Card>
|
|
<Card title="Computed columns" icon="table-columns" href="/feature-engineering/refresh-column">
|
|
Refresh semantics, and the SQL alternative.
|
|
</Card>
|
|
<Card title="Embedding functions" icon="vector-square" href="/embedding">
|
|
Built-in embedding providers, which need no registration.
|
|
</Card>
|
|
</CardGroup>
|