Using Microsoft SQL Server from Linux Machines with ODBC & FreeTDS
(http://coolemerald.blogspot.com/2018/10/using-microsoft-sql-server-from-linux.html)
မှာ ဖော်ပြ ထားတဲ့ အတိုင်း FreeTDS နဲ့ ODBC ကို တပ်ဆင် ထားဖို့ လို ပါတယ်။
- ODBC Handles
- Data source အားဆက်သွယ်ခြင်း
- Connection Attributes
- အခြေခံ result-set generating function များ
- Result-set များကို ထုတ်ယူခြင်း
- Return Statuses
- ODBC C++ Class နှင့် wxWidgets
- အကိုးအကားများ
ODBC Handles
ODBC မှာ အဓိက handle အမျိုးအစား လေးမျိုး ရှိပါ တယ်။- SQLHENV:
- သူက environment handle ဖြစ်ပြီး၊ အရင် ဆုံး လိုအပ် တဲ့ handle ဖြစ်ပါတယ်။ ဒီ environment handle ရှိပြီ ဆိုမှ ODBC version သတ်မှတ် တာတွေ၊ တခြား connection handle တွေကို နေရာ ချထား တာတွေ လုပ်နိုင် မှာပါ။ သူ့ရဲ့ attribute တွေ သတ်မှတ် ဖို့ အတွက် SQLSetEnvAttr ဆိုတဲ့ function ကို သုံးနိုင်ပြီး၊ တခြား handle တွေကို နေရာ ချထား ဖို့အတွက် SQLAllocHandle ကို သုံးနိုင် ပါတယ်။
- SQLHDBC:
- ဒါက connection handle ဖြစ်ပြီး၊ Data source တစ်ခု စီကို ဆက်သွယ် ဖို့ အတွက် connection handle တစ်ခု စီ လိုပါတယ်။ သူ့ရဲ့ attribute တွေကို သတ်မှတ်ဖို့ နဲ့ ရယူဖို့ အတွက် SQLSetConnectAttr နဲ့ SQLGetConnectAttr တို့ကို သုံးနိုင် ပါတယ်။
- SQLHSTMT:
- ဒါက တော့ statement handle ဖြစ်ပါတယ်။ Data source တစ်ခု ကို connection handle တစ်ခု နဲ့ ဆက်သွယ် ပြီးတဲ့ အခါ၊ statement handle ကို နေရာ ချထား ပြီး၊ SQL လုပ်ဆောင်မှု တွေကို ပြုလုပ် နိုင် ပါတယ်။ သူ့ရဲ့ attribute တွေကို သတ်မှတ်ဖို့ နဲ့ ရယူဖို့ အတွက် တော့ SQLSetStmtAttr နဲ့ SQLGetStmtAttr တို့ကို သုံးနိုင် ပါတယ်။
- SQLHDESC:
- descriptor handle ပါ။ တော်တော် အသုံးဝင် ပြီး၊ ရှုပ်ထွေးတဲ့ လုပ်ဆောင် မှုတွေ အတွက် ပဲ တခါတလေ မှ သုံးကြ ပါတယ်။
Handle တွေကို allocate လုပ်ဖို့ အတွက် SQLAllocHandle ကို အောက်က အတိုင်း သုံးနိုင် ပါတယ်။
SQLRETURN SQLAllocHandle( SQLSMALLINT HandleType, SQLHANDLE InputHandle, SQLHANDLE *OutputHandlePtr)
Handle type က ခုနက ဖော်ပြ ခဲ့တဲ့
- SQLHENV
- SQLHDBC
- SQLHSTMT
- SQLHDESC
InputHandle ဆိုတဲ့ argument နေရာမှာ စစခြင်း environment handle အတွက် ဆိုရင် တော့ SQL_NULL_HANDLE ကို သုံးနိုင် ပါတယ်။ Connection handle ကို allocate လုပ်တဲ့ အခါ မှာတော့ သူ့ရဲ့ enclosing handle ဖြစ်တဲ့ environment handle ကို သုံးရမှာ ဖြစ်ပါတယ်။ နောက် statement handle နဲ့ descriptor handle တွေအတွက် InputHandle နေရာ မှာ သူတို့ရဲ့ connection handle ကို သတ်မှတ် ရမှာ ဖြစ်ပါတယ်။ OutputHandlePtr ကတော့ ရလာမယ့် returned handle ရဲ့ pointer ပါ။
Handle တွေနဲ့ သူ့တို့ ရဲ့ resources တွေကို free ပြန်လုပ်ဖို့ အတွက် တော့ SQLFreeHandle ဆိုတဲ့ API ကို အောက်က အတိုင်း သုံးနိုင် ပါတယ်။
SQLRETURN SQLFreeHandle( SQLSMALLINT HandleType, SQLHANDLE Handle)
Handle တွေကို ပြန် free လုပ်တဲ့ အခါမှာ သူတို့ ကို allocate လုပ်ခဲ့ တဲ့ အစီ အစဉ် နဲ့ ပြောင်းပြန် ပြန် free လုပ် ပါတယ်။ ODBC application တွေမှာ ပုံမှန် တွေ့ရ တတ် တဲ့ code တချို့ကို Listing 1 မှာ ပြထား ပါတယ်။
SQLHENV env; SQLHDBC dbc; SQLHSTMT stmt; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &env); SQLAllocHandle(SQL_HANDLE_DBC, env, &dbc); // code to connect to the data source SQLAllocHandle(SQL_HANDLE_STMT, dbc, &stmt); // do something with the statement handle SQLFreeHandle(SQL_HANDLE_STMT, stmt); // disconnect SQLFreeHandle(SQL_HANDLE_DBC, dbc); SQLFreeHandle(SQL_HANDLE_ENV, env);Listing 1. ODBC applications များရှိ ပုံမှန် တွေ့ရ တတ်သည့် code များ။
Application အသစ် တွေမှာ တော့ ODBC version 3 ကိုပဲ သုံး သင့်ပြီး၊ အဲဒီ အတွက် environment attribute ကို အောက်ပါ အတိုင်း သတ်မှတ် နိုင် ပါတယ်။
SQLSetEnvAttr(env, SQL_ATTR_ODBC_VERSION, (void *) SQL_OV_ODBC3, 0);
Data source အားဆက်သွယ်ခြင်း
Connection handle ကို သုံးပြီး data source တစ်ခု ကို ဆက်သွယ် ဖို့ အတွက် SQLDriverConnect ကို သုံးနိုင် ပါတယ်။ SQLDriverConnect ရဲ့ API ကို အောက်က စာရင်း မှာ ဖော်ပြ ထား ပါတယ်။
SQLRETURN SQLDriverConnect( SQLHDBC ConnectionHandle, SQLHWND WindowHandle, SQLCHAR *InConnectionString, SQLSMALLINT StringLength1, SQLCHAR *OutConnectionString, SQLSMALLINT BufferLength, SQLSMALLINT *StringLength2Ptr, SQLUSMALLINT DriverCompletion)
သူရဲ့ argument တွေက အောက်ပါ အတိုင်း ဖြစ်ပါတယ်။
- ConnectionHandle က connection လုပ်ဖို့ အတွက် allocated လုပ်ထားတဲ့ handle ဖြစ် ပါတယ်။
- WindowHandle က driver ကို dialog တစ်ခု လိုအပ်ရင် ဖော်ပြ ခိုင်းဖို့ handle ဖြစ် ပါတယ်။ Driver ကို dialog မဖော်ပြ စေချင် ရင်၊ အဲဒီ နေရာ မှာ NULL ကို ထည့်နိုင် ပါတယ်။
- InConnectionString နဲ့ StringLength1 က input connection string ပါ။ အဲဒီ မှာ connection အတွက် attribute နဲ့ value အတွဲ တွေကို "DSN = datasource; UID = username; PWD = password;" စသည်ဖြင့် semicolon တွေ ခြားပြီး သတ်မှတ် ဖော်ပြ နိုင် ပါတယ်။ StringLength1 က InConnectionString ရဲ့ length ဖြစ်ပြီး၊ null terminated string ဆိုရင် SQL_NTS ကို ထည့်ပေး နိုင်ပါတယ်။
- OutConnectionString, BufferLength နဲ့ StringLength2Ptr က full connection string ဖြစ်ပြီး၊ connection calls တွေ ပြန်လုပ် တဲ့ အခါ အသုံးပြု နိုင် ပါတယ်။ OutConnectionString က ရလာ တဲ့ output connection string သိမ်းဖို့ buffer ရဲ့ pointer ဖြစ်ပြီး၊ BufferLength က သူ့ရဲ့ သိမ်းနိုင်တဲ့ အရွယ် အစား ဖြစ် ပါတယ်။ StringLength2Ptr က point လုပ်တဲ့ integer မှာ ရလဒ် string ရဲ့ length ကို return ရလာ မှာ ဖြစ်ပါတယ်။ WindowHandle က NULL ဖြစ်ရင် တော့ output connection string က input connection string အတိုင်း ကော်ပီ ရလာ မှာ ဖြစ်ပါတယ်။ အဲလို မဟုတ်ပဲ dialog ကနေ နောက်ထပ် connection information တွေ ထပ်ရလာ ရင်၊ အချက် အလက် အပြည့် အစုံ ပါတဲ့ connection string ကို output connection string အနေနဲ့ ရလာ မှာ ဖြစ်ပါတယ်။
- DriverCompletion က input connection string မှာ အချက် အလက် အပြည့် အစုံ မပါ တဲ့ အခါ driver က ဘယ်လို ဆက်လုပ် ရမလဲ ဆိုတဲ့ ညွှန်ကြား ချက်ပါ။
- SQL_DRIVER_PROMPT က အချက် အလက် ပြည့်စုံ တဲ့ connection string တစ်ခု ရအောင် InConnectionString နဲ့ dialog တစ်ခု ဖော်ပြပြီး ထပ်တောင်း လို့ ရတဲ့ အချက် အလက် တွေ ပေါင်းပြီး တည်ဆောက် ပေးပါတယ်။ ရတာ တဲ့ ရလဒ် ကို OutConnectionString ရဲ့ buffer ထဲမှာ ထည့်ပေး ပါတယ်။
- SQL_DRIVER_COMPLETE က input connection string က ပြည့်စုံ မှန်ကန် နေရင် output connection string ဆီကို ကော်ပီ ကူးပေး ပြီး၊ data source ကို dialog မပြ ပဲနဲ့ ဆက်သွယ် ပေးပါတယ်။ အကယ်၍ input connection string က လိုနေ၊ မှားနေ ရင်တော့ dialog ဖော်ပြ ပြီး SQL_DRIVER_PROMPT အတိုင်း လုပ် ဆောင် ပါတယ်။
- SQL_DRIVER_COMPLETE_REQUIRED က SQL_DRIVER_COMPLETE အတိုင်း ဖြစ်ပြီး၊ နောက်ထပ် အလုပ် အနေ နဲ့ မလိုအပ် တဲ့ အချက်အလက် အပို တွေကို ပါ disable လုပ်ပေး ပါတယ်။
- SQL_DRIVER_NOPROMPT ဆိုရင်တော့ input connection string ကို output connection string ဆီကို ကော်ပီ ကူးပေး ပြီး၊ data source ကို dialog မပြ ပဲနဲ့ ဆက်သွယ် ပေးပါတယ်။ မှားနေ၊ လိုနေ ရင် dialog မဖော် ပြပဲ SQL_ERROR ကို return ပြန်ပေး ပါတယ်။
Connection Attributes
Data source တစ်ခု ကို ဆက်သွယ် တဲ့ အခါ အဓိက က InConnectionString argument ပါပဲ။ အဲဒီ ဆက်သွယ် တဲ့ attributes တွေကို အောက်မှာ ဖော်ပြ ထား ပါတယ်။
- DSN က data source name ပါ။ သူ့ကို ကြိုတင် ဖန်တီး သတ်မှတ် ထားဖို့ လိုပါတယ်။ Windows စက်တွေ မှာ ODBC Administrator ကို သုံးနိုင် ပြီး၊ Linux စက်တွေ မှာ တော့ ရှေ့မှာ ဖော်ပြ ခဲ့တဲ့ အတိုင်း odbc.ini ထဲမှာ သတ်မှတ် နိုင် ပါတယ်။
- DRIVER က သုံးမယ့် driver ရဲ့ နာမည် ပါ။ DSN-less connection တွေမှာ သုံးနိုင် ပါတယ်။
- FILEDSN က connection attributes တွေ ပါတဲ့ ဖိုင်ရဲ့ နာမည် ပါ။
- UID/PWD က database ကို authenticate လုပ်ဖို့ အတွက် username နဲ့ password ပါ။
- SAVEFILE က DSN attributes တွေ save လုပ်ဖို့ request လုပ် တာဖြစ်ပြီး၊ သိမ်းဖို့ ဖိုင် နာမည် ဖြစ်ပါတယ်။
con.cpp (https://github.com/yan9a/odroid/blob/master/database-mssql/con.cpp)
မှာ ပြထား ပါတယ်။ ပရိုဂရမ် ကို build လုပ်တဲ့ အခါ ODBC library နဲ့ ချိတ်ဆက် ပေးဖို့ အတွက် -lodbc ကို အောက်က Listing 1. con-bar.sh မှာ ပြထား သလို ထည့်ပေးနိုင် ပါတယ်။
g++ con.cpp -lodbc -std=c++11 -o con ./conListing 1. con-bar.sh - ODBC အသုံးပြု သည့် C++ ပရိုဂရမ် တစ်ခု ကို build လုပ်၍ run ခြင်း။
အခြေခံ result-set generating function များ
Reselt-set generating function တွေက ODBC ရဲ့ အဓိက အလုပ် လုပ် တဲ့ အစိတ် အပိုင်း တွေဖြစ်ပြီး SQL queries တွေ လုပ်ဆောင်တာ၊ metadata တွေ ရယူ တာ စတာ တွေကို လုပ်ဆောင် နိုင် ပါတယ်။ ရလာ တဲ့ result-set တွေက row နဲ့ column ဒေတာ တွေ ဖြစ် ပါတယ်။
SQL queries တွေ လုပ်ဆောင် ဖို့ အတွက် SQLExecDirect ကို သုံးနိုင် ပါတယ်။ ဥပမာ "mytable" ဆိုတဲ့ table ထဲက column အားလုံး ကို ရွေးဖို့ အတွက် အောက်က စာရင်း မှာ ပြထား သလို သုံးနိုင် ပါတယ်။
SQLExecDirect(stmt_handle, "select * from mytable", SQL_NTS);
တကယ်လို့ တူညီတဲ့ query တစ်ခု ထဲကိုပဲ criteria အမျိုးမျိုး နဲ့ ထပ်ကာ တလဲလဲ လုပ်ဆောင် မယ် ဆိုရင် တော့ SQLPrepare နဲ့ တစ်ခါ ပဲပြင် ဆင်ပြီး SQLExecute နဲ့ အကြိမ်ကြိမ် ခေါ်သုံးနိုင် ပါတယ်။
SQLPrepare(stmt_handle, "select * from mytable where mycol = ?", SQL_NTS); // bind a parameter with SQLBindParam // set parameter's value loop { SQLExecute(stmt_handle); // get results // change parameter's value }
တခြား အခြေခံ meta-data API တွေကတော့ SQLTables, SQLColumns, SQLTablePrivileges, SQLStatistics, SQLSpecialColumns, SQLProcedures, SQLProcedureColumns, SQLPrimaryKeys, SQLForeignKeys, SQLColumnPrivileges နဲ့ SQLGetTypeInfo စတာ တွေဖြစ် ပါတယ်။
Result-set များကို ထုတ်ယူခြင်း
Reselt-set generating function တွေ လုပ်ဆောင် ပြီးတဲ့ အခါ ရလာ တဲ့ result-set တွေကို အောက်မှာ ဖော်ပြ ထားတဲ့ function သုံးခု သုံးပြီး ထုတ်ယူ လေ့ ရှိ ပါတယ်။
- SQLNumResultCols ကို သုံးပြီး ရလာ တဲ့ result-set မှာ column ဘယ်နှစ်ခု ရှိလဲ ဆိုတာ စစ်ကြည့် နိုင် ပါတယ်။
- SQLFetch ကို သုံးပြီး row တွေကို တစ်ခု ခြင်း ထုတ်ယူနိုင် ပါတယ်။ နောက်ထပ် ထုတ်ယူ စရာ row မရှိ တော့ရင် SQL_NO_DATA ကို return ရ ပါလိမ့်မယ်။
- SQLGetData ကို သုံးပြီး column တွေကို loop ပတ်ပြီး ထုတ်ယူ နိုင် ပါတယ်။
fetch.cpp (https://github.com/yan9a/odroid/blob/master/database-mssql/fetch.cpp)
Return Statuses
ODBC API တွေက ပြန်ရ လာတဲ့ return status ကို ကြည့်ပြီး၊ function လုပ်ဆောင်တာ အောင်မြင်မှု ရှိ မရှိ စစ်ကြည့် နိုင် ပါတယ်။ C/C++ မှာ SQL_SUCCEEDED ဆိုတဲ့ macro ကို သုံးပြီး စစ်လို့ ရပါတယ်။ သူ့ရဲ့ ဖွင့်ဆို သတ်မှတ် ချက်က အောက်က အတိုင်း ဖြစ်ပါတယ်။
#define SQL_SUCCEEDED(rc) (((rc)&(~1))==0)
ODBC function တွေ အားလုံး လိုလို က အောင်မြင် တဲ့ အခါ အောက်က တန်ဖိုး နှစ်ခု ကို return လုပ်ပေး လေ့ရှိ ပါတယ်။
- SQL_SUCCESS
- SQL_SUCCESS_WITH_INFO
SQL_SUCCEEDED macro ကို သုံးလိုက်ရင် အဲဒီ တန်ဖိုး နှစ်ခု လုံးက true ဖြစ် ပါတယ်။ SQL_SUCCESS_WITH_INFO ကို return ပြန်ရင် အဲဒီ function က အောင်မြင် ပေမယ့် သတိပြု စရာ အချက် အလက် တွေပါ ရလာ တယ်လို့ ဆိုလို ပါတယ်။ ဥပမာ SQLGetData က SQL_SUCCESS_WITH_INFO ကို return ပြန်ရင် ဖတ်လို့ ရတဲ့ column data က buffer နဲ့ မဆံ့ တဲ့ အတွက် truncated ဖြစ်နေတာ မျိုးပါ။ တချို့ ODBC function တွေ မှာတော့ SQL_SUCCEEDED macro က fail ဖြစ် ပေမယ့်၊ error ဟုတ်ချင် မှ ဟုတ်ပါမယ်။ ဥပမာ SQLFetch က SQL_NO_DATA ကို return ပြန်ရ တဲ့ အခါ error မဟုတ်ပဲ နောက်ထပ် ထပ် ဖတ်စရာ row တွေ မရှိ တာကို ဆိုလို တာပါ။
ODBC function တစ်ခု က error ဒါမှ မဟုတ် SQL_SUCCESS_WITH_INFO ကို return ပြန် တဲ့ အခါ သက်ဆိုင်ရာ ပြဿနာ နဲ့ ပတ်သက် တဲ့ အချက် အလက် အဖြေ က သူ့ရဲ့ handle နဲ့ တွဲပြီး ပါလာ ပါတယ်။ ပြဿနာ နဲ့ ပတ်သက် တဲ့ အချက် အလက် အဖြေ တွေကို ရယူ ဖို့ အတွက် SQLGetDiagRec ကို သုံးနိုင် ပါတယ်။ Handle တစ်ခု မှာ အချက် အလက် အဖြေ တွေ အများကြီး တွဲပါ လာနိုင် ပါတယ်။ ဒါကြောင့် diagnostic record တွေကို 1 ကနေ စပြီး SQL_NO_DATA ရတဲ့ အထိ SQLGetDiagRec ကို အကြိမ်ကြိမ် ခေါ်နိုင် ပါတယ်။ ပြဿနာ နဲ့ ပတ်သက် တဲ့ အချက် အလက် အဖြေ တွေကို အကုန် ထုတ်ယူ ဖို့ နမူနာ C++ function တစ်ခု ကို အောက်က Listing 2 မှာ ဖော်ပြ ထား ပါတယ်။
vector < string > GetError(char const *fn,SQLHANDLE handle,SQLSMALLINT type) { SQLINTEGER i = 0; SQLINTEGER native; SQLCHAR state[7]; SQLCHAR text[256]; SQLSMALLINT len; SQLRETURN ret; vector < string > emes; emes.push_back(fn); do { ret = SQLGetDiagRec(type, handle, ++i, state, &native, text,sizeof(text), &len); if (SQL_SUCCEEDED(ret)) { printf("%s:%ld:%ld:%s\n", state, (long int)i, (long int)native, text); emes.push_back((char*)text); } } while( ret == SQL_SUCCESS ); return emes; }Listing 2. Diagnostic record များကို ထုတ်ယူခြင်း။
ODBC C++ Class နှင့် wxWidgets
ODBC datasource တွေကို အလွယ် တကူ ယူသုံးဖို့ အတွက် ရိုးရှင်း တဲ့ C++ class နမူနာ တစ်ခု ကို
ceODBC.h (https://github.com/yan9a/odroid/blob/master/database-mssql/ceODBC.h) နဲ့
ceODBC.cpp (https://github.com/yan9a/odroid/blob/master/database-mssql/ceODBC.cpp)
မှာ ဖော်ပြ ထား ပါတယ်။ အဲဒီ class ကို အသုံးပြု တဲ့ ရိုးရှင်းတဲ့ ပရိုဂရမ် နမူနာ ကို
simpleodbc.cpp (https://github.com/yan9a/odroid/blob/master/database-mssql/simpleodbc.cpp)
မှာ တွေ့နိုင် ပါတယ်။ ပရိုဂရမ် စစခြင်း ceODBC instance တစ်ခု ကို initialize လုပ်တဲ့ အချိန်မှာ environment handle နဲ့ connection handle တွေကို allocate လုပ်ပေး ပါတယ်။ အဲဒီ နောက် Connect method နဲ့ datasource တစ်ခု ကို ဆက်သွယ် နိုင် ပါတယ်။ Connection အခြေ အနေ ကို IsConnected method နဲ့ စစ်လို့ ရပြီး၊ SQL command တွေကို လုပ်ဆောင်ဖို့ SQL method ကို သုံးနိုင် ပါတယ်။ SELECT စတဲ့ command တွေနဲ့ table တွေကို ဖတ်တဲ့ အခါ ရလာ တဲ့ row နဲ့ column ဒေတာ တွေက 2 dimensional string vector အနေနဲ့ return ပြန်လာ မှာ ဖြစ်ပါတယ်။ UPDATE, INSERT, DELETE စတဲ့ dataset ထုတ် မပေးတဲ့ statement တွေ အတွက် တော့ affected row အရေ အတွက် ကို NumberOfRowsAffected ဆိုတဲ့ member variable မှာ တွေ့နိုင် မှာပါ။ Datasource နဲ့ ဆက်သွယ်မှု ကို Disconnect method နဲ့ ဖြတ်နိုင် ပါတယ်။ Environment handle နဲ့ connection handle တွေကို ပြန် free up လုပ်ပေး တာကို destructor ထဲမှာ အလို အလျောက် လုပ်ပေး ပါတယ်။ ပရိုဂရမ် ကို build နဲ့ run လုပ်ဖို့ အတွက် Listing 3. simpleodbc-bar.sh မှာ ပြထားတဲ့ command တွေကို သုံးနိုင် ပါတယ်။
g++ simpleodbc.cpp ceODBC.cpp -lodbc -std=c++11 -o simpleodbc ./simpleodbcListing 3. simpleodbc-bar.sh - simpleodbc.cpp ကို build လုပ်၍ run ခြင်း။
အဲဒီ ceODBC class ကိုပဲ wxWidgets နဲ့ တွဲသုံး တဲ့ နမူနာ ကို
wxodbc.cpp (https://github.com/yan9a/odroid/blob/master/database-mssql/wxodbc.cpp)
မှာ ဖော်ပြ ထား ပါတယ်။ ခလုတ် ကို နှိပ်လိုက်ရင် table တစ်ခု ကို ဖတ်ပြီး၊ list box မှာ ဖော်ပြ ပေး ပါတယ်။ GUI ကို Figure 1 မှာ တွေ့နိုင် ပါတယ်။
သူ့ကို build လုပ်ပြီး၊ run ဖို့ အတွက် command တွေကို Listing 4. wxodbc-bar.sh မှာ ဖော်ပြ ထား ပါတယ်။ Class ရဲ့ implementation code တွေကို cpp ဖိုင် သပ်သပ် ခွဲ ရေးထား တာက wxWidgets ထဲမှာ UNICODE ကို define လုပ်ထား တာမို့ SQLxxx APIs အစား SQLxxxW ပြောင်း မသွားအောင် လို့ပါ။
g++ wxodbc.cpp ceODBC.cpp -lodbc -std=c++11 `wx-config --cxxflags --libs` -o wxodbc ./wxodbcListing 4. wxodbc-bar.sh - wxodbc.cpp ကို build လုပ်၍ run ခြင်း။
အကိုးအကားများ
[Eas18] Easysoft. Easysoft tutorials on using ODBC from C. 2018.
url: https://www.easysoft.com/developer/languages/c/odbc_tutorial.html.
No comments:
Post a Comment