c/c++操作数据库:odbc和数据库API

一、Mysql

1.1 数据库安装和样本制造

环境:ubuntu18.04.6

镜像名:ubuntu-18.04.6-desktop-amd64.iso

使用:Vmware Workstation中搭建虚拟机

1.1.1 数据库安装

安装:https://www.bbsmax.com/A/B0zqqmMGzv/

sudo apt-get install mysql-server mysql-client

连接信息:root/123456,密码修改可参照下面。

查看版本,命令输入

mysql -V

结果:mysql Ver 14.14 Distrib 5.7.41, for Linux (x86_64) using EditLine wrapper

为mysql5.7版本

1.1.2 样本制造

数据库名:test

数据库表名:accounts

数据库表结构:name+passwd

终端登录:mysql -u root -p
创建数据库:create database test;
选择数据库:use test;
创建数据库表:create table accounts(name char(20) not null primary key,passwd char(20) not null);
退出:exit;
增加数据:
手动:
INSERT accounts values('fengxin','123');
INSERT accounts values('axin','456');
或用下面的函数
通过终端连接数据库查看内容:select * from accounts;

1.2 odbc连接配置

https://blog.csdn.net/u014257954/article/details/78525100

步骤:

1. 安装unixODBC和soci
2. 然后配置odbc manager和odbc driver
3. c++代码访问

1.2.1 unixodbc:apt-get

参考:

https://blog.csdn.net/TheThree_body/article/details/125011951

https://blog.csdn.net/MrTsai_cpp/article/details/116674570

https://www.cnblogs.com/yoyotl/p/9980269.html

1.安装unixodbc
# odbc管理工具
sudo apt-get install unixodbc
# odbc开发包
sudo apt-get install unixodbc-dev
2.查看安装信息
odbcinst -j

结果显示如下:

root@lym-virtual-machine:~# odbcinst -j
unixODBC 2.3.4
DRIVERS............: /etc/odbcinst.ini
SYSTEM DATA SOURCES: /etc/odbc.ini
FILE DATA SOURCES..: /etc/ODBCDataSources
USER DATA SOURCES..: /root/.odbc.ini
SQLULEN Size.......: 8
SQLLEN Size........: 8
SQLSETPOSIROW Size.: 8

# 查看版本信息
isql --v 

odbc有两个配置文件:安装后在/etc底下

  • odbc.ini : ODBC数据源的配置文件
  • odbcinst.ini : ODBC的驱动配置文件
3.安装数据库
sudo apt-get install mysql-server mysql-client
4.安装mysql的odbc驱动

输入命令(apt-get install libmyodbc)--无用:软件包 libmyodbc 没有可安装候选

改为手动下载安装:

官网地址:https://dev.mysql.com/downloads/connector/odbc/

查看安装机器版本:

输入:uname -a
结果:Linux lym-virtual-machine 5.4.0-135-generic #152~18.04.2-Ubuntu SMP Tue Nov 29 08:23:49 UTC 2022 x86_64 x86_64 x86_64 GNU/Linux

根据系统版本选择odbc的ubuntu18.04,x86,64bit版本

四个deb包全部下载,通过以下命令依次安装(可能中途会安装有问题,不用管)

提示缺少依赖,需要提前安装以下包

# mysql-community-client-plugins,下载目录:https://dev.mysql.com/downloads/mysql/
# 下载mysql-community-client-plugins_8.0.32-1ubuntu18.04_amd64.deb,执行以下命令安装
dpkg -i mysql-community-client-plugins_8.0.32-1ubuntu18.04_amd64.deb

安装顺序如下

dpkg -i mysql-connector-odbc_8.0.32-1ubuntu18.04_amd64.deb
dpkg -i mysql-connector-odbc-setup_8.0.32-1ubuntu18.04_amd64.deb
dpkg -i mysql-connector-odbc-dbgsym_8.0.32-1ubuntu18.04_amd64.deb
dpkg -i mysql-connector-odbc-setup-dbgsym_8.0.32-1ubuntu18.04_amd64.deb
5.使用配置文件进行测试

vim /etc/odbc.ini,注意每个前后或中间不要有多余的空格或空白符,俺深受其害

[mysql]
Description=Data source MySQL
Driver=MySQL ODBC 8.0 Unicode Driver
Server=localhost
Host=localhost
Database=test
Port=3306
User=root
Password=123456
# 123456 #数据库登录密码
# CHARSET=UTF8
# OPTION=67108864

输入命令,出现以下内容为成功连接,至此安装和配置成功。

lym@lym-virtual-machine:/root$ isql mysql -v
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL>

若未成功连接,错误排查如下

root@lym-virtual-machine:~# isql mysql
[ISQL]ERROR: Could not SQLConnect

出现错误:无法连接

排查:

1、输入isql -v mysql,-v设置显示调试信息,mysql对应/etc/odbc.ini配置的[]中的信息,出现
[IM002][unixODBC][Driver Manager]Data source name not found, and no default driver specified
[ISQL]ERROR: Could not SQLConnect
解决:将/etc/odbc.ini中的空格注释掉之后,未出现驱动找不到,出现Access denied:
[S1000][unixODBC][MySQL][ODBC 8.0(w) Driver]Access denied for user 'root'@'localhost'
[ISQL]ERROR: Could not SQLConnect
注释:Data source name not found, and no default driver specified此报错一般是驱动Driver的值选项设置有问题
2、针对上面的访问被拒绝问题:
尝试:尝试在终端用普通用户登录,输入密码123456,依然是访问被拒绝,说明密码有误,需更改密码
    lym@lym-virtual-machine:/root$ mysql -u root -p
    Enter password:
    ERROR 1698 (28000): Access denied for user 'root'@'localhost'
原因:安装新版本mysql,root密码是随机的,也不是空密码,所以要通过查看随机密码进入,再进行修改原来的密码
设置:参照https://blog.csdn.net/m0_70885101/article/details/127414184
普通用户修改修改密码见下部分
修改密码后;能够成功连接,如下
lym@lym-virtual-machine:/root$ isql mysql -v
+---------------------------------------+
| Connected!                            |
|                                       |
| sql-statement                         |
| help [tablename]                      |
| quit                                  |
|                                       |
+---------------------------------------+
SQL>

【普通用户修改密码】
(1)使用其他用户登录MySQL终端
命令:sudo cat /etc/mysql/debian.cnf
结果:
# Automatically generated for Debian scripts. DO NOT TOUCH!
[client]
host     = localhost
user     = debian-sys-maint
password = adFIjhVxAoYeMWOJ
socket   = /var/run/mysqld/mysqld.sock
[mysql_upgrade]
host     = localhost
user     = debian-sys-maint
password = adFIjhVxAoYeMWOJ
socket   = /var/run/mysqld/mysqld.sock
登录:使用[mysql_upgrade]下的host和user登录,mysql -u debian-sys-maint -p,输入密码adFIjhVxAoYeMWOJ
(2)操作用户表
切换数据库:use mysql;
查看user表:select user,plugin from user;
结果:
    Database changed
    mysql> select user,plugin from user;
    +------------------+-----------------------+
    | user             | plugin                |
    +------------------+-----------------------+
    | root             | auth_socket           |
    | mysql.session    | mysql_native_password |
    | mysql.sys        | mysql_native_password |
    | debian-sys-maint | mysql_native_password |
    +------------------+-----------------------+
设置:此时root的plugin为auth_socket,需修改密码格式
(3)修改密码格式
update user set plugin='mysql_native_password' where user='root'; # 修改其密码格式
select user,plugin from user; # 查询其用户
flush privileges; #注意刷新权限
结果:
    Query OK, 1 row affected (0.00 sec)
    Rows matched: 1  Changed: 1  Warnings: 0

    mysql> select user,plugin from user;
    +------------------+-----------------------+
    | user             | plugin                |
    +------------------+-----------------------+
    | root             | mysql_native_password |
    | mysql.session    | mysql_native_password |
    | mysql.sys        | mysql_native_password |
    | debian-sys-maint | mysql_native_password |
    +------------------+-----------------------+
(4)增加root密码
alter user 'root'@'localhost' identified by '123456'; #123456为我上设置的密码
flush privileges; #再次刷新权限
exit #退出登录
(5)重启mysql服务
service mysql restart
(6)root登录输入密码,成功
mysql -u root -p
6.C测试代码

代码参考:

https://blog.51cto.com/lhrbest/5110107?b=totalstatistic

使用:

①ODBCAPI中的常用数据类型与我们在 C 语言中使用的数据类型的对应关系

②相关API函数

/*************************************************************************
    > File Name:odbctest.c
    > Function:testing unixODBC
    > Author:
    > Mail:
    > Created Time:2023年04月10日 星期一 14时08分01秒
 ************************************************************************/

#include <stdlib.h>
#include <stdio.h>
// #include <odbc/sql.h>
// #include <odbc/sqlext.h>
// #include <odbc/sqltypes.h>
#include <sql.h>
#include <sqlext.h>
#include <sqltypes.h>

SQLHENV         V_OD_Env;           // Handle ODBC environment:odbc环境句柄,通过调用SQLAllocHandle获取有关的odbc环境信息
SQLHDBC         V_OD_hdbc;          // Handle connection:数据库连接句柄,存放数据库连接信息,调用SQLAllocHandle获得连接句柄
SQLHSTMT        V_OD_hstmt;         // Handle SQL:sql语句句柄,存放SQL语句信息,调用SQLAllocHandle获取句柄
long            V_OD_erg;           // result of functions:函数返回值
SQLLEN          v_OD_len;           // 数据库查询字段值返沪长度
char            V_OD_name[32], V_OD_keyw[32]; // 存储查询结果
SQLSMALLINT     V_OD_colanz;        // 查询结果列数
SQLLEN          V_OD_rowanz;        // 查询结果条数
SQLINTEGER      V_OD_err;           //存放错误信息的编号
unsigned char   V_OD_stat[10];      // Status SQL
SQLSMALLINT     V_OD_mlen;
unsigned char   V_OD_msg[200];

// 获取DNS信息
void OD_ListDSN()
{
    unsigned char  l_dsn[100], l_desc[100];
    short int      l_len1, l_len2, l_next;

    l_next = SQL_FETCH_FIRST;
    while(SQLDataSources(V_OD_Env, l_next, l_dsn, sizeof(l_dsn), &l_len1, l_desc, sizeof(l_desc), &l_len2) == SQL_SUCCESS)
    {
        printf("Server=(%s) Beschreibung=(%s)\n", l_dsn, l_desc);
        l_next = SQL_FETCH_NEXT;
    }
}

int main(int argc,char *argv[])
{
    // 1. allocate Environment handle and register version:设置odbc环境句柄并设置参数
    // 获取odbc环境变量句柄V_OD_Env
    V_OD_erg = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &V_OD_Env);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Error AllocHandle\n");
        exit(0);
    }
    // 对odbc环境句柄V_OD_Env设定所用的odbc版本:SQL_ATTR_ODBC_VERSION-版本号,SQL_OV_ODBC3-odbc3.0
    V_OD_erg = SQLSetEnvAttr(V_OD_Env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0); 
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Error SetEnv\n");
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }

    // 获取DSN
    printf("DSN_begin:\n");
    OD_ListDSN();
    printf("DSN_end  :\n");
    
    // 2. allocate connection handle, set timeout:设定连接句柄并设置超时参数
    // 获取连接句柄V_OD_hdbc,入参环境变量句柄
    V_OD_erg = SQLAllocHandle(SQL_HANDLE_DBC, V_OD_Env, &V_OD_hdbc);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Error AllocHDB %ld\n", V_OD_erg);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }
    // 对数据库连接句柄设置超时参数
    SQLSetConnectAttr(V_OD_hdbc, SQL_LOGIN_TIMEOUT, (SQLPOINTER *)5, 0);
    
    // 3. Connect to the datasource "test":连接数据库
    // 连接数据库,需要设置数据库名称、用户名和密码,SQL_NTS-让SQLConnect决定参数长度,也可自定义,mysql需要提前在配置文件/etc/odbc.ini配置好
    V_OD_erg = SQLConnect(V_OD_hdbc, (SQLCHAR*) "mysql", SQL_NTS,
                                     (SQLCHAR*) "root", SQL_NTS,
                                     (SQLCHAR*) "123456", SQL_NTS);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Error SQLConnect %ld\n", V_OD_erg);
          
        // SQLRETURN SQLGetDiagRec(  
        //      SQLSMALLINT     HandleType,  
        //      SQLHANDLE       Handle,  
        //      SQLSMALLINT     RecNumber,  
        //      SQLCHAR *       SQLState,  
        //      SQLINTEGER *    NativeErrorPtr,  
        //      SQLCHAR *       MessageText,  
        //      SQLSMALLINT     BufferLength,  
        //      SQLSMALLINT *   TextLengthPtr);
        // 返回包含错误、警告和状态信息的诊断记录的多个字段的当前值
        SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc, 1, V_OD_stat, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
        printf("%s (%d)\n",V_OD_msg,V_OD_err);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }
    printf("Connected !\n");
    
    // 4. 分配 SQL语句的句柄并进行查询
    // 获取SQL语句句柄
    V_OD_erg = SQLAllocHandle(SQL_HANDLE_STMT, V_OD_hdbc, &V_OD_hstmt);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Fehler im AllocStatement %ld\n", V_OD_erg);
        SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc,1, V_OD_stat, &V_OD_err, V_OD_msg, 100, &V_OD_mlen);
        printf("%s (%d)\n", V_OD_msg, V_OD_err);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }    
    // 把查询结果和我们定义的变量进行绑定,V_OD_err-存放错误信息编号的变量
    SQLBindCol(V_OD_hstmt, 1, SQL_C_CHAR, &V_OD_name, 32, &v_OD_len);
    SQLBindCol(V_OD_hstmt, 2, SQL_C_CHAR, &V_OD_keyw, 32, &v_OD_len);

    // 查询
    V_OD_erg = SQLExecDirect(V_OD_hstmt, (unsigned char *)"SELECT * from accounts;", SQL_NTS);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Error in Select %ld\n", V_OD_erg);
        SQLGetDiagRec(SQL_HANDLE_DBC, V_OD_hdbc,1, V_OD_stat,&V_OD_err,V_OD_msg,100,&V_OD_mlen);
        printf("%s (%d)\n",V_OD_msg,V_OD_err);
        SQLFreeHandle(SQL_HANDLE_STMT,V_OD_hstmt);
        SQLFreeHandle(SQL_HANDLE_DBC,V_OD_hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }
    // 获取列数,SQLSMALLINT 
    V_OD_erg = SQLNumResultCols(V_OD_hstmt, &V_OD_colanz);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        SQLFreeHandle(SQL_HANDLE_STMT, V_OD_hstmt);
        SQLDisconnect(V_OD_hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, V_OD_hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }
    printf("Number of Columns %d\n",V_OD_colanz);

    // 获取条数,SQLINTEGER
    V_OD_erg = SQLRowCount(V_OD_hstmt, &V_OD_rowanz);
    if ((V_OD_erg != SQL_SUCCESS) && (V_OD_erg != SQL_SUCCESS_WITH_INFO))
    {
        printf("Number of RowCount %ld\n", V_OD_erg); 
        SQLFreeHandle(SQL_HANDLE_STMT, V_OD_hstmt);
        SQLDisconnect(V_OD_hdbc);
        SQLFreeHandle(SQL_HANDLE_DBC, V_OD_hdbc);
        SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
        exit(0);
    }
    printf("Number of Rows %ld\n", V_OD_rowanz);

    // 获得第一条结果也可以用来都下一条 
    V_OD_erg = SQLFetch(V_OD_hstmt); 
    while(V_OD_erg != SQL_NO_DATA)
    {
        printf("Result: %s %s\n", V_OD_name, V_OD_keyw);
        V_OD_erg=SQLFetch(V_OD_hstmt); 
    }

    // 5. 关闭连接和释放句柄:具有一定的顺序性
    // 5.1 释放SQL语句句柄
    SQLFreeHandle(SQL_HANDLE_STMT, V_OD_hstmt);
    // 5.2 关闭数据库连接
    SQLDisconnect(V_OD_hdbc);
    // 5.3 释放连接句柄和ODBC环境句柄
    SQLFreeHandle(SQL_HANDLE_DBC, V_OD_hdbc);
    SQLFreeHandle(SQL_HANDLE_ENV, V_OD_Env);
    return(0);
}

编译命令:

g++ odbctest.c -o odbctest -lodbc

1.3 mysql自己的C API

参考:

①连接:https://blog.csdn.net/m0_67391518/article/details/124290613

②API文档:https://dev.mysql.com/doc/refman/5.7/en/c-api-functions.html

1.3.1 安装和使用:apt-get

1.安装依赖

安装MYSQL数据库:mysql-client和mysql-server

安装MYSQL的开发包:mysql-devel

# 安装MYSQL:https://www.bbsmax.com/A/B0zqqmMGzv/
apt-get install mysql-server
apt-get install mysql-client
# 安装开发包
sudo yum install mysql-devel
sudo apt-get install libmysqld-dev
# 使用
加上<mysql/mysql.h>头文件
# 查看版本
mysql --version
2.使用

使用mysql_init()初始化连接
使用mysql_real_connect()建立一个到mysql数据库的连接
使用mysql_query()执行查询语句
result = mysql_store_result(mysql)获取结果集
mysql_num_fields(result)获取查询的列数,mysql_num_rows(result)获取结果集的行数
通过mysql_fetch_row(result)不断获取下一行,然后循环输出
释放结果集所占内存mysql_free_result(result)
mysql_close(conn)关闭连接

3.示例

代码参考:https://blog.csdn.net/m0_67391518/article/details/124290613

/*************************************************************************
    > File Name: mydb.h
    > Author:
    > Mail:
    > Created Time: 2017年07月21日 星期五 15时17分17秒
 ************************************************************************/

#ifndef _MYDB_H
#define _MYDB_H
#include<iostream>
#include<string>
#include<mysql/mysql.h>
using namespace std;

class MyDB
{
    public:
    MyDB();
    ~MyDB();
    bool initDB(string host,string user,string pwd,string db_name); //连接mysql
    bool exeSQL(string sql);   //执行sql语句
    private:
    MYSQL *mysql;          //连接mysql句柄指针
    MYSQL_RES *result;    //指向查询结果的指针
    MYSQL_ROW row;       //按行返回的查询信息
};


#endif
/*************************************************************************
> File Name: MyDB.cpp
> Author:
> Mail:
> Created Time: 2017年07月21日 星期五 16时02分51秒
************************************************************************/

#include<iostream>
#include<string>
#include "MyDB.h"

using namespace std;

MyDB::MyDB()
{
    mysql=mysql_init(NULL);   //初始化数据库连接变量
    if(mysql==NULL)
    {
        cout<<"Error:"<<mysql_error(mysql);
        exit(1);
    }
}

MyDB::~MyDB()
{
    if(mysql!=NULL)  //关闭数据连接
    {
        mysql_close(mysql);
    }
}


bool MyDB::initDB(string host,string user,string passwd,string db_name)
{
    // 函数mysql_real_connect建立一个数据库连接  
    // 成功返回MYSQL*连接句柄,失败返回NULL  
    mysql = mysql_real_connect(mysql, host.c_str(), user.c_str(), passwd.c_str(), db_name.c_str(), 0, NULL, 0);  
    if(mysql == NULL)  
    {  
        cout << "Error: " << mysql_error(mysql);  
        exit(1);  
    }  
    return true;  
}

bool MyDB::exeSQL(string sql)
{
    //mysql_query()执行成功返回0,执行失败返回非0值。
    if (mysql_query(mysql,sql.c_str()))
    {
        cout<<"Query Error: "<<mysql_error(mysql);
        return false;
    }
    else // 查询成功
    {
        result = mysql_store_result(mysql);  //获取结果集
        if (result)  // 返回了结果集
        {
           int  num_fields = mysql_num_fields(result);   //获取结果集中总共的字段数,即列数
           int  num_rows=mysql_num_rows(result);       //获取结果集中总共的行数
           for(int i=0;i<num_rows;i++) //输出每一行
            {
                //获取下一行数据
                row=mysql_fetch_row(result);
                if(row<0) break;

                for(int j=0;j<num_fields;j++)  //输出每一字段
                {
                    cout<<row[j]<<"		";
                }
                cout<<endl;
            }

        }
        else  // result==NULL
        {
            if(mysql_field_count(mysql) == 0)   //代表执行的是update,insert,delete类的非查询语句
            {
                // (it was not a SELECT)
                int num_rows = mysql_affected_rows(mysql);  //返回update,insert,delete影响的行数
            }
            else // error
            {
                cout<<"Get result error: "<<mysql_error(mysql);
                return false;
            }
        }
    }

    return true;

}

test.cpp

/*************************************************************************
    > File Name: test.cpp
    > Author:
    > Mail:
    > Created Time: 2017年07月21日 星期五 16时43分55秒
 ************************************************************************/

#include<iostream>
#include"MyDB.h"
using namespace std;

int main()
{
    MyDB db; 
    //连接数据库
    db.initDB("localhost","root","fengxin","test");
    //将用户信息添加到数据库
    //db.exeSQL("INSERT accounts values('fengxin','123');");
    //db.exeSQL("INSERT accounts values('axin','456');");
    //将所有用户信息读出,并输出。
    db.exeSQL("SELECT * from accounts;");

    return 0;
}

命令行编译编译

g++ MyDB.cpp test.cpp  -o test    `mysql_config --cflags --libs` 

或Makefile编译

test:test.cpp MyDB.cpp  
    g++  test.cpp MyDB.cpp -o test  `mysql_config --cflags --libs` 
posted @ 2023-04-10 15:39  circlelll  阅读(367)  评论(0)    收藏  举报