Prepared statements

Prepared statements are the final step for taking features to production. They pre-compile a feature and its entire dependency graph — including all data source calls and cross-source joins — into a single optimized query. This eliminates parsing and resolution overhead at request time, giving you predictable latency for real-time serving.

Defining the base features

A prepared statement wraps an existing feature. Start by creating the features you want to serve:

FeatureQL
CREATE OR REPLACE FEATURES IN FM.TUTORIALS.PREPARED_STATEMENTS AS
SELECT
    FEATURE_A := INPUT(BIGINT),
    FEATURE_B := INPUT(BIGINT),
    FEATURE_C := INPUT(BIGINT),
    FEATURE_Q := FEATURE_A + 1,
    FEATURE_R := FEATURE_A + FEATURE_B + FEATURE_C
;
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_ACREATEDFeature created as not exists
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_BCREATEDFeature created as not exists
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_CCREATEDFeature created as not exists
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_QCREATEDFeature created as not exists
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_RCREATEDFeature created as not exists

Here, FEATURE_Q has one input, while FEATURE_R combines three inputs. A prepared statement resolves the selected feature's entire dependency chain at compile time.

In a real deployment, these features would typically include EXTERNAL_REDIS(), EXTERNAL_COLUMNS(), or EXTERNAL_HTTP() calls — the prepared statement compiles all of that into a single execution plan.

Creating the prepared statement

Use PREPARED_STATEMENT() to compile a feature for real-time serving:

FeatureQL
CREATE OR REPLACE FEATURE FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS AS
PREPARED_STATEMENT(
    FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q
    USING FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_A
);
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PSCREATEDFeature created as not exists

The USING clause declares the input tables. The single-input statement above produces one input table. To create multiple tables, group inputs that must be paired row-by-row and list independent inputs separately:

FeatureQL
CREATE OR REPLACE FEATURE FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PS AS
PREPARED_STATEMENT(
    FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R
    USING
        (FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_A, FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_B),
        FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_C
);
Result
feature_name VARCHARstatus VARCHARmessage VARCHAR
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PSCREATEDFeature created as not exists

(FEATURE_A, FEATURE_B) becomes input_table_1, while FEATURE_C becomes input_table_2. The tables are combined with a cross product during execution.

You can inspect which persisted features are prepared statements:

FeatureQL
SHOW FEATURES (COLUMNS (NAME, FUNCTION))
WHERE NAME IN (
    'FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS',
    'FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PS'
);
Result
NAME VARCHARFUNCTION VARCHAR
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PSPREPARED_STATEMENT
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PSPREPARED_STATEMENT

Refresh before serving

Persisting a prepared statement updates the registry. REFRESH FEATURES synchronously installs the latest definition in a local, development serving process:

FeatureQL
REFRESH FEATURES
    FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS,
    FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PS
;
Result
FEATURE VARCHARKIND VARCHARSTATUS VARCHARMESSAGE VARCHAR
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PSPREPARED_STATEMENTREFRESHED(empty)
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PSPREPARED_STATEMENTREFRESHED(empty)

The command accepts an explicit list only. It is disabled by default and should not be enabled in hosted or multi-pod deployments, where refresh requires cluster-wide coordination.

Call a prepared statement

From Python

from featuremesh import ServingClient

client = ServingClient(access_token="YOUR_ACCESS_TOKEN")
result = client.execute_prepared_statement(
    "FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS",
    {"input_table_1": [[1], [5]]},
)
python

The same call is executable in the documentation SLT:

Prepared
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS
Call 1
{"input_table_1": [[1], [5]]}
FEATURE_A BIGINTFEATURE_Q_PS BIGINT
12
56

One JSON object represents one serving request. Multiple objects make independent calls, and numbered expected groups keep each response separate:

Prepared
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_Q_PS
Call 1
{"input_table_1": [[2], [1]]}
FEATURE_A BIGINTFEATURE_Q_PS BIGINT
12
23
Call 2
{"input_table_1": [[10]]}
FEATURE_A BIGINTFEATURE_Q_PS BIGINT
1011

For a statement with two input tables, supply both tables in the same object:

Prepared
FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PS
Call 1
{"input_table_1": [[1, 2], [4, 5]], "input_table_2": [[10]]}
FEATURE_A BIGINTFEATURE_B BIGINTFEATURE_C BIGINTFEATURE_R_PS BIGINT
121013
451019

From curl

curl -X POST "http://localhost:10090/v1/featureql" \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer YOUR_ACCESS_TOKEN" \
  -d '{
    "statement": {
      "id": "FM.TUTORIALS.PREPARED_STATEMENTS.FEATURE_R_PS",
      "inputs": {
        "input_table_1": [[1, 2], [4, 5]],
        "input_table_2": [[10]]
      }
    }
  }'
bash

The inputs object maps generated input-table names to rows. The order of values in each row follows the order declared in its USING group.