06-数据库(dbm、MySQL)

数据库

dbm 数据库

Linux自带的适合存储比较静态的索引化数据。适用于被频繁访问但很少修改的数据,创建慢,检索快。dbm数据库存在不同的版本,有两套使用接口分为由X/Open组织标准化的ndbm和原始的gdbm。

安装

apt install libgdbm-dev

使用

不同Linux发行版编译时的包含库与链接库略有不同。默认支持ndbm兼容模式,如Red Hat:

  1. C源文件包含头文件 ndbm.h
  2. 使用编译选项 -I /usr/include/gdbm 包含头文件目录 /usr/include/gdbm
  3. 使用编译选项 -lgdbm 链接 gdbm 库

但已经安装了gdbm但需要使用ndbm兼容模式时,需要明确指示,如 Ubuntu SUSE:

  1. C源文件包含 gdbm.h 而不是 ndbm.h
  2. 使用编译项 -I /usr/include/gdbm 包含头文件目录
  3. 使用编译项 -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);
    }
posted @ 2022-06-16 09:40  某某人8265  阅读(257)  评论(0编辑  收藏  举报