| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159 |
- //! \file nanodbc.cpp Implementation details.
- #ifndef DOXYGEN
- #include "nanodbc.h"
- #include <algorithm>
- #include <cassert>
- #include <clocale>
- #include <codecvt>
- #include <cstdint>
- #include <cstdio>
- #include <cstring>
- #include <ctime>
- #include <iomanip>
- #include <map>
- #include <sstream>
- #if defined(_MSC_VER) && _MSC_VER <= 1800
- // silence spurious Visual C++ warnings
- #pragma warning(disable:4244) // warning about integer conversion issues.
- #pragma warning(disable:4312) // warning about 64-bit portability issues.
- #pragma warning(disable:4996) // warning about snprintf() deprecated.
- #endif
- #ifdef __APPLE__
- // silence spurious OS X deprecation warnings
- #define MAC_OS_X_VERSION_MIN_REQUIRED MAC_OS_X_VERSION_10_6
- #endif
- #ifdef _WIN32
- // needs to be included above sql.h for windows
- #define NOMINMAX
- #include <windows.h>
- #endif
- #include <sql.h>
- #include <sqlext.h>
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Unicode Support
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #if defined(_MSC_VER)
- #ifdef NANODBC_USE_UNICODE
- #define NANODBC_TEXT(s) L ## s
- #define NANODBC_SNPRINTF std::swprintf
- #define NANODBC_STRFTIME std::wcsftime
- #define NANODBC_STRLEN std::wcslen
- #define NANADBC_STRNCMP std::wcsncmp
- #define NANODBC_UNICODE(f) f ## W
- #define NANODBC_SQLCHAR SQLWCHAR
- #else
- #define NANODBC_TEXT(s) s
- #define NANODBC_SNPRINTF _snprintf
- #define NANODBC_STRFTIME std::strftime
- #define NANODBC_STRLEN std::strlen
- #define NANADBC_STRNCMP std::strncmp
- #define NANODBC_UNICODE(f) f
- #define NANODBC_SQLCHAR SQLCHAR
- #endif
- #else
- #ifdef NANODBC_USE_UNICODE
- #define NANODBC_TEXT(s) L ## s
- #define NANODBC_SNPRINTF std::swprintf
- #define NANODBC_STRFTIME std::wcsftime
- #define NANODBC_STRLEN std::wcslen
- #define NANADBC_STRNCMP std::wcsncmp
- #define NANODBC_UNICODE(f) f ## W
- #define NANODBC_SQLCHAR SQLWCHAR
- #else
- #define NANODBC_TEXT(s) s
- #define NANODBC_SNPRINTF std::snprintf
- #define NANODBC_STRFTIME std::strftime
- #define NANODBC_STRLEN std::strlen
- #define NANADBC_STRNCMP std::strncmp
- #define NANODBC_UNICODE(f) f
- #define NANODBC_SQLCHAR SQLCHAR
- #endif
- #endif
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // ODBC API Interface
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- #define NANODBC_STRINGIZE_I(text) #text
- #define NANODBC_STRINGIZE(text) NANODBC_STRINGIZE_I(text)
- // By making all calls to ODBC functions through this macro, we can easily get
- // runtime debugging information of which ODBC functions are being called,
- // in what order, and with what parameters by defining NANODBC_ODBC_API_DEBUG.
- #ifdef NANODBC_ODBC_API_DEBUG
- #include <iostream>
- #define NANODBC_CALL_RC(FUNC, RC, ...) \
- do \
- { \
- std::cerr << __FILE__ ":" NANODBC_STRINGIZE(__LINE__) " " \
- NANODBC_STRINGIZE(FUNC) "(" #__VA_ARGS__ ")" << std::endl; \
- RC = FUNC(__VA_ARGS__); \
- } while(false) \
- /**/
- #define NANODBC_CALL(FUNC, ...) \
- do \
- { \
- std::cerr << __FILE__ ":" NANODBC_STRINGIZE(__LINE__) " " \
- NANODBC_STRINGIZE(FUNC) "(" #__VA_ARGS__ ")" << std::endl; \
- FUNC(__VA_ARGS__); \
- } while(false) \
- /**/
- #else
- #define NANODBC_CALL_RC(FUNC, RC, ...) RC = FUNC(__VA_ARGS__)
- #define NANODBC_CALL(FUNC, ...) FUNC(__VA_ARGS__)
- #endif
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Error and Exception Handling
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace
- {
- #ifdef NANODBC_ODBC_API_DEBUG
- inline nanodbc::string_type return_code(RETCODE rc)
- {
- switch(rc)
- {
- case SQL_SUCCESS: return NANODBC_UNICODE("SQL_SUCCESS");
- case SQL_SUCCESS_WITH_INFO: return NANODBC_UNICODE("SQL_SUCCESS_WITH_INFO");
- case SQL_ERROR: return NANODBC_UNICODE("SQL_ERROR");
- case SQL_INVALID_HANDLE: return NANODBC_UNICODE("SQL_INVALID_HANDLE");
- case SQL_NO_DATA: return NANODBC_UNICODE("SQL_NO_DATA");
- case SQL_NEED_DATA: return NANODBC_UNICODE("SQL_NEED_DATA");
- case SQL_STILL_EXECUTING: return NANODBC_UNICODE("SQL_STILL_EXECUTING");
- }
- assert(0);
- return "unknown"; // should never make it here
- }
- #endif
- // Easy way to check if a return code signifies success.
- inline bool success(RETCODE rc)
- {
- #ifdef NANODBC_ODBC_API_DEBUG
- std::cerr << "<-- rc: " << return_code(rc) << " | ";
- #endif
- return rc == SQL_SUCCESS || rc == SQL_SUCCESS_WITH_INFO;
- }
- // Returns the array size.
- template<typename T, std::size_t N>
- inline std::size_t arrlen(T(&)[N])
- {
- return N;
- }
- // Operates like strlen() on a character array.
- template<typename T, std::size_t N>
- inline std::size_t strarrlen(T(&a)[N])
- {
- const T* s = &a[0];
- std::size_t i = 0;
- while(*s++ && i < N) i++;
- return i;
- }
- inline void convert(const std::wstring& in, std::string& out)
- {
- std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
- out = conv.to_bytes(in);
- }
- #ifdef NANODBC_USE_UNICODE
- inline void convert(const std::string& in, std::wstring& out)
- {
- std::wstring_convert<std::codecvt_utf8<wchar_t>> conv;
- out = conv.from_bytes(in);
- }
- inline void convert(const std::wstring& in, std::wstring & out)
- {
- out = in;
- }
- #else
- inline void convert(const std::string& in, std::string& out)
- {
- out = in;
- }
- #endif
- // Attempts to get the most recent ODBC error as a string.
- // Always returns std::string, even in unicode mode.
- inline std::string recent_error(SQLHANDLE handle, SQLSMALLINT handle_type)
- {
- nanodbc::string_type result;
- std::string rvalue;
- std::vector<NANODBC_SQLCHAR> sql_message(SQL_MAX_MESSAGE_LENGTH);
- sql_message[0] = '\0';
- SQLINTEGER i = 1;
- SQLINTEGER native_error = 0;
- SQLSMALLINT total_bytes;
- NANODBC_SQLCHAR sql_state[6];
- RETCODE rc;
- do
- {
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLGetDiagRec)
- , rc
- , handle_type
- , handle
- , i
- , sql_state
- , &native_error
- , 0
- , 0
- , &total_bytes);
- if(success(rc) && total_bytes > 0)
- sql_message.resize(total_bytes + 1);
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLGetDiagRec)
- , rc
- , handle_type
- , handle
- , i
- , sql_state
- , &native_error
- , sql_message.data()
- , (SQLSMALLINT)sql_message.size()
- , &total_bytes);
- if(!success(rc))
- {
- convert(result, rvalue);
- return rvalue;
- }
- if(!result.empty())
- result += ' ';
- result += nanodbc::string_type(sql_message.begin(), sql_message.end());
- i++;
- } while(rc != SQL_NO_DATA);
- convert(result, rvalue);
- std::string status(&sql_state[0], &sql_state[arrlen(sql_state)]);
- status += ": ";
- status += rvalue;
- // some drivers insert \0 into error messages for unknown reasons
- using std::replace;
- replace(status.begin(), status.end(), '\0', ' ');
- return status;
- }
- } // namespace
- namespace nanodbc
- {
- type_incompatible_error::type_incompatible_error()
- : std::runtime_error("type incompatible") { }
- const char* type_incompatible_error::what() const noexcept
- {
- return std::runtime_error::what();
- }
- null_access_error::null_access_error()
- : std::runtime_error("null access") { }
- const char* null_access_error::what() const noexcept
- {
- return std::runtime_error::what();
- }
- index_range_error::index_range_error()
- : std::runtime_error("index out of range") { }
- const char* index_range_error::what() const noexcept
- {
- return std::runtime_error::what();
- }
- programming_error::programming_error(const std::string& info)
- : std::runtime_error(info.c_str()) { }
- const char* programming_error::what() const noexcept
- {
- return std::runtime_error::what();
- }
- database_error::database_error(void* handle, short handle_type, const std::string& info)
- : std::runtime_error(info + recent_error(handle, handle_type)) { }
- const char* database_error::what() const noexcept
- {
- return std::runtime_error::what();
- }
- } // namespace nanodbc
- // Throwing exceptions using NANODBC_THROW_DATABASE_ERROR enables file name
- // and line numbers to be inserted into the error message. Useful for debugging.
- #define NANODBC_THROW_DATABASE_ERROR(handle, handle_type) \
- throw nanodbc::database_error( \
- handle \
- , handle_type \
- , __FILE__ ":" NANODBC_STRINGIZE(__LINE__) ": ") \
- /**/
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Implementation Details
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace
- {
- using namespace std; // if int64_t is in std namespace (in c++11)
- // A utility for calculating the ctype from the given type T.
- // I essentially create a lookup table based on the MSDN ODBC documentation.
- // See http://msdn.microsoft.com/en-us/library/windows/desktop/ms714556(v=vs.85).aspx
- template<class T>
- struct sql_ctype { };
- template<>
- struct sql_ctype<nanodbc::string_type::value_type>
- {
- #ifdef NANODBC_USE_UNICODE
- static const SQLSMALLINT value = SQL_C_WCHAR;
- #else
- static const SQLSMALLINT value = SQL_C_CHAR;
- #endif
- };
- template<>
- struct sql_ctype<short>
- {
- static const SQLSMALLINT value = SQL_C_SSHORT;
- };
- template<>
- struct sql_ctype<unsigned short>
- {
- static const SQLSMALLINT value = SQL_C_USHORT;
- };
- template<>
- struct sql_ctype<int32_t>
- {
- static const SQLSMALLINT value = SQL_C_SLONG;
- };
- template<>
- struct sql_ctype<uint32_t>
- {
- static const SQLSMALLINT value = SQL_C_ULONG;
- };
- template<>
- struct sql_ctype<int64_t>
- {
- static const SQLSMALLINT value = SQL_C_SBIGINT;
- };
- template<>
- struct sql_ctype<uint64_t>
- {
- static const SQLSMALLINT value = SQL_C_UBIGINT;
- };
- template<>
- struct sql_ctype<float>
- {
- static const SQLSMALLINT value = SQL_C_FLOAT;
- };
- template<>
- struct sql_ctype<double>
- {
- static const SQLSMALLINT value = SQL_C_DOUBLE;
- };
- template<>
- struct sql_ctype<nanodbc::string_type>
- {
- #ifdef NANODBC_USE_UNICODE
- static const SQLSMALLINT value = SQL_C_WCHAR;
- #else
- static const SQLSMALLINT value = SQL_C_CHAR;
- #endif
- };
- template<>
- struct sql_ctype<nanodbc::date>
- {
- static const SQLSMALLINT value = SQL_C_DATE;
- };
- template<>
- struct sql_ctype<nanodbc::timestamp>
- {
- static const SQLSMALLINT value = SQL_C_TIMESTAMP;
- };
- // Encapsulates resources needed for column binding.
- class bound_column
- {
- public:
- bound_column(const bound_column& rhs) =delete;
- bound_column& operator=(bound_column rhs) =delete;
- bound_column()
- : name_()
- , column_(0)
- , sqltype_(0)
- , sqlsize_(0)
- , scale_(0)
- , ctype_(0)
- , clen_(0)
- , blob_(false)
- , rowset_size_(0)
- , cbdata_(0)
- , pdata_(0)
- {
- }
- ~bound_column()
- {
- delete[] cbdata_;
- delete[] pdata_;
- }
- public:
- nanodbc::string_type name_;
- short column_;
- SQLSMALLINT sqltype_;
- SQLULEN sqlsize_;
- SQLSMALLINT scale_;
- SQLSMALLINT ctype_;
- SQLULEN clen_;
- bool blob_;
- long rowset_size_;
- nanodbc::null_type* cbdata_;
- char* pdata_;
- };
- // Allocates the native ODBC handles.
- inline void allocate_handle(SQLHENV& env, SQLHDBC& conn)
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLAllocHandle
- , rc
- , SQL_HANDLE_ENV
- , SQL_NULL_HANDLE
- , &env);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
- try
- {
- NANODBC_CALL_RC(
- SQLSetEnvAttr
- , rc
- , env
- , SQL_ATTR_ODBC_VERSION
- , (SQLPOINTER)SQL_OV_ODBC3
- , SQL_IS_UINTEGER);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
- NANODBC_CALL_RC(
- SQLAllocHandle
- , rc
- , SQL_HANDLE_DBC
- , env
- , &conn);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(env, SQL_HANDLE_ENV);
- }
- catch(...)
- {
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_ENV
- , env);
- throw;
- }
- }
- } // namespace
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Private Implementation: connection
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- class connection::connection_impl
- {
- public:
- connection_impl(const connection_impl&) =delete;
- connection_impl& operator=(const connection_impl&) =delete;
- connection_impl()
- : env_(0)
- , conn_(0)
- , connected_(false)
- , transactions_(0)
- , rollback_(false)
- {
- allocate_handle(env_, conn_);
- }
- connection_impl(
- const string_type& dsn
- , const string_type& user
- , const string_type& pass
- , long timeout)
- : env_(0)
- , conn_(0)
- , connected_(false)
- , transactions_(0)
- , rollback_(false)
- {
- allocate_handle(env_, conn_);
- try
- {
- connect(dsn, user, pass, timeout);
- }
- catch(...)
- {
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_DBC
- , conn_);
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_ENV
- , env_);
- throw;
- }
- }
- connection_impl(const string_type& connection_string, long timeout)
- : env_(0)
- , conn_(0)
- , connected_(false)
- , transactions_(0)
- , rollback_(false)
- {
- allocate_handle(env_, conn_);
- try
- {
- connect(connection_string, timeout);
- }
- catch(...)
- {
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_DBC
- , conn_);
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_ENV
- , env_);
- throw;
- }
- }
- ~connection_impl() noexcept
- {
- try
- {
- disconnect();
- }
- catch(...)
- {
- // ignore exceptions thrown during disconnect
- }
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_DBC
- , conn_);
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_ENV
- , env_);
- }
- void connect(
- const string_type& dsn
- , const string_type& user
- , const string_type& pass
- , long timeout)
- {
- disconnect();
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLFreeHandle
- , rc
- , SQL_HANDLE_DBC
- , conn_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- NANODBC_CALL_RC(
- SQLAllocHandle
- , rc
- , SQL_HANDLE_DBC
- , env_
- , &conn_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
- NANODBC_CALL_RC(
- SQLSetConnectAttr
- , rc
- , conn_
- , SQL_LOGIN_TIMEOUT
- , (SQLPOINTER)timeout
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLConnect)
- , rc
- , conn_
- , (NANODBC_SQLCHAR*)dsn.c_str(), SQL_NTS
- , !user.empty() ? (NANODBC_SQLCHAR*)user.c_str() : 0, SQL_NTS
- , !pass.empty() ? (NANODBC_SQLCHAR*)pass.c_str() : 0, SQL_NTS);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- connected_ = success(rc);
- }
- void connect(const string_type& connection_string, long timeout)
- {
- disconnect();
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLFreeHandle
- , rc
- , SQL_HANDLE_DBC
- , conn_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- NANODBC_CALL_RC(
- SQLAllocHandle
- , rc
- , SQL_HANDLE_DBC
- , env_
- , &conn_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(env_, SQL_HANDLE_ENV);
- NANODBC_CALL_RC(
- SQLSetConnectAttr
- , rc
- , conn_
- , SQL_LOGIN_TIMEOUT
- , (SQLPOINTER)timeout
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- NANODBC_SQLCHAR dsn[1024];
- SQLSMALLINT dsn_size = 0;
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLDriverConnect)
- , rc
- , conn_
- , 0
- , (NANODBC_SQLCHAR*)connection_string.c_str(), SQL_NTS
- , dsn
- , sizeof(dsn) / sizeof(NANODBC_SQLCHAR)
- , &dsn_size
- , SQL_DRIVER_NOPROMPT);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- connected_ = success(rc);
- }
- bool connected() const
- {
- return connected_;
- }
- void disconnect()
- {
- if(connected())
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLDisconnect
- , rc
- , conn_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- }
- connected_ = false;
- }
- std::size_t transactions() const
- {
- return transactions_;
- }
- void* native_dbc_handle() const
- {
- return conn_;
- }
- void* native_env_handle() const
- {
- return env_;
- }
- string_type driver_name() const
- {
- NANODBC_SQLCHAR name[1024];
- SQLSMALLINT length;
- RETCODE rc;
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLGetInfo)
- , rc
- , conn_
- , SQL_DRIVER_NAME
- , name
- , sizeof(name) / sizeof(NANODBC_SQLCHAR)
- , &length);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_, SQL_HANDLE_DBC);
- return string_type(&name[0], &name[strarrlen(name)]);
- }
- std::size_t ref_transaction()
- {
- return --transactions_;
- }
- std::size_t unref_transaction()
- {
- return ++transactions_;
- }
- bool rollback() const
- {
- return rollback_;
- }
- void rollback(bool onoff)
- {
- rollback_ = onoff;
- }
- private:
- HENV env_;
- HDBC conn_;
- bool connected_;
- std::size_t transactions_;
- bool rollback_; // if true, this connection is marked for eventual transaction rollback
- };
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Private Implementation: transaction
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- class transaction::transaction_impl
- {
- public:
- transaction_impl(const transaction_impl&) =delete;
- transaction_impl& operator=(const transaction_impl&) =delete;
- transaction_impl(const class connection& conn)
- : conn_(conn)
- , committed_(false)
- {
- if(conn_.transactions() == 0 && conn_.connected())
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLSetConnectAttr
- , rc
- , conn_.native_dbc_handle()
- , SQL_ATTR_AUTOCOMMIT
- , (SQLPOINTER)SQL_AUTOCOMMIT_OFF
- , SQL_IS_UINTEGER);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
- }
- conn_.ref_transaction();
- }
- ~transaction_impl() noexcept
- {
- if(!committed_)
- {
- conn_.rollback(true);
- conn_.unref_transaction();
- }
- if(conn_.transactions() == 0 && conn_.connected())
- {
- if(conn_.rollback())
- {
- NANODBC_CALL(
- SQLEndTran
- , SQL_HANDLE_DBC
- , conn_.native_dbc_handle()
- , SQL_ROLLBACK);
- conn_.rollback(false);
- }
- NANODBC_CALL(
- SQLSetConnectAttr
- , conn_.native_dbc_handle()
- , SQL_ATTR_AUTOCOMMIT
- , (SQLPOINTER)SQL_AUTOCOMMIT_ON
- , SQL_IS_UINTEGER);
- }
- }
- void commit()
- {
- if(committed_)
- return;
- committed_ = true;
- if(conn_.unref_transaction() == 0 && conn_.connected())
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLEndTran
- , rc
- , SQL_HANDLE_DBC
- , conn_.native_dbc_handle()
- , SQL_COMMIT);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(conn_.native_dbc_handle(), SQL_HANDLE_DBC);
- }
- }
- void rollback() noexcept
- {
- if(committed_)
- return;
- conn_.rollback(true);
- }
- class connection& connection()
- {
- return conn_;
- }
- const class connection& connection() const
- {
- return conn_;
- }
- private:
- class connection conn_;
- bool committed_;
- };
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Private Implementation: statement
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- class statement::statement_impl
- {
- public:
- statement_impl(const statement_impl&) =delete;
- statement_impl& operator=(const statement_impl&) =delete;
- statement_impl()
- : stmt_(0)
- , open_(false)
- , conn_()
- , bind_len_or_null_()
- {
- }
- statement_impl(class connection& conn)
- : stmt_(0)
- , open_(false)
- , conn_()
- , bind_len_or_null_()
- {
- open(conn);
- }
- statement_impl(class connection& conn, const string_type& query, long timeout)
- : stmt_(0)
- , open_(false)
- , conn_()
- , bind_len_or_null_()
- {
- prepare(conn, query, timeout);
- }
- ~statement_impl() noexcept
- {
- if(open() && connected())
- {
- NANODBC_CALL(
- SQLCancel
- , stmt_);
- reset_parameters();
- NANODBC_CALL(
- SQLFreeHandle
- , SQL_HANDLE_STMT
- , stmt_);
- }
- }
- void open(class connection& conn)
- {
- close();
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLAllocHandle
- , rc
- , SQL_HANDLE_STMT
- , conn.native_dbc_handle()
- , &stmt_);
- open_ = success(rc);
- if(!open_)
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- conn_ = conn;
- }
- bool open() const
- {
- return open_;
- }
- bool connected() const
- {
- return conn_.connected();
- }
- const class connection& connection() const
- {
- return conn_;
- }
- class connection& connection()
- {
- return conn_;
- }
- void* native_statement_handle() const
- {
- return stmt_;
- }
- void close()
- {
- if(open() && connected())
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLCancel
- , rc
- , stmt_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- reset_parameters();
- NANODBC_CALL_RC(
- SQLFreeHandle
- , rc
- , SQL_HANDLE_STMT
- , stmt_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- open_ = false;
- stmt_ = 0;
- }
- void cancel()
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLCancel
- , rc
- , stmt_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- void prepare(class connection& conn, const string_type& query, long timeout)
- {
- open(conn);
- prepare(query, timeout);
- }
- void prepare(const string_type& query, long timeout)
- {
- if(!open())
- throw programming_error("statement has no associated open connection");
- RETCODE rc;
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLPrepare)
- , rc
- , stmt_
- , (NANODBC_SQLCHAR*)query.c_str()
- , (SQLINTEGER)query.size());
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- this->timeout(timeout);
- }
- void timeout(long timeout)
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLSetStmtAttr
- , rc
- , stmt_
- , SQL_ATTR_QUERY_TIMEOUT
- , (SQLPOINTER)timeout,
- 0);
- // some drivers don't support timeout for statements,
- // so only raise the error if a non-default timeout was requested.
- if(!success(rc) && (timeout != 0))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- result execute_direct(
- class connection& conn
- , const string_type& query
- , long batch_operations
- , long timeout
- , statement& statement)
- {
- #ifdef NANODBC_HANDLE_NODATA_BUG
- const RETCODE rc = just_execute_direct(conn, query, batch_operations, timeout, statement);
- if(rc == SQL_NO_DATA)
- return result();
- #else
- just_execute_direct(conn, query, batch_operations, timeout, statement);
- #endif
- return result(statement, batch_operations);
- }
- RETCODE just_execute_direct(
- class connection& conn
- , const string_type& query
- , long batch_operations
- , long timeout
- , statement& statement)
- {
- open(conn);
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLSetStmtAttr
- , rc
- , stmt_
- , SQL_ATTR_PARAMSET_SIZE
- , (SQLPOINTER)batch_operations
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- this->timeout(timeout);
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLExecDirect)
- , rc
- , stmt_
- , (NANODBC_SQLCHAR*)query.c_str()
- , SQL_NTS);
- if(!success(rc) && rc != SQL_NO_DATA)
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return rc;
- }
- result execute(long batch_operations, long timeout, statement& statement)
- {
- #ifdef NANODBC_HANDLE_NODATA_BUG
- const RETCODE rc = just_execute(batch_operations, timeout, statement);
- if(rc == SQL_NO_DATA)
- return result();
- #else
- just_execute(batch_operations, timeout, statement);
- #endif
- return result(statement, batch_operations);
- }
- RETCODE just_execute(long batch_operations, long timeout, statement& statement)
- {
- RETCODE rc;
- if(open())
- {
- // The ODBC cursor must be closed before subsequent executions, as described
- // here http://msdn.microsoft.com/en-us/library/windows/desktop/ms713584%28v=vs.85%29.aspx
- //
- // However, we don't necessarily want to call SQLCloseCursor() because that
- // will cause an invalid cursor state in the case that no cursor is currently open.
- // A better solution is to use SQLFreeStmt() with the SQL_CLOSE option, which has
- // the same effect without the undesired limitations.
- NANODBC_CALL_RC(
- SQLFreeStmt
- , rc
- , stmt_
- , SQL_CLOSE);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- NANODBC_CALL_RC(
- SQLSetStmtAttr
- , rc
- , stmt_
- , SQL_ATTR_PARAMSET_SIZE
- , (SQLPOINTER)batch_operations
- , 0);
- if(!success(rc) && rc != SQL_NO_DATA)
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- this->timeout(timeout);
- NANODBC_CALL_RC(
- SQLExecute
- , rc
- , stmt_);
- if(!success(rc) && rc != SQL_NO_DATA)
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return rc;
- }
- result procedure_columns(
- const string_type& catalog
- , const string_type& schema
- , const string_type& procedure
- , const string_type& column
- , statement& statement)
- {
- if(!open())
- throw programming_error("statement has no associated open connection");
- RETCODE rc;
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLProcedureColumns)
- , rc
- , stmt_
- , (NANODBC_SQLCHAR*)(catalog.empty() ? NULL : catalog.c_str())
- , (catalog.empty() ? 0 : SQL_NTS)
- , (NANODBC_SQLCHAR*)(schema.empty() ? NULL : schema.c_str())
- , (schema.empty() ? 0 : SQL_NTS)
- , (NANODBC_SQLCHAR*)procedure.c_str()
- , SQL_NTS
- , (NANODBC_SQLCHAR*)(column.empty() ? NULL : column.c_str())
- , (column.empty() ? 0 : SQL_NTS));
- if (!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return result(statement, 1);
- }
- long affected_rows() const
- {
- SQLLEN rows;
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLRowCount
- , rc
- , stmt_
- , &rows);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return rows;
- }
- short columns() const
- {
- SQLSMALLINT cols;
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLNumResultCols
- , rc
- , stmt_
- , &cols);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return cols;
- }
- void reset_parameters() noexcept
- {
- NANODBC_CALL(
- SQLFreeStmt
- , stmt_
- , SQL_RESET_PARAMS);
- }
- unsigned long parameter_size(short param) const
- {
- RETCODE rc;
- SQLSMALLINT data_type;
- SQLULEN parameter_size;
- NANODBC_CALL_RC(
- SQLDescribeParam
- , rc
- , stmt_
- , param + 1
- , &data_type
- , ¶meter_size
- , 0
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- return parameter_size;
- }
- static SQLSMALLINT param_type_from_direction(param_direction direction)
- {
- switch(direction)
- {
- default:
- assert(false);
- // fallthrough
- case PARAM_IN:
- return SQL_PARAM_INPUT;
- break;
- case PARAM_OUT:
- return SQL_PARAM_OUTPUT;
- break;
- case PARAM_INOUT:
- return SQL_PARAM_INPUT_OUTPUT;
- break;
- case PARAM_RETURN:
- return SQL_PARAM_OUTPUT;
- break;
- }
- // Remove warning C4702 : unreachable code nanodbc.cpp 1284 Nanodbc
- assert(false);
- }
- // initializes bind_len_or_null_ and gets information for bind
- void prepare_bind(
- short param
- , std::size_t elements
- , param_direction direction
- , SQLSMALLINT& data_type
- , SQLSMALLINT& param_type
- , SQLULEN& parameter_size)
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLDescribeParam
- , rc
- , stmt_
- , param + 1
- , &data_type
- , ¶meter_size
- , 0
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- param_type = param_type_from_direction(direction);
- if(!bind_len_or_null_.count(param))
- bind_len_or_null_[param] = std::vector<null_type>();
- std::vector<null_type>().swap(bind_len_or_null_[param]);
- // ODBC weirdness: this must be at least 8 elements in size
- const std::size_t indicator_size = elements > 8 ? elements : 8;
- bind_len_or_null_[param].reserve(indicator_size);
- bind_len_or_null_[param].assign(indicator_size, SQL_NULL_DATA);
- }
- // calls actual ODBC bind parameter function
- template<class T>
- void bind_parameter(
- short param
- , const T* data
- , std::size_t /*elements*/
- , SQLSMALLINT data_type
- , SQLSMALLINT param_type
- , SQLULEN parameter_size)
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLBindParameter
- , rc
- , stmt_ // handle
- , param + 1 // parameter number
- , param_type // input or output type
- , sql_ctype<T>::value // value type
- , data_type // parameter type
- , parameter_size // column size ignored for many types, but needed for strings
- , 0 // decimal digits
- , (SQLPOINTER)data // parameter value
- , parameter_size // buffer length
- , bind_len_or_null_[param].data());
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- // handles a single value (possibly a single string value), or multiple non-string values
- template<class T>
- void bind(short param, const T* values, std::size_t elements, param_direction direction);
- // handles multiple string values
- void bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t /*length*/
- , std::size_t elements
- , param_direction direction)
- {
- bind(param, values, elements, direction);
- }
- // handles multiple null values
- void bind_null(short param, std::size_t elements)
- {
- SQLSMALLINT data_type;
- SQLSMALLINT param_type;
- SQLULEN parameter_size;
- prepare_bind(param, elements, PARAM_IN, data_type, param_type, parameter_size);
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLBindParameter
- , rc
- , stmt_
- , param + 1
- , param_type
- , SQL_C_CHAR
- , data_type
- , parameter_size // column size ignored for many types, but needed for strings
- , 0
- , (SQLPOINTER)0 // null value
- , 0 // parameter_size
- , bind_len_or_null_[param].data());
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- // comparator for null sentry values
- template<class T>
- bool equals(const T& lhs, const T& rhs)
- {
- return lhs == rhs;
- }
- // handles multiple non-string values with a null sentry
- template<class T>
- void bind(
- short param
- , const T* values
- , std::size_t elements
- , const bool* nulls
- , const T* null_sentry
- , param_direction direction);
- // handles multiple string values
- void bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t length
- , std::size_t elements
- , const bool* nulls
- , const string_type::value_type* null_sentry
- , param_direction direction);
- private:
- HSTMT stmt_;
- bool open_;
- class connection conn_;
- std::map<short, std::vector<null_type> > bind_len_or_null_;
- };
- // Supports code like: query.bind(0, std_string.c_str())
- // In this case, we need to pass NULL to the final parameter of SQLBindParameter().
- template<>
- void statement::statement_impl::bind_parameter<string_type::value_type>(
- short param
- , const string_type::value_type* data
- , std::size_t elements
- , SQLSMALLINT data_type
- , SQLSMALLINT param_type
- , SQLULEN parameter_size)
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLBindParameter
- , rc
- , stmt_ // handle
- , param + 1 // parameter number
- , param_type // input or output type
- , sql_ctype<string_type::value_type>::value // value type
- , data_type // parameter type
- , parameter_size // column size ignored for many types, but needed for strings
- , 0 // decimal digits
- , (SQLPOINTER)data // parameter value
- , parameter_size // buffer length
- , (elements <= 1 ? NULL : bind_len_or_null_[param].data()));
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_, SQL_HANDLE_STMT);
- }
- template<class T>
- void statement::statement_impl::bind(
- short param
- , const T* values
- , std::size_t elements
- , param_direction direction)
- {
- SQLSMALLINT data_type;
- SQLSMALLINT param_type;
- SQLULEN parameter_size;
- prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
- for(std::size_t i = 0; i < elements; ++i)
- bind_len_or_null_[param][i] = parameter_size;
- bind_parameter(param, values, elements, data_type, param_type, parameter_size);
- }
- template<class T>
- void statement::statement_impl::bind(
- short param
- , const T* values
- , std::size_t elements
- , const bool* nulls
- , const T* null_sentry
- , param_direction direction)
- {
- SQLSMALLINT data_type;
- SQLSMALLINT param_type;
- SQLULEN parameter_size;
- prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
- for(std::size_t i = 0; i < elements; ++i)
- if((null_sentry && !equals(values[i], *null_sentry)) || (nulls && !nulls[i]))
- bind_len_or_null_[param][i] = parameter_size;
- bind_parameter(param, values, elements, data_type, param_type, parameter_size);
- }
- void statement::statement_impl::bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t length
- , std::size_t elements
- , const bool* nulls
- , const string_type::value_type* null_sentry
- , param_direction direction)
- {
- SQLSMALLINT data_type;
- SQLSMALLINT param_type;
- SQLULEN parameter_size;
- prepare_bind(param, elements, direction, data_type, param_type, parameter_size);
- if(null_sentry)
- {
- const string_type rhs(null_sentry);
- for(std::size_t i = 0; i < elements; ++i)
- {
- const string_type lhs(values + i * length, values + (i + 1) * length);
- if(NANADBC_STRNCMP(lhs.c_str(), rhs.c_str(), length))
- bind_len_or_null_[param][i] = parameter_size;
- }
- }
- else if(nulls)
- {
- for(std::size_t i = 0; i < elements; ++i)
- {
- if(!nulls[i])
- bind_len_or_null_[param][i] = SQL_NTS; // null terminated
- }
- }
- bind_parameter(param, values, elements, data_type, param_type, parameter_size);
- }
- template<>
- bool statement::statement_impl::equals(const date& lhs, const date& rhs)
- {
- return lhs.year == rhs.year
- && lhs.month == rhs.month
- && lhs.day == rhs.day;
- }
- template<>
- bool statement::statement_impl::equals(const timestamp& lhs, const timestamp& rhs)
- {
- return lhs.year == rhs.year
- && lhs.month == rhs.month
- && lhs.day == rhs.day
- && lhs.hour == rhs.hour
- && lhs.min == rhs.min
- && lhs.sec == rhs.sec
- && lhs.fract == rhs.fract;
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Private Implementation: result
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- class result::result_impl
- {
- public:
- result_impl(const result_impl&) =delete;
- result_impl& operator=(const result_impl&) =delete;
- result_impl(statement stmt, long rowset_size)
- : stmt_(stmt)
- , rowset_size_(rowset_size)
- , row_count_(0)
- , bound_columns_(0)
- , bound_columns_size_(0)
- , rowset_position_(0)
- , bound_columns_by_name_()
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLSetStmtAttr
- , rc
- , stmt_.native_statement_handle()
- , SQL_ATTR_ROW_ARRAY_SIZE
- , (SQLPOINTER)rowset_size_
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- NANODBC_CALL_RC(
- SQLSetStmtAttr
- , rc
- , stmt_.native_statement_handle()
- , SQL_ATTR_ROWS_FETCHED_PTR
- , &row_count_
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- auto_bind();
- }
- ~result_impl() noexcept
- {
- cleanup_bound_columns();
- }
- void* native_statement_handle() const
- {
- return stmt_.native_statement_handle();
- }
- long rowset_size() const
- {
- return rowset_size_;
- }
- long affected_rows() const
- {
- return stmt_.affected_rows();
- }
- long rows() const noexcept
- {
- return row_count_;
- }
- short columns() const
- {
- return stmt_.columns();
- }
- bool first()
- {
- rowset_position_ = 0;
- return fetch(0, SQL_FETCH_FIRST);
- }
- bool last()
- {
- rowset_position_ = 0;
- return fetch(0, SQL_FETCH_LAST);
- }
- bool next()
- {
- if(rows() && ++rowset_position_ < rowset_size_)
- return rowset_position_ < rows();
- rowset_position_ = 0;
- return fetch(0, SQL_FETCH_NEXT);
- }
- bool prior()
- {
- if(rows() && --rowset_position_ >= 0)
- return true;
- rowset_position_ = 0;
- return fetch(0, SQL_FETCH_PRIOR);
- }
- bool move(long row)
- {
- rowset_position_ = 0;
- return fetch(row, SQL_FETCH_ABSOLUTE);
- }
- bool skip(long rows)
- {
- rowset_position_ += rows;
- if(this->rows() && rowset_position_ < rowset_size_)
- return rowset_position_ < this->rows();
- rowset_position_ = 0;
- return fetch(rows, SQL_FETCH_RELATIVE);
- }
- unsigned long position() const
- {
- SQLULEN pos = 0; // necessary to initialize to 0
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLGetStmtAttr
- , rc
- , stmt_.native_statement_handle()
- , SQL_ATTR_ROW_NUMBER
- , &pos
- , SQL_IS_UINTEGER
- , 0);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- return pos - 1 + rowset_position_;
- }
- bool end() const noexcept
- {
- SQLULEN pos = 0; // necessary to initialize to 0
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLGetStmtAttr
- , rc
- , stmt_.native_statement_handle()
- , SQL_ATTR_ROW_NUMBER
- , &pos
- , SQL_IS_UINTEGER
- , 0);
- return (!success(rc) || rows() < 0 || pos - 1 > static_cast<unsigned long>(rows()));
- }
- bool is_null(short column) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- bound_column& col = bound_columns_[column];
- if(rowset_position_ >= rows())
- throw index_range_error();
- return col.cbdata_[rowset_position_] == SQL_NULL_DATA;
- }
- bool is_null(const string_type& column_name) const
- {
- const short column = this->column(column_name);
- return is_null(column);
- }
- string_type column_name(short column) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- return bound_columns_[column].name_;
- }
- long column_size(short column) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- bound_column& col = bound_columns_[column];
- return col.sqlsize_;
- }
- short column(const string_type& column_name) const
- {
- typedef std::map<string_type, bound_column*>::const_iterator iter;
- iter i = bound_columns_by_name_.find(column_name);
- if(i == bound_columns_by_name_.end())
- throw index_range_error();
- return i->second->column_;
- }
- int column_datatype(short column) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- bound_column& col = bound_columns_[column];
- return col.sqltype_;
- }
- int column_datatype(const string_type& column_name) const
- {
- const short column = this->column(column_name);
- bound_column& col = bound_columns_[column];
- return col.sqltype_;
- }
- int column_c_datatype(short column) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- bound_column& col = bound_columns_[column];
- return col.ctype_;
- }
- int column_c_datatype(const string_type& column_name) const
- {
- const short column = this->column(column_name);
- bound_column& col = bound_columns_[column];
- return col.ctype_;
- }
- bool next_result()
- {
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLMoreResults
- , rc
- , stmt_.native_statement_handle());
- if(rc == SQL_NO_DATA)
- return false;
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- auto_bind();
- return true;
- }
- template<class T>
- void get_ref(short column, T& result) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- if(is_null(column))
- throw null_access_error();
- get_ref_impl<T>(column, result);
- }
- template<class T>
- void get_ref(short column, const T& fallback, T& result) const
- {
- if(column >= bound_columns_size_)
- throw index_range_error();
- if(is_null(column))
- {
- result = fallback;
- return;
- }
- get_ref_impl<T>(column, result);
- }
- template<class T>
- void get_ref(const string_type& column_name, T& result) const
- {
- const short column = this->column(column_name);
- if(is_null(column))
- throw null_access_error();
- get_ref_impl<T>(column, result);
- }
- template<class T>
- void get_ref(const string_type& column_name, const T& fallback, T& result) const
- {
- const short column = this->column(column_name);
- if(is_null(column))
- {
- result = fallback;
- return;
- }
- get_ref_impl<T>(column, result);
- }
- template<class T>
- T get(short column) const
- {
- T result;
- get_ref(column, result);
- return result;
- }
- template<class T>
- T get(short column, const T& fallback) const
- {
- T result;
- get_ref(column, fallback, result);
- return result;
- }
- template<class T>
- T get(const string_type& column_name) const
- {
- T result;
- get_ref(column_name, result);
- return result;
- }
- template<class T>
- T get(const string_type& column_name, const T& fallback) const
- {
- T result;
- get_ref(column_name, fallback, result);
- return result;
- }
- private:
- template<class T>
- void get_ref_impl(short column, T& result) const;
- void before_move() noexcept
- {
- for(short i = 0; i < bound_columns_size_; ++i)
- {
- bound_column& col = bound_columns_[i];
- for(long j = 0; j < col.rowset_size_; ++j)
- col.cbdata_[j] = 0;
- if(col.blob_ && col.pdata_)
- release_bound_resources(i);
- }
- }
- void release_bound_resources(short column) noexcept
- {
- assert(column < bound_columns_size_);
- bound_column& col = bound_columns_[column];
- delete[] col.pdata_;
- col.pdata_ = 0;
- col.clen_ = 0;
- }
- void cleanup_bound_columns() noexcept
- {
- before_move();
- delete[] bound_columns_;
- bound_columns_ = NULL;
- bound_columns_size_ = 0;
- bound_columns_by_name_.clear();
- }
- bool fetch(long rows, SQLUSMALLINT orientation)
- {
- before_move();
- RETCODE rc;
- NANODBC_CALL_RC(
- SQLFetchScroll
- , rc
- , stmt_.native_statement_handle()
- , orientation
- , rows);
- if(rc == SQL_NO_DATA)
- return false;
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- return true;
- }
- void auto_bind()
- {
- cleanup_bound_columns();
- const short n_columns = columns();
- if(n_columns < 1)
- return;
- assert(!bound_columns_);
- assert(!bound_columns_size_);
- bound_columns_ = new bound_column[n_columns];
- bound_columns_size_ = n_columns;
- RETCODE rc;
- NANODBC_SQLCHAR column_name[1024];
- SQLSMALLINT sqltype, scale, nullable, len;
- SQLULEN sqlsize;
- for(SQLSMALLINT i = 0; i < n_columns; ++i)
- {
- NANODBC_CALL_RC(
- NANODBC_UNICODE(SQLDescribeCol)
- , rc
- , stmt_.native_statement_handle()
- , i + 1
- , (NANODBC_SQLCHAR*)column_name
- , sizeof(column_name)/sizeof(NANODBC_SQLCHAR)
- , &len
- , &sqltype
- , &sqlsize
- , &scale
- , &nullable);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- // Adjust the sqlsize parameter in case of "unlimited" data (varchar(max), nvarchar(max)).
- bool is_blob = false;
- if (sqlsize == 0)
- {
- switch (sqltype)
- {
- case SQL_VARCHAR:
- case SQL_WVARCHAR:
- {
- //// Divide in half, due to sqlsize being 32-bit in Win32 (and 64-bit in x64)
- //sqlsize = std::numeric_limits<int32_t>::max() / 2 - 1;
- is_blob = true;
- }
- }
- }
- bound_column& col = bound_columns_[i];
- col.name_ = reinterpret_cast<string_type::value_type*>(column_name);
- col.column_ = i;
- col.sqltype_ = sqltype;
- col.sqlsize_ = sqlsize;
- col.scale_ = scale;
- bound_columns_by_name_[col.name_] = &col;
- using namespace std; // if int64_t is in std namespace (in c++11)
- switch(col.sqltype_)
- {
- case SQL_BIT:
- case SQL_TINYINT:
- case SQL_SMALLINT:
- case SQL_INTEGER:
- col.ctype_ = SQL_C_LONG;
- col.clen_ = sizeof(int32_t);
- break;
- case SQL_BIGINT:
- col.ctype_ = SQL_C_SBIGINT;
- col.clen_ = sizeof(int64_t);
- break;
- case SQL_DOUBLE:
- case SQL_FLOAT:
- case SQL_DECIMAL:
- case SQL_REAL:
- case SQL_NUMERIC:
- col.ctype_ = SQL_C_DOUBLE;
- col.clen_ = sizeof(double);
- break;
- case SQL_DATE:
- case SQL_TYPE_DATE:
- col.ctype_ = SQL_C_DATE;
- col.clen_ = sizeof(date);
- break;
- case SQL_TIMESTAMP:
- case SQL_TYPE_TIMESTAMP:
- col.ctype_ = SQL_C_TIMESTAMP;
- col.clen_ = sizeof(timestamp);
- break;
- case SQL_CHAR:
- case SQL_VARCHAR:
- col.ctype_ = SQL_C_CHAR;
- col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLCHAR);
- if (is_blob)
- {
- col.clen_ = 0;
- col.blob_ = true;
- }
- break;
- case SQL_WCHAR:
- case SQL_WVARCHAR:
- col.ctype_ = SQL_C_WCHAR;
- col.clen_ = (col.sqlsize_ + 1) * sizeof(SQLWCHAR);
- if (is_blob)
- {
- col.clen_ = 0;
- col.blob_ = true;
- }
- break;
- case SQL_LONGVARCHAR:
- col.ctype_ = SQL_C_CHAR;
- col.blob_ = true;
- col.clen_ = 0;
- break;
- case SQL_BINARY:
- case SQL_VARBINARY:
- col.ctype_ = SQL_C_BINARY;
- col.clen_ = col.sqlsize_ + sizeof(NANODBC_SQLCHAR);
- break;
- case SQL_LONGVARBINARY:
- col.ctype_ = SQL_C_BINARY;
- col.blob_ = true;
- col.clen_ = 0;
- break;
- default:
- col.ctype_ = sql_ctype<string_type>::value;
- col.clen_ = 128;
- break;
- }
- }
- for(SQLSMALLINT i = 0; i < n_columns; ++i)
- {
- bound_column& col = bound_columns_[i];
- col.cbdata_ = new null_type[rowset_size_];
- if(col.blob_)
- {
- NANODBC_CALL_RC(
- SQLBindCol
- , rc
- , stmt_.native_statement_handle()
- , i + 1
- , col.ctype_
- , 0
- , 0
- , col.cbdata_);
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- }
- else
- {
- col.rowset_size_ = rowset_size_;
- col.pdata_ = new char[rowset_size_ * col.clen_];
- NANODBC_CALL_RC(
- SQLBindCol
- , rc
- , stmt_.native_statement_handle()
- , i + 1 // ColumnNumber
- , col.ctype_ // TargetType
- , col.pdata_ // TargetValuePtr
- , col.clen_ // BufferLength
- , col.cbdata_); // StrLen_or_Ind
- if(!success(rc))
- NANODBC_THROW_DATABASE_ERROR(stmt_.native_statement_handle(), SQL_HANDLE_STMT);
- }
- }
- }
- private:
- statement stmt_;
- const long rowset_size_;
- SQLULEN row_count_;
- bound_column* bound_columns_;
- short bound_columns_size_;
- long rowset_position_;
- std::map<string_type, bound_column*> bound_columns_by_name_;
- };
- template<>
- inline void result::result_impl::get_ref_impl<date>(short column, date& result) const
- {
- bound_column& col = bound_columns_[column];
- switch(col.ctype_)
- {
- case SQL_C_DATE:
- result = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
- return;
- case SQL_C_TIMESTAMP:
- {
- timestamp stamp = *( (timestamp*)( col.pdata_ + rowset_position_ * col.clen_ ) );
- date d = { stamp.year, stamp.month, stamp.day };
- result = d;
- return;
- }
- }
- throw type_incompatible_error();
- }
- template<>
- inline void result::result_impl::get_ref_impl<timestamp>(short column, timestamp& result) const
- {
- bound_column& col = bound_columns_[column];
- switch(col.ctype_)
- {
- case SQL_C_DATE:
- {
- date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
- timestamp stamp = { d.year, d.month, d.day, 0, 0, 0, 0 };
- result = stamp;
- return;
- }
- case SQL_C_TIMESTAMP:
- result = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
- return;
- }
- throw type_incompatible_error();
- }
- template<>
- inline void result::result_impl::get_ref_impl<string_type>(short column, string_type& result) const
- {
- const bound_column& col = bound_columns_[column];
- const SQLULEN column_size = col.sqlsize_;
- switch(col.ctype_)
- {
- case SQL_C_CHAR:
- {
- if(col.blob_)
- {
- // Input is always std::string, while output may be std::string or std::wstring
- std::stringstream ss;
- char buff[1024] = {0};
- std::size_t buff_size = sizeof(buff);
- SQLLEN ValueLenOrInd;
- SQLRETURN rc;
- void* handle = native_statement_handle();
- do
- {
- NANODBC_CALL_RC(
- SQLGetData
- , rc
- , handle // StatementHandle
- , column + 1 // Col_or_Param_Num
- , SQL_C_CHAR // TargetType
- , buff // TargetValuePtr
- , buff_size // BufferLength
- , &ValueLenOrInd); // StrLen_or_IndPtr
- if (ValueLenOrInd > 0)
- ss << buff;
- } while(rc > 0);
- convert(ss.str(), result);
- }
- else
- {
- const char* s = col.pdata_ + rowset_position_ * col.clen_;
- const std::string::size_type str_size = std::strlen(s);
- result.assign(s, s + str_size);
- }
- return;
- }
- case SQL_C_WCHAR:
- {
- if (col.blob_)
- {
- // Input is always std::wstring, output might be std::string or std::wstring.
- // Use a string builder to build the output string.
- std::wstringstream ss;
- wchar_t buffer[512] = {0};
- std::size_t buffer_size = sizeof(buffer);
- SQLLEN ValueLenOrInd;
- SQLRETURN rc;
- void* handle = native_statement_handle();
- do
- {
- NANODBC_CALL_RC(
- SQLGetData
- , rc
- , handle // StatementHandle
- , column + 1 // Col_or_Param_Num
- , SQL_C_WCHAR // TargetType
- , buffer // TargetValuePtr
- , buffer_size // BufferLength
- , &ValueLenOrInd); // StrLen_or_IndPtr
- if (ValueLenOrInd > 0)
- ss << buffer;
- } while (rc > 0);
- convert(ss.str(), result);
- }
- else
- {
- // Type is unicode in the database, convert if necessary
- const SQLWCHAR* s = reinterpret_cast<SQLWCHAR*>(col.pdata_ + rowset_position_ * col.clen_);
- const string_type::size_type str_size = *col.cbdata_ / sizeof(SQLWCHAR);
- std::wstring temp(s, s + str_size);
- convert(temp, result);
- }
- return;
- }
- case SQL_C_GUID:
- case SQL_C_BINARY:
- {
- if(col.blob_)
- throw std::runtime_error("blob not implemented yet");
- const char* s = col.pdata_ + rowset_position_ * col.clen_;
- result.assign(s, s + column_size);
- return;
- }
- case SQL_C_LONG:
- {
- result.resize(column_size);
- if(NANODBC_SNPRINTF(
- const_cast<string_type::value_type*>(result.data())
- , column_size
- , NANODBC_TEXT("%d")
- , *(int32_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
- throw type_incompatible_error();
- result.resize(NANODBC_STRLEN(result.c_str()));
- return;
- }
- case SQL_C_SBIGINT:
- {
- using namespace std; // in case intmax_t is in namespace std
- result.resize(column_size);
- if(NANODBC_SNPRINTF(
- const_cast<string_type::value_type*>(result.data())
- , column_size
- , NANODBC_TEXT("%jd")
- , (intmax_t) *(int64_t*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
- throw type_incompatible_error();
- result.resize(NANODBC_STRLEN(result.c_str()));
- return;
- }
- case SQL_C_FLOAT:
- {
- result.resize(column_size);
- if(NANODBC_SNPRINTF(
- const_cast<string_type::value_type*>(result.data())
- , column_size
- , NANODBC_TEXT("%f")
- , *(float*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
- throw type_incompatible_error();
- result.resize(NANODBC_STRLEN(result.c_str()));
- return;
- }
- case SQL_C_DOUBLE:
- {
- result.resize(column_size);
- if(NANODBC_SNPRINTF(
- const_cast<string_type::value_type*>(result.data())
- , column_size
- , NANODBC_TEXT("%lf")
- , *(double*)(col.pdata_ + rowset_position_ * col.clen_)) == -1)
- throw type_incompatible_error();
- result.resize(NANODBC_STRLEN(result.c_str()));
- return;
- }
- case SQL_C_DATE:
- {
- date d = *((date*)(col.pdata_ + rowset_position_ * col.clen_));
- std::tm st = { 0 };
- st.tm_year = d.year - 1900;
- st.tm_mon = d.month - 1;
- st.tm_mday = d.day;
- char* old_lc_time = std::setlocale(LC_TIME, NULL);
- std::setlocale(LC_TIME, "");
- string_type::value_type date_str[512];
- NANODBC_STRFTIME(
- date_str
- , sizeof(date_str) / sizeof(string_type::value_type)
- , NANODBC_TEXT("%Y-%m-%d")
- , &st);
- std::setlocale(LC_TIME, old_lc_time);
- result.assign(date_str);
- return;
- }
- case SQL_C_TIMESTAMP:
- {
- timestamp stamp = *((timestamp*)(col.pdata_ + rowset_position_ * col.clen_));
- std::tm st = { 0 };
- st.tm_year = stamp.year - 1900;
- st.tm_mon = stamp.month - 1;
- st.tm_mday = stamp.day;
- st.tm_hour = stamp.hour;
- st.tm_min = stamp.min;
- st.tm_sec = stamp.sec;
- char* old_lc_time = std::setlocale(LC_TIME, NULL);
- std::setlocale(LC_TIME, "");
- string_type::value_type date_str[512];
- NANODBC_STRFTIME(
- date_str
- , sizeof(date_str) / sizeof(string_type::value_type)
- , NANODBC_TEXT("%Y-%m-%d %H:%M:%S %z")
- , &st);
- std::setlocale(LC_TIME, old_lc_time);
- result.assign(date_str);
- return;
- }
- }
- throw type_incompatible_error();
- }
- template<class T>
- void result::result_impl::get_ref_impl(short column, T& result) const
- {
- bound_column& col = bound_columns_[column];
- using namespace std; // if int64_t is in std namespace (in c++11)
- const char* s = col.pdata_ + rowset_position_ * col.clen_;
- switch(col.ctype_)
- {
- case SQL_C_CHAR: result = (T)*(char*)(s); return;
- case SQL_C_SSHORT: result = (T)*(short*)(s); return;
- case SQL_C_USHORT: result = (T)*(unsigned short*)(s); return;
- case SQL_C_LONG: result = (T)*(int32_t*)(s); return;
- case SQL_C_SLONG: result = (T)*(int32_t*)(s); return;
- case SQL_C_ULONG: result = (T)*(uint32_t*)(s); return;
- case SQL_C_FLOAT: result = (T)*(float*)(s); return;
- case SQL_C_DOUBLE: result = (T)*(double*)(s); return;
- case SQL_C_SBIGINT: result = (T)*(int64_t*)(s); return;
- case SQL_C_UBIGINT: result = (T)*(uint64_t*)(s); return;
- }
- throw type_incompatible_error();
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Free Functions
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- result execute(connection& conn, const string_type& query, long batch_operations, long timeout)
- {
- class statement statement;
- return statement.execute_direct(conn, query, batch_operations, timeout);
- }
- void just_execute(connection& conn, const string_type& query, long batch_operations, long timeout) {
- class statement statement;
- statement.just_execute_direct(conn, query, batch_operations, timeout);
- }
- result execute(statement& stmt, long batch_operations)
- {
- return stmt.execute(batch_operations);
- }
- void just_execute(statement& stmt, long batch_operations)
- {
- return stmt.just_execute(batch_operations);
- }
- result transact(statement& stmt, long batch_operations)
- {
- class transaction transaction(stmt.connection());
- result rvalue = stmt.execute(batch_operations);
- transaction.commit();
- return rvalue;
- }
- void just_transact(statement& stmt, long batch_operations)
- {
- class transaction transaction(stmt.connection());
- stmt.just_execute(batch_operations);
- transaction.commit();
- }
- void prepare(statement& stmt, const string_type& query, long timeout)
- {
- stmt.prepare(stmt.connection(), query, timeout);
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Pimpl Forwards: connection
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- connection::connection()
- : impl_(new connection_impl())
- {
- }
- connection::connection(const connection& rhs)
- : impl_(rhs.impl_)
- {
- }
- connection& connection::operator=(connection rhs)
- {
- swap(rhs);
- return *this;
- }
- void connection::swap(connection& rhs) noexcept
- {
- using std::swap;
- swap(impl_, rhs.impl_);
- }
- connection::connection(
- const string_type& dsn
- , const string_type& user
- , const string_type& pass
- , long timeout)
- : impl_(new connection_impl(dsn, user, pass, timeout))
- {
- }
- connection::connection(const string_type& connection_string, long timeout)
- : impl_(new connection_impl(connection_string, timeout))
- {
- }
- connection::~connection() noexcept
- {
- }
- void connection::connect(
- const string_type& dsn
- , const string_type& user
- , const string_type& pass
- , long timeout)
- {
- impl_->connect(dsn, user, pass, timeout);
- }
- void connection::connect(const string_type& connection_string, long timeout)
- {
- impl_->connect(connection_string, timeout);
- }
- bool connection::connected() const
- {
- return impl_->connected();
- }
- void connection::disconnect()
- {
- impl_->disconnect();
- }
- std::size_t connection::transactions() const
- {
- return impl_->transactions();
- }
- void* connection::native_dbc_handle() const
- {
- return impl_->native_dbc_handle();
- }
- void* connection::native_env_handle() const
- {
- return impl_->native_env_handle();
- }
- string_type connection::driver_name() const
- {
- return impl_->driver_name();
- }
- std::size_t connection::ref_transaction()
- {
- return impl_->ref_transaction();
- }
- std::size_t connection::unref_transaction()
- {
- return impl_->unref_transaction();
- }
- bool connection::rollback() const
- {
- return impl_->rollback();
- }
- void connection::rollback(bool onoff)
- {
- impl_->rollback(onoff);
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Pimpl Forwards: transaction
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- transaction::transaction(const class connection& conn)
- : impl_(new transaction_impl(conn))
- {
- }
- transaction::transaction(const transaction& rhs)
- : impl_(rhs.impl_)
- {
- }
- transaction& transaction::operator=(transaction rhs)
- {
- swap(rhs);
- return *this;
- }
- void transaction::swap(transaction& rhs) noexcept
- {
- using std::swap;
- swap(impl_, rhs.impl_);
- }
- transaction::~transaction() noexcept
- {
- }
- void transaction::commit()
- {
- impl_->commit();
- }
- void transaction::rollback() noexcept
- {
- impl_->rollback();
- }
- class connection& transaction::connection()
- {
- return impl_->connection();
- }
- const class connection& transaction::connection() const
- {
- return impl_->connection();
- }
- transaction::operator class connection&()
- {
- return impl_->connection();
- }
- transaction::operator const class connection&() const
- {
- return impl_->connection();
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Pimpl Forwards: statement
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- statement::statement()
- : impl_(new statement_impl())
- {
- }
- statement::statement(class connection& conn)
- : impl_(new statement_impl(conn))
- {
- }
- statement::statement(class connection& conn, const string_type& query, long timeout)
- : impl_(new statement_impl(conn, query, timeout))
- {
- }
- statement::statement(const statement& rhs)
- : impl_(rhs.impl_)
- {
- }
- statement& statement::operator=(statement rhs)
- {
- swap(rhs);
- return *this;
- }
- void statement::swap(statement& rhs) noexcept
- {
- using std::swap;
- swap(impl_, rhs.impl_);
- }
- statement::~statement() noexcept
- {
- }
- void statement::open(class connection& conn)
- {
- impl_->open(conn);
- }
- bool statement::open() const
- {
- return impl_->open();
- }
- bool statement::connected() const
- {
- return impl_->connected();
- }
- const class connection& statement::connection() const
- {
- return impl_->connection();
- }
- class connection& statement::connection()
- {
- return impl_->connection();
- }
- void* statement::native_statement_handle() const
- {
- return impl_->native_statement_handle();
- }
- void statement::close()
- {
- impl_->close();
- }
- void statement::cancel()
- {
- impl_->cancel();
- }
- void statement::prepare(class connection& conn, const string_type& query, long timeout)
- {
- impl_->prepare(conn, query, timeout);
- }
- void statement::prepare(const string_type& query, long timeout)
- {
- impl_->prepare(query, timeout);
- }
- void statement::timeout(long timeout)
- {
- impl_->timeout(timeout);
- }
- result statement::execute_direct(
- class connection& conn
- , const string_type& query
- , long batch_operations
- , long timeout)
- {
- return impl_->execute_direct(conn, query, batch_operations, timeout, *this);
- }
- void statement::just_execute_direct(
- class connection& conn
- , const string_type& query
- , long batch_operations
- , long timeout)
- {
- impl_->just_execute_direct(conn, query, batch_operations, timeout, *this);
- }
- result statement::execute(long batch_operations, long timeout)
- {
- return impl_->execute(batch_operations, timeout, *this);
- }
- void statement::just_execute(long batch_operations, long timeout)
- {
- impl_->just_execute(batch_operations, timeout, *this);
- }
- result statement::procedure_columns(
- const string_type& catalog
- , const string_type& schema
- , const string_type& procedure
- , const string_type& column)
- {
- return impl_->procedure_columns(catalog, schema, procedure, column, *this);
- }
- long statement::affected_rows() const
- {
- return impl_->affected_rows();
- }
- short statement::columns() const
- {
- return impl_->columns();
- }
- void statement::reset_parameters() noexcept
- {
- impl_->reset_parameters();
- }
- unsigned long statement::parameter_size(short param) const
- {
- return impl_->parameter_size(param);
- }
- // We need to instantiate each form of bind() for each of our supported data types.
- #define NANODBC_INSTANTIATE_BINDS(type) \
- template void statement::bind(short, const type*, param_direction); /* 1-ary */ \
- template void statement::bind(short, const type*, std::size_t, param_direction); /* n-ary */ \
- template void statement::bind(short, const type*, std::size_t, const type*, param_direction); /* n-ary, sentry */ \
- template void statement::bind(short, const type*, std::size_t, const bool*, param_direction) /* n-ary, flags */ \
- /**/
- // The following are the only supported instantiations of statement::bind().
- NANODBC_INSTANTIATE_BINDS(string_type::value_type);
- NANODBC_INSTANTIATE_BINDS(short);
- NANODBC_INSTANTIATE_BINDS(unsigned short);
- NANODBC_INSTANTIATE_BINDS(int32_t);
- NANODBC_INSTANTIATE_BINDS(uint32_t);
- NANODBC_INSTANTIATE_BINDS(int64_t);
- NANODBC_INSTANTIATE_BINDS(uint64_t);
- NANODBC_INSTANTIATE_BINDS(float);
- NANODBC_INSTANTIATE_BINDS(double);
- NANODBC_INSTANTIATE_BINDS(date);
- NANODBC_INSTANTIATE_BINDS(timestamp);
- #undef NANODBC_INSTANTIATE_BINDS
- template<class T>
- void statement::bind(short param, const T* value, param_direction direction)
- {
- impl_->bind(param, value, 1, direction);
- }
- template<class T>
- void statement::bind(short param, const T* values, std::size_t elements, param_direction direction)
- {
- impl_->bind(param, values, elements, direction);
- }
- template<class T>
- void statement::bind(
- short param
- , const T* values
- , std::size_t elements
- , const T* null_sentry
- , param_direction direction)
- {
- impl_->bind(param, values, elements, 0, null_sentry, direction);
- }
- template<class T>
- void statement::bind(
- short param
- , const T* values
- , std::size_t elements
- , const bool* nulls
- , param_direction direction)
- {
- impl_->bind(param, values, elements, nulls, (T*)0, direction);
- }
- void statement::bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t length
- , std::size_t elements
- , param_direction direction)
- {
- impl_->bind_strings(param, values, length, elements, direction);
- }
- void statement::bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t length
- , std::size_t elements
- , const string_type::value_type* null_sentry
- , param_direction direction)
- {
- impl_->bind_strings(param, values, length, elements, (bool*)0, null_sentry, direction);
- }
- void statement::bind_strings(
- short param
- , const string_type::value_type* values
- , std::size_t length
- , std::size_t elements
- , const bool* nulls
- , param_direction direction)
- {
- impl_->bind_strings(
- param
- , values
- , length
- , elements
- , nulls
- , (string_type::value_type*)0
- , direction);
- }
- void statement::bind_null(short param, std::size_t elements)
- {
- impl_->bind_null(param, elements);
- }
- } // namespace nanodbc
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- // Pimpl Forwards: result
- ///////////////////////////////////////////////////////////////////////////////////////////////////
- namespace nanodbc
- {
- result::result()
- : impl_()
- {
- }
- result::~result() noexcept
- {
- }
- result::result(statement stmt, long rowset_size)
- : impl_(new result_impl(stmt, rowset_size))
- {
- }
- result::result(const result& rhs)
- : impl_(rhs.impl_)
- {
- }
- result& result::operator=(result rhs)
- {
- swap(rhs);
- return *this;
- }
- void result::swap(result& rhs) noexcept
- {
- using std::swap;
- swap(impl_, rhs.impl_);
- }
- void* result::native_statement_handle() const
- {
- return impl_->native_statement_handle();
- }
- long result::rowset_size() const noexcept
- {
- return impl_->rowset_size();
- }
- long result::affected_rows() const
- {
- return impl_->affected_rows();
- }
- long result::rows() const noexcept
- {
- return impl_->rows();
- }
- short result::columns() const
- {
- return impl_->columns();
- }
- bool result::first()
- {
- return impl_->first();
- }
- bool result::last()
- {
- return impl_->last();
- }
- bool result::next()
- {
- return impl_->next();
- }
- bool result::prior()
- {
- return impl_->prior();
- }
- bool result::move(long row)
- {
- return impl_->move(row);
- }
- bool result::skip(long rows)
- {
- return impl_->skip(rows);
- }
- unsigned long result::position() const
- {
- return impl_->position();
- }
- bool result::end() const noexcept
- {
- return impl_->end();
- }
- bool result::is_null(short column) const
- {
- return impl_->is_null(column);
- }
- bool result::is_null(const string_type& column_name) const
- {
- return impl_->is_null(column_name);
- }
- string_type result::column_name(short column) const
- {
- return impl_->column_name(column);
- }
- long result::column_size(short column) const
- {
- return impl_->column_size(column);
- }
- short result::column(const string_type& column_name) const
- {
- return impl_->column(column_name);
- }
- int result::column_datatype(short column) const
- {
- return impl_->column_datatype(column);
- }
- int result::column_datatype(const string_type& column_name) const
- {
- return impl_->column_datatype(column_name);
- }
- int result::column_c_datatype(short column) const
- {
- return impl_->column_c_datatype(column);
- }
- int result::column_c_datatype(const string_type& column_name) const
- {
- return impl_->column_c_datatype(column_name);
- }
- bool result::next_result()
- {
- return impl_->next_result();
- }
- template<class T>
- void result::get_ref(short column, T& result) const
- {
- return impl_->get_ref<T>(column, result);
- }
- template<class T>
- void result::get_ref(short column, const T& fallback, T& result) const
- {
- return impl_->get_ref<T>(column, fallback, result);
- }
- template<class T>
- void result::get_ref(const string_type& column_name, T& result) const
- {
- return impl_->get_ref<T>(column_name, result);
- }
- template<class T>
- void result::get_ref(const string_type& column_name, const T& fallback, T& result) const
- {
- return impl_->get_ref<T>(column_name, fallback, result);
- }
- template<class T>
- T result::get(short column) const
- {
- return impl_->get<T>(column);
- }
- template<class T>
- T result::get(short column, const T& fallback) const
- {
- return impl_->get<T>(column, fallback);
- }
- template<class T>
- T result::get(const string_type& column_name) const
- {
- return impl_->get<T>(column_name);
- }
- template<class T>
- T result::get(const string_type& column_name, const T& fallback) const
- {
- return impl_->get<T>(column_name, fallback);
- }
- result::operator bool() const
- {
- return static_cast<bool>(impl_);
- }
- // The following are the only supported instantiations of result::get_ref().
- template void result::get_ref(short, string_type::value_type&) const;
- template void result::get_ref(short, short&) const;
- template void result::get_ref(short, unsigned short&) const;
- template void result::get_ref(short, int32_t&) const;
- template void result::get_ref(short, uint32_t&) const;
- template void result::get_ref(short, int64_t&) const;
- template void result::get_ref(short, uint64_t&) const;
- template void result::get_ref(short, float&) const;
- template void result::get_ref(short, double&) const;
- template void result::get_ref(short, string_type&) const;
- template void result::get_ref(short, date&) const;
- template void result::get_ref(short, timestamp&) const;
- template void result::get_ref(const string_type&, string_type::value_type&) const;
- template void result::get_ref(const string_type&, short&) const;
- template void result::get_ref(const string_type&, unsigned short&) const;
- template void result::get_ref(const string_type&, int32_t&) const;
- template void result::get_ref(const string_type&, uint32_t&) const;
- template void result::get_ref(const string_type&, int64_t&) const;
- template void result::get_ref(const string_type&, uint64_t&) const;
- template void result::get_ref(const string_type&, float&) const;
- template void result::get_ref(const string_type&, double&) const;
- template void result::get_ref(const string_type&, string_type&) const;
- template void result::get_ref(const string_type&, date&) const;
- template void result::get_ref(const string_type&, timestamp&) const;
- // The following are the only supported instantiations of result::get_ref() with fallback.
- template void result::get_ref(short, const string_type::value_type&, string_type::value_type&) const;
- template void result::get_ref(short, const short&, short&) const;
- template void result::get_ref(short, const unsigned short&, unsigned short&) const;
- template void result::get_ref(short, const int32_t&, int32_t&) const;
- template void result::get_ref(short, const uint32_t&, uint32_t&) const;
- template void result::get_ref(short, const int64_t&, int64_t&) const;
- template void result::get_ref(short, const uint64_t&, uint64_t&) const;
- template void result::get_ref(short, const float&, float&) const;
- template void result::get_ref(short, const double&, double&) const;
- template void result::get_ref(short, const string_type&, string_type&) const;
- template void result::get_ref(short, const date&, date&) const;
- template void result::get_ref(short, const timestamp&, timestamp&) const;
- template void result::get_ref(const string_type&, const string_type::value_type&, string_type::value_type&) const;
- template void result::get_ref(const string_type&, const short&, short&) const;
- template void result::get_ref(const string_type&, const unsigned short&, unsigned short&) const;
- template void result::get_ref(const string_type&, const int32_t&, int32_t&) const;
- template void result::get_ref(const string_type&, const uint32_t&, uint32_t&) const;
- template void result::get_ref(const string_type&, const int64_t&, int64_t&) const;
- template void result::get_ref(const string_type&, const uint64_t&, uint64_t&) const;
- template void result::get_ref(const string_type&, const float&, float&) const;
- template void result::get_ref(const string_type&, const double&, double&) const;
- template void result::get_ref(const string_type&, const string_type&, string_type&) const;
- template void result::get_ref(const string_type&, const date&, date&) const;
- template void result::get_ref(const string_type&, const timestamp&, timestamp&) const;
- // The following are the only supported instantiations of result::get().
- template string_type::value_type result::get(short) const;
- template short result::get(short) const;
- template unsigned short result::get(short) const;
- template int32_t result::get(short) const;
- template uint32_t result::get(short) const;
- template int64_t result::get(short) const;
- template uint64_t result::get(short) const;
- template float result::get(short) const;
- template double result::get(short) const;
- template string_type result::get(short) const;
- template date result::get(short) const;
- template timestamp result::get(short) const;
- template string_type::value_type result::get(const string_type&) const;
- template short result::get(const string_type&) const;
- template unsigned short result::get(const string_type&) const;
- template int32_t result::get(const string_type&) const;
- template uint32_t result::get(const string_type&) const;
- template int64_t result::get(const string_type&) const;
- template uint64_t result::get(const string_type&) const;
- template float result::get(const string_type&) const;
- template double result::get(const string_type&) const;
- template string_type result::get(const string_type&) const;
- template date result::get(const string_type&) const;
- template timestamp result::get(const string_type&) const;
- // The following are the only supported instantiations of result::get() with fallback.
- template string_type::value_type result::get(short, const string_type::value_type&) const;
- template short result::get(short, const short&) const;
- template unsigned short result::get(short, const unsigned short&) const;
- template int32_t result::get(short, const int32_t&) const;
- template uint32_t result::get(short, const uint32_t&) const;
- template int64_t result::get(short, const int64_t&) const;
- template uint64_t result::get(short, const uint64_t&) const;
- template float result::get(short, const float&) const;
- template double result::get(short, const double&) const;
- template string_type result::get(short, const string_type&) const;
- template date result::get(short, const date&) const;
- template timestamp result::get(short, const timestamp&) const;
- template string_type::value_type result::get(const string_type&, const string_type::value_type&) const;
- template short result::get(const string_type&, const short&) const;
- template unsigned short result::get(const string_type&, const unsigned short&) const;
- template int32_t result::get(const string_type&, const int32_t&) const;
- template uint32_t result::get(const string_type&, const uint32_t&) const;
- template int64_t result::get(const string_type&, const int64_t&) const;
- template uint64_t result::get(const string_type&, const uint64_t&) const;
- template float result::get(const string_type&, const float&) const;
- template double result::get(const string_type&, const double&) const;
- template string_type result::get(const string_type&, const string_type&) const;
- template date result::get(const string_type&, const date&) const;
- template timestamp result::get(const string_type&, const timestamp&) const;
- } // namespace nanodbc
- #undef NANODBC_THROW_DATABASE_ERROR
- #undef NANODBC_STRINGIZE
- #undef NANODBC_STRINGIZE_I
- #undef NANODBC_CALL_RC
- #undef NANODBC_CALL
- #endif // DOXYGEN
|