feat: add compat.nanodbc and compat.unixodbc - #210
Merged
Conversation
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.
Sunrisepeak
requested changes
Aug 15, 2026
| -- 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", |
Collaborator
Author
There was a problem hiding this comment.
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.
…syntax boundaries
Collaborator
Author
|
跟进两个评审/CI 问题,均已修复且全绿:
第二轮 CI 全部通过(linux default / linux llvm / macos / windows / lint / mirror-cn-reachable)。 |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Closes #209.
收录内容
形态
nanodbc 为 Shape A(
nanodbc.cpp单 TU);unixodbc 为 Shape E 叠 A —— 冻结 configure 快照 + 纯 C 源码直编,消费端零 configure/make,DM + odbcinst + ini/log/lst + libltdl 编入单一odbc目标(即上游libodbc.a的符号集)。三个值得审的判断
-lodbc能编能链,但运行时闭包检查拒绝:mcpp 二进制的 PT_INTERP 是私有 loader,宿主/usr/lib不可达(同 mcpp#352)。静态源码构建让产物不带libodbc.so.2NEEDED,闭包天然通过,亦无宿主 glibc 错配。conan / vcpkg 对 linux ODBC 也是同样结论。dlopen驱动;静态构建下 libtool 的-dlpreopen接线用-DLTDLOPEN=libltdlc+ 一张生成的lt_libltdlc_LTX_preloaded_symbols表(从 libtool 目标文件的重定位还原)复刻。两份 config.h(顶层 / libltdl)合并为一份 —— ltdl 源码不读冲突宏,且带引号的-DLT_CONFIG_H无法穿过描述符 → 命令行管道(实测)。char_traits<unsigned char>:force-include 显式特化(标准定制点,_LIBCPP_VERSION为界,-include走cxxflags,仅作用于本包编译)。nanodbc 截掉 SQL state 末字符的上游 off-by-one 不改源码,测试按真实行为断言前缀。验证
mcpp test -p nanodbc冷构建 1 passed / 0 failed(无任何宿主 include/lib 路径注入,全链路自包含)。lt_dlopen+lt_dlsym行为一致。check_mirror_urls/check_package_name/check_cross_package_refs/check_platform_version_parity全过。mcpp xpkg parse:全部 98 个描述符在钉住的 2026.8.10.3 下通过。测试断言什么
无数据库、无驱动的错误路径:连接不存在的 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先例),维护者建仓后可补。