利用DeepSeek辅助修改luadbi-duckdb读取DuckDB decimal数据类型

DuckDB的decimal数据类型在duckdb.h的定义如下,

c 复制代码
//! DECIMAL is composed of a width and a scale.
//! Their value is stored in a HUGEINT.
typedef struct {
	uint8_t width;
	uint8_t scale;
	duckdb_hugeint value;
} duckdb_decimal;

但实际上value的类型是变化的,通常的方法无法正确读取,参考https://github.com/duckdb/duckdb/issues/10986 回帖的方法

var logicalType = duckdb_vector_get_column_type(vector);

byte scale = duckdb_decimal_scale(logicalType);

var type = duckdb_decimal_internal_type(logicalType);

var data = duckdb_vector_get_data(vector);

//cast data to either short, int, long or hugeint depending on the type variable above and divide it by 10 to the power of scale

If it helps you can find C# version here: https://github.com/Giorgi/DuckDB.NET/blob/8520bf5005d9309f762ef61d71412d60d24ca32c/DuckDB.NET.Data/Internal/Reader/DecimalVectorDataReader.cs#L43

让DeepSeek编写函数,根据输入的数据类型输出decimal(width,scale,data,type)的c字符串,当type==1,data是指向int8_t的指针,type==2,指向int16_t, type==3,指向int32_t,type==4,指向int64_t,输出结果是 *data / (10^scale),同时小数点后保留scale位的c字符串。

他给出的代码如下:

c 复制代码
// 将decimal数据转换为字符串
void decimal_to_string_format(uint8_t width, uint8_t scale, void* data, int type, char* buffer, size_t buffer_size) {
    if (data == NULL || buffer == NULL || buffer_size == 0) {
        return;
    }
    
    // 根据类型获取整数值
    int64_t int_value = 0;
    switch (type) {
        case DUCKDB_TYPE_TINYINT: // int8_t
            int_value = *(int8_t*)data;
            break;
        case DUCKDB_TYPE_SMALLINT: // int16_t
            int_value = *(int16_t*)data;
            break;
        case DUCKDB_TYPE_INTEGER: // int32_t
            int_value = *(int32_t*)data;
            break;
        case DUCKDB_TYPE_BIGINT: // int64_t
            int_value = *(int64_t*)data;
            break;
        case DUCKDB_TYPE_HUGEINT: // int128_t
            {
                duckdb_hugeint huge_val = *(duckdb_hugeint*)data;
        
        // 简化处理:如果upper为0,只使用lower部分
        if (huge_val.upper == 0) {
            // 直接使用lower部分
            uint64_t value = huge_val.lower;
            int is_negative = (huge_val.upper < 0); // 检查符号
            
            if (scale == 0) {
                snprintf(buffer, buffer_size, "%s%lu", is_negative ? "-" : "", value);
            } else {
                uint64_t divisor = 1;
                for (int i = 0; i < scale; i++) {
                    divisor *= 10;
                }
                
                uint64_t integer_part = value / divisor;
                uint64_t fractional_part = value % divisor;
                
                char format[32];
                snprintf(format, sizeof(format), "%%s%%lu.%%0%dlu", scale);
                snprintf(buffer, buffer_size, format, is_negative ? "-" : "", integer_part, fractional_part);
            }
        } else {
            // 对于非常大的HUGEINT值,使用简化表示
            if (scale == 0) {
                snprintf(buffer, buffer_size, "%s%ld%09lu", 
                        huge_val.upper < 0 ? "-" : "", 
                        huge_val.upper < 0 ? -huge_val.upper : huge_val.upper,
                        huge_val.lower);
            } else {
                // 对于有小数位的大数,输出简化信息
                snprintf(buffer, buffer_size, "hugeint[upper=%ld,lower=%lu,scale=%d]", 
                        huge_val.upper, huge_val.lower, scale);
            }
        }
        return;
    }
            break;
        default:
            // 无效类型
            snprintf(buffer, buffer_size, "decimal(%d,%d,invalid_type,%d)", width, scale, type);
            return;
    }
    
    // 处理负号
    int is_negative = (int_value < 0);
    uint64_t abs_value = is_negative ? (uint64_t)(-int_value) : (uint64_t)int_value;
    
    if (scale == 0) {
        // 整数情况,直接输出
        snprintf(buffer, buffer_size, "%s%lu", is_negative ? "-" : "", abs_value);
    } else {
        // 计算10^scale
        uint64_t divisor = 1;
        for (int i = 0; i < scale; i++) {
            divisor *= 10;
        }
        
        // 分离整数部分和小数部分
        uint64_t integer_part = abs_value / divisor;
        uint64_t fractional_part = abs_value % divisor;
        
        // 构建格式字符串,确保小数部分有正确的位数(前导零)
        char format[32];
        snprintf(format, sizeof(format), "%%s%%lu.%%0%dlu", scale);
        
        // 格式化输出
        snprintf(buffer, buffer_size, format, is_negative ? "-" : "", integer_part, fractional_part);
    }
}

再自行在函数statement_fetch_impl中添加:

c 复制代码
				case DUCKDB_TYPE_DECIMAL:{
					
duckdb_logical_type logicalType = duckdb_vector_get_column_type(col1);
uint8_t width = duckdb_decimal_width(logicalType);
uint8_t scale = duckdb_decimal_scale(logicalType);
duckdb_type type = duckdb_decimal_internal_type(logicalType);
void* data = duckdb_vector_get_data(col1);
char decimal_str[100];
decimal_to_string_format(width, scale, data, type, decimal_str, sizeof(decimal_str) );
duckdb_string_t str= convert_to_duckdb_string(decimal_str);

  					if (named_columns) {
						LDB_PUSH_ATTRIB_STRING( str );
					} else {
  						LDB_PUSH_ARRAY_STRING(i + 1, str);
					}
				}
  					break;	

编译和运行

复制代码
oot@6ae32a5ffcde:/par/luadbi-0.7.5# luarocks make luadbi-duckdb-scm-0.rockspec --pack-binary-rock DUCKDB_DIR=/par/141 DUCKDB_INCDIR=/par/141

Packed: /par/luadbi-0.7.5/luadbi-duckdb-scm-0.linux-x86_64.rock

root@6ae32a5ffcde:/par/luadbi-0.7.5# luarocks install luadbi-duckdb-scm-0.linux-x86_64.rock --force

luadbi-duckdb scm-0 is now installed in /usr/local (license: MIT/X11)

root@6ae32a5ffcde:/par/luadbi-0.7.5# lua /par/duckdtpath.lua
[123456789012345.79000]15
2025-09-25 12:26:55.123456
123456789012345.79000
2025-09-23

root@6ae32a5ffcde:/par/luadbi-0.7.5# lua /par/duckdtpath.lua
[hugeint[upper=6,lower=12776324570088369304,scale=5]]15
2025-09-25 12:26:55.123456
hugeint[upper=6,lower=12776324570088369304,scale=5]
2025-09-23

root@6ae32a5ffcde:/par/luadbi-0.7.5# lua /par/duckdtpath.lua
[hugeint[upper=-1,lower=6101065172474972616,scale=5]]15
2025-09-25 12:26:55.123456
hugeint[upper=-1,lower=6101065172474972616,scale=5]
2025-09-23

对于hugeint高位不为0的情况,简化了处理。

测试脚本如下

lua 复制代码
DBI = require "DBI"

dbd, err = DBI.Connect( 'DuckDB', 'lua_duckdb', 'dbuser', 'password' )
assert(dbd, err)

dbd:autocommit(true)
statement = dbd:prepare( "create or replace table table_1 as select 15 id, timestamp'2025-09-25 12:26:55.123456' column1,-123456789012345.79::decimal(38, 5) column2,date'2025-09-23' column3 ;" )
statement:execute()

statement = assert(dbd:prepare( "select * from table_1 where id = $1;" ))
statement:execute( 15 )

for row in statement:rows(true) do
         print(row['id'])
	 print(row['column1'])
	 print(row['column2'])
	 print(row['column3'])

end
相关推荐
代码派10 分钟前
业务不停机迁移 MySQL 到 TiDB,NineData 数据增量同步实操演示
数据库·mysql·tidb·ninedata·无停机迁移·增量迁移·全量迁移
Gauss松鼠会14 分钟前
GaussDB 中 gsql 与 DBeaver(JDBC)客户端工具查询时间不一致:原因、排查与处理
运维·服务器·数据库·sql·gaussdb·经验总结
Elastic 中国社区官方博客20 分钟前
基于 Jina OCR v1 与 Elasticsearch 的 PDF 智能混合检索与 RAG 系统实战指南
大数据·数据库·elasticsearch·搜索引擎·ocr·jina
楚识科技27 分钟前
信创环境下 OCR 部署实践:从麒麟系统到达梦数据库的全栈适配
数据库·ocr
飞浪纪元[FWC–FE]32 分钟前
Qt 构建排错实录:从 MSB 1009 到生成器选型
开发语言·数据库·qt
Paper数据分析-经管学术32 分钟前
1985-2026年中国低空经济专利明细数据库
数据库
RuoyiOffice32 分钟前
SpringBoot3 + openGauss:RuoYi Office 登录、流程发布与用印填报适配实战
java·数据库·spring boot·opengauss·信创
画中有画43 分钟前
时序数据库在物联网系统中的应用
数据库·物联网·时序数据库
Decay__7 小时前
05_MySQL 常用 SQL 大全 —— 诊断、管理、优化辅助 SQL 与三大系统库速查
运维·数据库·sql·mysql·架构
梦想不只是梦与想7 小时前
鸿蒙 AI自动化测试实现
单元测试·harmonyos·hypium