Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions plugins/exasol/skills/exasol-database/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,10 @@ After the connection is established, determine the task type and load **only** t
Multiple routes can apply — load all that match.

8. **Before writing any SQL** (applies to routes 2–7):
- Check all identifiers (column names, table names, aliases) against the **reserved keyword list in `references/exasol-sql.md`** (Common Traps section)
- Double-quote any identifier that appears in that list
- If a query fails with a syntax error that may be caused by a reserved keyword, fetch the live list: `exapump sql "SELECT KEYWORD FROM EXA_SQL_KEYWORDS WHERE RESERVED ORDER BY KEYWORD"`
- This is critical — Exasol reserves many common words (e.g., `YEAR`, `PROFILE`, `FILE`, `POSITION`) that are unreserved in other databases
- **Always double-quote every identifier** (column names, table names, schema names) in SELECT, FROM, WHERE, GROUP BY, ORDER BY, and JOIN clauses — without exception
- This preserves mixed-case names and prevents reserved-keyword errors in a single rule
- Do NOT quote SQL keywords, functions, or aliases — only object identifiers
- If a query fails with a syntax error, fetch the live reserved keyword list: `exapump sql "SELECT KEYWORD FROM EXA_SQL_KEYWORDS WHERE RESERVED ORDER BY KEYWORD"`

## Related Skills

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ SELECT "myColumn" FROM "myTable"; -- works
SELECT mycolumn FROM myTable; -- ERROR: "MYTABLE" not found
```

**Rule of thumb:** Don't use quoted identifiers unless you have a specific reason. Let everything be uppercase.
**Rule of thumb:** Never use quoted identifiers in DDL statements. Always double-quote **every** identifier in SELECT (and all DML) statements — columns, tables, schemas — unconditionally, not just reserved words.

---

Expand Down
53 changes: 52 additions & 1 deletion plugins/exasol/skills/exasol-udfs/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ description: "Exasol User Defined Functions (UDFs) and Script Language Container

# Exasol UDFs & Script Language Containers

Trigger when the user mentions **UDF**, **user defined function**, **CREATE SCRIPT**, **ExaIterator**, **SCALAR**, **SET EMITS**, **BucketFS**, **script language container**, **SLC**, **exaslct**, **custom packages**, **GPU UDF**, **ctx.emit**, **ctx.next**, or any UDF/SLC-related topic.
Trigger when the user mentions **UDF**, **user defined function**, **CREATE SCRIPT**, **ExaIterator**, **SCALAR**, **SET EMITS**, **BucketFS**, **script language container**, **SLC**, **exaslct**, **custom packages**, **GPU UDF**, **ctx.emit**, **ctx.next**, **variadic script**, **dynamic parameters**, **EMITS(...)**, **default_output_columns**, or any UDF/SLC-related topic.

## When to Use UDFs

Expand Down Expand Up @@ -135,6 +135,57 @@ run <- function(ctx) {
/
```

## Variadic Scripts (Dynamic Parameters)

Use `...` to accept any number of input columns, output columns, or both.

### Dynamic Input

```sql
CREATE OR REPLACE PYTHON3 SCALAR SCRIPT schema.to_json(...) RETURNS VARCHAR(2000000) AS
import simplejson
def run(ctx):
obj = {}
for i in range(0, exa.meta.input_column_count, 2):
obj[ctx[i]] = ctx[i+1] # caller passes: name, value, name, value, ...
return simplejson.dumps(obj)
/

SELECT to_json('fruit', fruit, 'price', price) FROM products;
```

- Access by index: `ctx[i]` — **0-based in Python/Java, 1-based in Lua/R**
- Parameter names inside a variadic script are always `0`, `1`, `2`, ... — never the original column names
- `exa.meta.input_column_count` — total number of input columns
- `exa.meta.input_columns[i].name / .sql_type` — per-column metadata

### Dynamic Output (`EMITS(...)`)

Declare `EMITS(...)` in `CREATE SCRIPT`. At call time, columns must be provided one of two ways:

| Method | Where specified | Use when |
|--------|----------------|----------|
| **EMITS in SELECT** | Caller's SQL query | Output structure depends on data values |
| **`default_output_columns()`** | Script body | Output structure derivable from input column count/types alone |

```sql
-- EMITS in SELECT (required when output depends on data content)
SELECT split_csv(line) EMITS (a VARCHAR(100), b VARCHAR(100), c VARCHAR(100)) FROM t;
```

```python
# default_output_columns() — called before run(), no ctx/data access available
def default_output_columns():
parts = []
for i in range(exa.meta.input_column_count):
parts.append("c" + exa.meta.input_columns[i].name + " " + exa.meta.input_columns[i].sql_type)
return ",".join(parts)
```

If neither is provided, the query fails with:
> *The script has dynamic return arguments. Either specify the return arguments in the query via EMITS or implement the method default_output_columns in the UDF.*


## ExaIterator API Quick Reference

### Python
Expand Down