{"id":23497,"date":"2025-01-02T00:22:01","date_gmt":"2025-01-02T08:22:01","guid":{"rendered":"https:\/\/tdengine.com\/?p=23497"},"modified":"2025-03-30T23:29:10","modified_gmt":"2025-03-31T06:29:10","slug":"unlocking-database-customization-a-comprehensive-guide-to-python-udfs-in-tdengine","status":"publish","type":"post","link":"https:\/\/tdengine.com\/unlocking-database-customization-a-comprehensive-guide-to-python-udfs-in-tdengine\/","title":{"rendered":"Mastering Python UDFs in TDengine: From Beginner to Pro"},"content":{"rendered":"\n<p>With the release of TDengine 3.0.4.0, we unveiled an exciting new feature: the ability to create User-Defined Functions (UDFs) using Python. This innovative capability delivers unparalleled flexibility for database operations while making customization more accessible\u2014even for those new to programming. With Python UDFs, users can easily tailor and manage their databases, seamlessly integrating these custom functions into SQL queries as if they were built-in. In this article, we&#8217;ll explore how to leverage this feature effectively and help you take the first steps toward creating your own database customizations.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Getting Started with UDFs<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Creating a <\/strong><strong>UDF<\/strong><\/h3>\n\n\n\n<p>Here&#8217;s how to create a Python UDF function in TDengine:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">       CREATE  &#091;OR REPLACE] &#091;AGGREGATE] FUNCTION function_name\n                               as library_path  OUTPUTTYPE output_type  &#091;BUFSIZE buffer_size] &#091;LANGUAGE &#039;C\u2018 | &#039;Python&#039;]<\/code><\/pre>\n\n\n\n<p><strong>Options Explained:<\/strong><\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong><code class=\"\" data-line=\"\">CREATE [OR REPLACE]<\/code><\/strong>: Use <code class=\"\" data-line=\"\">CREATE<\/code> for new functions and add <code class=\"\" data-line=\"\">OR REPLACE<\/code> to update existing ones.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">AGGREGATE<\/code><\/strong>: Optional. Indicates an aggregate function. Without it, the function defaults to a scalar function.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">function_name<\/code><\/strong>: The name of the UDF, which can be up to 64 bytes long. Names exceeding this limit will be truncated.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">OUTPUTTYPE<\/code><\/strong>: Specifies the output data type of the UDF. Supported types include:<\/li>\n<\/ol>\n\n\n\n<table id=\"tablepress-74\" class=\"tablepress tablepress-id-74\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Serial Number<\/th><th class=\"column-2\">Supported Data Type<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-striping row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">1<\/td><td class=\"column-2\">TIMESTAMP<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">2<\/td><td class=\"column-2\">INT<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">3<\/td><td class=\"column-2\">INT UNSIGNED<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">4<\/td><td class=\"column-2\">BIGINT<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">5<\/td><td class=\"column-2\">BIGINT UNSIGNED<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">6<\/td><td class=\"column-2\">FLOAT<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">7<\/td><td class=\"column-2\">DOUBLE<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">8<\/td><td class=\"column-2\">BINARY<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">9<\/td><td class=\"column-2\">SMALLINT<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">10<\/td><td class=\"column-2\">SMALLINT UNSIGNED<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">11<\/td><td class=\"column-2\">TINYINT<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">12<\/td><td class=\"column-2\">TINYINT UNSIGNED<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">13<\/td><td class=\"column-2\">BOOL<\/td>\n<\/tr>\n<tr class=\"row-15\">\n\t<td class=\"column-1\">14<\/td><td class=\"column-2\">NCHAR<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-74 from cache -->\n\n\n<ol start=\"5\" class=\"wp-block-list\">\n<li><strong><code class=\"\" data-line=\"\">BUFSIZE<\/code><\/strong>: Sets the memory buffer size for aggregate functions (up to 256 KB). This buffer is allocated for the lifetime of the aggregation process, making it useful as a global variable.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">LANGUAGE<\/code><\/strong>: Specifies the programming language. Currently, TDengine supports Python and C.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Deleting a <\/strong><strong>UDF<\/strong><\/h3>\n\n\n\n<p>To remove a UDF, use the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">DROP FUNCTION function_name;<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Viewing UDFs<\/strong><\/h3>\n\n\n\n<p>To view all created UDFs:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">SHOW FUNCTIONS;<\/code><\/pre>\n\n\n\n<p>For detailed information:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">SELECT * FROM information_schema.ins_functions\\G;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Setting Up the Environment<\/strong><\/h2>\n\n\n\n<p>Before diving into Python UDF development, ensure your environment is ready:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>CMake<\/strong>: Minimum version 3.0.2.<\/li>\n\n\n\n<li><strong>GCC<\/strong>: Required for compiling the shared library (<code class=\"\" data-line=\"\">.so<\/code> file). Minimum version: 7.5.<\/li>\n\n\n\n<li><strong>Python<\/strong>: Version 3.7 or higher.<\/li>\n<\/ol>\n\n\n\n<p>Install the UDF plugin with the following command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">python3 -m pip install taospyudf\nldconfig<\/code><\/pre>\n\n\n\n<p>Your development environment is now ready!<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Writing UDFs<\/strong><\/h2>\n\n\n\n<p>UDFs in TDengine fall into two categories: <strong>scalar <\/strong>and <strong>aggregate<\/strong> functions. Before diving into the details of the functions, let&#8217;s first take a look at the process of how custom functions are invoked:<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-8c9c62f9\"><img decoding=\"async\" width=\"842\" height=\"1024\" class=\"gb-image gb-image-8c9c62f9\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9-842x1024.png?strip=all&sharp=1\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9-842x1024.png?strip=all&amp;sharp=1 842w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9-247x300.png?strip=all&amp;sharp=1 247w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9-768x934.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9-1263x1536.png?strip=all&amp;sharp=1 1263w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9.png?strip=all&amp;sharp=1 1434w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9.png?strip=all&amp;sharp=1&amp;w=573 573w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9.png?strip=all&amp;sharp=1&amp;w=1147 1147w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-9.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 842px) 100vw, 842px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>How UDFs Work<\/strong><\/h3>\n\n\n\n<p>TDengine processes data in blocks for efficiency. When a UDF is invoked, the input data is a block, and you can access any cell in the block using the <code class=\"\" data-line=\"\">data(row, col)<\/code> method. This approach minimizes the overhead of calls between the C framework and Python, boosting performance.<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Scalar Functions<\/strong>: Must return the same number of rows as the input.<\/li>\n\n\n\n<li><strong>Aggregate Functions<\/strong>: Aggregate data and return a single row.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Creating a Scalar Function<\/strong><\/h2>\n\n\n\n<p>Let&#8217;s create a Python UDF equivalent of the <code class=\"\" data-line=\"\">CONCAT<\/code> string concatenation function:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Write the Function<\/strong><\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-c8486130\"><img decoding=\"async\" width=\"709\" height=\"1024\" class=\"gb-image gb-image-c8486130\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1-709x1024.png?strip=all&sharp=1\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1-709x1024.png?strip=all&amp;sharp=1 709w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1-208x300.png?strip=all&amp;sharp=1 208w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1.png?strip=all&amp;sharp=1 724w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1.png?strip=all&amp;sharp=1&amp;w=144 144w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1.png?strip=all&amp;sharp=1&amp;w=289 289w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1.png?strip=all&amp;sharp=1&amp;w=434 434w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-13-1.png?strip=all&amp;sharp=1&amp;w=579 579w\" sizes=\"(max-width: 709px) 100vw, 709px\" \/><\/figure>\n\n\n\n<p>The function must include the following methods:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong><code class=\"\" data-line=\"\">init<\/code><\/strong>: Called once during the initialization of the UDF module. Use this to perform any necessary setup tasks.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">destroy<\/code><\/strong>: Called once when the UDF module exits. Use this for cleanup tasks.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">process<\/code><\/strong>: The main function that processes each incoming data block. Use the <code class=\"\" data-line=\"\">shape()<\/code> method to retrieve the number of rows and columns in the data block.\n<ul class=\"wp-block-list\">\n<li><strong><code class=\"\" data-line=\"\">nrows<\/code><\/strong>: Returns the number of rows in the data block.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">ncols<\/code><\/strong>: Returns the number of columns, which corresponds to the number of parameters passed to the <code class=\"\" data-line=\"\">concat()<\/code> function.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<p>Return Values:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>A scalar function must return a list. If the return value is not a list, an error will occur.<\/li>\n\n\n\n<li>The number of elements in the returned list must match the number of rows (<code class=\"\" data-line=\"\">nrows<\/code>) in the data block. Mismatched row counts will result in an error.<\/li>\n<\/ul>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Create the function<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">create function py_concat as &#039;\/home\/py_concat.py&#039; outputtype varchar(256) language &#039;Python&#039;;<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Execute the function<\/strong> Use the UDF like any built-in function:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">select sf_concat(factory_name,room_name), concat(factory_name,room_name) from devices;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Creating an Aggregate Function<\/strong><\/h2>\n\n\n\n<p>Aggregate functions perform computations on data and output a single aggregated result. Let&#8217;s create a Python UDF for counting rows:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Write the Function<\/strong><\/li>\n<\/ol>\n\n\n\n<figure class=\"gb-block-image gb-block-image-27f10c4c\"><img decoding=\"async\" width=\"534\" height=\"978\" class=\"gb-image gb-image-27f10c4c\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1.png?strip=all&sharp=1\" alt=\"\" title=\"image (14)\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1.png?strip=all&amp;sharp=1 534w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1-164x300.png?strip=all&amp;sharp=1 164w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1.png?strip=all&amp;sharp=1&amp;w=106 106w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1.png?strip=all&amp;sharp=1&amp;w=320 320w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-14-1.png?strip=all&amp;sharp=1&amp;w=427 427w\" sizes=\"(max-width: 534px) 100vw, 534px\" \/><\/figure>\n\n\n\n<p>Implementation Principles:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>During the <code class=\"\" data-line=\"\">start<\/code> initialization callback, the value <code class=\"\" data-line=\"\">0<\/code> is stored in the buffer (<code class=\"\" data-line=\"\">buf<\/code>) as the initial value for counting.<\/li>\n\n\n\n<li>In the <code class=\"\" data-line=\"\">reduce<\/code> function, as data blocks are processed, any non-<code class=\"\" data-line=\"\">None<\/code> values are added to the cumulative count. The result is stored back into the buffer (<code class=\"\" data-line=\"\">buf<\/code>), which is passed as a parameter during subsequent calls to <code class=\"\" data-line=\"\">reduce<\/code>. This allows the buffer to be reused across iterations.<\/li>\n\n\n\n<li>Finally, in the <code class=\"\" data-line=\"\">finish<\/code> function, the buffer (<code class=\"\" data-line=\"\">buf<\/code>) is passed as a parameter. The value stored in the buffer is retrieved and returned as the final result, representing the total count.<\/li>\n<\/ul>\n\n\n\n<p>Return Value:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li>The return type of the function must match the <code class=\"\" data-line=\"\">OUTPUTTYPE<\/code> specified when the UDF function was created. If the return type is incorrect, an error will be triggered.<\/li>\n\n\n\n<li>Returning a <code class=\"\" data-line=\"\">None<\/code> object is allowed, but it must be handled appropriately.<\/li>\n<\/ul>\n\n\n\n<ol start=\"2\" class=\"wp-block-list\">\n<li><strong>Create the <\/strong><strong>UDF<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">create aggregate function af_count as &#039;&#039;\/home\/af_count.py&#039;&#039; outputtype bigint bufsize 4096 language &#039;Python&#039;;<\/code><\/pre>\n\n\n\n<ol start=\"3\" class=\"wp-block-list\">\n<li><strong>Execute the <\/strong><strong>UDF<\/strong><\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">select af_count(col1) from devices;<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Data Type Mappings<\/strong><\/h2>\n\n\n\n<p>Python interacts with C through specific data type mappings. Key points include:<\/p>\n\n\n\n<table id=\"tablepress-75\" class=\"tablepress tablepress-id-75\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">TDengine Data Type<\/th><th class=\"column-2\">Mapped to Python Object<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-striping row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">TINYINT \/ TINYINT UNSIGNED \/ SMALLINT \/ SMALLINT UNSIGNED \/ INT \/ INT UNSIGNED \/ BIGINT \/ BIGINT UNSIGNED<\/td><td class=\"column-2\">int<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">FLOAT \/ DOUBLE<\/td><td class=\"column-2\">float<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">BOOL<\/td><td class=\"column-2\">bool<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">BINARY \/ NCHAR \/ VARCHAR<\/td><td class=\"column-2\">bytes<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">TIMESTAMP<\/td><td class=\"column-2\">int<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">JSON<\/td><td class=\"column-2\">Not supported<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-75 from cache -->\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong>Binary, NCHAR, and VARCHAR<\/strong> map to Python&#8217;s <code class=\"\" data-line=\"\">bytes<\/code> object. Use appropriate decoding:\n<ol class=\"wp-block-list\">\n<li>For <strong>binary<\/strong>: <code class=\"\" data-line=\"\">bytes.decode(&#039;utf-8&#039;)<\/code><\/li>\n\n\n\n<li>For <strong>nchar<\/strong>: <code class=\"\" data-line=\"\">bytes.decode(&#039;utf_32_le&#039;)<\/code><\/li>\n<\/ol>\n<\/li>\n\n\n\n<li>When returning a <code class=\"\" data-line=\"\">str<\/code> object:\n<ol class=\"wp-block-list\">\n<li>For <strong>binary<\/strong>: <code class=\"\" data-line=\"\">str.encode(&#039;utf-8&#039;)<\/code><\/li>\n\n\n\n<li>For <strong>nchar<\/strong>: <code class=\"\" data-line=\"\">str.encode(&#039;utf_32_le&#039;)<\/code><\/li>\n<\/ol>\n<\/li>\n<\/ol>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Development Tips<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Updating Function Code<\/strong><\/h3>\n\n\n\n<figure class=\"gb-block-image gb-block-image-a248edc0\"><img decoding=\"async\" width=\"1024\" height=\"562\" class=\"gb-image gb-image-a248edc0\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10-1024x562.png?strip=all&sharp=1\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10-1024x562.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10-300x165.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10-768x421.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10-1536x843.png?strip=all&amp;sharp=1 1536w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10.png?strip=all&amp;sharp=1 1680w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10.png?strip=all&amp;sharp=1&amp;w=672 672w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10.png?strip=all&amp;sharp=1&amp;w=1344 1344w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/whiteboard_exported_image-10.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<p>The .py file path specified during UDF creation is only used at that moment. The file&#8217;s content is stored in the mnode, making the function accessible across the cluster. After creation, the .py file is no longer needed.<\/p>\n\n\n\n<p>To update the UDF code, you must update mnode with the new content. Use the OR REPLACE option to overwrite the existing function and apply the updated code for future calls.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">CREATE &#091;OR REPLACE] FUNCTION function_name AS library_path OUTPUTTYPE output_type &#091;LANGUAGE &#039;C&#039; | &#039;Python&#039;];<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Logging<\/strong><\/h3>\n\n\n\n<p>Python UDFs in TDengine do not support direct debugging of Python code but do support logging. You can use commonly used libraries like <code class=\"\" data-line=\"\">logging<\/code> within UDF functions. It is recommended to output logs to a file for review. Note that messages printed using the <code class=\"\" data-line=\"\">print<\/code> function will not be visible, so avoid using it for output. For example:<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-ba80e9be\"><img decoding=\"async\" width=\"1024\" height=\"452\" class=\"gb-image gb-image-ba80e9be\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1-1024x452.png?strip=all&sharp=1\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1-1024x452.png?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1-300x132.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1-768x339.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1.png?strip=all&amp;sharp=1 1536w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1.png?strip=all&amp;sharp=1&amp;w=614 614w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1.png?strip=all&amp;sharp=1&amp;w=921 921w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1.png?strip=all&amp;sharp=1&amp;w=1228 1228w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-15-1.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Exception Handling<\/strong><\/h3>\n\n\n\n<p>Raise exceptions to terminate queries if needed. Exceptions are logged in <code class=\"\" data-line=\"\">taosudfpy.log<\/code>.<\/p>\n\n\n\n<figure class=\"gb-block-image gb-block-image-e3c933f2\"><img decoding=\"async\" width=\"1024\" height=\"145\" class=\"gb-image gb-image-e3c933f2\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584-1024x145.jpg?strip=all&sharp=1\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584-1024x145.jpg?strip=all&amp;sharp=1 1024w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584-300x43.jpg?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584-768x109.jpg?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584-1536x218.jpg?strip=all&amp;sharp=1 1536w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584.jpg?strip=all&amp;sharp=1 1760w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584.jpg?strip=all&amp;sharp=1&amp;w=352 352w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584.jpg?strip=all&amp;sharp=1&amp;w=704 704w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584.jpg?strip=all&amp;sharp=1&amp;w=1408 1408w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/WechatIMG1584.jpg?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 1024px) 100vw, 1024px\" \/><\/figure>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Checking <\/strong><strong>UDF<\/strong><strong> Framework Logs<\/strong><\/h3>\n\n\n\n<p>If the UDF framework returns an error, check the log files in the TDengine log directory for details:<\/p>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li><strong><code class=\"\" data-line=\"\">taospyudf.log<\/code><\/strong> Logs Python UDF activity, including function loading, execution errors, and exceptions. Use this log when debugging Python UDFs.<\/li>\n\n\n\n<li><strong><code class=\"\" data-line=\"\">udfdlog.0<\/code><\/strong> Logs framework-level issues for all UDF languages (C, Python, etc.). Check this log for broader framework errors, though it&#8217;s rarely needed.<\/li>\n<\/ol>\n\n\n\n<h3 class=\"wp-block-heading\"><strong>Common Errors and Solutions<\/strong><\/h3>\n\n\n\n<table id=\"tablepress-76\" class=\"tablepress tablepress-id-76\">\n<thead>\n<tr class=\"row-1\">\n\t<th class=\"column-1\">Serial Number<\/th><th class=\"column-2\">Error Code<\/th><th class=\"column-3\">Error Description<\/th><th class=\"column-4\">Explanation<\/th>\n<\/tr>\n<\/thead>\n<tbody class=\"row-striping row-hover\">\n<tr class=\"row-2\">\n\t<td class=\"column-1\">1<\/td><td class=\"column-2\">0x80002901<\/td><td class=\"column-3\">udf is stopping<\/td><td class=\"column-4\">The UDF framework is shutting down and cannot provide external services.<\/td>\n<\/tr>\n<tr class=\"row-3\">\n\t<td class=\"column-1\">2<\/td><td class=\"column-2\">0x80002902<\/td><td class=\"column-3\">udf pipe read error<\/td><td class=\"column-4\">A data read error occurred during communication between the taosd and udfd processes.<\/td>\n<\/tr>\n<tr class=\"row-4\">\n\t<td class=\"column-1\">3<\/td><td class=\"column-2\">0x80002903<\/td><td class=\"column-3\">udf pipe connect error<\/td><td class=\"column-4\">A connection error occurred during communication between the taosd and udfd processes.<\/td>\n<\/tr>\n<tr class=\"row-5\">\n\t<td class=\"column-1\">4<\/td><td class=\"column-2\">0x80002904<\/td><td class=\"column-3\">udf no pipe<\/td><td class=\"column-4\">Failed to create a pipe during communication between the taosd and udfd processes.<\/td>\n<\/tr>\n<tr class=\"row-6\">\n\t<td class=\"column-1\">5<\/td><td class=\"column-2\">0x80002905<\/td><td class=\"column-3\">udf load failure<\/td><td class=\"column-4\">Failed to load the UDF module.<\/td>\n<\/tr>\n<tr class=\"row-7\">\n\t<td class=\"column-1\">6<\/td><td class=\"column-2\">0x80002906<\/td><td class=\"column-3\">udf invalid state<\/td><td class=\"column-4\">The UDF framework is in an invalid state (e.g., during initialization) and cannot provide external services.<\/td>\n<\/tr>\n<tr class=\"row-8\">\n\t<td class=\"column-1\">7<\/td><td class=\"column-2\">0x80002907<\/td><td class=\"column-3\">udf invalid function input<\/td><td class=\"column-4\">Invalid parameters were provided to the UDF function.<\/td>\n<\/tr>\n<tr class=\"row-9\">\n\t<td class=\"column-1\">8<\/td><td class=\"column-2\">0x80002908<\/td><td class=\"column-3\">udf no function handle<\/td><td class=\"column-4\">Internal error: no function handle was found.<\/td>\n<\/tr>\n<tr class=\"row-10\">\n\t<td class=\"column-1\">9<\/td><td class=\"column-2\">0x80002909<\/td><td class=\"column-3\">udf invalid bufsize<\/td><td class=\"column-4\">The BUFSIZE specified for an aggregate function was invalid or exceeded the limit.<\/td>\n<\/tr>\n<tr class=\"row-11\">\n\t<td class=\"column-1\">10<\/td><td class=\"column-2\">0x8000290A<\/td><td class=\"column-3\">udf invalid output type<\/td><td class=\"column-4\">The actual data type does not match the OUTPUTTYPE specified when creating the UDF function.<\/td>\n<\/tr>\n<tr class=\"row-12\">\n\t<td class=\"column-1\">11<\/td><td class=\"column-2\">0x8000290B<\/td><td class=\"column-3\">udf program language not supported<\/td><td class=\"column-4\">The specified programming language is not supported.<\/td>\n<\/tr>\n<tr class=\"row-13\">\n\t<td class=\"column-1\">12<\/td><td class=\"column-2\">0x8000290C<\/td><td class=\"column-3\">udf function execution failure<\/td><td class=\"column-4\">The UDF function encountered an error during execution. Check logs for more details.<\/td>\n<\/tr>\n<tr class=\"row-14\">\n\t<td class=\"column-1\">13<\/td><td class=\"column-2\">0x8000290D<\/td><td class=\"column-3\">udf raise exception<\/td><td class=\"column-4\">The UDF function raised an exception or terminated explicitly, causing the query to stop.<\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<!-- #tablepress-76 from cache -->\n\n\n<p>Errors <strong>10<\/strong> and <strong>12<\/strong> are the most common issues when developing Python UDFs. For detailed causes, refer to the <code class=\"\" data-line=\"\">taospyudf.log<\/code> file.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Examples and Resources<\/strong><\/h2>\n\n\n\n<p>TDengine&#8217;s open-source repository provides several Python UDF test examples for reference:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Examples Repository<\/strong>: <a href=\"https:\/\/github.com\/taosdata\/TDengine\/tree\/3.0\/tests\/system-test\/0-others\/udfpy\" rel=\"noopener\">https:\/\/github.com\/taosdata\/TDengine\/tree\/3.0\/tests\/system-test\/0-others\/udfpy<\/a><\/li>\n\n\n\n<li><strong>CI Test Case<\/strong>: <a href=\"https:\/\/github.com\/taosdata\/TDengine\/blob\/3.0\/tests\/system-test\/0-others\/udfpy_main.py\" rel=\"noopener\">https:\/\/github.com\/taosdata\/TDengine\/blob\/3.0\/tests\/system-test\/0-others\/udfpy_main.py<\/a><\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">Steps to Run Test Cases:<\/h3>\n\n\n\n<ol start=\"1\" class=\"wp-block-list\">\n<li>Ensure Python3 is correctly installed.<\/li>\n\n\n\n<li>Clone the TDengine OSS code.<\/li>\n\n\n\n<li>Navigate to the <code class=\"\" data-line=\"\">TDengine\/tests\/system-test\/<\/code> directory.<\/li>\n\n\n\n<li>Run the test case using:<\/li>\n<\/ol>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">python3 test.py -f others\/udfpy_main.py<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Notes:<\/strong><\/h2>\n\n\n\n<h3 class=\"wp-block-heading\">1. <strong>Common Issues with <\/strong><strong>UDF<\/strong><strong> Scalar Functions<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Row Count Mismatch<\/strong>: The number of rows returned by a scalar function must match the number of input rows.\n<ul class=\"wp-block-list\">\n<li>If the logic is complex, it&#8217;s easy to miss rows during processing. Be cautious to avoid such errors.<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">2. <strong><code class=\"\" data-line=\"\">OUTPUTTYPE<\/code><\/strong><strong> Mismatch<\/strong><\/h3>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Output Type Consistency<\/strong>: The return type of the UDF must match the <code class=\"\" data-line=\"\">OUTPUTTYPE<\/code> specified during its creation.\n<ul class=\"wp-block-list\">\n<li>Mismatched types will result in an error.<\/li>\n<\/ul>\n<\/li>\n\n\n\n<li><strong>Scalar Functions<\/strong>: The return value of the <code class=\"\" data-line=\"\">process<\/code> function must be a <code class=\"\" data-line=\"\">list<\/code>, and all elements in the list must match the data type specified by <code class=\"\" data-line=\"\">OUTPUTTYPE<\/code>.<\/li>\n\n\n\n<li><strong>Aggregate Functions<\/strong>: The final return value in the <code class=\"\" data-line=\"\">finish<\/code> function must match the <code class=\"\" data-line=\"\">OUTPUTTYPE<\/code> as a Python data type.<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\">3. <strong>Error: Unable to Load <\/strong><strong><code class=\"\" data-line=\"\">libtaospyudf.so<\/code><\/strong><\/h3>\n\n\n\n<figure class=\"gb-block-image gb-block-image-590256e6\"><img decoding=\"async\" width=\"885\" height=\"160\" class=\"gb-image gb-image-590256e6\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&sharp=1\" alt=\"\" title=\"image (16)\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1 885w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16-300x54.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16-768x139.png?strip=all&amp;sharp=1 768w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1&amp;w=177 177w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1&amp;w=354 354w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1&amp;w=531 531w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1&amp;w=708 708w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-16.png?strip=all&amp;sharp=1&amp;w=450 450w\" sizes=\"(max-width: 885px) 100vw, 885px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Check Installation<\/strong>: Ensure the <code class=\"\" data-line=\"\">taospyudf<\/code> plugin was installed properly.\n<ul class=\"wp-block-list\">\n<li>The library is typically installed in <code class=\"\" data-line=\"\">\/usr\/local\/lib\/libtaospyudf.so<\/code> or Python&#8217;s plugin directory.<\/li>\n\n\n\n<li>Use the command below to locate the file:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">find \/ -name &#039;libtaospyudf.so&#039;<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Missing File<\/strong>: If the file is missing, the plugin installation may have failed. Reinstall the plugin to resolve the issue.<\/li>\n\n\n\n<li><strong>File Exists but Still Fails<\/strong>:\n<ul class=\"wp-block-list\">\n<li>Use <code class=\"\" data-line=\"\">ldd<\/code> to check the library&#8217;s dependencies:<\/li>\n<\/ul>\n<\/li>\n<\/ul>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\" data-line=\"\">ldd \/usr\/local\/lib\/libtaospyudf.so<\/code><\/pre>\n\n\n\n<ul class=\"wp-block-list\">\n<li>Missing Python dependencies are a common issue. For example, an improperly installed Python environment can cause errors.<\/li>\n<\/ul>\n\n\n\n<figure class=\"gb-block-image gb-block-image-9c4479ba\"><img decoding=\"async\" width=\"588\" height=\"120\" class=\"gb-image gb-image-9c4479ba\" src=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&sharp=1\" alt=\"\" title=\"image (18)\" srcset=\"https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&amp;sharp=1 588w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18-300x61.png?strip=all&amp;sharp=1 300w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&amp;sharp=1&amp;w=117 117w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&amp;sharp=1&amp;w=235 235w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&amp;sharp=1&amp;w=352 352w, https:\/\/eujqw4hwudm.exactdn.com\/wp-content\/uploads\/image-18.png?strip=all&amp;sharp=1&amp;w=470 470w\" sizes=\"(max-width: 588px) 100vw, 588px\" \/><\/figure>\n\n\n\n<ul class=\"wp-block-list\">\n<li><strong>Solution<\/strong>: Reinstall Python (e.g., Python 3.9) correctly to resolve the dependency issue.<\/li>\n<\/ul>\n\n\n\n<h2 class=\"wp-block-heading\"><strong>Conclusion<\/strong><\/h2>\n\n\n\n<p>The Python UDF feature in TDengine is an exciting step forward, offering unmatched customization for your database. This is just the beginning\u2014we&#8217;re continuously improving and expanding its capabilities. We invite you to explore this feature, unleash your creativity, and share your feedback to make it even better!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>A guide to creating and troubleshooting Python UDFs in TDengine, taking you from beginner to expert.<\/p>\n","protected":false},"author":81,"featured_media":23522,"comment_status":"closed","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"content-type":"","footnotes":""},"categories":[21],"tags":[],"ppma_author":[167],"class_list":["post-23497","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-engineering"],"authors":[{"term_id":167,"user_id":81,"is_guest":0,"slug":"chait","display_name":"Chait Diwadkar","avatar_url":{"url":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg","url2x":"https:\/\/tdengine.com\/wp-content\/uploads\/29.03-05-cdiwadkar.jpg"},"1":"","2":"","3":"","4":"","5":"","6":"","7":"","8":""}],"_links":{"self":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/23497","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/users\/81"}],"replies":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/comments?post=23497"}],"version-history":[{"count":7,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/23497\/revisions"}],"predecessor-version":[{"id":24068,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/posts\/23497\/revisions\/24068"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media\/23522"}],"wp:attachment":[{"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/media?parent=23497"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/categories?post=23497"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/tags?post=23497"},{"taxonomy":"author","embeddable":true,"href":"https:\/\/tdengine.com\/wp-json\/wp\/v2\/ppma_author?post=23497"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}