{"id":13494157,"url":"https://github.com/maharmstone/tdscpp","last_synced_at":"2025-08-23T13:17:02.851Z","repository":{"id":86747965,"uuid":"250610912","full_name":"maharmstone/tdscpp","owner":"maharmstone","description":"C++ library to interface with Microsoft SQL Server","archived":false,"fork":false,"pushed_at":"2025-03-01T16:12:12.000Z","size":1008,"stargazers_count":15,"open_issues_count":0,"forks_count":5,"subscribers_count":5,"default_branch":"master","last_synced_at":"2025-07-25T13:41:39.717Z","etag":null,"topics":["cpp","mssql","tds"],"latest_commit_sha":null,"homepage":"","language":"C++","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"lgpl-3.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/maharmstone.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-03-27T18:15:35.000Z","updated_at":"2025-07-23T14:16:06.000Z","dependencies_parsed_at":"2023-11-07T15:27:13.989Z","dependency_job_id":"1cbf5f36-182c-4686-b489-7925689a08b8","html_url":"https://github.com/maharmstone/tdscpp","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"purl":"pkg:github/maharmstone/tdscpp","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maharmstone%2Ftdscpp","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maharmstone%2Ftdscpp/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maharmstone%2Ftdscpp/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maharmstone%2Ftdscpp/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/maharmstone","download_url":"https://codeload.github.com/maharmstone/tdscpp/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/maharmstone%2Ftdscpp/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":271749046,"owners_count":24814113,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-23T02:00:09.327Z","response_time":69,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["cpp","mssql","tds"],"created_at":"2024-07-31T19:01:22.408Z","updated_at":"2025-08-23T13:17:02.831Z","avatar_url":"https://github.com/maharmstone.png","language":"C++","funding_links":[],"categories":["C++"],"sub_categories":[],"readme":"tdscpp\n======\n\ntdscpp is a C++ library for Microsoft's Tabluar Data Stream (TDS) protocol, which is used to communicate with Microsoft SQL Server. Using this library you can write programs which interface directly with MSSQL, without relying on ODBC or OLEDB. It requires C++20, and works on the latest versions of GCC, MSVC, and Clang.\n\nInstallation\n------------\n\nFor vcpkg, run `vcpkg install tdscpp`.\n\nFor Gentoo, install the GURU repository and run `emerge tdscpp`.\n\nFor manual installation, it's much the same as for any CMake project:\n\n````\nmkdir build\ncd build\ncmake ..\nmake\nmake install\n````\n\nOn Linux, to add Kerberos support add `-DENABLE_KRB5=ON` to `cmake`. To add encryption\nsupport, add `-DWITH_OPENSSL=ON` to `cmake`.\n\nOn Windows, both Kerberos support and encryption are provided by the operating system,\nso you don't need to do this. If you set `WITH_OPENSSL` OpenSSL will be linked in,\nbut otherwise Windows' own Schannel will be used instead.\n\nOnce installed, you can add it to your CMakeLists.txt:\n\n````\nset(CMAKE_CXX_STANDARD 20)\nfind_package(tdscpp REQUIRED)\ntarget_link_libraries(project tdscpp)\n````\n\nMake sure that you're using at least C++20.\n\nFor GCC, just add `-ltdscpp`:\n````\ng++ -std=c++20 -ltdscpp test.cpp\n````\n\nRelease history\n---------------\n\n* 20250301\n    * Fixed for libfmt 11\n\n* 20240707\n    * Added support for older compiler versions\n    * pkgconfig now used to find Kerberos on Linux\n    * Fixed static compilation\n\n* 20240629\n    * Fixed compilation on 32-bit platforms\n    * Fixed compilation on Clang\n    * Fixed case where sp_prepare doesn't know columns\n    * Fixed handling of short NUMERICs in BCP\n    * Sped up reading large VARCHAR(MAX) values\n    * Added CI\n\n* 20240212\n    * Initial release\n\nSample code\n-----------\n\n````cpp\n#include \u003ctdscpp.h\u003e\n#include \u003cnlohmann/json.hpp\u003e\n#include \u003cformat\u003e\n#include \u003ciostream\u003e\n\nint main() {\n    try {\n        /* Connect to server mssql with username \"sa\" and password \"password\". If you omit\n           the credentials, Kerberos / Active Directory authentication will be used.\n           You can also pass more options by using a tds::options struct instead. */\n        tds::tds conn(\"mssql\", \"sa\", \"password\");\n\n        /* Issue commands by using the run method. These will throw an exception if the server\n           returns an error. */\n        conn.run(\"DROP TABLE IF EXISTS dbo.tmp\");\n        conn.run(\"CREATE TABLE dbo.tmp(num INT, str VARCHAR(10), str2 NVARCHAR(10), dt DATETIME2(0), not_set INT)\");\n\n        {\n            /* Begin a transaction. This will automatically get rolled back if it goes out of\n               scope without tran.commit() being called, such as if an exception is thrown. */\n            tds::trans tran(conn);\n\n            /* You can use question marks for placeholder variables. There's basic constexpr\n               sanity checking, meaning that if you forget a variable or have mismatched brackets\n               your code won't compile. There's also full Unicode support. */\n            conn.run(\"INSERT INTO dbo.tmp(num, str, str2, dt) VALUES(42, ?, ?, GETDATE())\",\n                     \"hello\", u8\"🔥\");\n\n            {\n                /* Run a query. You can only have one active query at a time per connection,\n                   so it's a good idea to wrap it in curly brackets so it gets released when\n                   it goes out of scope. */\n                tds::query sq(conn, \"SELECT * FROM dbo.tmp WHERE num = ?\", 42);\n\n                /* fetch_row() returns false when there's no more results. */\n                while (sq.fetch_row()) {\n                    /* The returned data is in tds::value structs, which can be cast to int,\n                       std::string, etc., or passed to std::format as below. */\n                    std::cout \u003c\u003c std::format(\"{}, {}, {}, {}\", sq[0], sq[1], sq[2], sq[3]) \u003c\u003c std::endl;\n\n                    /* Or you can pass it to nlohmann::json. */\n                    auto j = nlohmann::json::object();\n\n                    for (unsigned int i = 0; i \u003c sq.num_columns(); i++) {\n                        /* MSSQL stores column names at UTF-16, but you can use tds::utf16_to_utf8\n                           to fix this. */\n                        j[tds::utf16_to_utf8(sq[i].name)] = sq[i];\n                    }\n\n                    std::cout \u003c\u003c j.dump() \u003c\u003c std::endl;\n                }\n            }\n\n            /* Commit the transaction we opened before. */\n            tran.commit();\n        }\n    } catch (const std::exception\u0026 e) {\n        std::cerr \u003c\u003c std::format(\"Exception: {}\\n\", e.what());\n        return 1;\n    }\n\n    return 0;\n}\n````\n\nSample output:\n````\n42, hello, 🔥, 2024-02-12 21:21:05\n{\"dt\":\"2024-02-12 21:21:05\",\"not_set\":null,\"num\":42,\"str\":\"hello\",\"str2\":\"🔥\"}\n````\n\n### Remote Procedure Calls (RPC)\n\nYou can call Remote Procedure Calls, either inbuilt ones or custom stored procedures,\nby using `tds::rpc` in much the same way as you use `tds::query`.\n\n````cpp\nconn.run(\"DROP VIEW IF EXISTS dbo.test_view\");\nconn.run(\"CREATE VIEW dbo.test_view AS SELECT * FROM dbo.tmp\");\n\n{\n    tds::rpc r(conn, \"sp_depends\", \"dbo.test_view\");\n\n    while (r.fetch_row()) {\n        for (size_t i = 0; i \u003c r.num_columns(); i++) {\n            std::cout \u003c\u003c (std::string)r[i] \u003c\u003c \" (\" \u003c\u003c tds::utf16_to_utf8(r[i].name) \u003c\u003c \")\" \u003c\u003c std::endl;\n        }\n        std::cout \u003c\u003c \"---\" \u003c\u003c std::endl;\n    }\n}\n````\n\nOutput:\n````\ndbo.tmp (name)\nuser table (type)\nno (updated)\nyes (selected)\nnum (column)\n---\n...\n````\n\n### Bulk Copy Protocol (BCP)\n\nYou can use `tds::bcp` to load data into the database quickly. It takes three arguments:\nthe name of the table, an input range of the column names, and an input range of\nan input range of the values. If you pass normal strings rather than UTF-16, they will\nget converted for you.\n\n`std::optional` can be used to represent nullable values.\n\n````cpp\nconn.bcp(u\"dbo.tmp\", std::vector{u\"num\", u\"str\", u\"str2\"}, std::vector\u003cstd::vector\u003ctds::value\u003e\u003e{\n    {28, \"foo\", \"bar\"},\n    {29, \"baz\", std::optional\u003cstd::string\u003e{std::nullopt}}\n});\n````\n\n### Messages\n\nThe `tds::tds` constructor can also be passed a function pointer or a lambda to receive\nany messages that are sent by the server, including error messages. The default behaviour\nis to throw an exception if any messages are received with a severity more than 10.\n\n````cpp\nstatic void show_msg(std::string_view server, std::string_view message, std::string_view proc_name,\n                     int32_t msgno, int32_t line_number, int16_t state, uint8_t severity,\n                     bool error) {\n    if (severity \u003e 10) /* print errors in red */\n        std::cout \u003c\u003c std::format(\"\\x1b[31;1mError {}: {}\\x1b[0m\\n\", msgno, message);\n    else if (msgno == 50000) /* match SSMS by not displaying message no. if 50000 (RAISERROR etc.) */\n        std::cout \u003c\u003c std::format(\"{}\\n\", message);\n    else\n        std::cout \u003c\u003c std::format(\"{}: {}\\n\", msgno, message);\n}\n\nint main() {\n    /* See the definition of tds::options for an exhaustive list of all\n       the possible parameters here. */\n    tds::tds conn(\"mssql\", \"sa\", \"password\", \"test program\", \"\", show_msg);\n\n    conn.run(\"RAISERROR('hello world', 10, 1)\");\n    conn.run(\"THROW 50000, ':-(', 1\");\n\n    return 0;\n}\n````\n\nOutput:\n````\nhello world\nError 50000: :-(\n````\n\nData types\n----------\n\nThe struct `tds::value` represents any value that might be stored in a MSSQL database. The spaceship\noperator `\u003c=\u003e` is defined, meaning that you can compare values using (hopefully) the\nexact same logic as on the server itself.\n\n`tds::numeric\u003cN\u003e` can be used to represent NUMERIC(N) or DECIMAL(N) types, where\nN is a number between 0 and 38.\n\n`tds::datetime` can be used to represent DATETIME and DATETIME2 types. `tds::datetimeoffset`\nadds an offset value, for DATETIMEOFFSET. DATE maps to `std::chrono::year_month_day`,\nand TIME to `std::chrono::time_point`.\n\nCompile-time checks\n-------------------\n\nIf you pass a string to `tds::query` or `tds::rpc`, the library will do some basic sanity checking\nat compile-time, and refuse to compile if these fail. These include making sure that there is the\nsame number of open brackets and close brackets, and that there are no unterminated quotation\nmarks.\n\nIf you want to pass a string that you have constructed in as a query, you will need to\nwrap it in `tds::no_check`, e.g.:\n\n````cpp\nstatic void do_insert(tds::tds\u0026 tds, const std::string\u0026 column_name, std::string_view value) {\n    std::string q;\n\n    q = \"INSERT INTO tbl(\" + column + \") VALUES(?)\";\n\n    tds.run(tds::no_check{q}, value);\n\n    ...\n}\n````\n\nNote that you should be only doing this if you have to, and never for values. The safe way to\npass arbitrary values if to use the question mark notation as above. Inserting them directly\ninto the string leaves you vulnerable to SQL injection.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaharmstone%2Ftdscpp","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fmaharmstone%2Ftdscpp","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fmaharmstone%2Ftdscpp/lists"}