Skip to content
2 changes: 1 addition & 1 deletion apps/dev-playground/shared/appkit-types/analytics.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@ declare module "@databricks/appkit-ui/react" {
parameters: {
/** STRING - use sql.string() */
stringParam: SQLStringMarker;
/** NUMERIC - use sql.number() */
/** NUMERIC - use sql.numeric() */
numberParam: SQLNumberMarker;
/** BOOLEAN - use sql.boolean() */
booleanParam: SQLBooleanMarker;
Expand Down
220 changes: 212 additions & 8 deletions docs/docs/api/appkit/Variable.sql.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,10 +2,25 @@

```ts
const sql: {
bigint: SQLNumberMarker & {
__sql_type: "BIGINT";
};
binary: SQLBinaryMarker;
boolean: SQLBooleanMarker;
date: SQLDateMarker;
double: SQLNumberMarker & {
__sql_type: "DOUBLE";
};
float: SQLNumberMarker & {
__sql_type: "FLOAT";
};
int: SQLNumberMarker & {
__sql_type: "INT";
};
number: SQLNumberMarker;
numeric: SQLNumberMarker & {
__sql_type: "NUMERIC";
};
string: SQLStringMarker;
timestamp: SQLTimestampMarker;
};
Expand All @@ -15,6 +30,43 @@ SQL helper namespace

## Type Declaration

### bigint()

```ts
bigint(value: string | number | bigint): SQLNumberMarker & {
__sql_type: "BIGINT";
};
```

Creates a `BIGINT` (64-bit signed integer) parameter. Accepts JS
`bigint` so callers can round-trip values outside `Number.MAX_SAFE_INTEGER`
without precision loss; for `number` inputs, requires
`Number.isSafeInteger(value)`.

Rejects values outside the signed 64-bit range `[-2^63, 2^63 - 1]`.

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `value` | `string` \| `number` \| `bigint` | Integer number, bigint, or integer-shaped string |

#### Returns

`SQLNumberMarker` & \{
`__sql_type`: `"BIGINT"`;
\}

Marker pinned to `BIGINT`

#### Example

```typescript
sql.bigint(42); // { __sql_type: "BIGINT", value: "42" }
sql.bigint(9007199254740993n); // { __sql_type: "BIGINT", value: "9007199254740993" }
sql.bigint("9007199254740993"); // { __sql_type: "BIGINT", value: "9007199254740993" }
```

### binary()

```ts
Expand Down Expand Up @@ -134,14 +186,133 @@ const params = { startDate: sql.date("2024-01-01") };
params = { startDate: "2024-01-01" }
```

### double()

```ts
double(value: string | number): SQLNumberMarker & {
__sql_type: "DOUBLE";
};
```

Creates a `DOUBLE` (double-precision, 64-bit) parameter. Same precision
as a JS `number`, so `sql.double(value)` is exact for any JS number.

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `value` | `string` \| `number` | Number or numeric string |

#### Returns

`SQLNumberMarker` & \{
`__sql_type`: `"DOUBLE"`;
\}

Marker pinned to `DOUBLE`

#### Example

```typescript
sql.double(3.14); // { __sql_type: "DOUBLE", value: "3.14" }
```

### float()

```ts
float(value: string | number): SQLNumberMarker & {
__sql_type: "FLOAT";
};
```

Creates a `FLOAT` (single-precision, 32-bit) parameter. Note that JS
numbers are 64-bit doubles, so values may be rounded to fit FLOAT
precision at bind time.

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `value` | `string` \| `number` | Number or numeric string |

#### Returns

`SQLNumberMarker` & \{
`__sql_type`: `"FLOAT"`;
\}

Marker pinned to `FLOAT`

#### Example

```typescript
sql.float(3.14); // { __sql_type: "FLOAT", value: "3.14" }
```

### int()

```ts
int(value: string | number): SQLNumberMarker & {
__sql_type: "INT";
};
```

Creates an `INT` (32-bit signed integer) parameter. Use when the column
or context requires `INT` specifically (e.g. legacy schemas, or to make
the wire type explicit).

Rejects non-integers, values outside `Number.MAX_SAFE_INTEGER` (for
number inputs), and values outside the signed 32-bit range
`[-2^31, 2^31 - 1]`.

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `value` | `string` \| `number` | Integer number or integer-shaped string |

#### Returns

`SQLNumberMarker` & \{
`__sql_type`: `"INT"`;
\}

Marker pinned to `INT`

#### Example

```typescript
sql.int(42); // { __sql_type: "INT", value: "42" }
sql.int("42"); // { __sql_type: "INT", value: "42" }
```

### number()

```ts
number(value: string | number): SQLNumberMarker;
```

Creates a NUMERIC type parameter
Accepts numbers or numeric strings
Creates a numeric type parameter. The wire SQL type is inferred from the
value so the parameter binds correctly in any context, including `LIMIT`
and `OFFSET`:

- JS integer in `[-2^31, 2^31 - 1]` → `INT`
- JS integer outside `INT` but within `Number.MAX_SAFE_INTEGER` → `BIGINT`
- JS non-integer (`3.14`) → `DOUBLE`
- integer-shaped string in `INT` range → `INT` (common HTTP-input case)
- integer-shaped string outside `INT` but within `BIGINT` → `BIGINT`
- decimal-shaped string (`"123.45"`) → `NUMERIC` (preserves precision)

Why default to `INT`? Spark's `LIMIT` and `OFFSET` operators require
`IntegerType` specifically — `BIGINT` (`LongType`) is rejected with
`INVALID_LIMIT_LIKE_EXPRESSION.DATA_TYPE`. Catalyst auto-widens `INT`
to `BIGINT` / `DECIMAL` / `DOUBLE` for wider columns, so `INT` is a
strictly better default than `BIGINT`.

Throws on `NaN`, `Infinity`, JS integers outside `Number.MAX_SAFE_INTEGER`,
integer-shaped strings outside the `BIGINT` range, or non-numeric strings.
Reach for `sql.int()`, `sql.bigint()`, `sql.float()`, `sql.double()`, or
`sql.numeric()` to override the inferred type.

#### Parameters

Expand All @@ -153,18 +324,51 @@ Accepts numbers or numeric strings

`SQLNumberMarker`

Marker object for NUMERIC type parameter
Marker for a numeric SQL parameter

#### Examples
#### Example

```typescript
const params = { userId: sql.number(123) };
params = { userId: "123" }
sql.number(123); // { __sql_type: "INT", value: "123" }
sql.number(3_000_000_000); // { __sql_type: "BIGINT", value: "3000000000" }
sql.number(0.5); // { __sql_type: "DOUBLE", value: "0.5" }
sql.number("10"); // { __sql_type: "INT", value: "10" }
sql.number("123.45"); // { __sql_type: "NUMERIC", value: "123.45" }
```

### numeric()

```ts
numeric(value: string | number): SQLNumberMarker & {
__sql_type: "NUMERIC";
};
```

Creates a `NUMERIC` (fixed-point DECIMAL) parameter. Use when you need
exact decimal arithmetic (currency, percentages) — pass values as
strings to avoid JS-number precision loss.

Note: passing a JS `number` is accepted but lossy for many values
(e.g. `0.1 + 0.2` → `"0.30000000000000004"`). Prefer strings.

#### Parameters

| Parameter | Type | Description |
| ------ | ------ | ------ |
| `value` | `string` \| `number` | Number or numeric string (strings preferred for precision) |

#### Returns

`SQLNumberMarker` & \{
`__sql_type`: `"NUMERIC"`;
\}

Marker pinned to `NUMERIC`

#### Example

```typescript
const params = { userId: sql.number("123") };
params = { userId: "123" }
sql.numeric("12345.6789"); // { __sql_type: "NUMERIC", value: "12345.6789" }
```

### string()
Expand Down
13 changes: 11 additions & 2 deletions docs/docs/plugins/analytics.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,14 +43,23 @@ Use `:paramName` placeholders and optionally annotate parameter types using SQL
```sql
-- @param startDate DATE
-- @param endDate DATE
-- @param limit NUMERIC
-- @param limit INT
SELECT ...
WHERE usage_date BETWEEN :startDate AND :endDate
LIMIT :limit
```

`LIMIT` / `OFFSET` require Spark `IntegerType` specifically — `BIGINT`
(`LongType`) is rejected with `INVALID_LIMIT_LIKE_EXPRESSION.DATA_TYPE`.
Annotate with `INT`, or use `sql.number()` (auto-infers `INT` for values in
`[-2^31, 2^31-1]`, falling back to `BIGINT` for wider values) / `sql.int()`
at the call site.

**Supported `-- @param` types** (case-insensitive):
- `STRING`, `NUMERIC`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `BINARY`
- `STRING`, `BOOLEAN`, `DATE`, `TIMESTAMP`, `BINARY`
- `INT`, `BIGINT`, `TINYINT`, `SMALLINT` — bind via `sql.int()` / `sql.bigint()`
- `FLOAT`, `DOUBLE` — bind via `sql.float()` / `sql.double()`
- `NUMERIC`, `DECIMAL` — bind via `sql.numeric()` (pass strings for precision)

## Server-injected parameters

Expand Down
Loading
Loading