Hi Weihao, I think this is a great feature. It reminds me of the influxdb3_local API provided by the Python plugin interface in the Processing Engine introduced in InfluxDB 3.0[1].
With IoTDBLocal, it becomes much more convenient when we write customized UDFs for customers and need to perform secondary queries or lookups inside the UDF. Without this kind of internal query interface, we would otherwise have to use an external client and hard-code the username and password in the UDF, which is very unsafe. Also, once the password changes, we would have to recompile and redeploy the UDF. So I think this capability is very valuable for real-world UDF development. 我觉得这个特性很棒。它和 InfluxDB 3.0 中 Processing Engine 的 Python plugin 接口提供的 influxdb3_local 很类似。 有了 IoTDBLocal 之后,当我们为客户编写定制 UDF,并且需要在 UDF 内部做二次查询或查询补充信息时,会方便很多。否则我们就需要在 UDF 中使用外部 client,并把用户名和密码硬编码进去,这是一种很不安全的做法。而且一旦密码发生变化,还需要重新编译和重新部署 UDF。 所以我认为这个能力对于实际的 UDF 开发场景非常有价值。 [1] https://docs.influxdata.com/influxdb3/core/plugins/ Best regards, ---------------------- Yuan Tian On Thu, 2 Jul 2026 11:15:04 +0800 (CST), Weihao Li [email protected] wrote: Hi all, I’d like to share a new capability introduced by PR #18035: https://github.com/apache/iotdb/pull/18035 This PR adds IoTDBLocal, a framework-injected API for table-model UDFs (scalar functions, aggregate functions, and table functions). With it, UDF authors can execute read-only embedded SQL directly inside the server during UDF lifecycle hooks — without pulling in an external IoTDB client or opening a separate network connection. And it is compatible with previous versions, the UDF of old version can still run normally. Following are examples: Example 1 — Preload an id to device-name mapping once using query interface of IoTDBLocal, to be used in later process: // The beforeStart method of a scalar UDF @Override public void beforeStart(FunctionArguments arguments, IoTDBLocal local) throws UDFException { local.info("DeviceNameFunction: loading device_info"); Map<String, String> map = new HashMap<>(); try (UDFResultSet rs = local.query("SELECT device_id, device_name FROM device_info")) { while (rs.hasNext()) { Record row = rs.next(); map.put(row.getString(0), row.getString(1)); } } idToName = map; } 、、、 Example2 — Write server log using info / warn / error interfaces of IoTDBLocal: // The evaluate method of a scalar UDF @Override public Object evaluate(Record input, IoTDBLocal local) { if (!firstRowLogged) { firstRowLogged = true; local.info(“MyUdf: start processing, first device_id={}”, input.getString(0)); } if (input.isNull(1)) { local.error(“MyUdf: temperature is null, device_id={}”, input.getString(0)); return null; } double temp = input.getDouble(1); if (temp > 30.0) { local.warn(“MyUdf: high temperature detected, device_id={}, temp={}”, input.getString(0), temp); } return buildResult(input); } Yours, Weihao Li
