惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Project Zero
Project Zero
月光博客
月光博客
Y
Y Combinator Blog
T
The Blog of Author Tim Ferriss
O
OpenAI News
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Know Your Adversary
Know Your Adversary
Last Week in AI
Last Week in AI
S
Securelist
Engineering at Meta
Engineering at Meta
博客园 - 司徒正美
P
Privacy & Cybersecurity Law Blog
T
Tailwind CSS Blog
F
Fortinet All Blogs
博客园 - 三生石上(FineUI控件)
Scott Helme
Scott Helme
MyScale Blog
MyScale Blog
P
Proofpoint News Feed
云风的 BLOG
云风的 BLOG
C
Cisco Blogs
CTFtime.org: upcoming CTF events
CTFtime.org: upcoming CTF events
小众软件
小众软件
U
Unit 42
Microsoft Azure Blog
Microsoft Azure Blog
Hacker News: Ask HN
Hacker News: Ask HN
Hugging Face - Blog
Hugging Face - Blog
K
KPMG report finds enterprise disconnect between AI and its ROI | CIO
SecWiki News
SecWiki News
宝玉的分享
宝玉的分享
P
Proofpoint News Feed
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
H
Hackread – Cybersecurity News, Data Breaches, AI and More
L
Lohrmann on Cybersecurity
IT之家
IT之家
Security Archives - TechRepublic
Security Archives - TechRepublic
I
InfoQ
S
Security @ Cisco Blogs
Webroot Blog
Webroot Blog
Hacker News - Newest:
Hacker News - Newest: "LLM"
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
F
Full Disclosure
D
Darknet – Hacking Tools, Hacker News & Cyber Security
The GitHub Blog
The GitHub Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
Jina AI
Jina AI
Cyberwarzone
Cyberwarzone
人人都是产品经理
人人都是产品经理
Exploit-DB.com RSS Feed
Exploit-DB.com RSS Feed
B
Blog RSS Feed
Apple Machine Learning Research
Apple Machine Learning Research

博客园 - 黄洪波

安装openclaw 排查项目中依赖的mybatis 拦截器 ModelAttribute 老革命遇上新问题 OpenWebUI单点登录之解决动态参数问题 IDEA自带的Maven 3.9.x无法刷新http nexus私服(转) windows docker安装rocketmq之踩坑记 分布式系统设计经典论文(转载) 训练自己的yolo-v11数据集(二) 训练自己的yolo-v11数据集(一) 本地使用pycharm进行yolo推理 2024年的云原生架构需要哪些技术栈 (转) yolo v11学习,入门篇 OpenWebUI单点登录之深坑 AI工具验证 解决Win10无法进入睡眠模式(转) idea常用插件 内网离线模式下激活JRebel java导入json数据至doris 将SpringBoot打包之后的jar设为守护进程
使用calcite构造ddl建表语句
黄洪波 · 2025-03-26 · via 博客园 - 黄洪波

核心实现:

// 列定义
SqlNode columnDef = SqlDdlNodes.column(
SqlParserPos.ZERO,
columnName,
dataTypeSpec,
defaultValue,
defaultValue == null ? null : ColumnStrategy.DEFAULT
);

备注,这个地方卡了我三个小时以上,所有ai给出的示例最后一个参数都是传入的null,

一直报空指针异常:

java.lang.NullPointerException
at org.apache.calcite.sql.ddl.SqlColumnDeclaration.unparse(SqlColumnDeclaration.java:77)
直到某段AI的分解过程中出现:

Calcite SqlColumnDeclaration 类中 unparse 方法的实现细节

SqlColumnDeclaration 类中的 unparse 方法的具体实现细节并未直接在任何证据中提及。

但是,可以基于其他 SqlNode 类的 unparse 方法实现进行推测。

通常,unparse 方法负责将 SQL 节点转换回 SQL 字符串。

以下是一个可能的实现示例,基于其他 SqlNode 类的 unparse 方法实现:

@Override
public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
    // 输出列名
    name.unparse(writer, leftPrec, rightPrec);
    // 输出数据类型
    type.unparse(writer, leftPrec, rightPrec);
    // 如果有默认值,则输出默认值
    if (defaultValue != null) {
        writer.keyword("DEFAULT");
        defaultValue.unparse(writer, leftPrec, rightPrec);
    }
    // 如果有约束,则输出约束
    if (constraints != null) {
        for (SqlNode constraint : constraints) {
            constraint.unparse(writer, leftPrec, rightPrec);
        }
    }
}

我于是去翻源码,看到以下内容,源码第45行:

public void unparse(SqlWriter writer, int leftPrec, int rightPrec) {
        this.name.unparse(writer, 0, 0);
        this.dataType.unparse(writer, 0, 0);
        if (Boolean.FALSE.equals(this.dataType.getNullable())) {
            writer.keyword("NOT NULL");
        }

        SqlNode expression = this.expression;
        if (expression != null) {
            switch (this.strategy) {
                case VIRTUAL:
                case STORED:
                    writer.keyword("AS");
                    exp(writer, expression);
                    writer.keyword(this.strategy.name());
                    break;
                case DEFAULT:
                    writer.keyword("DEFAULT");
                    exp(writer, expression);
                    break;
                default:
                    throw new AssertionError("unexpected: " + this.strategy);
            }
        }

    }

反应过来,如果我想要设置默认值,那么第五个参数

ColumnStrategy 就必须传入
ColumnStrategy.DEFAULT

完整代码如下:

import org.apache.calcite.schema.ColumnStrategy;
import org.apache.calcite.sql.*;
import org.apache.calcite.sql.ddl.SqlColumnDeclaration;
import org.apache.calcite.sql.ddl.SqlCreateTable;
import org.apache.calcite.sql.dialect.PostgresqlSqlDialect;
import org.apache.calcite.sql.parser.SqlParserPos;
import org.apache.calcite.sql.pretty.SqlPrettyWriter;
import org.apache.calcite.sql.type.SqlTypeName;
import org.apache.calcite.sql.util.SqlBuilder;
import org.apache.calcite.sql.ddl.SqlDdlNodes;

import java.util.List;

public class DdlSqlGenerator {

    /**
     * 生成 CREATE TABLE 语句
     *
     * @param tableSchema 表模式(如 "public")
     * @param tableName   表名
     * @param columns     列定义列表
     * @return 生成的 SQL 字符串
     */
    public static String
    generateCreateTableSql(SqlDialect dialect, String tableSchema, String tableName, List<Column> columns) {
        // 创建表标识符(schema.tableName)
        SqlIdentifier tableIdentifier = new SqlIdentifier(tableName, SqlParserPos.ZERO);

        // 创建列定义列表
        SqlNodeList columnList = new SqlNodeList(SqlParserPos.ZERO);
        for (Column column : columns) {
            // 列名
            SqlIdentifier columnName = new SqlIdentifier(column.getColumnName(), SqlParserPos.ZERO);

            // 数据类型
            SqlDataTypeSpec dataTypeSpec = createDataTypeSpec(column);

            SqlNode defaultValue = createDefaultValue(column.getDataDefault());

            // 列定义
            SqlNode columnDef = SqlDdlNodes.column(
                    SqlParserPos.ZERO,
                    columnName,
                    dataTypeSpec,
                    defaultValue,
                    defaultValue == null ? null : ColumnStrategy.DEFAULT
            );


            columnList.add(columnDef);
        }

        // 创建 CREATE TABLE 语句
        SqlCreateTable createTable = SqlDdlNodes.createTable(
                SqlParserPos.ZERO,  // 位置
                false,              // 是否替换(ifNotExists)
                false,              // 是否临时表(isTemporary)
                tableIdentifier,    // 表名
                columnList,         // 列定义
                null                // 查询(这里无)
        );

        return createTable.toSqlString(dialect).getSql();
    }

    /**
     * 根据 Column 属性创建数据类型规格
     *
     * @param column 列对象
     * @return 数据类型规格
     */
    private static SqlDataTypeSpec createDataTypeSpec(Column column) {
        SqlTypeName typeName = column.getDataType();
        SqlDataTypeSpec dataTypeSpec;

        // 根据数据类型处理长度、精度和标度
        switch (typeName) {
            case CHAR:
            case VARCHAR:
                // 有长度的数据类型
                dataTypeSpec = new SqlDataTypeSpec(
                        new SqlBasicTypeNameSpec(typeName, (int) column.getDataLength(), SqlParserPos.ZERO),
                        SqlParserPos.ZERO
                );
                break;
            case DECIMAL:
                // 有精度和标度的数据类型
                dataTypeSpec = new SqlDataTypeSpec(
                        new SqlBasicTypeNameSpec(typeName, column.getDataPrecision(), column.getDataScale(), SqlParserPos.ZERO),
                        SqlParserPos.ZERO
                );
                break;
            default:
                // 无需长度、精度的数据类型
                dataTypeSpec = new SqlDataTypeSpec(
                        new SqlBasicTypeNameSpec(typeName, SqlParserPos.ZERO),
                        SqlParserPos.ZERO
                );
                break;
        }

        // 设置可空性
        dataTypeSpec = dataTypeSpec.withNullable(column.isNullable());



        return dataTypeSpec;
    }

    //创建默认值
    public static SqlNode createDefaultValue(Object defaultObj) {
        if (defaultObj == null) {
            return null;
        }

        try {
            if (defaultObj instanceof Number) {
                // 数字类型(整数、浮点数)
                return SqlLiteral.createExactNumeric(defaultObj.toString(), SqlParserPos.ZERO);
            } else if (defaultObj instanceof Boolean) {
                // 布尔类型
                return SqlLiteral.createBoolean((Boolean) defaultObj, SqlParserPos.ZERO);
            } else if (defaultObj instanceof String) {
                // 字符串类型
                // 添加额外的空字符串检查
                String strValue = (String) defaultObj;
                if (strValue.isEmpty()) {
                    return SqlLiteral.createCharString("", SqlParserPos.ZERO);
                }
                return SqlLiteral.createCharString(strValue, SqlParserPos.ZERO);
            } else {
                // 其他类型默认转换为字符串
                return SqlLiteral.createCharString(defaultObj.toString(), SqlParserPos.ZERO);
            }
        } catch (Exception e) {
            // 如果创建默认值失败,返回null或记录日志
            System.err.println("Failed to create default value: " + e.getMessage());
            return null;
        }
    }
}
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Column {
    private String tableSchema;
    private String tableName;
    private String columnName;
    private SqlTypeName dataType;
    private long dataLength;
    private int dataPrecision;
    private int dataScale;
    private boolean nullable;
    private Object dataDefault;
    private int columnId;
    private String columnComment;

}

测试代码:

@Test
void testGenerateCreateTableSql() {
// 示例列定义
List<Column> columns = new ArrayList<>();
columns.add(new Column("public", "users", "id", SqlTypeName.BIGINT, 0, 0, 0, false, 11, 1, "Primary key"));
columns.add(new Column("public", "users", "name", SqlTypeName.VARCHAR, 50, 0, 0, true, "unknown", 2, "User name"));
columns.add(new Column("public", "users", "balance", SqlTypeName.DECIMAL, 0, 10, 2, true, 40, 3, "User balance"));

// 生成 SQL
String sql = DdlSqlGenerator.generateCreateTableSql(MysqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(PostgresqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(OracleSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);
sql = DdlSqlGenerator.generateCreateTableSql(MssqlSqlDialect.DEFAULT,"public", "users", columns);
System.out.println(sql);

}

输出结果:

CREATE TABLE `users` (`id` BIGINT NOT NULL DEFAULT (11), `name` VARCHAR(50) DEFAULT ('unknown'), `balance` DECIMAL(10, 2) DEFAULT (40))
CREATE TABLE "users" ("id" BIGINT NOT NULL DEFAULT (11), "name" VARCHAR(50) DEFAULT ('unknown'), "balance" DECIMAL(10, 2) DEFAULT (40))
CREATE TABLE "users" ("id" BIGINT NOT NULL DEFAULT (11), "name" VARCHAR(50) DEFAULT ('unknown'), "balance" DECIMAL(10, 2) DEFAULT (40))
CREATE TABLE [users] ([id] BIGINT NOT NULL DEFAULT (11), [name] VARCHAR(50) DEFAULT ('unknown'), [balance] DECIMAL(10, 2) DEFAULT (40))

posted on 2025-03-26 22:53  黄洪波  阅读(209)  评论()    收藏  举报