From 33837ec903ad425851c1016873ba12efbbde82fd Mon Sep 17 00:00:00 2001
From: SwallowGG <1558143046@qq.com>
Date: Mon, 28 Oct 2024 14:42:14 +0800
Subject: [PATCH 1/8] feat:support download task
---
 .../chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java | 2 ++
 .../chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java   | 1 +
 .../java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java     | 2 ++
 .../web/api/controller/rdb/converter/RdbWebConverter.java       | 2 ++
 4 files changed, 7 insertions(+)
diff --git a/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java
index 9f313c27c..0eef415af 100644
--- a/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java
+++ b/chat2db-server/chat2db-plugins/chat2db-clickhouse/src/main/java/ai/chat2db/plugin/clickhouse/builder/ClickHouseSqlBuilder.java
@@ -10,6 +10,8 @@
 import ai.chat2db.spi.model.TableIndex;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.List;
+
 
 public class ClickHouseSqlBuilder extends DefaultSqlBuilder {
     @Override
diff --git a/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java b/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java
index 1b1cc2fd6..05f938c3b 100644
--- a/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java
+++ b/chat2db-server/chat2db-plugins/chat2db-h2/src/main/java/ai/chat2db/plugin/h2/H2Meta.java
@@ -3,6 +3,7 @@
 import java.sql.Connection;
 import java.sql.ResultSet;
 import java.sql.ResultSetMetaData;
+import java.sql.SQLException;
 import java.util.*;
 import java.util.stream.Collectors;
 
diff --git a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java
index 02a761df7..a3ca677dd 100644
--- a/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java
+++ b/chat2db-server/chat2db-plugins/chat2db-hive/src/main/java/ai/chat2db/plugin/hive/builder/HiveSqlBuilder.java
@@ -2,6 +2,7 @@
 
 import ai.chat2db.plugin.hive.type.HiveColumnTypeEnum;
 import ai.chat2db.plugin.hive.type.HiveIndexTypeEnum;
+import ai.chat2db.spi.SqlBuilder;
 import ai.chat2db.spi.jdbc.DefaultSqlBuilder;
 import ai.chat2db.spi.model.Database;
 import ai.chat2db.spi.model.Table;
@@ -9,6 +10,7 @@
 import ai.chat2db.spi.model.TableIndex;
 import org.apache.commons.lang3.StringUtils;
 
+import java.util.List;
 
 
 public class HiveSqlBuilder extends DefaultSqlBuilder implements SqlBuilder
 {
diff --git a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
index f9c529a5b..7881cf93a 100644
--- a/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
+++ b/chat2db-server/chat2db-server-web/chat2db-server-web-api/src/main/java/ai/chat2db/server/web/api/controller/rdb/converter/RdbWebConverter.java
@@ -42,6 +42,8 @@ public abstract class RdbWebConverter {
     public abstract DlExecuteParam request2param(DmlRequest request);
 
 
+
+    public abstract GroupByParam request2param(GroupByRequest request);
     /**
      * Parameter conversion
      *
From d37fd66981b6d09a63a93692c145149a46c56765 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=E8=B4=BA=E5=96=9C?=
 <86969353+shanhexi@users.noreply.github.com>
Date: Mon, 28 Oct 2024 22:40:08 +0800
Subject: [PATCH 2/8] =?UTF-8?q?=E4=BF=AE=E6=94=B9jar?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
 .github/workflows/release.yml | 6 +-----
 1 file changed, 1 insertion(+), 5 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 61b492976..c5c31fbdf 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -86,11 +86,7 @@ jobs:
       - name: Enable tls1
         if: ${{ runner.os == 'Windows' }}
         run: |
-          # sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
-          $filePath = "${{ env.JAVA_HOME }}\conf\security\java.security"
-          $content = Get-Content $filePath -Raw
-          $updatedContent = $content -replace '^(jdk.tls.disabledAlgorithms=)(.*)( TLSv1, TLSv1.1,)(.*)', '$1$2$4'
-          $updatedContent | Set-Content $filePath
+          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
         shell: pwsh
 
       # java.security open tls1 macOS
From 85b99252f697a4296d94ac9ed143ce4eaac26213 Mon Sep 17 00:00:00 2001
From: SwallowGG <1558143046@qq.com>
Date: Tue, 29 Oct 2024 09:50:57 +0800
Subject: [PATCH 3/8] feat:support download task
---
 .github/workflows/release.yml | 34 ++++++++++++++++++++++++----------
 1 file changed, 24 insertions(+), 10 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index c5c31fbdf..9f2590a88 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -83,17 +83,31 @@ jobs:
           java-package: "jre"
 
       # java.security open tls1 Windows
-      - name: Enable tls1
-        if: ${{ runner.os == 'Windows' }}
-        run: |
-          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
-        shell: pwsh
-
-      # java.security open tls1 macOS
-      - name: Enable tls1
-        if: ${{ runner.os == 'macOS' }}
+#      - name: Enable tls1
+#        if: ${{ runner.os == 'Windows' }}
+#        run: |
+#          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
+#        shell: pwsh
+#
+#      # java.security open tls1 macOS
+#      - name: Enable tls1
+#        if: ${{ runner.os == 'macOS' }}
+#        run: |
+#          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" $JAVA_HOME/conf/security/java.security
+      # 开放TLS
+      - name: Enable TLS 1.0 and 1.1 in java.security
         run: |
-          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" $JAVA_HOME/conf/security/java.security
+          if [ "$RUNNER_OS" = "Windows" ]; then
+            sed -i "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
+          elif [ "$RUNNER_OS" = "Linux" ]; then
+            sed -i "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
+          elif [ "$RUNNER_OS" = "macOS" ]; then
+            sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
+          fi
+        shell: bash
+        env:
+          RUNNER_OS: ${{ runner.os }}
+          JAVA_HOME: ${{ env.JAVA_HOME }}
 
       # Copy jre Windows
       - name: Copy Jre for Windows
From cb4fb975d5660c99b4b7961c2dc2414e1b02f118 Mon Sep 17 00:00:00 2001
From: SwallowGG <1558143046@qq.com>
Date: Tue, 29 Oct 2024 16:25:08 +0800
Subject: [PATCH 4/8] feat:support download task
---
 .github/workflows/release.yml | 89 +++++++++++++++--------------------
 1 file changed, 37 insertions(+), 52 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 9f2590a88..3d83f04f2 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,14 +2,14 @@
 name: Build Client
 
 # Workflow's trigger
-# Pack when creating tags
+# 在创建标签的时候打包
 on:
   push:
     tags:
       - v*
 
 # Workflow's jobs
-# A total of 3 computers are required to run
+# 一共需要3台电脑运行
 # windows
 # macos-latest x86_64
 # macos-latest arm64
@@ -31,7 +31,7 @@ jobs:
       - name: Check out git repository
         uses: actions/checkout@main
 
-      # Obtaining the version number is not supported by workflow, so a plug-in is used.
+      # 获取版本号 workflow不支持 所以用插件
       - name: Create version
         id: chat2db_version
         uses: bhowell2/github-substring-action@1.0.1
@@ -39,13 +39,13 @@ jobs:
           value: ${{ github.ref }}
           index_of_str: "refs/tags/v"
 
-      # Output basic information
+      # 输出基础信息
       - name: Print basic information
         run: |
           echo "current environment: ${{ env.CHAT2DB_ENVIRONMENT }}"
           echo "current version: ${{ steps.chat2db_version.outputs.substring }}"
 
-      # Install jre Windows
+      # 安装jre Windows
       - name: Install Jre for Windows
         if: ${{ runner.os == 'Windows' }}
         uses: actions/setup-java@main
@@ -54,7 +54,7 @@ jobs:
           distribution: "temurin"
           java-package: "jre"
 
-      # Install jre MacOS X64
+      # 安装jre MacOS X64
       - name: Install Jre MacOS X64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         uses: actions/setup-java@main
@@ -63,7 +63,7 @@ jobs:
           distribution: "temurin"
           java-package: "jre"
 
-      # Install jre MacOS arm64
+      # 安装jre MacOS arm64
       - name: Install Jre MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         uses: actions/setup-java@main
@@ -73,7 +73,7 @@ jobs:
           java-package: "jre"
           architecture: "aarch64"
 
-          # Install jre Linux
+          # 安装jre Linux
       - name: Install Jre for Linux
         if: ${{ runner.os == 'Linux' }}
         uses: actions/setup-java@main
@@ -82,41 +82,26 @@ jobs:
           distribution: "temurin"
           java-package: "jre"
 
-      # java.security open tls1 Windows
-#      - name: Enable tls1
-#        if: ${{ runner.os == 'Windows' }}
-#        run: |
-#          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
-#        shell: pwsh
-#
-#      # java.security open tls1 macOS
-#      - name: Enable tls1
-#        if: ${{ runner.os == 'macOS' }}
-#        run: |
-#          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" $JAVA_HOME/conf/security/java.security
-      # 开放TLS
-      - name: Enable TLS 1.0 and 1.1 in java.security
+      # java.security 开放tls1 Windows
+      - name: Enable tls1
+        if: ${{ runner.os == 'Windows' }}
+        run: |
+          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
+
+      # java.security 开放tls1 macOS
+      - name: Enable tls1
+        if: ${{ runner.os == 'macOS' }}
         run: |
-          if [ "$RUNNER_OS" = "Windows" ]; then
-            sed -i "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
-          elif [ "$RUNNER_OS" = "Linux" ]; then
-            sed -i "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
-          elif [ "$RUNNER_OS" = "macOS" ]; then
-            sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\(TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "$JAVA_HOME/conf/security/java.security"
-          fi
-        shell: bash
-        env:
-          RUNNER_OS: ${{ runner.os }}
-          JAVA_HOME: ${{ env.JAVA_HOME }}
-
-      # Copy jre Windows
+          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" $JAVA_HOME/conf/security/java.security
+
+      # 复制jre Windows
       - name: Copy Jre for Windows
         if: ${{ runner.os == 'Windows' }}
         run: |
           mkdir chat2db-client/static
           cp -r "${{ env.JAVA_HOME }}" chat2db-client/static/jre
 
-      # Copy jre macOS
+      # 复制jre macOS
       - name: Copy Jre for macOS
         if: ${{ runner.os == 'macOS' }}
         run: |
@@ -124,7 +109,7 @@ jobs:
           cp -r $JAVA_HOME chat2db-client/static/jre
           chmod -R 777 chat2db-client/static/jre/
 
-      # Copy jre Linux
+      # 复制jre Linux
       - name: Copy Jre for Linux
         if: ${{ runner.os == 'Linux' }}
         run: |
@@ -132,7 +117,7 @@ jobs:
           cp -r $JAVA_HOME chat2db-client/static/jre
           chmod -R 777 chat2db-client/static/jre/
 
-      # Install node
+      # 安装node
       - name: Install Node.js
         uses: actions/setup-node@main
         with:
@@ -140,7 +125,7 @@ jobs:
           cache: "yarn"
           cache-dependency-path: chat2db-client/yarn.lock
 
-      # Install java
+      # 安装java
       - name: Install Java and Maven
         uses: actions/setup-java@main
         with:
@@ -148,7 +133,7 @@ jobs:
           distribution: "temurin"
           cache: "maven"
 
-      # Build static file information
+      # 构建静态文件信息
       - name: Yarn install & build & copy
         run: |
           cd chat2db-client
@@ -160,7 +145,7 @@ jobs:
           yarn
           yarn run build
 
-      # Compile server-side java version
+      # 编译服务端java版本
       - name: Build Java
         run: mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml
 
@@ -175,7 +160,7 @@ jobs:
           echo -n ${{ steps.chat2db_version.outputs.substring }} > version
           cp -r version ./versions/
 
-      # Copy server-side java to the specified location
+      # 复制服务端java 到指定位置
       - name: Copy App
         run: |
           cp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/static/
@@ -220,7 +205,7 @@ jobs:
         run: |
           xcrun notarytool store-credentials "Chat2DB" --apple-id "${{secrets.MAC_APPLE_ID}}" --password "${{secrets.MAC_APPLE_PASSWORD}}" --team-id "${{secrets.MAC_TEAM_ID}}"
           xcrun notarytool submit chat2db-client/release/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg --keychain-profile "Chat2DB"
-      
+
       # macos arm64
       - name: Build/release Electron app for MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
@@ -260,14 +245,14 @@ jobs:
           args: "-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --linux"
           release: true
 
-      # Prepare the required data Windows
+      # 准备要需要的数据 Windows
       - name: Prepare upload for Windows
         if: runner.os == 'Windows'
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*Setup*.exe ./oss_temp_file
 
-      # Prepare the required data MacOS x86_64
+      # 准备要需要的数据 MacOS x86_64
       - name: Prepare upload for MacOS x86_64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         run: |
@@ -280,21 +265,21 @@ jobs:
           cd static/ && zip -r chat2db-server-start.zip ./ 
           cp -r chat2db-server-start.zip ../../../../oss_temp_file
 
-      # Prepare the required data MacOS arm64
+      # 准备要需要的数据 MacOS arm64
       - name: Prepare upload for MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*.dmg ./oss_temp_file
 
-      # Prepare the required data Linux
+      # 准备要需要的数据 Linux
       - name: Prepare upload for Linux
         if: runner.os == 'Linux'
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*.AppImage ./oss_temp_file
 
-      # Upload files to OSS for easy downloading
+      # 把文件上传到OSS 方便下载
       - name: Set up oss utils
         uses: yizhoumo/setup-ossutil@v1
         with:
@@ -306,7 +291,7 @@ jobs:
         run: |
           ossutil cp -rf --acl=public-read ./oss_temp_file/ oss://chat2db-client/release/${{ steps.chat2db_version.outputs.substring }}/
 
-      # Build completion notification
+      # 构建完成通知
       - name: Send dingtalk message for Windows
         if: ${{ runner.os == 'Windows' }}
         uses: ghostoy/dingtalk-action@master
@@ -319,7 +304,7 @@ jobs:
               "text": "# Windows-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \n ### Windows下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe) "
             }
 
-      # Build completion notification
+      # 构建完成通知
       - name: Send dingtalk message for MacOS x86_64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         uses: ghostoy/dingtalk-action@master
@@ -332,7 +317,7 @@ jobs:
               "text": "# MacOS-x86_64-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})    \n ### Intel芯片下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg)   \n ### jar包下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip) "
             }
 
-      # Build completion notification
+      # 构建完成通知
       - name: Send dingtalk message for MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         uses: ghostoy/dingtalk-action@master
@@ -345,7 +330,7 @@ jobs:
               "text": "# MacOS-arm64-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \n ### Apple芯片下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg) "
             }
 
-      # Build completion notification
+      # 构建完成通知
       - name: Send dingtalk message for Linux
         if: ${{ runner.os == 'Linux' }}
         uses: ghostoy/dingtalk-action@master
From ecd350d41e682065dfd63425cbdda3eea10d38d3 Mon Sep 17 00:00:00 2001
From: SwallowGG <1558143046@qq.com>
Date: Tue, 29 Oct 2024 17:09:42 +0800
Subject: [PATCH 5/8] feat:support download task
---
 .github/workflows/release.yml | 66 +++++++++++++++++++----------------
 1 file changed, 36 insertions(+), 30 deletions(-)
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 3d83f04f2..cca88c92a 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -2,14 +2,14 @@
 name: Build Client
 
 # Workflow's trigger
-# 在创建标签的时候打包
+# Pack when creating tags
 on:
   push:
     tags:
       - v*
 
 # Workflow's jobs
-# 一共需要3台电脑运行
+# A total of 3 computers are required to run
 # windows
 # macos-latest x86_64
 # macos-latest arm64
@@ -31,7 +31,7 @@ jobs:
       - name: Check out git repository
         uses: actions/checkout@main
 
-      # 获取版本号 workflow不支持 所以用插件
+      # Obtaining the version number is not supported by workflow, so a plug-in is used.
       - name: Create version
         id: chat2db_version
         uses: bhowell2/github-substring-action@1.0.1
@@ -39,13 +39,13 @@ jobs:
           value: ${{ github.ref }}
           index_of_str: "refs/tags/v"
 
-      # 输出基础信息
+      # Output basic information
       - name: Print basic information
         run: |
           echo "current environment: ${{ env.CHAT2DB_ENVIRONMENT }}"
           echo "current version: ${{ steps.chat2db_version.outputs.substring }}"
 
-      # 安装jre Windows
+      # Install jre Windows
       - name: Install Jre for Windows
         if: ${{ runner.os == 'Windows' }}
         uses: actions/setup-java@main
@@ -54,7 +54,7 @@ jobs:
           distribution: "temurin"
           java-package: "jre"
 
-      # 安装jre MacOS X64
+      # Install jre MacOS X64
       - name: Install Jre MacOS X64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         uses: actions/setup-java@main
@@ -62,8 +62,9 @@ jobs:
           java-version: "17"
           distribution: "temurin"
           java-package: "jre"
+          architecture: "x64"
 
-      # 安装jre MacOS arm64
+      # Install jre MacOS arm64
       - name: Install Jre MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         uses: actions/setup-java@main
@@ -73,7 +74,7 @@ jobs:
           java-package: "jre"
           architecture: "aarch64"
 
-          # 安装jre Linux
+          # Install jre Linux
       - name: Install Jre for Linux
         if: ${{ runner.os == 'Linux' }}
         uses: actions/setup-java@main
@@ -82,26 +83,31 @@ jobs:
           distribution: "temurin"
           java-package: "jre"
 
-      # java.security 开放tls1 Windows
+      # java.security open tls1 Windows
       - name: Enable tls1
         if: ${{ runner.os == 'Windows' }}
         run: |
-          sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}/conf/security/java.security"
-
-      # java.security 开放tls1 macOS
+          # sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" "${{ env.JAVA_HOME }}\conf\security\java.security"
+          $filePath = "${{ env.JAVA_HOME }}\conf\security\java.security"
+          $content = Get-Content $filePath -Raw
+          $updatedContent = $content -replace '^(jdk.tls.disabledAlgorithms=)(.*)( TLSv1, TLSv1.1,)(.*)', '$1$2$4'
+          $updatedContent | Set-Content $filePath
+        shell: pwsh
+
+      # java.security open tls1 macOS
       - name: Enable tls1
         if: ${{ runner.os == 'macOS' }}
         run: |
           sed -i '' "s/\(^jdk.tls.disabledAlgorithms=\)\(.*\)\( TLSv1, TLSv1.1,\)\(.*\)/\1\2\4/" $JAVA_HOME/conf/security/java.security
 
-      # 复制jre Windows
+      # Copy jre Windows
       - name: Copy Jre for Windows
         if: ${{ runner.os == 'Windows' }}
         run: |
           mkdir chat2db-client/static
           cp -r "${{ env.JAVA_HOME }}" chat2db-client/static/jre
 
-      # 复制jre macOS
+      # Copy jre macOS
       - name: Copy Jre for macOS
         if: ${{ runner.os == 'macOS' }}
         run: |
@@ -109,7 +115,7 @@ jobs:
           cp -r $JAVA_HOME chat2db-client/static/jre
           chmod -R 777 chat2db-client/static/jre/
 
-      # 复制jre Linux
+      # Copy jre Linux
       - name: Copy Jre for Linux
         if: ${{ runner.os == 'Linux' }}
         run: |
@@ -117,7 +123,7 @@ jobs:
           cp -r $JAVA_HOME chat2db-client/static/jre
           chmod -R 777 chat2db-client/static/jre/
 
-      # 安装node
+      # Install node
       - name: Install Node.js
         uses: actions/setup-node@main
         with:
@@ -125,7 +131,7 @@ jobs:
           cache: "yarn"
           cache-dependency-path: chat2db-client/yarn.lock
 
-      # 安装java
+      # Install java
       - name: Install Java and Maven
         uses: actions/setup-java@main
         with:
@@ -133,7 +139,7 @@ jobs:
           distribution: "temurin"
           cache: "maven"
 
-      # 构建静态文件信息
+      # Build static file information
       - name: Yarn install & build & copy
         run: |
           cd chat2db-client
@@ -145,7 +151,7 @@ jobs:
           yarn
           yarn run build
 
-      # 编译服务端java版本
+      # Compile server-side java version
       - name: Build Java
         run: mvn clean package -B '-Dmaven.test.skip=true' -f chat2db-server/pom.xml
 
@@ -160,7 +166,7 @@ jobs:
           echo -n ${{ steps.chat2db_version.outputs.substring }} > version
           cp -r version ./versions/
 
-      # 复制服务端java 到指定位置
+      # Copy server-side java to the specified location
       - name: Copy App
         run: |
           cp chat2db-server/chat2db-server-start/target/chat2db-server-start.jar chat2db-client/versions/${{ steps.chat2db_version.outputs.substring }}/static/
@@ -245,14 +251,14 @@ jobs:
           args: "-c.extraMetadata.version=${{ steps.chat2db_version.outputs.substring }} --linux"
           release: true
 
-      # 准备要需要的数据 Windows
+      # Prepare the required data Windows
       - name: Prepare upload for Windows
         if: runner.os == 'Windows'
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*Setup*.exe ./oss_temp_file
 
-      # 准备要需要的数据 MacOS x86_64
+      # Prepare the required data MacOS x86_64
       - name: Prepare upload for MacOS x86_64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         run: |
@@ -265,21 +271,21 @@ jobs:
           cd static/ && zip -r chat2db-server-start.zip ./ 
           cp -r chat2db-server-start.zip ../../../../oss_temp_file
 
-      # 准备要需要的数据 MacOS arm64
+      # Prepare the required data MacOS arm64
       - name: Prepare upload for MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*.dmg ./oss_temp_file
 
-      # 准备要需要的数据 Linux
+      # Prepare the required data Linux
       - name: Prepare upload for Linux
         if: runner.os == 'Linux'
         run: |
           mkdir oss_temp_file
           cp -r chat2db-client/release/*.AppImage ./oss_temp_file
 
-      # 把文件上传到OSS 方便下载
+      # Upload files to OSS for easy downloading
       - name: Set up oss utils
         uses: yizhoumo/setup-ossutil@v1
         with:
@@ -291,7 +297,7 @@ jobs:
         run: |
           ossutil cp -rf --acl=public-read ./oss_temp_file/ oss://chat2db-client/release/${{ steps.chat2db_version.outputs.substring }}/
 
-      # 构建完成通知
+      # Build completion notification
       - name: Send dingtalk message for Windows
         if: ${{ runner.os == 'Windows' }}
         uses: ghostoy/dingtalk-action@master
@@ -304,7 +310,7 @@ jobs:
               "text": "# Windows-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \n ### Windows下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB%20Setup%20${{ steps.chat2db_version.outputs.substring }}.exe) "
             }
 
-      # 构建完成通知
+      # Build completion notification
       - name: Send dingtalk message for MacOS x86_64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'x86_64' }}
         uses: ghostoy/dingtalk-action@master
@@ -317,7 +323,7 @@ jobs:
               "text": "# MacOS-x86_64-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})    \n ### Intel芯片下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.dmg)   \n ### jar包下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/chat2db-server-start.zip) "
             }
 
-      # 构建完成通知
+      # Build completion notification
       - name: Send dingtalk message for MacOS arm64
         if: ${{ runner.os == 'macOS' && matrix.arch == 'arm64' }}
         uses: ghostoy/dingtalk-action@master
@@ -330,7 +336,7 @@ jobs:
               "text": "# MacOS-arm64-release-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \n ### Apple芯片下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}-arm64.dmg) "
             }
 
-      # 构建完成通知
+      # Build completion notification
       - name: Send dingtalk message for Linux
         if: ${{ runner.os == 'Linux' }}
         uses: ghostoy/dingtalk-action@master
@@ -341,4 +347,4 @@ jobs:
             {
               "title": "Linux-test-打包完成通知",
               "text": "# Linux-test-打包完成通知   \n    \n ###  任务id:[${{ github.run_id }}](https://github.com/chat2db/Chat2DB/actions/runs/${{ github.run_id }})   \n ### Linux下载地址:[https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.AppImage](https://oss.sqlgpt.cn/release/${{ steps.chat2db_version.outputs.substring }}/Chat2DB-${{ steps.chat2db_version.outputs.substring }}.AppImage)"
-            }
+            }
\ No newline at end of file
From d383444213d5510b9e46294ec044ef36f676da3d Mon Sep 17 00:00:00 2001
From: SwallowGG <1558143046@qq.com>
Date: Tue, 29 Oct 2024 17:33:01 +0800
Subject: [PATCH 6/8] delete demo data error
---
 .../main/resources/db/migration/V2_1_10__REMOVEdEMO.sql    | 7 +++++++
 1 file changed, 7 insertions(+)
 create mode 100644 chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__REMOVEdEMO.sql
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__REMOVEdEMO.sql b/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__REMOVEdEMO.sql
new file mode 100644
index 000000000..b0f5f6300
--- /dev/null
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-repository/src/main/resources/db/migration/V2_1_10__REMOVEdEMO.sql
@@ -0,0 +1,7 @@
+delete from DATA_SOURCE where ALIAS ='DEMO@db.sqlgpt.cn';
+
+delete from DASHBOARD where id =ID;
+
+delete from CHART where id<=3;
+
+delete  from DASHBOARD_CHART_RELATION where CHART_ID<=3;
From aca27de139fe97e76636c86ebc131df09614263b Mon Sep 17 00:00:00 2001
From: tmlx1990 
Date: Thu, 21 Nov 2024 12:37:20 +0800
Subject: [PATCH 7/8] add duckdb
---
 .../ConnectionEdit/config/dataSource.ts       |  40 ++
 .../components/ConnectionEdit/config/enum.ts  |   1 +
 chat2db-client/src/constants/common.ts        |   1 +
 chat2db-client/src/constants/database.ts      |   7 +
 .../chat2db-plugins/chat2db-duckdb/pom.xml    |  35 ++
 .../chat2db/plugin/duckdb/DuckDBManage.java   | 253 +++++++++++
 .../chat2db/plugin/duckdb/DuckDBMetaData.java | 360 +++++++++++++++
 .../chat2db/plugin/duckdb/DuckDBPlugin.java   |  25 ++
 .../duckdb/builder/DuckDBSqlBuilder.java      | 424 ++++++++++++++++++
 .../chat2db/plugin/duckdb/builder/form.json   |  45 ++
 .../java/ai/chat2db/plugin/duckdb/duckDB.json |  18 +
 .../duckdb/type/DuckDBColumnTypeEnum.java     | 316 +++++++++++++
 .../duckdb/type/DuckDBDefaultValueEnum.java   |  27 ++
 .../duckdb/type/DuckDBIndexTypeEnum.java      | 129 ++++++
 .../META-INF/services/ai.chat2db.spi.Plugin   |   1 +
 chat2db-server/chat2db-plugins/pom.xml        |   1 +
 .../chat2db-server-domain-core/pom.xml        |   5 +
 17 files changed, 1688 insertions(+)
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/pom.xml
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBPlugin.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/form.json
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/duckDB.json
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBDefaultValueEnum.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin
diff --git a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
index c95ca70ef..ec9ec7486 100644
--- a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
+++ b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
@@ -2104,4 +2104,44 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
     },
     ssh: sshConfig,
   },
+  // DUCKDB
+  {
+    type: DatabaseTypeCode.DUCKDB,
+    baseInfo: {
+      items: [
+        {
+          defaultValue: '@localhost',
+          inputType: InputType.INPUT,
+          labelNameCN: '名称',
+          labelNameEN: 'Name',
+          name: 'alias',
+          required: true,
+        },
+        envItem,
+        {
+          defaultValue: 'localhost',
+          inputType: InputType.INPUT,
+          labelNameCN: '路径',
+          labelNameEN: 'Host',
+          name: 'host',
+          required: true,
+          styles: {
+            width: '70%',
+          },
+        },
+        {
+          defaultValue: 'jdbc:duckdb:{filePath}',
+          inputType: InputType.INPUT,
+          labelNameCN: 'URL',
+          labelNameEN: 'URL',
+          name: 'url',
+          required: true,
+        },
+      ],
+      pattern: /jdbc:duckdb:\/\/(\w+)/,
+      template: 'jdbc:duckdb://{host}',
+      excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable, OperationColumn.EditTable],
+    },
+    ssh: sshConfig,
+  },
 ];
diff --git a/chat2db-client/src/components/ConnectionEdit/config/enum.ts b/chat2db-client/src/components/ConnectionEdit/config/enum.ts
index 6c0e425cf..e66e37e93 100644
--- a/chat2db-client/src/components/ConnectionEdit/config/enum.ts
+++ b/chat2db-client/src/components/ConnectionEdit/config/enum.ts
@@ -2,6 +2,7 @@ export enum InputType {
   INPUT = 'input',
   PASSWORD = 'password',
   SELECT = 'select',
+  FILE = 'file',
 }
 
 export enum AuthenticationType {
diff --git a/chat2db-client/src/constants/common.ts b/chat2db-client/src/constants/common.ts
index 44824008d..8ea26bcbf 100644
--- a/chat2db-client/src/constants/common.ts
+++ b/chat2db-client/src/constants/common.ts
@@ -16,6 +16,7 @@ export enum DatabaseTypeCode {
   HIVE = 'HIVE',
   KINGBASE = 'KINGBASE',
   TIMEPLUS = 'TIMEPLUS',
+  DUCKDB = 'DUCKDB',
 }
 
 export enum ConsoleStatus {
diff --git a/chat2db-client/src/constants/database.ts b/chat2db-client/src/constants/database.ts
index e586cb883..7f174ed5e 100644
--- a/chat2db-client/src/constants/database.ts
+++ b/chat2db-client/src/constants/database.ts
@@ -125,6 +125,13 @@ export const databaseMap: {
     // port: 8123,
     icon: '\ue8f4',
   },
+  [DatabaseTypeCode.DUCKDB]: {
+    name: 'DuckDB',
+    img: moreDBLogo,
+    code: DatabaseTypeCode.DUCKDB,
+    // port: 8123,
+    icon: '\ue8f4',
+  },
   // [DatabaseTypeCode.REDIS]: {
   //   name: 'Redis',
   //   img: moreDBLogo,
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/pom.xml b/chat2db-server/chat2db-plugins/chat2db-duckdb/pom.xml
new file mode 100644
index 000000000..7eddf5958
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/pom.xml
@@ -0,0 +1,35 @@
+
+
+
+    
+        ai.chat2db
+        chat2db-plugins
+        ${revision}
+        ../pom.xml
+    
+    4.0.0
+    chat2db-duckdb
+
+    
+        
+            ai.chat2db
+            chat2db-spi
+        
+    
+    
+        
+            
+                src/main/java
+                
+                    
+                    **/*.json
+                
+            
+            
+                src/main/resources
+            
+        
+    
+
\ No newline at end of file
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
new file mode 100644
index 000000000..2014da51b
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
@@ -0,0 +1,253 @@
+package ai.chat2db.plugin.duckdb;
+
+import ai.chat2db.spi.DBManage;
+import ai.chat2db.spi.jdbc.DefaultDBManage;
+import ai.chat2db.spi.model.AsyncContext;
+import ai.chat2db.spi.model.Function;
+import ai.chat2db.spi.model.Procedure;
+import ai.chat2db.spi.sql.SQLExecutor;
+import cn.hutool.core.date.DateUtil;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.util.StringUtils;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Date;
+
+import static cn.hutool.core.date.DatePattern.NORM_DATETIME_PATTERN;
+
+@Slf4j
+public class DuckDBManage extends DefaultDBManage implements DBManage {
+
+    private static String PROCEDURE_SQL = "SELECT COUNT(*) FROM INFORMATION_SCHEMA.ROUTINES " +
+            "WHERE ROUTINE_SCHEMA = '%s' AND ROUTINE_NAME = '%s' AND ROUTINE_TYPE = 'PROCEDURE'";
+
+    @Override
+    public void exportDatabase(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {
+        asyncContext.write(String.format(EXPORT_TITLE, DateUtil.format(new Date(), NORM_DATETIME_PATTERN)));
+        exportTables(connection, databaseName, schemaName, asyncContext);
+        asyncContext.setProgress(50);
+        exportViews(connection, databaseName, asyncContext);
+        asyncContext.setProgress(60);
+        exportProcedures(connection, asyncContext);
+        asyncContext.setProgress(70);
+        exportTriggers(connection, asyncContext);
+        asyncContext.setProgress(90);
+        exportFunctions(connection, databaseName, asyncContext);
+        asyncContext.finish();
+    }
+
+    private void exportFunctions(Connection connection, String databaseName, AsyncContext asyncContext) throws SQLException {
+        try (ResultSet resultSet = connection.getMetaData().getFunctions(databaseName, null, null)) {
+            while (resultSet.next()) {
+                exportFunction(connection, resultSet.getString("FUNCTION_NAME"), asyncContext);
+            }
+
+        }
+    }
+
+    private void exportFunction(Connection connection, String functionName, AsyncContext asyncContext) throws SQLException {
+        String sql = String.format("SHOW CREATE FUNCTION %s;", functionName);
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            if (resultSet.next()) {
+                asyncContext.write(String.format(FUNCTION_TITLE, functionName));
+                StringBuilder sqlBuilder = new StringBuilder();
+                sqlBuilder.append("DROP FUNCTION IF EXISTS ").append(functionName).append(";").append("\n");
+
+                sqlBuilder.append("delimiter ;;").append("\n").append(resultSet.getString("Create Function")).append(";;")
+                        .append("\n").append("delimiter ;").append("\n\n");
+                asyncContext.write(sqlBuilder.toString());
+            }
+        }
+    }
+
+    private void exportTables(Connection connection, String databaseName, String schemaName, AsyncContext asyncContext) throws SQLException {
+        asyncContext.write("SET FOREIGN_KEY_CHECKS=0;");
+        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{"TABLE", "SYSTEM TABLE"})) {
+            while (resultSet.next()) {
+                String tableName = resultSet.getString("TABLE_NAME");
+                exportTable(connection, databaseName, schemaName, tableName, asyncContext);
+            }
+        }
+        asyncContext.write("SET FOREIGN_KEY_CHECKS=1;");
+    }
+
+
+    public void exportTable(Connection connection, String databaseName, String schemaName, String tableName, AsyncContext asyncContext) throws SQLException {
+        String sql = String.format("show create table %s ", tableName);
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            if (resultSet.next()) {
+                StringBuilder sqlBuilder = new StringBuilder();
+                asyncContext.write(String.format(TABLE_TITLE, tableName));
+                sqlBuilder.append("DROP TABLE IF EXISTS ").append(format(tableName)).append(";").append("\n")
+                        .append(resultSet.getString("Create Table")).append(";").append("\n");
+                asyncContext.write(sqlBuilder.toString());
+                if (asyncContext.isContainsData()) {
+                    exportTableData(connection, databaseName, schemaName, tableName, asyncContext);
+                }
+            }
+        } catch (Exception e) {
+            log.error("export table error", e);
+            asyncContext.error(String.format("export table %s error:%s", tableName, e.getMessage()));
+        }
+    }
+
+
+    private void exportViews(Connection connection, String databaseName, AsyncContext asyncContext) throws SQLException {
+        try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, null, null, new String[]{"VIEW"})) {
+            while (resultSet.next()) {
+                exportView(connection, resultSet.getString("TABLE_NAME"), asyncContext);
+            }
+        }
+    }
+
+    private void exportView(Connection connection, String viewName, AsyncContext asyncContext) throws SQLException {
+        String sql = String.format("show create view %s ", viewName);
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            if (resultSet.next()) {
+                asyncContext.write(String.format(VIEW_TITLE, viewName));
+                StringBuilder sqlBuilder = new StringBuilder();
+                sqlBuilder.append("DROP VIEW IF EXISTS ").append(format(viewName)).append(";").append("\n")
+                        .append(resultSet.getString("Create View")).append(";").append("\n\n");
+                asyncContext.write(sqlBuilder.toString());
+            }
+        }
+    }
+
+    private void exportProcedures(Connection connection, AsyncContext asyncContext) throws SQLException {
+        String sql = "SHOW PROCEDURE STATUS WHERE Db = DATABASE()";
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            while (resultSet.next()) {
+                exportProcedure(connection, resultSet.getString("Name"), asyncContext);
+            }
+        }
+    }
+
+    private void exportProcedure(Connection connection, String procedureName, AsyncContext asyncContext) throws SQLException {
+        String sql = String.format("show create procedure %s ", procedureName);
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            if (resultSet.next()) {
+                asyncContext.write(String.format(PROCEDURE_TITLE, procedureName));
+                StringBuilder sqlBuilder = new StringBuilder();
+                sqlBuilder.append("DROP PROCEDURE IF EXISTS ").append(format(procedureName)).append(";").append("\n")
+                        .append("delimiter ;;").append("\n").append(resultSet.getString("Create Procedure")).append(";;")
+                        .append("\n").append("delimiter ;").append("\n\n");
+                asyncContext.write(sqlBuilder.toString());
+            }
+        }
+    }
+
+    private void exportTriggers(Connection connection, AsyncContext asyncContext) throws SQLException {
+        String sql = "SHOW TRIGGERS";
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            while (resultSet.next()) {
+                String triggerName = resultSet.getString("Trigger");
+                exportTrigger(connection, triggerName, asyncContext);
+            }
+        }
+    }
+
+    private void exportTrigger(Connection connection, String triggerName, AsyncContext asyncContext) throws SQLException {
+        String sql = String.format("show create trigger %s ", triggerName);
+        try (ResultSet resultSet = connection.createStatement().executeQuery(sql)) {
+            if (resultSet.next()) {
+                asyncContext.write(String.format(TRIGGER_TITLE, triggerName));
+                StringBuilder sqlBuilder = new StringBuilder();
+                sqlBuilder.append("DROP TRIGGER IF EXISTS ").append(format(triggerName)).append(";").append("\n")
+                        .append("delimiter ;;").append("\n").append(resultSet.getString("SQL Original Statement")).append(";;")
+                        .append("\n").append("delimiter ;").append("\n\n");
+                asyncContext.write(sqlBuilder.toString());
+            }
+        }
+    }
+
+    @Override
+    public void updateProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) throws SQLException {
+        try {
+            connection.setAutoCommit(false);
+            String procedureBody = procedure.getProcedureBody();
+            if (procedureBody == null || !procedureBody.trim().toUpperCase().startsWith("CREATE")) {
+                throw new IllegalArgumentException("No CREATE statement found.");
+            }
+
+            String procedureNewName = getSchemaOrProcedureName(procedureBody, databaseName, procedure);
+            if (!procedureNewName.equals(procedure.getProcedureName())) {
+                procedureBody = procedureBody.replace(procedure.getProcedureName(), procedureNewName);
+            }
+            String checkProcedureSQL = String.format(PROCEDURE_SQL, databaseName, procedure.getProcedureName());
+            SQLExecutor.getInstance().execute(connection, checkProcedureSQL, resultSet -> {
+                try {
+                    if (resultSet.next()) {
+                        int count = resultSet.getInt(1);
+                        if (count >= 1) {
+                            throw new Exception("Procedure already exists");
+                        }
+                    }
+                } catch (Exception e) {
+                    e.printStackTrace();
+                }
+            });
+            SQLExecutor.getInstance().execute(connection, procedureBody, resultSet -> {});
+        } catch (Exception e) {
+            connection.rollback();
+            throw new RuntimeException(e);
+        } finally {
+            connection.setAutoCommit(true);
+        }
+
+    }
+
+    @Override
+    public void connectDatabase(Connection connection, String database) {
+        if (StringUtils.isEmpty(database)) {
+            return;
+        }
+        try {
+            SQLExecutor.getInstance().execute(connection, "use `" + database + "`;");
+        } catch (SQLException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+
+    @Override
+    public void dropTable(Connection connection, String databaseName, String schemaName, String tableName) {
+        String sql = "DROP TABLE " + format(tableName);
+        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);
+    }
+
+    @Override
+    public void deleteProcedure(Connection connection, String databaseName, String schemaName, Procedure procedure) {
+        String procedureNewName = getSchemaOrProcedureName(procedure.getProcedureBody(), databaseName, procedure);
+        String sql = "DROP PROCEDURE " + procedureNewName;
+        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);
+    }
+
+    @Override
+    public void deleteFunction(Connection connection, String databaseName, String schemaName, Function function) {
+        String functionNewName = getSchemaOrFunctionName(function.getFunctionBody(), databaseName, function);
+        String sql = "DROP FUNCTION " + functionNewName;
+        SQLExecutor.getInstance().execute(connection, sql, resultSet -> null);
+    }
+
+    private static String getSchemaOrProcedureName(String procedureBody, String schemaName, Procedure procedure) {
+        if (procedureBody.toLowerCase().contains(schemaName.toLowerCase())) {
+            return procedure.getProcedureName();
+        } else {
+            return schemaName + "." + procedure.getProcedureName();
+        }
+    }
+
+    private static String getSchemaOrFunctionName(String functionBody, String schemaName, Function function) {
+        if (functionBody.toLowerCase().contains(schemaName.toLowerCase())) {
+            return function.getFunctionName();
+        } else {
+            return schemaName + "." + function.getFunctionName();
+        }
+    }
+
+    public static String format(String tableName) {
+        return "`" + tableName + "`";
+    }
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
new file mode 100644
index 000000000..1e0bfb194
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
@@ -0,0 +1,360 @@
+package ai.chat2db.plugin.duckdb;
+
+import ai.chat2db.plugin.duckdb.builder.DuckDBSqlBuilder;
+import ai.chat2db.plugin.duckdb.type.DuckDBColumnTypeEnum;
+import ai.chat2db.plugin.duckdb.type.DuckDBDefaultValueEnum;
+import ai.chat2db.plugin.duckdb.type.DuckDBIndexTypeEnum;
+import ai.chat2db.spi.MetaData;
+import ai.chat2db.spi.SqlBuilder;
+import ai.chat2db.spi.jdbc.DefaultMetaService;
+import ai.chat2db.spi.model.*;
+import ai.chat2db.spi.sql.SQLExecutor;
+import jakarta.validation.constraints.NotEmpty;
+import org.apache.commons.lang3.StringUtils;
+
+import java.sql.Connection;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.*;
+import java.util.stream.Collectors;
+
+import static ai.chat2db.spi.util.SortUtils.sortDatabase;
+
+public class DuckDBMetaData extends DefaultMetaService implements MetaData {
+
+    private List systemDatabases = Arrays.asList("information_schema", "performance_schema", "mysql", "sys");
+
+    @Override
+    public List databases(Connection connection) {
+        List databases = SQLExecutor.getInstance().databases(connection);
+        return sortDatabase(databases, systemDatabases, connection);
+    }
+
+
+    private static String TABLES_SQL
+            = "SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, DATA_LENGTH, AUTO_INCREMENT, CREATE_TIME, UPDATE_TIME, TABLE_COLLATION, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = '%s'";
+    @Override
+    public List tables(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName) {
+        String sql = String.format(TABLES_SQL, databaseName);
+        if(StringUtils.isNotBlank(tableName)){
+            sql += " AND TABLE_NAME = '" + tableName + "'";
+        }
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            List tables = new ArrayList<>();
+            while (resultSet.next()) {
+                Table table = new Table();
+                table.setDatabaseName(databaseName);
+                table.setSchemaName(schemaName);
+                table.setName(resultSet.getString("TABLE_NAME"));
+                table.setEngine(resultSet.getString("ENGINE"));
+                table.setRows(resultSet.getLong("TABLE_ROWS"));
+                table.setDataLength(resultSet.getLong("DATA_LENGTH"));
+                table.setCreateTime(resultSet.getString("CREATE_TIME"));
+                table.setUpdateTime(resultSet.getString("UPDATE_TIME"));
+                table.setCollate(resultSet.getString("TABLE_COLLATION"));
+                table.setComment(resultSet.getString("TABLE_COMMENT"));
+                tables.add(table);
+            }
+            return tables;
+        });
+    }
+
+
+    @Override
+    public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
+                           @NotEmpty String tableName) {
+        String sql = "SHOW CREATE TABLE " + format(databaseName) + "."
+                + format(tableName);
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            if (resultSet.next()) {
+                return resultSet.getString("Create Table");
+            }
+            return null;
+        });
+    }
+
+    public static String format(String tableName) {
+        return "`" + tableName + "`";
+    }
+
+    private static String ROUTINES_SQL
+            =
+            "SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE "
+                    + "routine_type = '%s' AND ROUTINE_SCHEMA ='%s'  AND "
+                    + "routine_name = '%s';";
+
+    @Override
+    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
+                             String functionName) {
+
+        String functionInfoSql = String.format(ROUTINES_SQL, "FUNCTION", databaseName, functionName);
+        Function function = SQLExecutor.getInstance().execute(connection, functionInfoSql, resultSet -> {
+            Function f = new Function();
+            f.setDatabaseName(databaseName);
+            f.setSchemaName(schemaName);
+            f.setFunctionName(functionName);
+            if (resultSet.next()) {
+                f.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
+                f.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
+            }
+            return f;
+        });
+        String functionDDlSql = String.format("SHOW CREATE FUNCTION %s", functionName);
+        SQLExecutor.getInstance().execute(connection, functionDDlSql, resultSet -> {
+            if (resultSet.next()) {
+                function.setFunctionBody(resultSet.getString("Create Function"));
+            }
+        });
+        return function;
+
+    }
+
+    private static String TRIGGER_SQL
+            = "SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT  FROM INFORMATION_SCHEMA.TRIGGERS where "
+            + "TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';";
+
+    private static String TRIGGER_SQL_LIST
+            = "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';";
+
+    @Override
+    public List triggers(Connection connection, String databaseName, String schemaName) {
+        List triggers = new ArrayList<>();
+        String sql = String.format(TRIGGER_SQL_LIST, databaseName);
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            while (resultSet.next()) {
+                Trigger trigger = new Trigger();
+                trigger.setTriggerName(resultSet.getString("TRIGGER_NAME"));
+                trigger.setSchemaName(schemaName);
+                trigger.setDatabaseName(databaseName);
+                triggers.add(trigger);
+            }
+            return triggers;
+        });
+    }
+
+
+    @Override
+    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
+                           String triggerName) {
+
+        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            Trigger trigger = new Trigger();
+            trigger.setDatabaseName(databaseName);
+            trigger.setSchemaName(schemaName);
+            trigger.setTriggerName(triggerName);
+            if (resultSet.next()) {
+                trigger.setTriggerBody(resultSet.getString("ACTION_STATEMENT"));
+            }
+            return trigger;
+        });
+    }
+
+    @Override
+    public List procedures(Connection connection, String databaseName, String schemaName) {
+        String sql = "SHOW PROCEDURE STATUS WHERE Db = DATABASE()";
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            ArrayList procedures = new ArrayList<>();
+            while (resultSet.next()) {
+                Procedure procedure = new Procedure();
+                procedure.setProcedureName(resultSet.getString("Name"));
+                procedures.add(procedure);
+            }
+            return procedures;
+        });
+    }
+
+    @Override
+    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
+                               String procedureName) {
+        String routinesSql = String.format(ROUTINES_SQL, "PROCEDURE", databaseName, procedureName);
+        String showCreateProcedureSql = "SHOW CREATE PROCEDURE " + procedureName;
+        Procedure procedure = SQLExecutor.getInstance().execute(connection, routinesSql, resultSet -> {
+            Procedure p = new Procedure();
+            p.setDatabaseName(databaseName);
+            p.setSchemaName(schemaName);
+            p.setProcedureName(procedureName);
+            if (resultSet.next()) {
+                p.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
+                p.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
+            }
+            return p;
+        });
+        SQLExecutor.getInstance().execute(connection, showCreateProcedureSql, resultSet -> {
+            if (resultSet.next()) {
+                procedure.setProcedureBody(resultSet.getString("Create Procedure"));
+            }
+        });
+        return procedure;
+    }
+
+    private static String SELECT_TABLE_COLUMNS = "SELECT * FROM information_schema.COLUMNS  WHERE TABLE_SCHEMA =  '%s'  AND TABLE_NAME =  '%s'  order by ORDINAL_POSITION";
+
+    @Override
+    public List columns(Connection connection, String databaseName, String schemaName, String tableName) {
+        String sql = String.format(SELECT_TABLE_COLUMNS, databaseName, tableName);
+        List tableColumns = new ArrayList<>();
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            while (resultSet.next()) {
+                TableColumn column = new TableColumn();
+                column.setDatabaseName(databaseName);
+                column.setTableName(tableName);
+                column.setOldName(resultSet.getString("COLUMN_NAME"));
+                column.setName(resultSet.getString("COLUMN_NAME"));
+                //column.setColumnType(resultSet.getString("COLUMN_TYPE"));
+                column.setColumnType(resultSet.getString("DATA_TYPE").toUpperCase());
+                //column.setDataType(resultSet.getInt("DATA_TYPE"));
+                column.setDefaultValue(resultSet.getString("COLUMN_DEFAULT"));
+                column.setAutoIncrement(resultSet.getString("EXTRA").contains("auto_increment"));
+                column.setComment(resultSet.getString("COLUMN_COMMENT"));
+                column.setPrimaryKey("PRI".equalsIgnoreCase(resultSet.getString("COLUMN_KEY")));
+                column.setNullable("YES".equalsIgnoreCase(resultSet.getString("IS_NULLABLE")) ? 1 : 0);
+                column.setOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
+                column.setDecimalDigits(resultSet.getInt("NUMERIC_SCALE"));
+                column.setCharSetName(resultSet.getString("CHARACTER_SET_NAME"));
+                column.setCollationName(resultSet.getString("COLLATION_NAME"));
+                setColumnSize(column, resultSet.getString("COLUMN_TYPE"));
+                tableColumns.add(column);
+            }
+            return tableColumns;
+        });
+    }
+
+    private void setColumnSize(TableColumn column, String columnType) {
+        try {
+            if (columnType.contains("(")) {
+                String size = columnType.substring(columnType.indexOf("(") + 1, columnType.indexOf(")"));
+                if ("SET".equalsIgnoreCase(column.getColumnType()) || "ENUM".equalsIgnoreCase(column.getColumnType())) {
+                    column.setValue(size);
+                } else {
+                    if (size.contains(",")) {
+                        String[] sizes = size.split(",");
+                        if (StringUtils.isNotBlank(sizes[0])) {
+                            column.setColumnSize(Integer.parseInt(sizes[0]));
+                        }
+                        if (StringUtils.isNotBlank(sizes[1])) {
+                            column.setDecimalDigits(Integer.parseInt(sizes[1]));
+                        }
+                    } else {
+                        column.setColumnSize(Integer.parseInt(size));
+                    }
+                }
+            }
+        } catch (Exception e) {
+        }
+    }
+
+    private static String VIEW_DDL_SQL = "show create view %s";
+
+    @Override
+    public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
+        String sql = String.format(VIEW_DDL_SQL, viewName);
+        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
+            Table table = new Table();
+            table.setDatabaseName(databaseName);
+            table.setSchemaName(schemaName);
+            table.setName(viewName);
+            if (resultSet.next()) {
+                table.setDdl(resultSet.getString("Create View"));
+            }
+            return table;
+        });
+    }
+
+
+    @Override
+    public List indexes(Connection connection, String databaseName, String schemaName, String tableName) {
+        StringBuilder queryBuf = new StringBuilder("SHOW INDEX FROM ");
+        queryBuf.append("`").append(tableName).append("`");
+        queryBuf.append(" FROM ");
+        queryBuf.append("`").append(databaseName).append("`");
+        return SQLExecutor.getInstance().execute(connection, queryBuf.toString(), resultSet -> {
+            LinkedHashMap map = new LinkedHashMap();
+            while (resultSet.next()) {
+                String keyName = resultSet.getString("Key_name");
+                TableIndex tableIndex = map.get(keyName);
+                if (tableIndex != null) {
+                    List columnList = tableIndex.getColumnList();
+                    columnList.add(getTableIndexColumn(resultSet));
+                    columnList = columnList.stream().sorted(Comparator.comparing(TableIndexColumn::getOrdinalPosition))
+                            .collect(Collectors.toList());
+                    tableIndex.setColumnList(columnList);
+                } else {
+                    TableIndex index = new TableIndex();
+                    index.setDatabaseName(databaseName);
+                    index.setSchemaName(schemaName);
+                    index.setTableName(tableName);
+                    index.setName(keyName);
+                    index.setUnique(!resultSet.getBoolean("Non_unique"));
+                    index.setType(resultSet.getString("Index_type"));
+                    index.setComment(resultSet.getString("Index_comment"));
+                    List tableIndexColumns = new ArrayList<>();
+                    tableIndexColumns.add(getTableIndexColumn(resultSet));
+                    index.setColumnList(tableIndexColumns);
+                    if ("PRIMARY".equalsIgnoreCase(keyName)) {
+                        index.setType(DuckDBIndexTypeEnum.PRIMARY_KEY.getName());
+                    } else if (index.getUnique()) {
+                        index.setType(DuckDBIndexTypeEnum.UNIQUE.getName());
+                    } else {
+                        index.setType(DuckDBIndexTypeEnum.NORMAL.getName());
+                    }
+                    map.put(keyName, index);
+                }
+            }
+            return map.values().stream().collect(Collectors.toList());
+        });
+
+    }
+
+    private TableIndexColumn getTableIndexColumn(ResultSet resultSet) throws SQLException {
+        TableIndexColumn tableIndexColumn = new TableIndexColumn();
+        tableIndexColumn.setColumnName(resultSet.getString("Column_name"));
+        tableIndexColumn.setOrdinalPosition(resultSet.getShort("Seq_in_index"));
+        tableIndexColumn.setCollation(resultSet.getString("Collation"));
+        tableIndexColumn.setCardinality(resultSet.getLong("Cardinality"));
+        tableIndexColumn.setSubPart(resultSet.getLong("Sub_part"));
+        String collation = resultSet.getString("Collation");
+        if ("a".equalsIgnoreCase(collation)) {
+            tableIndexColumn.setAscOrDesc("ASC");
+        } else if ("d".equalsIgnoreCase(collation)) {
+            tableIndexColumn.setAscOrDesc("DESC");
+        }
+        return tableIndexColumn;
+    }
+
+    @Override
+    public SqlBuilder getSqlBuilder() {
+        return new DuckDBSqlBuilder();
+    }
+
+    @Override
+    public TableMeta getTableMeta(String databaseName, String schemaName, String tableName) {
+        return TableMeta.builder()
+                .columnTypes(DuckDBColumnTypeEnum.getTypes())
+                //.collations(MysqlCollationEnum.getCollations())
+                .indexTypes(DuckDBIndexTypeEnum.getIndexTypes())
+                .defaultValues(DuckDBDefaultValueEnum.getDefaultValues())
+                .build();
+    }
+
+    @Override
+    public String getMetaDataName(String... names) {
+        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> "`" + name + "`").collect(Collectors.joining("."));
+    }
+
+//    @Override
+//    public ValueHandler getValueHandler() {
+//        return new MysqlValueHandler();
+//    }
+
+    /*@Override
+    public ValueProcessor getValueProcessor() {
+        return new DuckDBValueProcessor();
+    }*/
+
+    @Override
+    public List getSystemDatabases() {
+        return systemDatabases;
+    }
+
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBPlugin.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBPlugin.java
new file mode 100644
index 000000000..648ecd3aa
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBPlugin.java
@@ -0,0 +1,25 @@
+package ai.chat2db.plugin.duckdb;
+
+import ai.chat2db.spi.DBManage;
+import ai.chat2db.spi.MetaData;
+import ai.chat2db.spi.Plugin;
+import ai.chat2db.spi.config.DBConfig;
+import ai.chat2db.spi.util.FileUtils;
+
+public class DuckDBPlugin implements Plugin {
+
+    @Override
+    public DBConfig getDBConfig() {
+        return FileUtils.readJsonValue(this.getClass(),"duckDB.json", DBConfig.class);
+    }
+
+    @Override
+    public MetaData getMetaData() {
+        return new DuckDBMetaData();
+    }
+
+    @Override
+    public DBManage getDBManage() {
+        return new DuckDBManage();
+    }
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
new file mode 100644
index 000000000..d6474d9b8
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
@@ -0,0 +1,424 @@
+package ai.chat2db.plugin.duckdb.builder;
+
+import ai.chat2db.plugin.duckdb.type.DuckDBColumnTypeEnum;
+import ai.chat2db.plugin.duckdb.type.DuckDBIndexTypeEnum;
+import ai.chat2db.spi.enums.EditStatus;
+import ai.chat2db.spi.jdbc.DefaultSqlBuilder;
+import ai.chat2db.spi.model.Database;
+import ai.chat2db.spi.model.Table;
+import ai.chat2db.spi.model.TableColumn;
+import ai.chat2db.spi.model.TableIndex;
+import ai.chat2db.spi.util.SqlUtils;
+import org.apache.commons.collections4.CollectionUtils;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.*;
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.stream.Collectors;
+
+
+public class DuckDBSqlBuilder extends DefaultSqlBuilder {
+    @Override
+    public String buildCreateTableSql(Table table) {
+        StringBuilder script = new StringBuilder();
+        script.append("CREATE TABLE ");
+        if (StringUtils.isNotBlank(table.getDatabaseName())) {
+            script.append("`").append(table.getDatabaseName()).append("`").append(".");
+        }
+        script.append("`").append(table.getName()).append("`").append(" (").append("\n");
+
+        // append column
+        for (TableColumn column : table.getColumnList()) {
+            if (StringUtils.isBlank(column.getName()) || StringUtils.isBlank(column.getColumnType())) {
+                continue;
+            }
+            DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
+            if (typeEnum == null) {
+                continue;
+            }
+            script.append("\t").append(typeEnum.buildCreateColumnSql(column)).append(",\n");
+        }
+
+        // append primary key and index
+        for (TableIndex tableIndex : table.getIndexList()) {
+            if (StringUtils.isBlank(tableIndex.getName()) || StringUtils.isBlank(tableIndex.getType())) {
+                continue;
+            }
+            DuckDBIndexTypeEnum mysqlIndexTypeEnum = DuckDBIndexTypeEnum.getByType(tableIndex.getType());
+            if (mysqlIndexTypeEnum == null) {
+                continue;
+            }
+            script.append("\t").append("").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append(",\n");
+        }
+
+        script = new StringBuilder(script.substring(0, script.length() - 2));
+        script.append("\n)");
+
+
+        if (StringUtils.isNotBlank(table.getEngine())) {
+            script.append(" ENGINE=").append(table.getEngine());
+        }
+
+        if (StringUtils.isNotBlank(table.getCharset())) {
+            script.append(" DEFAULT CHARACTER SET=").append(table.getCharset());
+        }
+
+        if (StringUtils.isNotBlank(table.getCollate())) {
+            script.append(" COLLATE=").append(table.getCollate());
+        }
+
+        if (table.getIncrementValue() != null) {
+            script.append(" AUTO_INCREMENT=").append(table.getIncrementValue());
+        }
+
+        if (StringUtils.isNotBlank(table.getComment())) {
+            script.append(" COMMENT='").append(table.getComment()).append("'");
+        }
+
+        if (StringUtils.isNotBlank(table.getPartition())) {
+            script.append(" \n").append(table.getPartition());
+        }
+        script.append(";");
+
+        return script.toString();
+    }
+
+    @Override
+    public String buildModifyTaleSql(Table oldTable, Table newTable) {
+        StringBuilder tableBuilder = new StringBuilder();
+        tableBuilder.append("ALTER TABLE ");
+        if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
+            tableBuilder.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
+        }
+        tableBuilder.append("`").append(oldTable.getName()).append("`").append("\n");
+
+        StringBuilder script = new StringBuilder();
+        if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {
+            script.append("\t").append("RENAME TO ").append("`").append(newTable.getName()).append("`").append(",\n");
+        }
+        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {
+            script.append("\t").append("COMMENT=").append("'").append(newTable.getComment()).append("'").append(",\n");
+        }
+        if (oldTable.getIncrementValue() != newTable.getIncrementValue()) {
+            script.append("\t").append("AUTO_INCREMENT=").append(newTable.getIncrementValue()).append(",\n");
+        }
+
+        // 判断新增字段
+        List addColumnList = new ArrayList<>();
+        for (TableColumn tableColumn : newTable.getColumnList()) {
+            if (tableColumn.getEditStatus() != null ? tableColumn.getEditStatus().equals("ADD") : false) {
+                addColumnList.add(tableColumn);
+            }
+        }
+
+        // 判断移动的字段
+        List moveColumnList = movedElements(oldTable.getColumnList(), newTable.getColumnList());
+
+        // append modify column
+        for (TableColumn tableColumn : newTable.getColumnList()) {
+            if ((StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType())
+                    && StringUtils.isNotBlank(tableColumn.getName())) || moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {
+                DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(tableColumn.getColumnType());
+                if (typeEnum == null) {
+                    continue;
+                }
+                if (moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {
+                    script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(",\n");
+                } else {
+                    script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(",\n");
+                }
+            }
+        }
+
+        // append modify index
+        for (TableIndex tableIndex : newTable.getIndexList()) {
+            if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {
+                DuckDBIndexTypeEnum mysqlIndexTypeEnum = DuckDBIndexTypeEnum.getByType(tableIndex.getType());
+                if (mysqlIndexTypeEnum == null) {
+                    continue;
+                }
+                script.append("\t").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(",\n");
+            }
+        }
+
+        // append reorder column
+        // script.append(buildGenerateReorderColumnSql(oldTable, newTable));
+
+        if (script.length() > 2) {
+            script = new StringBuilder(script.substring(0, script.length() - 2));
+            script.append(";");
+            return tableBuilder.append(script).toString();
+        } else {
+            return StringUtils.EMPTY;
+        }
+
+    }
+
+    private String findPrevious(TableColumn tableColumn, Table newTable) {
+        int index = newTable.getColumnList().indexOf(tableColumn);
+        if (index == 0) {
+            return "-1";
+        }
+        // Find the previous column that is not deleted
+        for (int i = index - 1; i >= 0; i--) {
+            if (newTable.getColumnList().get(i).getEditStatus() == null || !newTable.getColumnList().get(i).getEditStatus().equals(EditStatus.DELETE.name())) {
+                return newTable.getColumnList().get(i).getName();
+            }
+        }
+        return "-1";
+    }
+
+    @Override
+    public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
+        StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
+        sqlBuilder.append(sql);
+        if (offset == 0) {
+            sqlBuilder.append("\n LIMIT ");
+            sqlBuilder.append(pageSize);
+        } else {
+            sqlBuilder.append("\n LIMIT ");
+            sqlBuilder.append(offset);
+            sqlBuilder.append(",");
+            sqlBuilder.append(pageSize);
+        }
+        return sqlBuilder.toString();
+    }
+
+
+    @Override
+    public String buildCreateDatabaseSql(Database database) {
+        StringBuilder sqlBuilder = new StringBuilder();
+        sqlBuilder.append("CREATE DATABASE `" + database.getName() + "`");
+        if (StringUtils.isNotBlank(database.getCharset())) {
+            sqlBuilder.append(" DEFAULT CHARACTER SET=").append(database.getCharset());
+        }
+        if (StringUtils.isNotBlank(database.getCollation())) {
+            sqlBuilder.append(" COLLATE=").append(database.getCollation());
+        }
+        return sqlBuilder.toString();
+    }
+
+    public static List movedElements(List original, List modified) {
+        int[][] dp = new int[original.size() + 1][modified.size() + 1];
+
+        // 构建DP表
+        for (int i = 1; i <= original.size(); i++) {
+            for (int j = 1; j <= modified.size(); j++) {
+                if (original.get(i - 1).getName().equals(modified.get(j - 1).getOldName())) {
+                    dp[i][j] = dp[i - 1][j - 1] + 1;
+                } else {
+                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
+                }
+            }
+        }
+
+        // 追踪LCS,找出移动了位置的元素
+        List moved = new ArrayList<>();
+        int i = original.size();
+        int j = modified.size();
+        while (i > 0 && j > 0) {
+            if (original.get(i - 1).equals(modified.get(j - 1))) {
+                i--;
+                j--;
+            } else if (dp[i - 1][j] >= dp[i][j - 1]) {
+                moved.add(original.get(i - 1));
+                // modified List中找到original.get(i-1)的位置
+//                System.out.println("Moved elements:"+ original.get(i-1).getName() + " after " + modified.indexOf(original.get(i-1)) );
+                i--;
+            } else {
+                j--;
+            }
+        }
+
+        // 这里添加原始列表中未被包含在LCS中的元素
+        while (i > 0) {
+            moved.add(original.get(i - 1));
+            i--;
+        }
+
+        return moved;
+    }
+
+    public String buildGenerateReorderColumnSql(Table oldTable, Table newTable) {
+        StringBuilder sql = new StringBuilder();
+        int n = 0;
+        // Create a map to store the index of each column in the old table's column list
+        Map oldColumnIndexMap = new HashMap<>();
+        for (int i = 0; i < oldTable.getColumnList().size(); i++) {
+            oldColumnIndexMap.put(oldTable.getColumnList().get(i).getName(), i);
+        }
+        String[] oldColumnArray = oldTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);
+        String[] newColumnArray = newTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);
+
+        Set oldColumnSet = new HashSet<>(Arrays.asList(oldColumnArray));
+        Set newColumnSet = new HashSet<>(Arrays.asList(newColumnArray));
+        if (!oldColumnSet.equals(newColumnSet)) {
+            return "";
+        }
+
+        buildSql(oldColumnArray, newColumnArray, sql, oldTable, newTable, n);
+
+        return sql.toString();
+    }
+
+    private String[] buildSql(String[] originalArray, String[] targetArray, StringBuilder sql, Table oldTable, Table newTable, int n) {
+        // Complete the first move first
+        if (!originalArray[0].equals(targetArray[0])) {
+            int a = findIndex(originalArray, targetArray[0]);
+            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();
+            String[] newArray = moveElement(originalArray, a, 0, targetArray, new AtomicInteger(0));
+            sql.append(" MODIFY COLUMN ");
+            DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
+            sql.append(typeEnum.buildModifyColumn(column));
+            sql.append(" FIRST;\n");
+            n++;
+            if (Arrays.equals(newArray, targetArray)) {
+                return newArray;
+            }
+            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
+            if (Arrays.equals(resultArray, targetArray)) {
+                return resultArray;
+            }
+        }
+
+        // After completing the last move
+        int max = originalArray.length - 1;
+        if (!originalArray[max].equals(targetArray[max])) {
+            int a = findIndex(originalArray, targetArray[max]);
+            //System.out.println("Move " + originalArray[a] + " after " + (a > 0 ? originalArray[max] : "start"));
+            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();
+            String[] newArray = moveElement(originalArray, a, max, targetArray, new AtomicInteger(0));
+            if (n > 0) {
+                sql.append("ALTER TABLE ");
+                if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
+                    sql.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
+                }
+                sql.append("`").append(oldTable.getName()).append("`").append("\n");
+            }
+            sql.append(" MODIFY COLUMN ");
+            DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
+            sql.append(typeEnum.buildModifyColumn(column));
+            sql.append(" AFTER ");
+            sql.append(oldTable.getColumnList().get(max).getName());
+            sql.append(";\n");
+            n++;
+            if (Arrays.equals(newArray, targetArray)) {
+                return newArray;
+            }
+            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
+            if (Arrays.equals(resultArray, targetArray)) {
+                return resultArray;
+            }
+        }
+
+
+        for (int i = 0; i < originalArray.length; i++) {
+            int a = findIndex(targetArray, originalArray[i]);
+            if (i != a && isMoveValid(originalArray, targetArray, i, a)) {
+                // Find name a in oldTable.getColumnList
+                int finalI = i;
+                TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[finalI])).findFirst().get();
+                if (n > 0) {
+                    sql.append("ALTER TABLE ");
+                    if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
+                        sql.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
+                    }
+                    sql.append("`").append(oldTable.getName()).append("`").append("\n");
+                }
+                sql.append(" MODIFY COLUMN ");
+                DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
+                sql.append(typeEnum.buildModifyColumn(column));
+                sql.append(" AFTER ");
+                AtomicInteger continuousDataCount = new AtomicInteger(0);
+                String[] newArray = moveElement(originalArray, i, a, targetArray, continuousDataCount);
+                if (i < a) {
+                    sql.append(originalArray[a + continuousDataCount.get()]);
+                } else {
+                    sql.append(originalArray[a - 1]);
+                }
+
+                sql.append(";\n");
+                n++;
+
+                if (Arrays.equals(newArray, targetArray)) {
+                    return newArray;
+                }
+                String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
+                if (Arrays.equals(resultArray, targetArray)) {
+                    return resultArray;
+                }
+            }
+        }
+        return null;
+    }
+
+    private static int findIndex(String[] array, String element) {
+        for (int i = 0; i < array.length; i++) {
+            if (array[i].equals(element)) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    private static boolean isMoveValid(String[] originalArray, String[] targetArray, int i, int a) {
+        return ((i == 0 || a == 0 || !originalArray[i - 1].equals(targetArray[a - 1])) &&
+                (i >= originalArray.length - 1 || a >= targetArray.length - 1 || !originalArray[i + 1].equals(targetArray[a + 1])))
+                || (i > 0 && a > 0 && !originalArray[i - 1].equals(targetArray[a - 1]));
+    }
+
+    private static String[] moveElement(String[] originalArray, int from, int to, String[] targetArray, AtomicInteger continuousDataCount) {
+        String[] newArray = new String[originalArray.length];
+        System.arraycopy(originalArray, 0, newArray, 0, originalArray.length);
+        String temp = newArray[from];
+        // 是否有连续移动数据
+        boolean isContinuousData = false;
+        // 连续数据数量
+        if (from < to) {
+            for (int i = to; i < originalArray.length - 1; i++) {
+                if (originalArray[i + 1].equals(targetArray[findIndex(targetArray, originalArray[i]) + 1])) {
+                    continuousDataCount.set(continuousDataCount.incrementAndGet());
+                } else {
+                    break;
+                }
+            }
+            if (continuousDataCount.get() > 0) {
+                System.arraycopy(originalArray, from + 1, newArray, from, to - from + 1);
+                isContinuousData = true;
+            } else {
+                System.arraycopy(originalArray, from + 1, newArray, from, to - from);
+            }
+        } else {
+            System.arraycopy(originalArray, to, newArray, to + 1, from - to);
+        }
+        if (isContinuousData) {
+            newArray[to + continuousDataCount.get()] = temp;
+        } else {
+            newArray[to] = temp;
+        }
+        return newArray;
+    }
+
+
+    @Override
+    protected void buildTableName(String databaseName, String schemaName, String tableName, StringBuilder script) {
+        if (StringUtils.isNotBlank(databaseName)) {
+            script.append(SqlUtils.quoteObjectName(databaseName, "`")).append('.');
+        }
+        script.append(SqlUtils.quoteObjectName(tableName, "`"));
+    }
+
+    /**
+     * @param columnList
+     * @param script
+     */
+    @Override
+    protected void buildColumns(List columnList, StringBuilder script) {
+        if (CollectionUtils.isNotEmpty(columnList)) {
+            script.append(" (")
+                    .append(columnList.stream().map(s -> SqlUtils.quoteObjectName(s, "`")).collect(Collectors.joining(",")))
+                    .append(") ");
+        }
+    }
+
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/form.json b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/form.json
new file mode 100644
index 000000000..e24020fbe
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/form.json
@@ -0,0 +1,45 @@
+{
+  "baseInfo": {
+    "items": [
+      {
+        "defaultValue": "@localhost",
+        "inputType": "INPUT",
+        "labelNameCN": "名称",
+        "labelNameEN": "Name",
+        "name": "alias",
+        "required": true,
+        "width": 100,
+      },
+      {
+        "defaultValue": "localhost",
+        "inputType": "INPUT",
+        "labelNameCN": "主机",
+        "labelNameEN": "Host",
+        "name": "host",
+        "required": true,
+        "width": 70,
+      },
+      {
+        "defaultValue": "",
+        "inputType": "INPUT",
+        "labelNameCN": "数据库",
+        "labelNameEN": "Database",
+        "name": "database",
+        "required": false,
+        "width": 100
+      },
+      {
+        "defaultValue": "jdbc:duckdb:{file}",
+        "inputType": "INPUT",
+        "labelNameCN": "URL",
+        "labelNameEN": "URL",
+        "name": "url",
+        "required": true,
+        "width": 100
+      }
+    ],
+    "pattern": "/jdbc:duckdb:\/\/(\\w+)",
+    "template": "jdbc:duckdb://{host}"
+  },
+  "type":"DuckDB"
+}
\ No newline at end of file
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/duckDB.json b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/duckDB.json
new file mode 100644
index 000000000..0c4dec309
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/duckDB.json
@@ -0,0 +1,18 @@
+{
+  "dbType": "DUCKDB",
+  "supportDatabase": true,
+  "supportSchema": true,
+  "driverConfigList": [
+    {
+      "url": "jdbc:duckdb://",
+      "defaultDriver": true,
+      "custom": false,
+      "downloadJdbcDriverUrls": [
+        "https://cdn.chat2db-ai.com/lib/duckdb_jdbc-1.1.3.jar"
+      ],
+      "jdbcDriver": "duckdb_jdbc-1.1.3.jar",
+      "jdbcDriverClass": "org.duckdb.DuckDBDriver"
+    }
+  ],
+  "name": "DuckDB"
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
new file mode 100644
index 000000000..749f0d8c3
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
@@ -0,0 +1,316 @@
+package ai.chat2db.plugin.duckdb.type;
+
+import ai.chat2db.spi.ColumnBuilder;
+import ai.chat2db.spi.enums.EditStatus;
+import ai.chat2db.spi.model.ColumnType;
+import ai.chat2db.spi.model.TableColumn;
+import ai.chat2db.spi.util.SqlUtils;
+import com.google.common.collect.Maps;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+import java.util.Map;
+
+public enum DuckDBColumnTypeEnum implements ColumnBuilder {
+
+    JSON("JSON", false, false, true, false, false, false, true, true, false, false),
+
+    BIGINT("BIGINT", false, false, true, false, false, false, true, true, false, false),
+
+    BINARY("BINARY", false, false, true, false, false, false, true, true, false, false),
+
+    BIT("BIT", false, false, true, false, false, false, true, true, false, false),
+
+    BLOB("BLOB", false, false, true, false, false, false, true, true, false, false),
+
+    BOOL("BOOL", false, false, true, false, false, false, true, true, false, false),
+
+    BOOLEAN("BOOLEAN", false, false, true, false, false, false, true, true, false, false),
+
+    BPCHAR("BPCHAR", false, false, true, false, false, false, true, true, false, false),
+
+    BYTEA("BYTEA", false, false, true, false, false, false, true, true, false, false),
+
+    CHAR("CHAR", true, false, true, false, false, false, true, true, false, false),
+
+    DATE("DATE", false, false, true, false, false, false, true, true, false, false),
+
+    DATETIME("DATETIME", false, false, true, false, false, false, true, true, false, false),
+
+    DEC("DEC", false, false, true, false, false, false, true, true, false, false),
+
+    DECIMAL("DECIMAL", true, true, true, false, false, false, true, true, false, false),
+
+    DOUBLE("DOUBLE", false, false, true, false, false, false, true, true, false, false),
+
+    ENUM("ENUM", false, false, true, false, false, false, true, true, false, false),
+
+    FLOAT("FLOAT", true, false, true, false, false, false, true, true, false, false),
+
+    FLOAT4("FLOAT4", true, false, true, false, false, false, true, true, false, false),
+
+    FLOAT8("FLOAT8", true, false, true, false, false, false, true, true, false, false),
+
+    GUID("GUID", false, false, true, false, false, false, true, true, false, false),
+
+    HUGEINT("HUGEINT", false, false, true, true, false, false, true, true, false, false),
+
+    INT("INT", false, false, true, false, false, false, true, true, false, false),
+    INT1("INT1", false, false, true, false, false, false, true, true, false, false),
+    INT128("INT128", false, false, true, false, false, false, true, true, false, false),
+    INT16("INT16", false, false, true, false, false, false, true, true, false, false),
+    INT2("INT2", false, false, true, false, false, false, true, true, false, false),
+    INT32("INT32", false, false, true, false, false, false, true, true, false, false),
+    INT4("INT4", false, false, true, false, false, false, true, true, false, false),
+    INT64("INT64", false, false, true, false, false, false, true, true, false, false),
+    INT8("INT8", false, false, true, false, false, false, true, true, false, false),
+
+    INTEGER("INTEGER", false, false, true, false, false, false, true, true, false, false),
+
+    INTEGRAL("INTEGRAL", false, false, true, false, false, false, true, true, false, false),
+    INTERVAL("INTERVAL", false, false, true, false, false, false, true, true, false, false),
+
+    LIST("LIST", false, false, true, false, false, false, true, true, false, false),
+
+    LOGICAL("LOGICAL", false, false, true, false, false, false, true, true, false, false),
+
+    LONG("LONG", false, false, true, false, false, false, true, true, false, false),
+
+    MAP("MAP", false, false, true, false, false, false, true, true, false, false),
+
+    NULL("NULL", false, false, true, false, false, false, true, true, false, false),
+
+    NUMERIC("NUMERIC", false, false, true, false, false, false, true, true, false, false),
+
+    NVARCHAR("NVARCHAR", true, false, true, false, false, false, true, true, false, false),
+
+    OID("OID", false, false, true, false, false, false, true, true, false, false),
+
+    REAL("REAL", false, false, true, false, false, false, true, true, false, false),
+
+    ROW("ROW", false, false, true, false, false, false, true, true, false, false),
+
+    SHORT("SHORT", false, false, true, false, false, false, true, true, false, false),
+
+    SIGNED("SIGNED", false, false, true, false, false, false, true, true, false, false),
+
+    SMALLINT("SMALLINT", false, false, true, false, false, false, true, true, false, false),
+
+    STRING("STRING", false, false, true, false, false, false, true, true, false, false),
+
+    STRUCT("STRUCT", false, false, true, false, false, false, true, true, false, false),
+
+    TEXT("TEXT", false, false, true, false, false, false, true, true, false, false),
+
+    TIME("TIME", false, false, true, false, false, false, true, true, false, false),
+
+    TIMESTAMP("TIMESTAMP", false, false, true, false, false, false, true, true, false, false),
+
+    TIMSTAMP_MS("TIMSTAMP_MS", false, false, true, false, false, false, true, true, false, false),
+
+    TIMSTAMP_NS("TIMSTAMP_NS", false, false, true, false, false, false, true, true, false, false),
+    TIMSTAMP_S("TIMSTAMP_S", false, false, true, false, false, false, true, true, false, false),
+    TIMSTAMP_US("TIMSTAMP_US", false, false, true, false, false, false, true, true, false, false),
+
+    TIMESTAMP_WITH_TIME_ZONE("TIMESTAMP WITH TIME ZONE", false, false, true, false, false, false, true, true, false, false),
+
+    TIME_WITH_TIME_ZONE("TIME WITH TIME ZONE", false, false, true, false, false, false, true, true, false, false),
+
+
+    TINYINT("TINYINT", false, false, true, false, false, false, true, true, false, false),
+
+    UBIGINT("UBIGINT", false, false, true, false, false, false, true, true, false, false),
+
+    UHUGEINT("UHUGEINT", false, false, true, false, false, false, true, true, false, false),
+
+    UINT128("UINT128", false, false, true, false, false, false, true, true, false, false),
+
+    UINT16("UINT16", false, false, true, false, false, false, true, true, false, false),
+
+    UINT32("UINT32", false, false, true, false, false, false, true, true, false, false),
+
+    UINT64("UINT64", false, false, true, false, false, false, true, true, false, false),
+
+    UINT8("UINT8", false, false, true, false, false, false, true, true, false, false),
+
+    UINTEGER("UINTEGER", false, false, true, false, false, false, true, true, false, false),
+
+    UNION("UNION", false, false, true, false, false, false, true, true, false, false),
+
+    USMALLINT("USMALLINT", false, false, true, false, false, false, true, true, false, false),
+
+    UTINYINT("UTINYINT", false, false, true, false, false, false, true, true, false, false),
+
+    UUID("UUID", false, false, true, false, false, false, true, true, false, false),
+
+    VARBINARY("VARBINARY", false, false, true, false, false, false, true, true, false, false),
+
+    VARCHAR("VARCHAR", true, false, true, false, false, false, true, true, false, true),
+
+    VARINT("VARINT", false, false, true, false, false, false, true, true, false, false),
+
+    ARRAY("ARRAY", false, false, true, false, false, false, true, true, false, false),
+    ;
+    private ColumnType columnType;
+
+    public static DuckDBColumnTypeEnum getByType(String dataType) {
+        String type = SqlUtils.removeDigits(dataType.toUpperCase());
+        return COLUMN_TYPE_MAP.get(type);
+    }
+
+    private static Map COLUMN_TYPE_MAP = Maps.newHashMap();
+
+    static {
+        for (DuckDBColumnTypeEnum value : DuckDBColumnTypeEnum.values()) {
+            COLUMN_TYPE_MAP.put(value.getColumnType().getTypeName(), value);
+        }
+    }
+
+    public ColumnType getColumnType() {
+        return columnType;
+    }
+
+
+    DuckDBColumnTypeEnum(String dataTypeName, boolean supportLength, boolean supportScale, boolean supportNullable, boolean supportAutoIncrement, boolean supportCharset, boolean supportCollation, boolean supportComments, boolean supportDefaultValue, boolean supportExtent, boolean supportUnit) {
+        this.columnType = new ColumnType(dataTypeName, supportLength, supportScale, supportNullable, supportAutoIncrement, supportCharset, supportCollation, supportComments, supportDefaultValue, supportExtent, false, supportUnit);
+    }
+
+    @Override
+    public String buildCreateColumnSql(TableColumn column) {
+        DuckDBColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());
+        if (type == null) {
+            return "";
+        }
+        StringBuilder script = new StringBuilder();
+
+        script.append("\"").append(column.getName()).append("\"").append(" ");
+
+        script.append(buildDataType(column, type)).append(" ");
+
+        script.append(buildDefaultValue(column, type)).append(" ");
+
+        script.append(buildAutoIncrement(column,type)).append(" ");
+
+        script.append(buildNullable(column, type)).append(" ");
+
+        return script.toString();
+    }
+
+    private String buildAutoIncrement(TableColumn column, DuckDBColumnTypeEnum type) {
+        if(!type.getColumnType().isSupportAutoIncrement()){
+            return "";
+        }
+        if (column.getAutoIncrement() != null && column.getAutoIncrement()
+                && column.getSeed() != null && column.getSeed() > 0 && column.getIncrement() != null && column.getIncrement() > 0) {
+            return "IDENTITY(" + column.getSeed() + "," + column.getIncrement() + ")";
+        }
+        if (column.getAutoIncrement() != null && column.getAutoIncrement()) {
+            return "IDENTITY(1,1)";
+        }
+        return "";
+    }
+
+    private String buildNullable(TableColumn column, DuckDBColumnTypeEnum type) {
+        if (!type.getColumnType().isSupportNullable()) {
+            return "";
+        }
+        if (column.getNullable() != null && 1 == column.getNullable()) {
+            return "NULL";
+        } else {
+            return "NOT NULL";
+        }
+    }
+
+    private String buildDefaultValue(TableColumn column, DuckDBColumnTypeEnum type) {
+        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {
+            return "";
+        }
+
+        if ("EMPTY_STRING".equalsIgnoreCase(column.getDefaultValue().trim())) {
+            return StringUtils.join("DEFAULT ''");
+        }
+
+        if ("NULL".equalsIgnoreCase(column.getDefaultValue().trim())) {
+            return StringUtils.join("DEFAULT NULL");
+        }
+
+        return StringUtils.join("DEFAULT ", column.getDefaultValue());
+    }
+
+    private String buildDataType(TableColumn column, DuckDBColumnTypeEnum type) {
+        String columnType = type.columnType.getTypeName();
+        if (Arrays.asList(VARCHAR, STRING, BPCHAR, NVARCHAR, TEXT).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            script.append(columnType);
+            if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) {
+                script.append("(").append(column.getColumnSize()).append(")");
+            } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) {
+                script.append("(").append(column.getColumnSize()).append(" ").append(column.getUnit()).append(")");
+            }
+            return script.toString();
+        }
+
+        if (Arrays.asList(DECIMAL, FLOAT, TIMESTAMP).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            script.append(columnType);
+            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {
+                script.append("(").append(column.getColumnSize()).append(")");
+            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {
+                script.append("(").append(column.getColumnSize()).append(",").append(column.getDecimalDigits()).append(")");
+            }
+            return script.toString();
+        }
+
+        if (Arrays.asList(TIME_WITH_TIME_ZONE, TIMSTAMP_US).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            if (column.getColumnSize() == null) {
+                script.append(columnType);
+            } else {
+                String[] split = columnType.split("TIMESTAMP");
+                script.append("TIMESTAMP").append("(").append(column.getColumnSize()).append(")").append(split[1]);
+            }
+            return script.toString();
+        }
+        return columnType;
+    }
+
+
+    @Override
+    public String buildModifyColumn(TableColumn tableColumn) {
+
+        if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {
+            StringBuilder script = new StringBuilder();
+            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
+            script.append(" ").append("DROP COLUMN ").append("\"").append(tableColumn.getName()).append("\"");
+            return script.toString();
+        }
+        if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {
+            StringBuilder script = new StringBuilder();
+            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
+            script.append(" ").append("ADD (").append(buildCreateColumnSql(tableColumn)).append(")");
+            return script.toString();
+        }
+        if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {
+            StringBuilder script = new StringBuilder();
+            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
+            script.append(" ").append("MODIFY (").append(buildCreateColumnSql(tableColumn)).append(") \n");
+
+            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {
+                script.append(";");
+                script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
+                script.append(" ").append("RENAME COLUMN ").append("\"").append(tableColumn.getOldName()).append("\"").append(" TO ").append("\"").append(tableColumn.getName()).append("\"");
+
+            }
+            return script.toString();
+
+        }
+        return "";
+    }
+
+    public static List getTypes() {
+        return Arrays.stream(DuckDBColumnTypeEnum.values()).map(columnTypeEnum ->
+                columnTypeEnum.getColumnType()
+        ).toList();
+    }
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBDefaultValueEnum.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBDefaultValueEnum.java
new file mode 100644
index 000000000..ee1152776
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBDefaultValueEnum.java
@@ -0,0 +1,27 @@
+package ai.chat2db.plugin.duckdb.type;
+
+import ai.chat2db.spi.model.DefaultValue;
+
+import java.util.Arrays;
+import java.util.List;
+
+public enum DuckDBDefaultValueEnum {
+    EMPTY_STRING("EMPTY_STRING"),
+    NULL("NULL"),
+    ;
+    private DefaultValue defaultValue;
+
+    DuckDBDefaultValueEnum(String defaultValue) {
+        this.defaultValue = new DefaultValue(defaultValue);
+    }
+
+
+    public DefaultValue getDefaultValue() {
+        return defaultValue;
+    }
+
+    public static List getDefaultValues() {
+        return Arrays.stream(DuckDBDefaultValueEnum.values()).map(DuckDBDefaultValueEnum::getDefaultValue).collect(java.util.stream.Collectors.toList());
+    }
+
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
new file mode 100644
index 000000000..20e5ad388
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
@@ -0,0 +1,129 @@
+package ai.chat2db.plugin.duckdb.type;
+
+import ai.chat2db.spi.enums.EditStatus;
+import ai.chat2db.spi.model.IndexType;
+import ai.chat2db.spi.model.TableIndex;
+import ai.chat2db.spi.model.TableIndexColumn;
+import org.apache.commons.lang3.StringUtils;
+
+import java.util.Arrays;
+import java.util.List;
+
+public enum DuckDBIndexTypeEnum {
+
+    PRIMARY_KEY("Primary", "PRIMARY KEY"),
+
+    NORMAL("Normal", "INDEX"),
+
+    UNIQUE("Unique", "UNIQUE INDEX"),
+
+    BITMAP("BITMAP", "BITMAP INDEX");
+
+
+
+    public IndexType getIndexType() {
+        return indexType;
+    }
+
+    public void setIndexType(IndexType indexType) {
+        this.indexType = indexType;
+    }
+
+    private IndexType indexType;
+
+
+    public String getName() {
+        return name;
+    }
+
+    private String name;
+
+
+    public String getKeyword() {
+        return keyword;
+    }
+
+    private String keyword;
+
+    DuckDBIndexTypeEnum(String name, String keyword) {
+        this.name = name;
+        this.keyword = keyword;
+        this.indexType = new IndexType(name);
+    }
+
+
+    public static DuckDBIndexTypeEnum getByType(String type) {
+        for (DuckDBIndexTypeEnum value : DuckDBIndexTypeEnum.values()) {
+            if (value.name.equalsIgnoreCase(type)) {
+                return value;
+            }
+        }
+        return null;
+    }
+
+    public String buildIndexScript(TableIndex tableIndex) {
+        StringBuilder script = new StringBuilder();
+        if (PRIMARY_KEY.equals(this)) {
+            script.append("ALTER TABLE \"").append(tableIndex.getSchemaName()).append("\".\"").append(tableIndex.getTableName()).append("\" ADD PRIMARY KEY ").append(buildIndexColumn(tableIndex));
+        } else {
+            if (UNIQUE.equals(this)) {
+                script.append("CREATE UNIQUE INDEX ");
+            } else {
+                script.append("CREATE INDEX ");
+            }
+            script.append(buildIndexName(tableIndex)).append(" ON \"").append(tableIndex.getSchemaName()).append("\".\"").append(tableIndex.getTableName()).append("\" ").append(buildIndexColumn(tableIndex));
+        }
+        return script.toString();
+    }
+
+
+    private String buildIndexColumn(TableIndex tableIndex) {
+        StringBuilder script = new StringBuilder();
+        script.append("(");
+        for (TableIndexColumn column : tableIndex.getColumnList()) {
+            if (StringUtils.isNotBlank(column.getColumnName())) {
+                script.append("\"").append(column.getColumnName()).append("\"");
+                if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {
+                    script.append(" ").append(column.getAscOrDesc());
+                }
+                script.append(",");
+            }
+        }
+        script.deleteCharAt(script.length() - 1);
+        script.append(")");
+        return script.toString();
+    }
+
+    private String buildIndexName(TableIndex tableIndex) {
+        return "\"" + tableIndex.getSchemaName() + "\"." + "\"" + tableIndex.getName() + "\"";
+    }
+
+    public String buildModifyIndex(TableIndex tableIndex) {
+        if (EditStatus.DELETE.name().equals(tableIndex.getEditStatus())) {
+            return buildDropIndex(tableIndex);
+        }
+        if (EditStatus.MODIFY.name().equals(tableIndex.getEditStatus())) {
+            return StringUtils.join(buildDropIndex(tableIndex), ";\n", buildIndexScript(tableIndex));
+        }
+        if (EditStatus.ADD.name().equals(tableIndex.getEditStatus())) {
+            return StringUtils.join(buildIndexScript(tableIndex));
+        }
+        return "";
+    }
+
+    private String buildDropIndex(TableIndex tableIndex) {
+        if (DuckDBIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {
+            String tableName = "\"" + tableIndex.getSchemaName() + "\"." + "\"" + tableIndex.getTableName() + "\"";
+            return StringUtils.join("ALTER TABLE ",tableName," DROP PRIMARY KEY");
+        }
+        StringBuilder script = new StringBuilder();
+        script.append("DROP INDEX ");
+        script.append(buildIndexName(tableIndex));
+
+        return script.toString();
+    }
+
+    public static List getIndexTypes() {
+        return Arrays.asList(DuckDBIndexTypeEnum.values()).stream().map(DuckDBIndexTypeEnum::getIndexType).collect(java.util.stream.Collectors.toList());
+    }
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin
new file mode 100644
index 000000000..46d0d0d83
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/resources/META-INF/services/ai.chat2db.spi.Plugin
@@ -0,0 +1 @@
+ai.chat2db.plugin.duckdb.DuckDBPlugin
\ No newline at end of file
diff --git a/chat2db-server/chat2db-plugins/pom.xml b/chat2db-server/chat2db-plugins/pom.xml
index a98b555cb..878eddd17 100644
--- a/chat2db-server/chat2db-plugins/pom.xml
+++ b/chat2db-server/chat2db-plugins/pom.xml
@@ -30,6 +30,7 @@
         chat2db-hive
         chat2db-kingbase
         chat2db-timeplus
+        chat2db-duckdb
     
 
 
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml
index 39d5c2a2d..7fdb052b2 100644
--- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/pom.xml
@@ -126,6 +126,11 @@
             chat2db-timeplus
             ${revision}
         
+        
+            ai.chat2db
+            chat2db-duckdb
+            ${version}
+        
         
             commons-codec
             commons-codec
From 13acc0e98afb5d5c9f6ad64d09772b10c29d8d74 Mon Sep 17 00:00:00 2001
From: tmlx1990 
Date: Fri, 29 Nov 2024 00:27:16 +0800
Subject: [PATCH 8/8] add duckdb
---
 .../ConnectionEdit/config/dataSource.ts       |   1 -
 .../plugin/duckdb/DuckDBCommandExecutor.java  |  17 +
 .../chat2db/plugin/duckdb/DuckDBManage.java   |   2 +-
 .../chat2db/plugin/duckdb/DuckDBMetaData.java | 187 +++--------
 .../duckdb/builder/DuckDBSqlBuilder.java      | 305 ++----------------
 .../duckdb/type/DuckDBColumnTypeEnum.java     | 129 +++++++-
 .../duckdb/type/DuckDBIndexTypeEnum.java      |  20 +-
 .../domain/core/impl/TableServiceImpl.java    |   2 +-
 8 files changed, 218 insertions(+), 445 deletions(-)
 create mode 100644 chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBCommandExecutor.java
diff --git a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
index ec9ec7486..28f51acd7 100644
--- a/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
+++ b/chat2db-client/src/components/ConnectionEdit/config/dataSource.ts
@@ -2140,7 +2140,6 @@ export const dataSourceFormConfigs: IConnectionConfig[] = [
       ],
       pattern: /jdbc:duckdb:\/\/(\w+)/,
       template: 'jdbc:duckdb://{host}',
-      excludes: [OperationColumn.ViewDDL, OperationColumn.CreateTable, OperationColumn.EditTable],
     },
     ssh: sshConfig,
   },
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBCommandExecutor.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBCommandExecutor.java
new file mode 100644
index 000000000..faf5a0bd7
--- /dev/null
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBCommandExecutor.java
@@ -0,0 +1,17 @@
+package ai.chat2db.plugin.duckdb;
+
+import ai.chat2db.spi.model.Command;
+import ai.chat2db.spi.model.ExecuteResult;
+import ai.chat2db.spi.sql.SQLExecutor;
+
+import java.util.List;
+
+public class DuckDBCommandExecutor extends SQLExecutor {
+
+    @Override
+    public List executeSelectTable(Command command) {
+        String sql = "select * from " +command.getSchemaName() + "." + command.getTableName();
+        command.setScript(sql);
+        return execute(command);
+    }
+}
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
index 2014da51b..b4c117534 100644
--- a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBManage.java
@@ -204,7 +204,7 @@ public void connectDatabase(Connection connection, String database) {
             return;
         }
         try {
-            SQLExecutor.getInstance().execute(connection, "use `" + database + "`;");
+            SQLExecutor.getInstance().execute(connection, "use " + database + ";");
         } catch (SQLException e) {
             throw new RuntimeException(e);
         }
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
index 1e0bfb194..50ef4bd34 100644
--- a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/DuckDBMetaData.java
@@ -4,6 +4,7 @@
 import ai.chat2db.plugin.duckdb.type.DuckDBColumnTypeEnum;
 import ai.chat2db.plugin.duckdb.type.DuckDBDefaultValueEnum;
 import ai.chat2db.plugin.duckdb.type.DuckDBIndexTypeEnum;
+import ai.chat2db.spi.CommandExecutor;
 import ai.chat2db.spi.MetaData;
 import ai.chat2db.spi.SqlBuilder;
 import ai.chat2db.spi.jdbc.DefaultMetaService;
@@ -22,7 +23,7 @@
 
 public class DuckDBMetaData extends DefaultMetaService implements MetaData {
 
-    private List systemDatabases = Arrays.asList("information_schema", "performance_schema", "mysql", "sys");
+    private List systemDatabases = Arrays.asList("information_schema", "temp", "main", "system");
 
     @Override
     public List databases(Connection connection) {
@@ -30,12 +31,17 @@ public List databases(Connection connection) {
         return sortDatabase(databases, systemDatabases, connection);
     }
 
+    @Override
+    public CommandExecutor getCommandExecutor() {
+        return new DuckDBCommandExecutor();
+    }
+
 
     private static String TABLES_SQL
-            = "SELECT TABLE_SCHEMA, TABLE_NAME, ENGINE, VERSION, TABLE_ROWS, DATA_LENGTH, AUTO_INCREMENT, CREATE_TIME, UPDATE_TIME, TABLE_COLLATION, TABLE_COMMENT FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_SCHEMA = '%s'";
+            = "SELECT * FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE' AND TABLE_CATALOG = '%s' AND TABLE_SCHEMA = '%s'";
     @Override
     public List tables(Connection connection, @NotEmpty String databaseName, String schemaName, String tableName) {
-        String sql = String.format(TABLES_SQL, databaseName);
+        String sql = String.format(TABLES_SQL, databaseName, schemaName);
         if(StringUtils.isNotBlank(tableName)){
             sql += " AND TABLE_NAME = '" + tableName + "'";
         }
@@ -45,13 +51,13 @@ public List tables(Connection connection, @NotEmpty String databaseName,
                 Table table = new Table();
                 table.setDatabaseName(databaseName);
                 table.setSchemaName(schemaName);
-                table.setName(resultSet.getString("TABLE_NAME"));
-                table.setEngine(resultSet.getString("ENGINE"));
-                table.setRows(resultSet.getLong("TABLE_ROWS"));
-                table.setDataLength(resultSet.getLong("DATA_LENGTH"));
-                table.setCreateTime(resultSet.getString("CREATE_TIME"));
-                table.setUpdateTime(resultSet.getString("UPDATE_TIME"));
-                table.setCollate(resultSet.getString("TABLE_COLLATION"));
+                table.setName(resultSet.getString("table_name"));
+                //table.setEngine(resultSet.getString("ENGINE"));
+                //table.setRows(resultSet.getLong("TABLE_ROWS"));
+                //table.setDataLength(resultSet.getLong("DATA_LENGTH"));
+                //table.setCreateTime(resultSet.getString("CREATE_TIME"));
+                //table.setUpdateTime(resultSet.getString("UPDATE_TIME"));
+                //table.setCollate(resultSet.getString("TABLE_COLLATION"));
                 table.setComment(resultSet.getString("TABLE_COMMENT"));
                 tables.add(table);
             }
@@ -63,157 +69,46 @@ public List tables(Connection connection, @NotEmpty String databaseName,
     @Override
     public String tableDDL(Connection connection, @NotEmpty String databaseName, String schemaName,
                            @NotEmpty String tableName) {
-        String sql = "SHOW CREATE TABLE " + format(databaseName) + "."
-                + format(tableName);
+        String sql = "SELECT sql FROM duckdb_tables() WHERE database_name = " + format(databaseName)
+                + " AND schema_name = " + format(schemaName) + " AND table_name = " + format(tableName);
         return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
             if (resultSet.next()) {
-                return resultSet.getString("Create Table");
+                return resultSet.getString("sql");
             }
             return null;
         });
     }
 
     public static String format(String tableName) {
-        return "`" + tableName + "`";
-    }
-
-    private static String ROUTINES_SQL
-            =
-            "SELECT SPECIFIC_NAME, ROUTINE_COMMENT, ROUTINE_DEFINITION FROM information_schema.routines WHERE "
-                    + "routine_type = '%s' AND ROUTINE_SCHEMA ='%s'  AND "
-                    + "routine_name = '%s';";
-
-    @Override
-    public Function function(Connection connection, @NotEmpty String databaseName, String schemaName,
-                             String functionName) {
-
-        String functionInfoSql = String.format(ROUTINES_SQL, "FUNCTION", databaseName, functionName);
-        Function function = SQLExecutor.getInstance().execute(connection, functionInfoSql, resultSet -> {
-            Function f = new Function();
-            f.setDatabaseName(databaseName);
-            f.setSchemaName(schemaName);
-            f.setFunctionName(functionName);
-            if (resultSet.next()) {
-                f.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
-                f.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
-            }
-            return f;
-        });
-        String functionDDlSql = String.format("SHOW CREATE FUNCTION %s", functionName);
-        SQLExecutor.getInstance().execute(connection, functionDDlSql, resultSet -> {
-            if (resultSet.next()) {
-                function.setFunctionBody(resultSet.getString("Create Function"));
-            }
-        });
-        return function;
-
-    }
-
-    private static String TRIGGER_SQL
-            = "SELECT TRIGGER_NAME,EVENT_MANIPULATION, ACTION_STATEMENT  FROM INFORMATION_SCHEMA.TRIGGERS where "
-            + "TRIGGER_SCHEMA = '%s' AND TRIGGER_NAME = '%s';";
-
-    private static String TRIGGER_SQL_LIST
-            = "SELECT TRIGGER_NAME FROM INFORMATION_SCHEMA.TRIGGERS where TRIGGER_SCHEMA = '%s';";
-
-    @Override
-    public List triggers(Connection connection, String databaseName, String schemaName) {
-        List triggers = new ArrayList<>();
-        String sql = String.format(TRIGGER_SQL_LIST, databaseName);
-        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
-            while (resultSet.next()) {
-                Trigger trigger = new Trigger();
-                trigger.setTriggerName(resultSet.getString("TRIGGER_NAME"));
-                trigger.setSchemaName(schemaName);
-                trigger.setDatabaseName(databaseName);
-                triggers.add(trigger);
-            }
-            return triggers;
-        });
-    }
-
-
-    @Override
-    public Trigger trigger(Connection connection, @NotEmpty String databaseName, String schemaName,
-                           String triggerName) {
-
-        String sql = String.format(TRIGGER_SQL, databaseName, triggerName);
-        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
-            Trigger trigger = new Trigger();
-            trigger.setDatabaseName(databaseName);
-            trigger.setSchemaName(schemaName);
-            trigger.setTriggerName(triggerName);
-            if (resultSet.next()) {
-                trigger.setTriggerBody(resultSet.getString("ACTION_STATEMENT"));
-            }
-            return trigger;
-        });
-    }
-
-    @Override
-    public List procedures(Connection connection, String databaseName, String schemaName) {
-        String sql = "SHOW PROCEDURE STATUS WHERE Db = DATABASE()";
-        return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
-            ArrayList procedures = new ArrayList<>();
-            while (resultSet.next()) {
-                Procedure procedure = new Procedure();
-                procedure.setProcedureName(resultSet.getString("Name"));
-                procedures.add(procedure);
-            }
-            return procedures;
-        });
-    }
-
-    @Override
-    public Procedure procedure(Connection connection, @NotEmpty String databaseName, String schemaName,
-                               String procedureName) {
-        String routinesSql = String.format(ROUTINES_SQL, "PROCEDURE", databaseName, procedureName);
-        String showCreateProcedureSql = "SHOW CREATE PROCEDURE " + procedureName;
-        Procedure procedure = SQLExecutor.getInstance().execute(connection, routinesSql, resultSet -> {
-            Procedure p = new Procedure();
-            p.setDatabaseName(databaseName);
-            p.setSchemaName(schemaName);
-            p.setProcedureName(procedureName);
-            if (resultSet.next()) {
-                p.setSpecificName(resultSet.getString("SPECIFIC_NAME"));
-                p.setRemarks(resultSet.getString("ROUTINE_COMMENT"));
-            }
-            return p;
-        });
-        SQLExecutor.getInstance().execute(connection, showCreateProcedureSql, resultSet -> {
-            if (resultSet.next()) {
-                procedure.setProcedureBody(resultSet.getString("Create Procedure"));
-            }
-        });
-        return procedure;
+        return "'" + tableName + "'";
     }
 
     private static String SELECT_TABLE_COLUMNS = "SELECT * FROM information_schema.COLUMNS  WHERE TABLE_SCHEMA =  '%s'  AND TABLE_NAME =  '%s'  order by ORDINAL_POSITION";
 
     @Override
     public List columns(Connection connection, String databaseName, String schemaName, String tableName) {
-        String sql = String.format(SELECT_TABLE_COLUMNS, databaseName, tableName);
+        String sql = String.format(SELECT_TABLE_COLUMNS, schemaName, tableName);
         List tableColumns = new ArrayList<>();
         return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
             while (resultSet.next()) {
                 TableColumn column = new TableColumn();
                 column.setDatabaseName(databaseName);
                 column.setTableName(tableName);
-                column.setOldName(resultSet.getString("COLUMN_NAME"));
-                column.setName(resultSet.getString("COLUMN_NAME"));
+                column.setOldName(resultSet.getString("column_name"));
+                column.setName(resultSet.getString("column_name"));
                 //column.setColumnType(resultSet.getString("COLUMN_TYPE"));
-                column.setColumnType(resultSet.getString("DATA_TYPE").toUpperCase());
+                column.setColumnType(resultSet.getString("data_type").toUpperCase());
                 //column.setDataType(resultSet.getInt("DATA_TYPE"));
-                column.setDefaultValue(resultSet.getString("COLUMN_DEFAULT"));
-                column.setAutoIncrement(resultSet.getString("EXTRA").contains("auto_increment"));
+                column.setDefaultValue(resultSet.getString("column_default"));
+                //column.setAutoIncrement(resultSet.getString("EXTRA").contains("auto_increment"));
                 column.setComment(resultSet.getString("COLUMN_COMMENT"));
-                column.setPrimaryKey("PRI".equalsIgnoreCase(resultSet.getString("COLUMN_KEY")));
-                column.setNullable("YES".equalsIgnoreCase(resultSet.getString("IS_NULLABLE")) ? 1 : 0);
-                column.setOrdinalPosition(resultSet.getInt("ORDINAL_POSITION"));
-                column.setDecimalDigits(resultSet.getInt("NUMERIC_SCALE"));
-                column.setCharSetName(resultSet.getString("CHARACTER_SET_NAME"));
-                column.setCollationName(resultSet.getString("COLLATION_NAME"));
-                setColumnSize(column, resultSet.getString("COLUMN_TYPE"));
+                //column.setPrimaryKey("PRI".equalsIgnoreCase(resultSet.getString("COLUMN_KEY")));
+                column.setNullable("YES".equalsIgnoreCase(resultSet.getString("is_nullable")) ? 1 : 0);
+                column.setOrdinalPosition(resultSet.getInt("ordinal_position"));
+                column.setDecimalDigits(resultSet.getInt("numeric_precision"));
+                column.setCharSetName(resultSet.getString("character_set_name"));
+                column.setCollationName(resultSet.getString("collation_name"));
+                setColumnSize(column, resultSet.getString("data_type"));
                 tableColumns.add(column);
             }
             return tableColumns;
@@ -244,18 +139,18 @@ private void setColumnSize(TableColumn column, String columnType) {
         }
     }
 
-    private static String VIEW_DDL_SQL = "show create view %s";
+    private static String VIEW_DDL_SQL = "SELECT sql FROM duckdb_views() WHERE database_name = '%s' AND schema_name = '%s' AND view_name = '%s'";
 
     @Override
     public Table view(Connection connection, String databaseName, String schemaName, String viewName) {
-        String sql = String.format(VIEW_DDL_SQL, viewName);
+        String sql = String.format(VIEW_DDL_SQL, databaseName, schemaName, viewName);
         return SQLExecutor.getInstance().execute(connection, sql, resultSet -> {
             Table table = new Table();
             table.setDatabaseName(databaseName);
             table.setSchemaName(schemaName);
             table.setName(viewName);
             if (resultSet.next()) {
-                table.setDdl(resultSet.getString("Create View"));
+                table.setDdl(resultSet.getString("sql"));
             }
             return table;
         });
@@ -264,10 +159,10 @@ public Table view(Connection connection, String databaseName, String schemaName,
 
     @Override
     public List indexes(Connection connection, String databaseName, String schemaName, String tableName) {
-        StringBuilder queryBuf = new StringBuilder("SHOW INDEX FROM ");
-        queryBuf.append("`").append(tableName).append("`");
-        queryBuf.append(" FROM ");
-        queryBuf.append("`").append(databaseName).append("`");
+        StringBuilder queryBuf = new StringBuilder("SELECT * FROM duckdb_indexes WHERE schema_name = ");
+        queryBuf.append("'").append(schemaName).append("'");
+        queryBuf.append(" and table_name = ");
+        queryBuf.append("'").append(tableName).append("'");
         return SQLExecutor.getInstance().execute(connection, queryBuf.toString(), resultSet -> {
             LinkedHashMap map = new LinkedHashMap();
             while (resultSet.next()) {
@@ -339,7 +234,7 @@ public TableMeta getTableMeta(String databaseName, String schemaName, String tab
 
     @Override
     public String getMetaDataName(String... names) {
-        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).map(name -> "`" + name + "`").collect(Collectors.joining("."));
+        return Arrays.stream(names).filter(name -> StringUtils.isNotBlank(name)).collect(Collectors.joining("."));
     }
 
 //    @Override
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
index d6474d9b8..6a981ad6c 100644
--- a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/builder/DuckDBSqlBuilder.java
@@ -22,10 +22,10 @@ public class DuckDBSqlBuilder extends DefaultSqlBuilder {
     public String buildCreateTableSql(Table table) {
         StringBuilder script = new StringBuilder();
         script.append("CREATE TABLE ");
-        if (StringUtils.isNotBlank(table.getDatabaseName())) {
-            script.append("`").append(table.getDatabaseName()).append("`").append(".");
+        if (StringUtils.isNotBlank(table.getSchemaName())) {
+            script.append(table.getSchemaName()).append(".");
         }
-        script.append("`").append(table.getName()).append("`").append(" (").append("\n");
+        script.append(table.getName()).append(" (").append("\n");
 
         // append column
         for (TableColumn column : table.getColumnList()) {
@@ -48,36 +48,18 @@ public String buildCreateTableSql(Table table) {
             if (mysqlIndexTypeEnum == null) {
                 continue;
             }
-            script.append("\t").append("").append(mysqlIndexTypeEnum.buildIndexScript(tableIndex)).append(",\n");
+            script.append("\t").append(mysqlIndexTypeEnum.buildCreateIndexScript(tableIndex)).append(",\n");
         }
 
         script = new StringBuilder(script.substring(0, script.length() - 2));
-        script.append("\n)");
+        script.append("\n);\n");
 
 
-        if (StringUtils.isNotBlank(table.getEngine())) {
-            script.append(" ENGINE=").append(table.getEngine());
-        }
-
-        if (StringUtils.isNotBlank(table.getCharset())) {
-            script.append(" DEFAULT CHARACTER SET=").append(table.getCharset());
-        }
-
-        if (StringUtils.isNotBlank(table.getCollate())) {
-            script.append(" COLLATE=").append(table.getCollate());
-        }
-
-        if (table.getIncrementValue() != null) {
-            script.append(" AUTO_INCREMENT=").append(table.getIncrementValue());
-        }
-
         if (StringUtils.isNotBlank(table.getComment())) {
-            script.append(" COMMENT='").append(table.getComment()).append("'");
+            script.append(" COMMENT ON TABLE ").append(table.getSchemaName()).append(".").append(table.getName())
+                    .append(" IS '").append(table.getComment()).append("'");
         }
 
-        if (StringUtils.isNotBlank(table.getPartition())) {
-            script.append(" \n").append(table.getPartition());
-        }
         script.append(";");
 
         return script.toString();
@@ -86,88 +68,55 @@ public String buildCreateTableSql(Table table) {
     @Override
     public String buildModifyTaleSql(Table oldTable, Table newTable) {
         StringBuilder tableBuilder = new StringBuilder();
-        tableBuilder.append("ALTER TABLE ");
-        if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
-            tableBuilder.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
-        }
-        tableBuilder.append("`").append(oldTable.getName()).append("`").append("\n");
 
-        StringBuilder script = new StringBuilder();
         if (!StringUtils.equalsIgnoreCase(oldTable.getName(), newTable.getName())) {
-            script.append("\t").append("RENAME TO ").append("`").append(newTable.getName()).append("`").append(",\n");
-        }
-        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {
-            script.append("\t").append("COMMENT=").append("'").append(newTable.getComment()).append("'").append(",\n");
-        }
-        if (oldTable.getIncrementValue() != newTable.getIncrementValue()) {
-            script.append("\t").append("AUTO_INCREMENT=").append(newTable.getIncrementValue()).append(",\n");
+            tableBuilder.append("ALTER TABLE ").append(oldTable.getSchemaName()).append(".").append(oldTable.getName())
+                    .append(" RENAME TO ").append("'").append(newTable.getName()).append("'").append(";\n");
         }
 
-        // 判断新增字段
-        List addColumnList = new ArrayList<>();
-        for (TableColumn tableColumn : newTable.getColumnList()) {
-            if (tableColumn.getEditStatus() != null ? tableColumn.getEditStatus().equals("ADD") : false) {
-                addColumnList.add(tableColumn);
-            }
+        if (!StringUtils.equalsIgnoreCase(oldTable.getComment(), newTable.getComment())) {
+            tableBuilder.append("COMMENT ON TABLE ").append(oldTable.getSchemaName()).append(".").append(oldTable.getName())
+                    .append(" IS ").append("'").append(newTable.getComment()).append("'").append(";\n");
         }
 
-        // 判断移动的字段
-        List moveColumnList = movedElements(oldTable.getColumnList(), newTable.getColumnList());
 
         // append modify column
         for (TableColumn tableColumn : newTable.getColumnList()) {
             if ((StringUtils.isNotBlank(tableColumn.getEditStatus()) && StringUtils.isNotBlank(tableColumn.getColumnType())
-                    && StringUtils.isNotBlank(tableColumn.getName())) || moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {
+                    && StringUtils.isNotBlank(tableColumn.getName()))) {
                 DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(tableColumn.getColumnType());
                 if (typeEnum == null) {
                     continue;
                 }
-                if (moveColumnList.contains(tableColumn) || addColumnList.contains(tableColumn)) {
-                    script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(",\n");
-                } else {
-                    script.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append(",\n");
-                }
+                tableBuilder.append("\t").append(typeEnum.buildModifyColumn(tableColumn)).append("\n");
+
             }
         }
 
         // append modify index
         for (TableIndex tableIndex : newTable.getIndexList()) {
             if (StringUtils.isNotBlank(tableIndex.getEditStatus()) && StringUtils.isNotBlank(tableIndex.getType())) {
-                DuckDBIndexTypeEnum mysqlIndexTypeEnum = DuckDBIndexTypeEnum.getByType(tableIndex.getType());
-                if (mysqlIndexTypeEnum == null) {
+                DuckDBIndexTypeEnum duckDBIndexTypeEnum = DuckDBIndexTypeEnum.getByType(tableIndex.getType());
+                if (duckDBIndexTypeEnum == null) {
                     continue;
                 }
-                script.append("\t").append(mysqlIndexTypeEnum.buildModifyIndex(tableIndex)).append(",\n");
+                tableBuilder.append("\t").append(duckDBIndexTypeEnum.buildModifyIndex(tableIndex)).append(";\n");
             }
         }
 
         // append reorder column
         // script.append(buildGenerateReorderColumnSql(oldTable, newTable));
 
-        if (script.length() > 2) {
-            script = new StringBuilder(script.substring(0, script.length() - 2));
-            script.append(";");
-            return tableBuilder.append(script).toString();
+        if (tableBuilder.length() > 2) {
+            tableBuilder = new StringBuilder(tableBuilder.substring(0, tableBuilder.length() - 2));
+            tableBuilder.append(";");
+            return tableBuilder.toString();
         } else {
             return StringUtils.EMPTY;
         }
 
     }
 
-    private String findPrevious(TableColumn tableColumn, Table newTable) {
-        int index = newTable.getColumnList().indexOf(tableColumn);
-        if (index == 0) {
-            return "-1";
-        }
-        // Find the previous column that is not deleted
-        for (int i = index - 1; i >= 0; i--) {
-            if (newTable.getColumnList().get(i).getEditStatus() == null || !newTable.getColumnList().get(i).getEditStatus().equals(EditStatus.DELETE.name())) {
-                return newTable.getColumnList().get(i).getName();
-            }
-        }
-        return "-1";
-    }
-
     @Override
     public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
         StringBuilder sqlBuilder = new StringBuilder(sql.length() + 14);
@@ -188,7 +137,7 @@ public String pageLimit(String sql, int offset, int pageNo, int pageSize) {
     @Override
     public String buildCreateDatabaseSql(Database database) {
         StringBuilder sqlBuilder = new StringBuilder();
-        sqlBuilder.append("CREATE DATABASE `" + database.getName() + "`");
+        sqlBuilder.append("CREATE DATABASE " + database.getName());
         if (StringUtils.isNotBlank(database.getCharset())) {
             sqlBuilder.append(" DEFAULT CHARACTER SET=").append(database.getCharset());
         }
@@ -198,214 +147,16 @@ public String buildCreateDatabaseSql(Database database) {
         return sqlBuilder.toString();
     }
 
-    public static List movedElements(List original, List modified) {
-        int[][] dp = new int[original.size() + 1][modified.size() + 1];
-
-        // 构建DP表
-        for (int i = 1; i <= original.size(); i++) {
-            for (int j = 1; j <= modified.size(); j++) {
-                if (original.get(i - 1).getName().equals(modified.get(j - 1).getOldName())) {
-                    dp[i][j] = dp[i - 1][j - 1] + 1;
-                } else {
-                    dp[i][j] = Math.max(dp[i - 1][j], dp[i][j - 1]);
-                }
-            }
-        }
-
-        // 追踪LCS,找出移动了位置的元素
-        List moved = new ArrayList<>();
-        int i = original.size();
-        int j = modified.size();
-        while (i > 0 && j > 0) {
-            if (original.get(i - 1).equals(modified.get(j - 1))) {
-                i--;
-                j--;
-            } else if (dp[i - 1][j] >= dp[i][j - 1]) {
-                moved.add(original.get(i - 1));
-                // modified List中找到original.get(i-1)的位置
-//                System.out.println("Moved elements:"+ original.get(i-1).getName() + " after " + modified.indexOf(original.get(i-1)) );
-                i--;
-            } else {
-                j--;
-            }
-        }
-
-        // 这里添加原始列表中未被包含在LCS中的元素
-        while (i > 0) {
-            moved.add(original.get(i - 1));
-            i--;
-        }
-
-        return moved;
-    }
-
-    public String buildGenerateReorderColumnSql(Table oldTable, Table newTable) {
-        StringBuilder sql = new StringBuilder();
-        int n = 0;
-        // Create a map to store the index of each column in the old table's column list
-        Map oldColumnIndexMap = new HashMap<>();
-        for (int i = 0; i < oldTable.getColumnList().size(); i++) {
-            oldColumnIndexMap.put(oldTable.getColumnList().get(i).getName(), i);
-        }
-        String[] oldColumnArray = oldTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);
-        String[] newColumnArray = newTable.getColumnList().stream().map(TableColumn::getName).toArray(String[]::new);
-
-        Set oldColumnSet = new HashSet<>(Arrays.asList(oldColumnArray));
-        Set newColumnSet = new HashSet<>(Arrays.asList(newColumnArray));
-        if (!oldColumnSet.equals(newColumnSet)) {
-            return "";
-        }
-
-        buildSql(oldColumnArray, newColumnArray, sql, oldTable, newTable, n);
-
-        return sql.toString();
-    }
-
-    private String[] buildSql(String[] originalArray, String[] targetArray, StringBuilder sql, Table oldTable, Table newTable, int n) {
-        // Complete the first move first
-        if (!originalArray[0].equals(targetArray[0])) {
-            int a = findIndex(originalArray, targetArray[0]);
-            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();
-            String[] newArray = moveElement(originalArray, a, 0, targetArray, new AtomicInteger(0));
-            sql.append(" MODIFY COLUMN ");
-            DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
-            sql.append(typeEnum.buildModifyColumn(column));
-            sql.append(" FIRST;\n");
-            n++;
-            if (Arrays.equals(newArray, targetArray)) {
-                return newArray;
-            }
-            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
-            if (Arrays.equals(resultArray, targetArray)) {
-                return resultArray;
-            }
-        }
-
-        // After completing the last move
-        int max = originalArray.length - 1;
-        if (!originalArray[max].equals(targetArray[max])) {
-            int a = findIndex(originalArray, targetArray[max]);
-            //System.out.println("Move " + originalArray[a] + " after " + (a > 0 ? originalArray[max] : "start"));
-            TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[a])).findFirst().get();
-            String[] newArray = moveElement(originalArray, a, max, targetArray, new AtomicInteger(0));
-            if (n > 0) {
-                sql.append("ALTER TABLE ");
-                if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
-                    sql.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
-                }
-                sql.append("`").append(oldTable.getName()).append("`").append("\n");
-            }
-            sql.append(" MODIFY COLUMN ");
-            DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
-            sql.append(typeEnum.buildModifyColumn(column));
-            sql.append(" AFTER ");
-            sql.append(oldTable.getColumnList().get(max).getName());
-            sql.append(";\n");
-            n++;
-            if (Arrays.equals(newArray, targetArray)) {
-                return newArray;
-            }
-            String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
-            if (Arrays.equals(resultArray, targetArray)) {
-                return resultArray;
-            }
-        }
-
-
-        for (int i = 0; i < originalArray.length; i++) {
-            int a = findIndex(targetArray, originalArray[i]);
-            if (i != a && isMoveValid(originalArray, targetArray, i, a)) {
-                // Find name a in oldTable.getColumnList
-                int finalI = i;
-                TableColumn column = oldTable.getColumnList().stream().filter(col -> StringUtils.equals(col.getName(), originalArray[finalI])).findFirst().get();
-                if (n > 0) {
-                    sql.append("ALTER TABLE ");
-                    if (StringUtils.isNotBlank(oldTable.getDatabaseName())) {
-                        sql.append("`").append(oldTable.getDatabaseName()).append("`").append(".");
-                    }
-                    sql.append("`").append(oldTable.getName()).append("`").append("\n");
-                }
-                sql.append(" MODIFY COLUMN ");
-                DuckDBColumnTypeEnum typeEnum = DuckDBColumnTypeEnum.getByType(column.getColumnType());
-                sql.append(typeEnum.buildModifyColumn(column));
-                sql.append(" AFTER ");
-                AtomicInteger continuousDataCount = new AtomicInteger(0);
-                String[] newArray = moveElement(originalArray, i, a, targetArray, continuousDataCount);
-                if (i < a) {
-                    sql.append(originalArray[a + continuousDataCount.get()]);
-                } else {
-                    sql.append(originalArray[a - 1]);
-                }
-
-                sql.append(";\n");
-                n++;
-
-                if (Arrays.equals(newArray, targetArray)) {
-                    return newArray;
-                }
-                String[] resultArray = buildSql(newArray, targetArray, sql, oldTable, newTable, n);
-                if (Arrays.equals(resultArray, targetArray)) {
-                    return resultArray;
-                }
-            }
-        }
-        return null;
-    }
-
-    private static int findIndex(String[] array, String element) {
-        for (int i = 0; i < array.length; i++) {
-            if (array[i].equals(element)) {
-                return i;
-            }
-        }
-        return -1;
-    }
-
-    private static boolean isMoveValid(String[] originalArray, String[] targetArray, int i, int a) {
-        return ((i == 0 || a == 0 || !originalArray[i - 1].equals(targetArray[a - 1])) &&
-                (i >= originalArray.length - 1 || a >= targetArray.length - 1 || !originalArray[i + 1].equals(targetArray[a + 1])))
-                || (i > 0 && a > 0 && !originalArray[i - 1].equals(targetArray[a - 1]));
-    }
-
-    private static String[] moveElement(String[] originalArray, int from, int to, String[] targetArray, AtomicInteger continuousDataCount) {
-        String[] newArray = new String[originalArray.length];
-        System.arraycopy(originalArray, 0, newArray, 0, originalArray.length);
-        String temp = newArray[from];
-        // 是否有连续移动数据
-        boolean isContinuousData = false;
-        // 连续数据数量
-        if (from < to) {
-            for (int i = to; i < originalArray.length - 1; i++) {
-                if (originalArray[i + 1].equals(targetArray[findIndex(targetArray, originalArray[i]) + 1])) {
-                    continuousDataCount.set(continuousDataCount.incrementAndGet());
-                } else {
-                    break;
-                }
-            }
-            if (continuousDataCount.get() > 0) {
-                System.arraycopy(originalArray, from + 1, newArray, from, to - from + 1);
-                isContinuousData = true;
-            } else {
-                System.arraycopy(originalArray, from + 1, newArray, from, to - from);
-            }
-        } else {
-            System.arraycopy(originalArray, to, newArray, to + 1, from - to);
-        }
-        if (isContinuousData) {
-            newArray[to + continuousDataCount.get()] = temp;
-        } else {
-            newArray[to] = temp;
-        }
-        return newArray;
-    }
-
 
     @Override
     protected void buildTableName(String databaseName, String schemaName, String tableName, StringBuilder script) {
         if (StringUtils.isNotBlank(databaseName)) {
-            script.append(SqlUtils.quoteObjectName(databaseName, "`")).append('.');
+            script.append(SqlUtils.quoteObjectName(databaseName, "'")).append('.');
+        }
+        if (StringUtils.isNotBlank(schemaName)) {
+            script.append(SqlUtils.quoteObjectName(schemaName, "'")).append('.');
         }
-        script.append(SqlUtils.quoteObjectName(tableName, "`"));
+        script.append(SqlUtils.quoteObjectName(tableName, "'"));
     }
 
     /**
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
index 749f0d8c3..9b4374284 100644
--- a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBColumnTypeEnum.java
@@ -184,7 +184,7 @@ public String buildCreateColumnSql(TableColumn column) {
         }
         StringBuilder script = new StringBuilder();
 
-        script.append("\"").append(column.getName()).append("\"").append(" ");
+        script.append(column.getName()).append(" ");
 
         script.append(buildDataType(column, type)).append(" ");
 
@@ -192,7 +192,7 @@ public String buildCreateColumnSql(TableColumn column) {
 
         script.append(buildAutoIncrement(column,type)).append(" ");
 
-        script.append(buildNullable(column, type)).append(" ");
+        script.append(buildCreateNullable(column, type)).append(" ");
 
         return script.toString();
     }
@@ -216,7 +216,18 @@ private String buildNullable(TableColumn column, DuckDBColumnTypeEnum type) {
             return "";
         }
         if (column.getNullable() != null && 1 == column.getNullable()) {
-            return "NULL";
+            return "DROP NOT NULL";
+        } else {
+            return "SET NOT NULL";
+        }
+    }
+
+    private String buildCreateNullable(TableColumn column, DuckDBColumnTypeEnum type) {
+        if (!type.getColumnType().isSupportNullable()) {
+            return "";
+        }
+        if (column.getNullable() != null && 1 == column.getNullable()) {
+            return "";
         } else {
             return "NOT NULL";
         }
@@ -227,15 +238,18 @@ private String buildDefaultValue(TableColumn column, DuckDBColumnTypeEnum type)
             return "";
         }
 
+        StringBuilder script = new StringBuilder();
+        script.append("ALTER TABLE ").append(column.getSchemaName()).append(".").append(column.getTableName());
+        script.append(column.getOldName()).append(" ");
         if ("EMPTY_STRING".equalsIgnoreCase(column.getDefaultValue().trim())) {
-            return StringUtils.join("DEFAULT ''");
+            return script.append("SET DEFAULT '';\n").toString();
         }
 
         if ("NULL".equalsIgnoreCase(column.getDefaultValue().trim())) {
-            return StringUtils.join("DEFAULT NULL");
+            return script.append("SET DEFAULT NULL;\n").toString();
         }
 
-        return StringUtils.join("DEFAULT ", column.getDefaultValue());
+        return script.append("SET DEFAULT ").append(column.getDefaultValue()).append(";\n").toString();
     }
 
     private String buildDataType(TableColumn column, DuckDBColumnTypeEnum type) {
@@ -275,32 +289,67 @@ private String buildDataType(TableColumn column, DuckDBColumnTypeEnum type) {
         return columnType;
     }
 
+    private String buildModifyDataType(TableColumn column, DuckDBColumnTypeEnum type) {
+        String columnType = type.columnType.getTypeName();
+        if (Arrays.asList(VARCHAR, STRING, BPCHAR, NVARCHAR, TEXT).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            script.append(columnType);
+            if (column.getColumnSize() != null && StringUtils.isEmpty(column.getUnit())) {
+                script.append("(").append(column.getColumnSize()).append(")");
+            } else if (column.getColumnSize() != null && !StringUtils.isEmpty(column.getUnit())) {
+                script.append("(").append(column.getColumnSize()).append(" ").append(column.getUnit()).append(")");
+            }
+            return script.toString();
+        }
+
+        if (Arrays.asList(DECIMAL, FLOAT, TIMESTAMP).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            script.append(columnType);
+            if (column.getColumnSize() != null && column.getDecimalDigits() == null) {
+                script.append("(").append(column.getColumnSize()).append(")");
+            } else if (column.getColumnSize() != null && column.getDecimalDigits() != null) {
+                script.append("(").append(column.getColumnSize()).append(",").append(column.getDecimalDigits()).append(")");
+            }
+            return script.toString();
+        }
+
+        if (Arrays.asList(TIME_WITH_TIME_ZONE, TIMSTAMP_US).contains(type)) {
+            StringBuilder script = new StringBuilder();
+            if (column.getColumnSize() == null) {
+                script.append(columnType);
+            } else {
+                String[] split = columnType.split("TIMESTAMP");
+                script.append("TIMESTAMP").append("(").append(column.getColumnSize()).append(")").append(split[1]);
+            }
+            return script.toString();
+        }
+        return columnType;
+    }
+
+
 
     @Override
     public String buildModifyColumn(TableColumn tableColumn) {
 
         if (EditStatus.DELETE.name().equals(tableColumn.getEditStatus())) {
             StringBuilder script = new StringBuilder();
-            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
-            script.append(" ").append("DROP COLUMN ").append("\"").append(tableColumn.getName()).append("\"");
+            script.append("ALTER TABLE ").append(tableColumn.getSchemaName()).append(".").append(tableColumn.getTableName());
+            script.append(" ").append("DROP ").append(tableColumn.getName()).append(";\n");
             return script.toString();
         }
         if (EditStatus.ADD.name().equals(tableColumn.getEditStatus())) {
             StringBuilder script = new StringBuilder();
-            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
-            script.append(" ").append("ADD (").append(buildCreateColumnSql(tableColumn)).append(")");
+            script.append(buildModifyADDColumnSql(tableColumn)).append(";\n");
             return script.toString();
         }
         if (EditStatus.MODIFY.name().equals(tableColumn.getEditStatus())) {
             StringBuilder script = new StringBuilder();
-            script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
-            script.append(" ").append("MODIFY (").append(buildCreateColumnSql(tableColumn)).append(") \n");
 
-            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {
-                script.append(";");
-                script.append("ALTER TABLE ").append("\"").append(tableColumn.getSchemaName()).append("\".\"").append(tableColumn.getTableName()).append("\"");
-                script.append(" ").append("RENAME COLUMN ").append("\"").append(tableColumn.getOldName()).append("\"").append(" TO ").append("\"").append(tableColumn.getName()).append("\"");
+            script.append(buildModifyColumnSql(tableColumn, tableColumn.getOldColumn())).append(" \n");
 
+            if (!StringUtils.equalsIgnoreCase(tableColumn.getOldName(), tableColumn.getName())) {
+                script.append("ALTER TABLE ").append(tableColumn.getSchemaName()).append(".").append(tableColumn.getTableName());
+                script.append(" ").append("RENAME ").append(tableColumn.getOldName()).append(" TO ").append(tableColumn.getName()).append(";\n");
             }
             return script.toString();
 
@@ -308,6 +357,54 @@ public String buildModifyColumn(TableColumn tableColumn) {
         return "";
     }
 
+    public String buildModifyColumnSql(TableColumn column, TableColumn oldColumn) {
+        DuckDBColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());
+        if (type == null) {
+            return "";
+        }
+        StringBuilder script = new StringBuilder();
+
+        if (!column.getColumnType().equals(oldColumn.getColumnType())) {
+            script.append("ALTER TABLE ").append(column.getSchemaName()).append(".").append(column.getTableName()).append(" ");
+            script.append("ALTER ").append(oldColumn.getName()).append(" SET DATA TYPE ").append(buildModifyDataType(column, type)).append(";\n");
+        }
+
+        script.append(buildDefaultValue(column, type)).append(" ");
+
+        if (oldColumn.getNullable() != column.getNullable()) {
+            script.append("ALTER TABLE ").append(column.getSchemaName()).append(".").append(column.getTableName()).append(" ");
+            script.append("ALTER COLUMN ").append(column.getName()).append(" ").append(buildNullable(column, type)).append(";\n");
+        }
+
+        return script.toString();
+    }
+
+    public String buildModifyADDColumnSql(TableColumn column) {
+        DuckDBColumnTypeEnum type = COLUMN_TYPE_MAP.get(column.getColumnType().toUpperCase());
+        if (type == null) {
+            return "";
+        }
+        StringBuilder script = new StringBuilder();
+        script.append("ALTER TABLE ").append(column.getSchemaName()).append(".").append(column.getTableName());
+        script.append(" ").append("ADD COLUMN ").append(column.getName()).append(" ").append(buildModifyDataType(column, type));
+        if (!type.getColumnType().isSupportDefaultValue() || StringUtils.isEmpty(column.getDefaultValue())) {
+            return script.append(";\n").toString();
+        } else {
+            if ("EMPTY_STRING".equalsIgnoreCase(column.getDefaultValue().trim())) {
+                return script.append(" ").append("DEFAULT '';\n").toString();
+            }
+
+            if ("NULL".equalsIgnoreCase(column.getDefaultValue().trim())) {
+                return script.append(" ").append("DEFAULT NULL;\n").toString();
+            }
+            script.append(" ").append("DEFAULT ").append(column.getDefaultValue()).append(";\n");
+        }
+
+
+
+        return script.toString();
+    }
+
     public static List getTypes() {
         return Arrays.stream(DuckDBColumnTypeEnum.values()).map(columnTypeEnum ->
                 columnTypeEnum.getColumnType()
diff --git a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
index 20e5ad388..eaea01003 100644
--- a/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
+++ b/chat2db-server/chat2db-plugins/chat2db-duckdb/src/main/java/ai/chat2db/plugin/duckdb/type/DuckDBIndexTypeEnum.java
@@ -64,7 +64,7 @@ public static DuckDBIndexTypeEnum getByType(String type) {
     public String buildIndexScript(TableIndex tableIndex) {
         StringBuilder script = new StringBuilder();
         if (PRIMARY_KEY.equals(this)) {
-            script.append("ALTER TABLE \"").append(tableIndex.getSchemaName()).append("\".\"").append(tableIndex.getTableName()).append("\" ADD PRIMARY KEY ").append(buildIndexColumn(tableIndex));
+            script.append("ALTER TABLE ").append(tableIndex.getSchemaName()).append(".").append(tableIndex.getTableName()).append(" ADD PRIMARY KEY ").append(buildIndexColumn(tableIndex));
         } else {
             if (UNIQUE.equals(this)) {
                 script.append("CREATE UNIQUE INDEX ");
@@ -76,13 +76,27 @@ public String buildIndexScript(TableIndex tableIndex) {
         return script.toString();
     }
 
+    public String buildCreateIndexScript(TableIndex tableIndex) {
+        StringBuilder script = new StringBuilder();
+        if (PRIMARY_KEY.equals(this)) {
+            script.append("CONSTRAINT ").append(tableIndex.getTableName()).append("_").append("PK PRIMARY KEY ").append(buildIndexColumn(tableIndex));
+        } else {
+            if (UNIQUE.equals(this)) {
+                script.append("CREATE UNIQUE INDEX ");
+            } else {
+                script.append("CREATE INDEX ");
+            }
+            script.append(buildIndexName(tableIndex)).append(" ON \"").append(tableIndex.getSchemaName()).append("\".\"").append(tableIndex.getTableName()).append("\" ").append(buildIndexColumn(tableIndex));
+        }
+        return script.toString();
+    }
 
     private String buildIndexColumn(TableIndex tableIndex) {
         StringBuilder script = new StringBuilder();
         script.append("(");
         for (TableIndexColumn column : tableIndex.getColumnList()) {
             if (StringUtils.isNotBlank(column.getColumnName())) {
-                script.append("\"").append(column.getColumnName()).append("\"");
+                script.append(column.getColumnName());
                 if (!StringUtils.isBlank(column.getAscOrDesc()) && !PRIMARY_KEY.equals(this)) {
                     script.append(" ").append(column.getAscOrDesc());
                 }
@@ -113,7 +127,7 @@ public String buildModifyIndex(TableIndex tableIndex) {
 
     private String buildDropIndex(TableIndex tableIndex) {
         if (DuckDBIndexTypeEnum.PRIMARY_KEY.getName().equals(tableIndex.getType())) {
-            String tableName = "\"" + tableIndex.getSchemaName() + "\"." + "\"" + tableIndex.getTableName() + "\"";
+            String tableName = "" + tableIndex.getSchemaName() + "."  + tableIndex.getTableName() ;
             return StringUtils.join("ALTER TABLE ",tableName," DROP PRIMARY KEY");
         }
         StringBuilder script = new StringBuilder();
diff --git a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
index 18038e95a..9834cdf10 100644
--- a/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
+++ b/chat2db-server/chat2db-server-domain/chat2db-server-domain-core/src/main/java/ai/chat2db/server/domain/core/impl/TableServiceImpl.java
@@ -424,7 +424,7 @@ private long addDBCache(Long dataSourceId, String databaseName, String schemaNam
         Connection connection = Chat2DBContext.getConnection();
         long n = 0;
         try (ResultSet resultSet = connection.getMetaData().getTables(databaseName, schemaName, null,
-                new String[]{"TABLE", "SYSTEM TABLE"})) {
+                new String[]{"TABLE", "SYSTEM TABLE", "BASE TABLE"})) {
             List cacheDOS = new ArrayList<>();
             while (resultSet.next()) {
                 TableCacheDO tableCacheDO = new TableCacheDO();