【原創】基於 MySQL Connector/C 實現客戶端程序之 API 總結


關於 mysql_query()

下面是官網對於 mysql_query() 的說明。
===

22.8.7.52. mysql_query()
int mysql_query(MYSQL *mysql, const char *stmt_str)

功能:
html

  • 執行由 stmt_str 指定的 NULL 結尾的 SQL 語句。一般狀況下,由 stmt_str 指定的內容是單條 SQL 語句,且結尾沒有分號 ";" 或者 "\g" 。若是使能了多語句執行功能,那麼由 stmt_str 指定的內容能夠包含多條由分號分隔的語句;
  • mysql_query() 不能正確應用於包含二進制數據的語句,必須使用 mysql_real_query() 替代;(二進制數據中可能包含 "\0" 字符,會被 mysql_query() 按串結束符理解)
  • 若是你想知道執行後是否返回告終果集,你可使用 mysql_field_count() 來檢查;
  • 若是執行成功,則返回 0 ;非 0 爲失敗。


錯誤碼:
CR_COMMANDS_OUT_OF_SYNC
Commands were executed in an improper order.
CR_SERVER_GONE_ERROR
The MySQL server has gone away.
CR_SERVER_LOST
The connection to the server was lost during the query.
CR_UNKNOWN_ERROR
An unknown error occurred.
===

關於 mysql_init()
使用 mysql_init 最好的方式是:
MYSQL *mysql_conn; 
mysql_conn = mysql_init( NULL );

關於mysql_connect() mysql

mysql_connect() 屬於老 API,已經被 mysql_real_connect() 所取代
#ifdef USE_OLD_FUNCTIONS 
MYSQL * STDCALL 
mysql_connect(MYSQL *mysql,const char *host, const char *user, const char *passwd) 
{
    MYSQL *res; 
    mysql=mysql_init(mysql); /* Make it thread safe */ 
    { 
        DBUG_ENTER("mysql_connect"); 
        if (!(res=mysql_real_connect(mysql,host,user,passwd,NullS,0,NullS,0))) 
        { 
            if (mysql->free_me) 
                my_free(mysql); 
        }
        mysql->reconnect= 1;   // 直接設置了重連 
        DBUG_RETURN(res); 
    } 
} 
#endif

      調用 mysql_connect() 與 mysql_real_connect() 時輸入參數意義相同,差異在於 mysql_connect() 中鏈接句柄能夠爲 NULL 。在這種狀況下,C API 將自動爲鏈接結構分配內存,並在調用 mysql_close() 時釋放分配的內存。該方法的缺點是,若是鏈接失敗,你沒法檢索錯誤消息。要想從 mysql_errno() 或 mysql_error() 得到錯誤消息,必須提供有效的 MYSQL 指針。而 mysql_real_connect() 中的鏈接句柄必須是已初始化過的 MYSQL 結構。 sql

關於「重連」的設置
在 mysql_init() 的實現代碼中有以下說明:  ( 如下內容取自 MySQL 5.6.10  )
/* 
    By default we don't reconnect because it could silently corrupt data (after 
    reconnection you potentially lose table locks, user variables, session 
    variables (transactions but they are specifically dealt with in 
    mysql_reconnect()). 
    This is a change: < 5.0.3 mysql->reconnect was set to 1 by default. 
    How this change impacts existing apps: 
    - existing apps which relyed on the default will see a behaviour change; 
    they will have to set reconnect=1 after mysql_real_connect(). 
    - existing apps which explicitely asked for reconnection (the only way they 
    could do it was by setting mysql.reconnect to 1 after mysql_real_connect()) 
    will not see a behaviour change. 
    - existing apps which explicitely asked for no reconnection 
    (mysql.reconnect=0) will not see a behaviour change. 
  */ 
  mysql->reconnect= 0;

      意思大概以下:默認狀況下,咱們不執行重連動做,由於可能會存在不易覺察的數據損壞(在重連後,有可能發生的狀況包括,丟失表鎖、用戶變量、會話變量等(事務固然也會終止,可是會在 mysql_reconnect() 時獲得正確處理))。 數據庫

在 MySQL 5.0.3 版本以前,mysql->reconnect 默認被設置爲 1 。
當前已經變動爲默認設置 0 ,可能會產生的影響:
  • 依賴於默認值 1 的已存在應用程序將會感知到此變動,因此須要在調用 mysql_real_connect() 後自行設置 reconnect=1 ;
  • 本來就顯式使能了重連功能的應用程序不會感覺到此變動(一種多是,其已經在調用 mysql_real_connect() 後自行設置了 reconnect=1);
  • 本來就顯式去使能重連功能的應用程序不會感覺到此變動(由於與當前默認行爲已經一致)。
       綜上,若是想要使能重連功能,正確的作法是,在調用 mysql_real_connect() 以後,經過 mysql_options() 顯式設置 MYSQL_OPT_RECONNECT 的值爲 1

按以下方式能夠設置重連的超時時間:
unsigned int timeout = 10; 
mysql_options( mysql_conn, MYSQL_OPT_CONNECT_TIMEOUT, (char *)&timeout )

關於「字符集」的設置 服務器

目前存在三種方式設置字符集:
  • mysql_options( mysql_conn , MYSQL_SET_CHARSET_NAME , "utf8" );
  • mysql_set_character_set( mysql_conn, "utf8" )
  • mysql_query( mysql_conn, "SET NAMES utf8" )

首先看一下下面這段說明
===
使用 MySQL 字符集時的建議:
  • 創建數據庫、表和進行數據庫操做時儘可能顯式指出使用的字符集,而不是依賴於 MySQL 的默認設置,不然 MySQL 升級時可能帶來很大困擾;
  • 數據庫和鏈接字符集都使用 latin1 時,雖然大部分狀況下均可以解決亂碼問題,但缺點是沒法以字符爲單位來進行 SQL 操做,通常狀況下將數據庫和鏈接字符集都置爲 utf8 是較好的選擇;
  • 使用 MySQl C API 時,初始化數據庫句柄後立刻經過 mysql_options() 設定 MYSQL_SET_CHARSET_NAME 屬性爲 utf8 ,這樣就不用再顯式地執行 "SET NAMES utf8" 語句指定鏈接字符集,且用 mysql_ping 重連斷開的長鏈接時也會把鏈接字符集重置爲 utf8 ;
其餘注意事項
  • my.cnf 中的 default_character_set 設置隻影響 mysql 命令鏈接服務器時的鏈接字符集,不會對使用 libmysqlclient 庫的應用程序產生任何做用!
  • 對字段進行的 SQL 函數操做一般都是之內部操做字符集進行的,不受鏈接字符集設置的影響。
  • SQL 語句中的裸字符串會受到鏈接字符集或 introducer 設置的影響,對於比較之類的操做可能產生徹底不一樣的結果,須要當心!

MySQL 5.6.10 源碼中對 mysql_set_character_set() 的註釋以下:
/*  
   mysql_set_character_set function sends SET NAMES cs_name to 
   the server (which changes character_set_client, character_set_result 
   and character_set_connection) and updates mysql->charset so other 
   functions like mysql_real_escape will work correctly. 
*/

      mysql_set_character_set() 會向服務器發送 "SET NAMES xxx" 命令來設置字符集信息(該設置會改變 character_set_client、character_set_result 和 character_set_connection 的值),並會更新 mysql->charset 的值,進而對 mysql_real_escape() 等函數產生影響。 session

該函數內部會
  • 調用 mysql_options(..., MYSQL_SET_CHARSET_NAME, ...); 設置 mysql->options.charset_name 的值
  • 調用 mysql_real_query(); 來發送 "SET NAMES xxx" 

關於 mysql_library_init()和mysql_library_end()
問題:是否必定須要調用 mysql_library_init() 和 mysql_library_end() 函數?如何設置 mysql_library_init() 的入參?

回答: 經過調用 mysql_library_init(),能夠初始化 MySQL 庫。庫能夠是 mysqlclient C 客戶端庫,或 mysqld 嵌入式服務器庫。

      調用 mysql_library_init() 和 mysql_library_end() 的目的在於,爲 MySQL 庫提供恰當的初始化和結束處理。對於與客戶端庫連接的應用程序,它們提供了改進的內存管理功能。若是不調用 mysql_library_end(),內存塊仍將保持分配狀態(這不會增長應用程序使用的內存量,但某些內存泄漏檢測器將抗議它)。對於與嵌入式服務器連接的應用程序,這些調用會啓動並中止服務器。

      mysql_library_init() 和 mysql_library_end() 其實是 #define 符號,這類符號使得它們等效於mysql_server_init() 和 mysql_server_end(),但其名稱更清楚地指明,不管應用程序使用的是 mysqlclient 或mysqld 庫,啓動或結束 MySQL 庫時,應調用它們。對於早期的 MySQL 版本,可調用 mysql_server_init() 和mysql_server_end() 取而代之。

調用 mysql_library_init() 時如何設置入參?能夠參考以下官網信息:【 22.8.7.40. mysql_library_init()  
int mysql_library_init(int argc, char **argv, char **groups)

對於常規的客戶端應用程序來講,一般以 mysql_library_init(0, NULL, NULL) 進行調用。 app

#include <mysql.h> 
#include <stdlib.h> 

int main(void) { 
  if (mysql_library_init(0, NULL, NULL)) { 
    fprintf(stderr, "could not initialize MySQL library\n"); 
    exit(1); 
  } 

  /* Use any MySQL API functions here */ 
  mysql_library_end(); 

  return EXIT_SUCCESS; 
}

The groups argument is an array of strings that indicate the groups in option files from which to read options. For convenience, if the groups argument itself is NULL, the [server] and [embedded] groups are used by default. 函數


關於mysql_next_result()
int mysql_next_result(MYSQL *mysql)

      若是存在多個查詢結果,mysql_next_result() 將讀取下一個查詢結果,並將狀態返回給應用程序。若是前面的查詢返回告終果集,必須爲其調用 mysql_free_result()。 測試

      調用 mysql_next_result() 後,鏈接狀態就像你已爲下一查詢調用了 mysql_real_query() 或 mysql_query() 同樣。這意味着你能調用 mysql_store_result()、mysql_warning_count()、mysql_affected_rows() 等等。

      若是 mysql_next_result() 返回錯誤,將不執行任何其餘語句,也不會獲取任何更多的結果。 this

返回值
描述
0 成功並有多個結果
-1 成功但沒有多個結果
>0 出錯

錯誤碼:
CR_COMMANDS_OUT_OF_SYNC
以不恰當的順序執行了命令。例如,沒有爲前面的結果集調用mysql_use_result()。
CR_SERVER_GONE_ERROR
MySQL服務器不可用。
CR_SERVER_LOST
在查詢過程當中,與服務器的鏈接丟失。
CR_UNKNOWN_ERROR
出現未知錯誤。

官網說明
===

兩種場景下使用:
  • 在單字串中執行了多條語句時;
  • 經過 CALL 語句執行存儲過程時。
      mysql_next_result() 會讀取下一條語句執行的結果,並經過返回值代表是否還存在下一條結果。若是 mysql_next_result() 返回錯誤,怎麼沒有更多的結果了。
      在每一次調用 mysql_next_result() 以前,必須針對當前已經返回結果集的語句調用 mysql_free_result() 以釋放結果集所佔內存。
      在調用 mysql_next_result() 以後,當前鏈接上的狀態就像再次調用 mysql_real_query() 或 mysql_query() 執行了下一條語句同樣。因此,你能夠繼續調用 mysql_store_result()、mysql_warning_count()、mysql_affected_rows() 等函數。
      若是經過 CALL 語句來執行存儲過程,則必須使能 CLIENT_MULTI_RESULTS 標識。由於在此場景下,執行 CALL 語句會得到一條 CALL 調用狀態的結果,和若干條與存儲過程當中所含的 sql 語句對應的結果集。由於 CALL 會返回多個結果信息,故一般會在循環中調用 mysql_next_result() 來肯定是否還有更多的結果待處理。  
      能夠在調用 mysql_real_connect() 中使能 CLIENT_MULTI_RESULTS 標識,直接的方式就是傳 CLIENT_MULTI_RESULTS 標識自己,間接的方式是傳 CLIENT_MULTI_STATEMENTS 標識(會間接使能 CLIENT_MULTI_RESULTS)。在 MySQL 5.6 中,CLIENT_MULTI_RESULTS 標識已經被默認使能。  
      使用 mysql_more_results() 來測試是否還有更多的結果待處理是能夠的。然而,該函數不會變動當前鏈接的狀態,因此即便其返回了 true ,你仍舊必須調用 mysql_next_result() 來切換到獲取下一結果的狀態。  
===

關於多查詢執行的C API處理
       MySQL 5.1 支持在單個查詢字符串中指定多語句執行。要想與給定的鏈接一塊兒使用該功能,打開鏈接時,必須將標誌參數中的 CLIENT_MULTI_STATEMENTS 選項指定給 mysql_real_connect() 。也能夠經過調用 mysql_set_server_option(MYSQL_OPTION_MULTI_STATEMENTS_ON),爲已有的鏈接設置它。  
在默認狀況下,mysql_query() 和 mysql_real_query() 僅返回第 1 個查詢的狀態,並能使用 mysql_more_results() 和 mysql_next_result() 對後續查詢的狀態進行處理。
/* Connect to server with option CLIENT_MULTI_STATEMENTS */ 
mysql_real_connect(..., CLIENT_MULTI_STATEMENTS); 
  
/* Now execute multiple queries */ 
mysql_query(mysql,"DROP TABLE IF EXISTS test_table;\ 
                   CREATE TABLE test_table(id INT);\ 
                   INSERT INTO test_table VALUES(10);\ 
                   UPDATE test_table SET id=20 WHERE id=10;\ 
                   SELECT * FROM test_table;\ 
                   DROP TABLE test_table"); 
do 
{ 
  /* Process all results */ 
  ... 
  printf("total affected rows: %lld", mysql_affected_rows(mysql)); 
  ... 
  if (!(result= mysql_store_result(mysql))) 
  { 
     printf(stderr, "Got fatal error processing query\n"); 
     exit(1); 
  } 
  process_result_set(result); /* client function */ 
  mysql_free_result(result); 
} while (!mysql_next_result(mysql));

      多語句功能可與 mysql_query() 或 mysql_real_query() 一塊兒使用。它不能與預處理語句接口一塊兒使用。按照定義,預處理語句僅能與包含單個語句的字符串一塊兒使用。

相關文章
相關標籤/搜索