Skip to content

feat: add compat.nanodbc and compat.unixodbc - #210

Merged
Sunrisepeak merged 4 commits into
mcpplibs:mainfrom
FarnaHerry:feat/add-nanodbc
Aug 15, 2026
Merged

feat: add compat.nanodbc and compat.unixodbc#210
Sunrisepeak merged 4 commits into
mcpplibs:mainfrom
FarnaHerry:feat/add-nanodbc

Conversation

@FarnaHerry

Copy link
Copy Markdown
Collaborator

Closes #209.

收录内容

  • compat.nanodbc 2.14.0(MIT)— 原生 C ODBC API 的 C++ 封装,上游冻结于 2022-03,单版本即全部。
  • compat.unixodbc 2.3.14(LGPL-2.1)— linux 的 ODBC driver manager,源码静态构建,nanodbc 在 linux 的依赖。windows 走 SDK odbc32、macOS 走系统 iODBC(描述符已声明,本索引 CI 不覆盖,成员测试按 linux 门控)。

形态

nanodbc 为 Shape A(nanodbc.cpp 单 TU);unixodbc 为 Shape E 叠 A —— 冻结 configure 快照 + 纯 C 源码直编,消费端零 configure/make,DM + odbcinst + ini/log/lst + libltdl 编入单一 odbc 目标(即上游 libodbc.a 的符号集)。

三个值得审的判断

  1. linux 的驱动管理器必须打包,不能是系统依赖。 系统依赖 + -lodbc 能编能链,但运行时闭包检查拒绝:mcpp 二进制的 PT_INTERP 是私有 loader,宿主 /usr/lib 不可达(同 mcpp#352)。静态源码构建让产物不带 libodbc.so.2 NEEDED,闭包天然通过,亦无宿主 glibc 错配。conan / vcpkg 对 linux ODBC 也是同样结论。
  2. 无 libtool 复刻 ltdl。 DM 经 libltdl dlopen 驱动;静态构建下 libtool 的 -dlpreopen 接线用 -DLTDLOPEN=libltdlc + 一张生成的 lt_libltdlc_LTX_preloaded_symbols 表(从 libtool 目标文件的重定位还原)复刻。两份 config.h(顶层 / libltdl)合并为一份 —— ltdl 源码不读冲突宏,且带引号的 -DLT_CONFIG_H 无法穿过描述符 → 命令行管道(实测)。
  3. 冻结上游的两处修复只在包内生效。 libc++ 缺 char_traits<unsigned char>:force-include 显式特化(标准定制点,_LIBCPP_VERSION 为界,-includecxxflags,仅作用于本包编译)。nanodbc 截掉 SQL state 末字符的上游 off-by-one 不改源码,测试按真实行为断言前缀。

验证

  • mcpp test -p nanodbc 冷构建 1 passed / 0 failed(无任何宿主 include/lib 路径注入,全链路自包含)。
  • unixODBC 直编 vs 同 tarball libtool 构建对拍:句柄分配、驱动枚举、IM002 错误路径、lt_dlopen+lt_dlsym 行为一致。
  • lint:语法 / check_mirror_urls / check_package_name / check_cross_package_refs / check_platform_version_parity 全过。
  • mcpp xpkg parse:全部 98 个描述符在钉住的 2026.8.10.3 下通过。
  • 两包 sha256 均双次下载复核。

测试断言什么

无数据库、无驱动的错误路径:连接不存在的 DSN 必抛 nanodbc::database_error,断言 SQL state 为 "IM002" 的非空前缀(nanodbc 的 off-by-one 会报 "IM00")且 what() 含管理器原文 "Data source name not found" —— 诊断确实来自真实 driver manager 而非空壳。

CN 镜像

mcpp-res 写权限,暂为纯字符串 GLOBAL url(lint 允许,同 compat.libmysqlclient 先例),维护者建仓后可补。

nanodbc is a thin C++ wrapper over the platform ODBC driver manager,
frozen upstream at v2.14.0 (March 2022). Two fixes make the four-year-old
source compile and run under the mcpp toolchain:

- a force-included char_traits<unsigned char> shim for libc++ (the
  standard's own customization point, guarded on _LIBCPP_VERSION;
  delivered via cxxflags, since cflags never reaches .cpp TUs)
- a per-platform answer to the driver manager: windows links the SDK's
  odbc32, macOS the OS's iODBC, and linux takes the new compat.unixodbc,
  because mcpp's runtime closure rejects a NEEDED libodbc.so.2 that only
  the host can provide (its PT_INTERP is a private loader)

compat.unixodbc builds the unixODBC 2.3.14 driver manager statically
from source (Shape E over A): DM + odbcinst + ini/log/lst + libltdl in
one odbc target, exactly upstream's libodbc.a symbol set, so consumers
carry no libodbc.so.2 NEEDED at all. The non-obvious pieces, both
verified against the libtool build of the same tarball:

- libtool-free ltdl wiring: -DLTDLOPEN=libltdlc plus a generated
  lt_libltdlc_LTX_preloaded_symbols table (reconstructed from the
  libtool object's relocations) registers the dlopen loader
- one merged config.h: ltdl's configure output folded into the
  top-level one (ltdl sources never read the clashing macros),
  sidestepping a quoted -DLT_CONFIG_H that does not survive the
  descriptor -> command-line pipeline

The member test is deliberately database-free: connecting to a
nonexistent DSN must throw nanodbc::database_error, and the SQL state
prefix plus the manager's own message text prove the diagnostics came
from the real driver manager. nanodbc 2.14.0's frozen off-by-one (it
drops the last SQL-state character) is asserted as a prefix, not
equality.

Verified: mcpp test -p nanodbc 1/1 cold with no host paths; all lint
scripts; mcpp xpkg parse for all 98 descriptors under the pinned
2026.8.10.3; sha256 double-checked for both tarballs. CN mirrors are
plain-string GLOBAL for now (no mcpp-res write access), same fallback
as compat.libmysqlclient.
Comment thread pkgs/c/compat.nanodbc.lua Outdated
-- cxxflags, not cflags -- this package has no C sources.
cxxflags = { "-include", "mcpp_nanodbc_char_traits.h" },
generated_files = {
["mcpp_generated/include/mcpp_nanodbc_char_traits.h"] = "// libc++ lacks std::char_traits<unsigned char> (SQLCHAR); nanodbc.cpp uses its ::length().\n// <string> must come FIRST: force-inclusion runs before any libc++ header, so\n// _LIBCPP_VERSION is only defined after <string> has been seen.\n#ifndef MCPP_NANODBC_CHAR_TRAITS_H\n#define MCPP_NANODBC_CHAR_TRAITS_H\n#include <cstdio>\n#include <cstring>\n#include <string>\n#if defined(_LIBCPP_VERSION)\nnamespace std {\ntemplate <>\nstruct char_traits<unsigned char> {\n using char_type = unsigned char;\n using int_type = int;\n using off_type = streamoff;\n using pos_type = fpos<mbstate_t>;\n using state_type = mbstate_t;\n static void assign(char_type& c1, char_type c2) noexcept { c1 = c2; }\n static bool eq(char_type c1, char_type c2) noexcept { return c1 == c2; }\n static bool lt(char_type c1, char_type c2) noexcept { return c1 < c2; }\n static int compare(const char_type* s1, const char_type* s2, size_t n) {\n return n == 0 ? 0 : memcmp(s1, s2, n);\n }\n static size_t length(const char_type* s) {\n return strlen(reinterpret_cast<const char*>(s));\n }\n static const char_type* find(const char_type* s, size_t n, const char_type& a) {\n return static_cast<const char_type*>(memchr(s, a, n));\n }\n static char_type* move(char_type* s1, const char_type* s2, size_t n) {\n return static_cast<char_type*>(memmove(s1, s2, n));\n }\n static char_type* copy(char_type* s1, const char_type* s2, size_t n) {\n return static_cast<char_type*>(memcpy(s1, s2, n));\n }\n static char_type* assign(char_type* s, size_t n, char_type a) {\n return static_cast<char_type*>(memset(s, a, n));\n }\n static int_type not_eof(int_type c) noexcept { return eq_int_type(c, eof()) ? 0 : c; }\n static char_type to_char_type(int_type c) noexcept { return static_cast<char_type>(c); }\n static int_type to_int_type(char_type c) noexcept { return static_cast<int_type>(c); }\n static bool eq_int_type(int_type c1, int_type c2) noexcept { return c1 == c2; }\n static int_type eof() noexcept { return static_cast<int_type>(EOF); }\n};\n} // namespace std\n#endif\n#endif\n",

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

这个可以分行

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6eef45b提交让ai修改了

… error

With string_view support on (C++17+), NANODBC_INSTANTIATE_BIND_STRINGS
over std::string and std::string_view emit identical explicit
instantiation definitions (both reduce to value_type = char; likewise
the u16 pair). Same TU, identical specializations, semantics unaffected
-- but GCC (the linux default leg) rejects duplicates where clang
accepts silently. CI run 31890353516, workspace (linux default).

-fpermissive is GCC's own downgrade for exactly this diagnostic and
clang ignores the flag, so the linux leg serves both toolchains.
Review feedback: the single \n-escaped line was unreadable. Lua's ..
concatenation is NOT an option here -- the mcpp segment parser rejects
it (malformed mcpp segment near key 'string'), so the shim moves to the
[==[ ]==] form compat.ffmpeg and compat.sdl2 already use for their
generated configs. Content byte-identical to the one-liner.
@FarnaHerry
FarnaHerry requested a review from Sunrisepeak August 15, 2026 14:51
@FarnaHerry

Copy link
Copy Markdown
Collaborator Author

跟进两个评审/CI 问题,均已修复且全绿:

  1. linux default(GCC)腿首轮红:string_view 支持(C++17 起默认开)使 NANODBC_INSTANTIATE_BIND_STRINGS(std::string)(std::string_view) 展开出完全相同的显式实例化定义,GCC 按 ill-formed 拒绝、clang 静默接受。修复:linux cxxflags-fpermissive(7d3576e)。
  2. shim 单行不可读的评审意见:改为 [==[ ]==] 多行块(6eef45b),与 compat.ffmpeg / compat.sdl2 的 generated config 同形。改写时实证 Lua .. 拼接不被 mcpp 段解析器支持,此边界已记入设计文档 §5.4。

第二轮 CI 全部通过(linux default / linux llvm / macos / windows / lint / mirror-cn-reachable)。

@Sunrisepeak
Sunrisepeak merged commit 0dd1e5d into mcpplibs:main Aug 15, 2026
11 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

收录 nanodbc(ODBC 的 C++ 封装)及其 linux 驱动管理器 unixODBC

2 participants