06-数据库(dbm、MySQL)
数据库
dbm 数据库
Linux自带的适合存储比较静态的索引化数据。适用于被频繁访问但很少修改的数据,创建慢,检索快。dbm数据库存在不同的版本,有两套使用接口分为由X/Open组织标准化的ndbm和原始的gdbm。
安装
apt install libgdbm-dev
使用
不同Linux发行版编译时的包含库与链接库略有不同。默认支持ndbm兼容模式,如Red Hat:
- C源文件包含头文件 ndbm.h
- 使用编译选项 -I /usr/include/gdbm 包含头文件目录 /usr/include/gdbm
- 使用编译选项 -lgdbm 链接 gdbm 库
但已经安装了gdbm但需要使用ndbm兼容模式时,需要明确指示,如 Ubuntu SUSE:
- C源文件包含 gdbm.h 而不是 ndbm.h
- 使用编译项 -I /usr/include/gdbm 包含头文件目录
- 使用编译项 -l gdbm_compat -lgdbm 链接其他的gdbm兼容库
一般默认使用第一种
dbm 例程
dbm 基本元素是要存储的数据块与关联的检索数据块。对于存储数据库要有唯一关键字。规范对关键字/数据对的长度限制为1023字节,但具体实现不会限制。ndbm.h 中定义了一个名为datum的新数据类型
/* The data and key structure. */
typedef struct
{
char *dptr;
int dsize;
} datum;
dbm 类型与FILE类型类似,打开dbm数据库时通常创建两个物理文件,后缀为pag与dir,并返回一个dbm指针用于访问文件。dbm中没有关系数据库中的表结构,它工作在非结构化的二进制数据块上。
dbm 访问函数
ubuntu 下gdbm接口
#include <gdbm.h>
extern GDBM_FILE gdbm_fd_open (
int fd,
const char *file_name,
int block_size,
int flags,
void (*fatal_func) (const char *)
);
// 执行成功则创建 数据文件pathname.dat 和 索引文件pathname.idx
extern GDBM_FILE gdbm_open (
const char *,
int openMode,
int createFileMode,
int,
void (*)(const char *)
);
// 关闭文件并释放使用的内存
extern void gdbm_close (GDBM_FILE);
// 存入记录,flag为 DB_INSERT DB_REPLACE DB_STORE
extern int gdbm_store (GDBM_FILE, datum key, datum data, int flag);
// 通过 key 获取记录
extern datum gdbm_fetch (GDBM_FILE, datum);
extern int gdbm_delete (GDBM_FILE, datum);
extern datum gdbm_firstkey (GDBM_FILE);
extern datum gdbm_nextkey (GDBM_FILE, datum);
extern int gdbm_reorganize (GDBM_FILE);
extern void gdbm_sync (GDBM_FILE);
extern int gdbm_exists (GDBM_FILE, datum);
extern int gdbm_setopt (GDBM_FILE, int, void *, int);
extern int gdbm_fdesc (GDBM_FILE);
extern int gdbm_export (GDBM_FILE, const char *, int, int);
extern int gdbm_export_to_file (GDBM_FILE dbf, FILE *fp);
extern int gdbm_import (GDBM_FILE, const char *, int);
extern int gdbm_import_from_file (GDBM_FILE dbf, FILE *fp, int flag);
extern int gdbm_count (GDBM_FILE dbf, gdbm_count_t *pcount);
统一的 ndbm 接口
#include <ndbm.h>
/*
filename 只是基本文件名,没有后缀
file_open_flags 控制数据库读写权限,与O_CREAT进行二进制或才能创建
file_mode 被创建文件的初始权限
*/
DBM *dbm_open(const char *filename, int file_open_flags, mode_t file_mode);
/*
store_mode 控制当试图以一个已有的关键字来存储数据时发生的情况,
dbm_insert 操作失败返回 1
dbm_replace 覆盖已有数据返回 0
其他错误返回负数
*/
int dbm_store(DBM *database_desc, datum key, datum content, int store_mode);
/*
没有找到数据时返回datem的值被设为null,找到数据时指针指向的是dbm库的本地存储空间中的数据。基于调用前要把数据复制到程序的变量中才行。
*/
datem dbm_fetch(DBM *database_desc, datem key);
void dbm_close(DBM *database_desc);
MySQL
连接
#include <mysql/mysql.h>
// 传递NULL进入,获得初始化连接句柄。传递一个已有的结构,它被重新初始化。
MYSQL *mysql_init(MYSQL *);
/* 只能在 mysql_init 与 mysql_real_connect 之间调用
enum mysql_option 的常见选项
MYSQL_OPT_CONNECT_TIMEOUT 超时秒数
MYSQL_OPT_COMPRESS 网络连接中压缩机制
MYSQL_INIT_COMMAND 每次建立连接后发送的命令
第三个参数要强行转为规定类型
*/
int STDCALL mysql_options(MYSQL *mysql,enum mysql_option option, const void *arg);
// 无法连接返回NULL,mysql_error 可提供信息
MYSQL * STDCALL mysql_real_connect(MYSQL *mysql, const char *host,
const char *user,
const char *passwd,
const char *db,
unsigned int port, // 为0使用默认值
const char *unix_socket, // 为NULL使用默认socket
unsigned long clientflag);
void mysql_close(MYSQL *connection);
/* 除了错误检查每次执行命令都会更新错误码
mysql_errno 返回错误码,其在 errmsg.h 或 mysqld_error.h 中定义
*/
unsigned int STDCALL mysql_errno(MYSQL *mysql);
// 返回文本信息,保存在静态空间中,若要保存需要把它复制到别的地方
const char * STDCALL mysql_error(MYSQL *mysql);
// gcc -I/usr/include/mysql main.c -L/usr/lib/mysql -lmysqlclient -o connect
#include <errno.h>
#include <mysql/mysql.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
MYSQL *conn;
MYSQL_RES *res_ptr;
int res;
MYSQL_ROW sqlrow;
// 初始化
conn = mysql_init(NULL);
if (conn == NULL) {
printf("init failed\n");
return 1;
}
// 连接
conn = mysql_real_connect(conn, "127.0.0.1", "root", "123", "test", 0, NULL, 0);
if (conn) {
printf("Connection success\n");
} else {
fprintf(stderr, "connect failed %d : %s\n", mysql_errno(conn), mysql_error(conn));
printf("Connection failed\n");
}
// 插入数据
res = mysql_query(conn, "INSERT INTO children(fname, age) VALUE('Ann', 3)");
if (res) {
printf("Insert error %d : %s\n", mysql_errno(conn), mysql_error(conn));
}
// 执行查询
res = mysql_query(conn, "SELECT LAST_INSERT_ID()");
if (res) {
printf("SELECT erro: %s\n", mysql_error(conn));
} else {
// 提取数据
res_ptr = mysql_use_result(conn);
if (res_ptr) {
// 处理数据
while ((sqlrow = mysql_fetch_row(res_ptr))) {
printf("insert childno: %s\n", sqlrow[0]);
}
}
// 释放资源
mysql_free_result(res_ptr);
}
mysql_close(conn);
return 0;
}
执行 sql 语句
int mysql_query(MYSQL *mysql, const char *q);
// 返回影响的行数,0表示没有收到影响,正数时实际的结果
my_ulonglong mysql_affected_rows(MYSQL *mysql);
/*
提取数据的步骤:
1. 执行查询
使用 mysql_query 发送SQL语句
2. 提取数据
用 mysql_store_result (一次调用提取所有数据) 或 mysql_use_result (一次提取一行) 提取数据
3. 处理数据
mysql_fetch_row 处理数据
4. 必要的清理工作
mysql_free_result 释放资源
*/
MYSQL_RES *mysql_store_result(MYSQL *mysql); // 保存返回的所有数据
my_ulonglong mysql_num_rows(MYSQL_RES *res); // 得到返回的行数
MYSQL_ROW mysql_fetch_row(MYSQL_RES *result); // 从结果中获取一行并放入行结构体
void mysql_data_seek(MYSQL_RES *result, my_ulonglong offset); // 在结果集中跳转,offset是行号,从0开始
MYSQL_ROW_OFFSET STDCALL mysql_row_tell(MYSQL_RES *res); // 在结果集中的当前位置,不是行号
MYSQL_ROW_OFFSET STDCALL mysql_row_seek(MYSQL_RES *result, MYSQL_ROW_OFFSET offset); // 它返回旧的位置,跳转到新的位置,可由 mysql_row_tell 获得
void mysql_free_result(MYSQL_RES *result);
// 因为一次只获取一条信息,mysql_data_seek mysql_num_rows
MYSQL_RES *mysql_use_result(MYSQL *mysql);
处理返回数据
提取到数据行后处理实际数据
unsigned int mysql_field_count(MYSQL *mysql); // 获取元数据个数,即列数
// 下一列
MYSQL_FIELD *mysql_fetch_field(MYSQL_RES *result);
// 覆盖当前字段编号,传递0,跳回到第一列
MYSQL_FIELD_OFFSET STDCALL mysql_field_seek(MYSQL_RES *result, MYSQL_FIELD_OFFSET offset);
typedef struct st_mysql_field {
char *name; /* Name of column */
char *org_name; /* Original column name, if an alias */
char *table; /* Table of column if column was a field */
char *org_table; /* Org table name, if table was an alias */
char *db; /* Database for table */
char *catalog; /* Catalog for table */
char *def; /* Default value (set by mysql_list_fields) */
unsigned long length; /* Width of column (create length) */
unsigned long max_length; /* Max width for selected set */
unsigned int name_length;
unsigned int org_name_length;
unsigned int table_length;
unsigned int org_table_length;
unsigned int db_length;
unsigned int catalog_length;
unsigned int def_length;
unsigned int flags; /* Div flags */
unsigned int decimals; /* Number of decimals in field */
unsigned int charsetnr; /* Character set */
enum enum_field_types type; /* Type of field. See mysql_com.h for types */
void *extension;
} MYSQL_FIELD;
// 执行查询
res = mysql_query(conn, "SELECT * FROM table");
if (res) {
printf("SELECT erro: %s\n", mysql_error(conn));
} else {
// 提取数据
res_ptr = mysql_use_result(conn);
if (res_ptr) {
// 处理数据,获取每一行
while ((sqlrow = mysql_fetch_row(res_ptr))) {
// 获取每一列
for (unsigned int field_count; field_count < mysql_field_count(conn); field_count++) {
printf("%s ", sqlrow[field_count]);
}
}
}
// 释放资源
mysql_free_result(res_ptr);
}