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

推荐订阅源

酷 壳 – CoolShell
酷 壳 – CoolShell
H
Hacker News: Front Page
P
Palo Alto Networks Blog
T
ThreatConnect
Apple Machine Learning Research
Apple Machine Learning Research
博客园_首页
T
True Tiger Recordings
P
Privacy & Cybersecurity Law Blog
B
Blog
IT之家
IT之家
Last Week in AI
Last Week in AI
F
Full Disclosure
Hacker News: Ask HN
Hacker News: Ask HN
C
Comments on: Blog
Microsoft Azure Blog
Microsoft Azure Blog
C
Cybersecurity and Infrastructure Security Agency CISA
Microsoft Security Blog
Microsoft Security Blog
博客园 - 【当耐特】
N
News and Events Feed by Topic
NISL@THU
NISL@THU
腾讯CDC
雷峰网
雷峰网
Security Latest
Security Latest
李成银的技术随笔
M
Microsoft Research Blog - Microsoft Research
L
LangChain Blog
L
Lohrmann on Cybersecurity
cs.CL updates on arXiv.org
cs.CL updates on arXiv.org
C
Check Point Blog
Y
Y Combinator Blog
Recent Announcements
Recent Announcements
博客园 - Franky
N
News | PayPal Newsroom
V
V2EX
A
About on SuperTechFans
The Register - Security
The Register - Security
月光博客
月光博客
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
Google Online Security Blog
Google Online Security Blog
MyScale Blog
MyScale Blog
Cisco Talos Blog
Cisco Talos Blog
Vercel News
Vercel News
WordPress大学
WordPress大学
C
Cyber Attacks, Cyber Crime and Cyber Security
The Hacker News
The Hacker News
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
IntelliJ IDEA : IntelliJ IDEA – the Leading IDE for Professional Development in Java and Kotlin | The JetBrains Blog
爱范儿
爱范儿
A
Arctic Wolf
L
LINUX DO - 最新话题
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More

博客园 - 黄洪波

安装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  黄洪波  阅读(198)  评论()    收藏  举报