linux下使用mysq Connector/C连接mysql服务端过程

写在前面:在网上找了很多资料,大部分都是直接安装mysql库,隐式的调用mysql的lib库和mysql.h头文件。当遇到服务器不能安装mysql时,但需要与安装有Mysql的服务器通信时,可以采用mysql链接库的方法。

  1. 下载mysql-connector-c-6.1.11-linux-glibc2.12-x86_64.tar.gz 
    下载地址:https://dev.mysql.com/downloads/connector/c/ (根据自己系统选择对应版本) 
    系统环境:centos7 64位

  2. 将解压后的文件复制到系统对应目录下 
    tar -zvxf mysql-connector-c-6.1.11-linux-glibc2.12-x86_64.tar.gz 
    cd mysql-connector-c-6.1.11-linux-glibc2.12-x86_64/ 
    cp lib/* /usr/lib64/mysql (如果mysql不存在,则事先建立) 
    cp include/* /usr/include/mysql (如果mysql不存在,则事先建立)

  3. 在编译时添加lib库: 
    LDLIBS += -L/usr/include/mysql -L/usr/lib64/mysql -lmysqlclient -lpthread -lm -lrt -ldl

  4. 主要数据结构

    MYSQL 
    mysql数据库连接句柄。在执行任何数据库操作之前首先就需要创建一个MYSQL结构。

    MYSQL_RES 
    执行查询语句(SELECT, SHOW, DESCRIBE, EXPLAIN)返回的结果。

    MYSQL_ROW 
    用来表示返回结果中的一行数据。由于每行数据格式不一致,因此使用此结构来统一表示。调用mysql_fetch_row()可从MYSQL_RES中返回一个MYSQL_ROW结构

    MYSQL_FIELD 
    用来表示一个field信息的元数据(元数据,即描述数据的数据),包括field name,field type以及field size等。MYSQL_FIELD不包含field的值(MYSQL_ROW真正保存各field的值)

    MYSQL_FIELD_OFFSET 
    field在row中的索引值,从0开始。

  5. 主要API

  • mysql_init()
1 MYSQL *mysql_init(MYSQL *mysql)`
2 创建一个MYSQL对象。
    mysql_real_connect()
MYSQL *mysql_real_connect(MYSQL *mysql, const char *host, const char *user, const char *passwd, const char *db, unsigned int port, const char *unix_socket, unsigned long client_flag) ` 连接到数据库服务器

 

  • mysql_real_query()
int mysql_real_query(MYSQL *mysql, const char *stmt_str, unsigned long length);

执行MySQL语句stmt_str,成功返回0

 

  • mysql_store_result()
MYSQL_RES *mysql_store_result(MYSQL *mysql);

在执行完查询语句(mysql_store_result() or mysql_use_result())后,调用此函数获得执行结果(result set)。如果执行正确且有结果返回,那么此函数返回非NULL的指针。

 

  • mysql_affected_rows()
my_ulonglong mysql_affected_rows(MYSQL *mysql);

 

如果执行的是UPDATE、INSERT和DELETE操作,那么MySQL会告诉你此操作影响了多少行(Rows)。调用此函数即能返回该值。有关在不同操作下此函数返回值的解释,详见官方文档。在以下几种情况下函数会返回0: 
1)带有WHERE的UPDATE操作没有匹配任何行; 
2)调用之前没有执行任何query操作; 
3)对于SELECT操作,在调用mysql_store_reuslt()之前调用此函数。

在三种情况下会返回NULL: 
1)执行的语句不是查询语句,例如INSERT/UPDATE等; 
2)有result set但读取出错(到server的连接出问题); 
3)调用malloc为result set分配空间出错(result set太大)。

第一种情况可通过mysql_field_count()是否返回0来判断;后两种情况可通过 mysql_error()返回非空字符串或者 mysql_errno() 返回大于0来判断。

因此,一般情况下执行SQL语句的流程如下所示:

MYSQL_RES *result;
    unsigned int num_fields;
    unsigned int num_rows;

    if (mysql_real_query(&mysql,query_string, srlen(query_string)))
    {
        // error
    }
    else // query succeeded, process any data returned by it
    {
        result = mysql_store_result(&mysql);
        if (result)  // there are rows
        {
            num_fields = mysql_num_fields(result);
            // retrieve rows, then call mysql_free_result(result)
        }
        else  // mysql_store_result() returned nothing; should it have?
        {
            if(mysql_field_count(&mysql) == 0)
            {
                // query does not return data
                // (it was not a SELECT)
                num_rows = mysql_affected_rows(&mysql);
            }
            else // mysql_store_result() should have returned data
            {
                fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
            }
        }
    }

 

6.解析返回结果

在上一节,假定我们成功地执行了语句,并获得了结果(MYSQL_RES结构)。那么怎么从MYSQL_RES中解析出我们想要的东西呢?

先来看一个示例:

#include <mysql.h>
#include <string>
#include <stdio.h>

using namespace std;

int main()
{
    if (argc != 2)
    {
        printf("Usage: ./test \"SQL Statement\"\n");
        return -1;
    }
    MYSQL mysql;
    mysql_init(&mysql);
    if (!mysql_real_connect(&mysql,"localhost","mysql","mysql","mytest",3306,NULL,0))//连接到本地数据库
    {
        fprintf(stderr, "Failed to connect to database: Error: %s\n",
        mysql_error(&mysql));
    }

    printf("connect to %s:%d success...\n", "localhost", 3306);

    string sql(argv[1]);
    if (mysql_real_query(&mysql, sql.c_str(), sql.length()))
    {
        //error
    }
    else 
    { // 成功执行SQL语句
        MYSQL_RES* res = mysql_store_result(&mysql);//获得结果
        if (res) // sucess
        {
            printf("\"%s\" success\n", sql.c_str());
            int num_fields = mysql_num_fields(res);
            int num_rows = mysql_num_rows(res);
            printf("result: %d rows  %d fields\n", num_rows, num_fields);
            printf("----------------------------\n");

            //1. 获得列属性(名称)
            MYSQL_FIELD* fields;//数组,包含所有field的元数据
            fields = mysql_fetch_fields(res);
            for (int i = 0; i < num_fields; ++i)
            {
                printf("%s\t", fields[i].name);
            }
            printf("\n");

            //2. 获得每行的数据
            MYSQL_ROW row;
            while ((row = mysql_fetch_row(res)))
            {
                unsigned long *lengths;
                lengths = mysql_fetch_lengths(res);
                for (int i = 0; i < num_fields; ++i)
                {
                    printf("%s\t",  row[i] ? row[i] : "NULL");
                }
                printf("\n");
            }
            printf("----------------------------\n");
            mysql_free_result(res);
        }
        else
        {
            //接下来就需要判断为什么res为NULL了

            int ret = mysql_field_count(&mysql);
            //printf("mysql_field_count %d\n", ret);
            if (ret == 0)
            {
                // 说明执行的是UPDATE/INSERT等这类操作
                int ret = mysql_affected_rows(&mysql);
                printf("Query OK, %d rows affected\n", ret);
            }
            else
            {
                fprintf(stderr, "Error: %s\n", mysql_error(&mysql));
            }
        }
    }
    mysql_close(&mysql);
    return 0;
}

 

可以看到,从MYSQL_RES中获取结果依靠的是两个函数:

mysql_fetch_fields() 获取field元数据 
mysql_fetch_rows() 获取每一行的数据。每调用一次,该MYSQL_RES的row offset自动增1,因此可以在while循环中调用此函数,直到返回NULL。 
以下是编译运行结果:

首先写个简单的makefile:

Makefile

test:test.cpp
    g++ -c `mysql_config --cflags` test.cpp
    g++ -o test test.o `mysql_config --libs`

.PHONY: clean

clean:
    rm -f *.o test

 

其中,可以使用mysql_config工具来获得安装Connector时候的头文件位置和库文件位置(详见官方文档)。

编译运行结果:

$ make
g++ -c `mysql_config --cflags` test.cpp
g++ -o test test.o `mysql_config --libs`
$ ./test "SELECT * FROM table1"
connect to localhost:3306 success...
"SELECT * from table1" success
result: 1 rows  3 fields
----------------------------

tid sid     name    

123 12345   haha
----------------------------
$ ./test "UPDATE table1 set sid=12345 where tid=123"
connect to localhost:3306 success...
Query OK, 0 rows affected
$ ./test "UPDATE table1 set sid=54321 where tid=123"
connect to localhost:3306 success...
Query OK, 1 rows affected
$ ./test "SELECT * from table1"
connect to localhost:3306 success...
"SELECT * from table1" success

result: 1 rows  3 fields
----------------------------

tid sid     name    

123 54321   haha
----------------------------

 

总结:虽然文章也参考很多别人的内容,已经写法,但总归是第一篇捎带技术性质的博客了,有任何疑问可以给我留言!

posted @ 2017-10-26 12:59  阿门-M  阅读(598)  评论(0)    收藏  举报