天天看點

【原創】基于 MySQL Connector/C 實作用戶端程式之 API 總結

【關于 mysql_query() 】

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

=== 

<a href="http://my.oschina.net/moooofly/blog/186327#">?</a>

1

<code>int</code> <code>mysql_query(mysql *mysql,</code><code>const</code> <code>char</code> <code>*stmt_str)</code>

功能: 

執行由 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 最好的方式是: 

2

<code>mysql *mysql_conn;</code>

<code>mysql_conn = mysql_init( null );</code>

【關于mysql_connect()】

mysql_connect() 屬于老 api,已經被 mysql_real_connect() 所取代。 

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

<code>#ifdef use_old_functions</code>

<code>mysql * stdcall</code>

<code>mysql_connect(mysql *mysql,</code><code>const</code> <code>char</code> <code>*host,</code><code>const</code> <code>char</code> <code>*user,</code><code>const</code> <code>char</code> <code>*passwd)</code>

<code>{</code>

<code>    </code><code>mysql *res;</code>

<code>    </code><code>mysql=mysql_init(mysql);</code><code>/* make it thread safe */</code>

<code>    </code><code>{</code>

<code>        </code><code>dbug_enter(</code><code>"mysql_connect"</code><code>);</code>

<code>        </code><code>if</code> <code>(!(res=mysql_real_connect(mysql,host,user,passwd,nulls,0,nulls,0)))</code>

<code>        </code><code>{</code>

<code>            </code><code>if</code> <code>(mysql-&gt;free_me)</code>

<code>                </code><code>my_free(mysql);</code>

<code>        </code><code>}</code>

<code>        </code><code>mysql-&gt;reconnect= 1;  </code><code>// 直接設定了重連</code>

<code>        </code><code>dbug_return(res);</code>

<code>    </code><code>}</code>

<code>}</code>

<code>#endif</code>

      調用 mysql_connect() 與 mysql_real_connect() 時輸入參數意義相同,差别在于 mysql_connect() 中連接配接句柄可以為 null 。在這種情況下,c api 将自動為連接配接結構配置設定記憶體,并在調用 mysql_close() 時釋放配置設定的記憶體。該方法的缺點是,如果連接配接失敗,你無法檢索錯誤消息。要想從 mysql_errno() 或 mysql_error() 獲得錯誤消息,必須提供有效的 mysql 指針。而 mysql_real_connect() 中的連接配接句柄必須是已初始化過的 mysql 結構。

【關于“重連”的設定】 

在 mysql_init() 的實作代碼中有如下說明:  ( 以下内容取自 mysql 5.6.10  ) 

<code>/*</code>

<code>   </code><code>by default we don't reconnect because it could silently corrupt data (after</code>

<code>   </code><code>reconnection you potentially lose table locks, user variables, session</code>

<code>   </code><code>variables (transactions but they are specifically dealt with in</code>

<code>   </code><code>mysql_reconnect()).</code>

<code>   </code><code>this is a change: &lt; 5.0.3 mysql-&gt;reconnect was set to 1 by default.</code>

<code>   </code><code>how this change impacts existing apps:</code>

<code>   </code><code>- existing apps which relyed on the default will see a behaviour change;</code>

<code>   </code><code>they will have to set reconnect=1 after mysql_real_connect().</code>

<code>   </code><code>- existing apps which explicitely asked for reconnection (the only way they</code>

<code>   </code><code>could do it was by setting mysql.reconnect to 1 after mysql_real_connect())</code>

<code>   </code><code>will not see a behaviour change.</code>

<code>   </code><code>- existing apps which explicitely asked for no reconnection</code>

<code>   </code><code>(mysql.reconnect=0) will not see a behaviour change.</code>

<code> </code><code>*/</code>

<code> </code><code>mysql-&gt;reconnect= 0;</code>

      意思大概如下:預設情況下,我們不執行重連動作,因為可能會存在不易覺察的資料損壞(在重連後,有可能發生的情況包括,丢失表鎖、使用者變量、會話變量等(事務當然也會終止,但是會在 mysql_reconnect() 時得到正确處理))。

在 mysql 5.0.3 版本之前,mysql-&gt;reconnect 預設被設定為 1 。 

目前已經變更為預設設定 0 ,可能會産生的影響: 

依賴于預設值 1 的已存在應用程式将會感覺到此變更,是以需要在調用 mysql_real_connect() 後自行設定 reconnect=1 ;

原本就顯式使能了重連功能的應用程式不會感受到此變更(一種可能是,其已經在調用 mysql_real_connect() 後自行設定了 reconnect=1);

原本就顯式去使能重連功能的應用程式不會感受到此變更(因為與目前預設行為已經一緻)。

       綜上,如果想要使能重連功能,正确的做法是,在調用 mysql_real_connect() 之後,通過 mysql_options() 顯式設定 mysql_opt_reconnect 的值為 1 。 

按如下方式可以設定重連的逾時時間: 

<code>unsigned</code><code>int</code> <code>timeout = 10;</code>

<code>mysql_options( mysql_conn, mysql_opt_connect_timeout, (</code><code>char</code> <code>*)&amp;timeout )</code>

【關于“字元集”的設定】

目前存在三種方式設定字元集: 

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() 的注釋如下: 

<code>/* </code>

<code>   </code><code>mysql_set_character_set function sends set names cs_name to</code>

<code>   </code><code>the server (which changes character_set_client, character_set_result</code>

<code>   </code><code>and character_set_connection) and updates mysql-&gt;charset so other</code>

<code>   </code><code>functions like mysql_real_escape will work correctly.</code>

<code>*/</code>

      mysql_set_character_set() 會向伺服器發送 "set names xxx" 指令來設定字元集資訊(該設定會改變 character_set_client、character_set_result 和 character_set_connection 的值),并會更新 mysql-&gt;charset 的值,進而對 mysql_real_escape() 等函數産生影響。

該函數内部會

調用 mysql_options(..., mysql_set_charset_name, ...); 來設定 mysql-&gt;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() 取而代之。 

<code>int</code> <code>mysql_library_init(</code><code>int</code> <code>argc,</code><code>char</code> <code>**argv,</code><code>char</code> <code>**groups)</code>

對于正常的用戶端應用程式來說,通常以 mysql_library_init(0, null, null) 進行調用。

<code>#include &lt;mysql.h&gt;</code>

<code>#include &lt;stdlib.h&gt;</code>

<code>int</code> <code>main(</code><code>void</code><code>) {</code>

<code>  </code><code>if</code> <code>(mysql_library_init(0, null, null)) {</code>

<code>    </code><code>fprintf</code><code>(stderr,</code><code>"could not initialize mysql library\n"</code><code>);</code>

<code>    </code><code>exit</code><code>(1);</code>

<code>  </code><code>}</code>

<code>  </code><code>/* use any mysql api functions here */</code>

<code>  </code><code>mysql_library_end();</code>

<code>  </code><code>return</code> <code>exit_success;</code>

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.

<code>int</code> <code>mysql_next_result(mysql *mysql)</code>

      如果存在多個查詢結果,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() 傳回錯誤,将不執行任何其他語句,也不會擷取任何更多的結果。

傳回值

描述

成功并有多個結果

-1

成功但沒有多個結果

&gt;0

出錯

以不恰當的順序執行了指令。例如,沒有為前面的結果集調用mysql_use_result()。 

mysql伺服器不可用。 

在查詢過程中,與伺服器的連接配接丢失。 

出現未知錯誤。

官網說明 

兩種場景下使用:

在單字串中執行了多條語句時;

通過 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() 對後續查詢的狀态進行處理。 

19

20

21

22

23

24

<code>/* connect to server with option client_multi_statements */</code>

<code>mysql_real_connect(..., client_multi_statements);</code>

<code>  </code> 

<code>/* now execute multiple queries */</code>

<code>mysql_query(mysql,"drop table if exists test_table;\</code>

<code>                   </code><code>create table test_table(id</code><code>int</code><code>);\</code>

<code>                   </code><code>insert into test_table values(10);\</code>

<code>                   </code><code>update test_table set id=20 where id=10;\</code>

<code>                   </code><code>select * from test_table;\</code>

<code>                   </code><code>drop table test_table");</code>

<code>do</code>

<code>  </code><code>/* process all results */</code>

<code>  </code><code>...</code>

<code>  </code><code>printf</code><code>(</code><code>"total affected rows: %lld"</code><code>, mysql_affected_rows(mysql));</code>

<code>  </code><code>if</code> <code>(!(result= mysql_store_result(mysql)))</code>

<code>  </code><code>{</code>

<code>     </code><code>printf</code><code>(stderr,</code><code>"got fatal error processing query\n"</code><code>);</code>

<code>     </code><code>exit</code><code>(1);</code>

<code>  </code><code>process_result_set(result);</code><code>/* client function */</code>

<code>  </code><code>mysql_free_result(result);</code>

<code>}</code><code>while</code> <code>(!mysql_next_result(mysql));</code>

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