一、源代码说明
freecplus是一个Linux系统下的C/C++开源框架,源代码请前往C语言技术网(www.freecplus.net )下载。
本文介绍的是freecplus框架中采用connection和sqlstatement类操作PostgreSQL数据库。
类的声明文件是freecplus/db/postgresql/_postgresql.h。
类的定义文件是freecplus/db/postgresql/_postgresql.cpp。
示例程序位于freecplus/db/postgresql目录中。
编译规则文件是freecplus/db/postgresql/makefile。
二、概述
本文不会介绍PostgreSQL数据库、SQL语言和C/C的基础知识,您应该是一个职业的C/C 程序员,在阅读本文之前,您已经掌握了PostgreSQL数据库和SQL语言的基础知识。
freecplus框架把PostgreSQL提供的库函数封装成了connection和sqlstatement类,采用封装后的类操作PostgreSQL数据库,代码简洁优雅,性能卓越。
接下来我先列出connection和sqlstatement类的声明,然后通过流程图和示例程序介绍它位的用法。
三、connection类
PostgreSQL数据库连接connection类的声明(程序员不必关心的私有成员和数据结构未列出):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 class connection { public : int m_state; CDA_DEF m_cda; char m_sql[10241 ]; connection (); ~connection (); int connecttodb (char *connstr,char *charset,unsigned int autocommitopt=0 ) ; int commit () ; int rollback () ; int disconnect () ; int execute (const char *fmt,...) ; };
四、sqlstatement类
PostgreSQL数据库的SQL语句操作sqlstatement类的声明(程序员不必关心的私有成员和数据结构未列出):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 class sqlstatement { public : int m_state; char m_sql[10241 ]; CDA_DEF m_cda; sqlstatement (); sqlstatement (connection *conn); ~sqlstatement (); int connect (connection *conn) ; int disconnect () ; int prepare (const char *fmt,...) ; int bindin (unsigned int position,int *value) ; int bindin (unsigned int position,long *value) ; int bindin (unsigned int position,unsigned int *value) ; int bindin (unsigned int position,unsigned long *value) ; int bindin (unsigned int position,float *value) ; int bindin (unsigned int position,double *value) ; int bindin (unsigned int position,char *value,unsigned int len) ; int bindout (unsigned int position,int *value) ; int bindout (unsigned int position,long *value) ; int bindout (unsigned int position,unsigned int *value) ; int bindout (unsigned int position,unsigned long *value) ; int bindout (unsigned int position,float *value) ; int bindout (unsigned int position,double *value) ; int bindout (unsigned int position,char *value,unsigned int len) ; int execute () ; int execute (const char *fmt,...) ; int next () ; };
五、程序流程
freecplus框架把对PostgreSQL数据库操作的SQL语句分为两种:有结果集的SQL语句和无结果集的SQL语句。
如果SQL语句被执行后,有结果集的产生,称为有结果集的SQL,即数据查询语言DQL,以select关键字,各种简单查询,连接查询等都属于DQL。
如果SQL语句被执行后,没有结果集的产生,称为无结果集的SQL,包括数据定义语言DDL(主要是create、drop和alter)和数据操纵语言DML(insert、update和insert)。
也可以这么说,查询的SQL语句会产生结果集,其它的SQL语句不会产生结果集。
1、无结果集SQL的程序的流程
这是一个完程的流程,在实际开发中,如果是执行简单的SQL语句,第6步和第7步可能不需要,如果SQL语句只执行一次,第7步和第8步之间的循环也不需要。
2、有结果集SQL的程序的流程
这是一个完程的流程,在实际开发中,如果是执行简单的查询语句,第6步、第7步和第8步可能不需要,如果结果集中最多只有一条记录,第10步和第11步之间的循环也不需要。
六、示例程序
1、创建超女信息表
示例(createtable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include "_postgresql.h" int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; stmt.prepare ("\ create table girls(id int,\ name varchar(30),\ weight numeric(8,2),\ btime timestamp,\ memo text,\ pic bytea,\ primary key (id))" ); if (stmt.execute () != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } conn.commit (); printf ("create table girls ok.\n" ); }
运行效果
2、向超女表中插入5条记录
示例(inserttable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include "_postgresql.h" struct st_girls { long id; char name[11 ]; double weight; char btime[20 ]; } stgirls; int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; stmt.prepare ("\ insert into girls(id,name,weight,btime) \ values(:1,:2,:3,to_date(:4,'yyyy-mm-dd hh24:mi:ss'))" ); stmt.bindin (1 ,&stgirls.id); stmt.bindin (2 , stgirls.name,10 ); stmt.bindin (3 ,&stgirls.weight); stmt.bindin (4 , stgirls.btime,19 ); for (int ii=1 ;ii<=5 ;ii++) { memset (&stgirls,0 ,sizeof (stgirls)); stgirls.id=ii; sprintf (stgirls.name,"超女%02d" ,ii); stgirls.weight=ii*2.11 ; strcpy (stgirls.btime,"2018-03-01 12:25:31" ); if (stmt.execute () != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } printf ("成功插入了%ld条记录。\n" ,stmt.m_cda.rpc); } printf ("insert table girls ok.\n" ); conn.commit (); }
运行效果
3、更新超女表中的记录
示例(updatetable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include "_postgresql.h" int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; char strbtime[20 ]; memset (strbtime,0 ,sizeof (strbtime)); strcpy (strbtime,"2019-12-20 09:45:30" ); stmt.prepare ("\ update girls set btime=to_date(:1,'yyyy-mm-dd hh24:mi:ss') where id>=2 and id<=4" ); stmt.bindin (1 ,strbtime,19 ); if (stmt.execute () != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } printf ("本次更新了girls表%ld条记录。\n" ,stmt.m_cda.rpc); conn.commit (); }
运行效果
4、查询超女表中的记录
示例(selecttable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include "_postgresql.h" struct st_girls { long id; char name[31 ]; double weight; char btime[20 ]; } stgirls; int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; int iminid,imaxid; stmt.prepare ("\ select id,name,weight,to_char(btime,'yyyy-mm-dd hh24:mi:ss') from girls where id>=:1 and id<=:2" ); stmt.bindin (1 ,&iminid); stmt.bindin (2 ,&imaxid); stmt.bindout (1 ,&stgirls.id); stmt.bindout (2 , stgirls.name,30 ); stmt.bindout (3 ,&stgirls.weight); stmt.bindout (4 , stgirls.btime,19 ); iminid=2 ; imaxid=4 ; if (stmt.execute () != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } while (1 ) { memset (&stgirls,0 ,sizeof (stgirls)); if (stmt.next () !=0 ) break ; printf ("id=%ld,name=%s,weight=%.02f,btime=%s\n" ,stgirls.id,stgirls.name,stgirls.weight,stgirls.btime); } printf ("本次查询了girls表%ld条记录。\n" ,stmt.m_cda.rpc); }
运行效果
5、查询超女表中的记录数
示例(counttable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include "_postgresql.h" int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; int icount=0 ; stmt.prepare ("select count(*) from girls where id>=2 and id<=4" ); stmt.bindout (1 ,&icount); if (stmt.execute () != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } stmt.next (); printf ("girls表中符合条件的记录数是%d。\n" ,icount); }
运行效果
7、删除超女表中的记录
示例(deletetable.cpp)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include "_postgresql.h" int main (int argc,char *argv[]) { connection conn; if (conn.connecttodb ("host=172.16.0.15 user=postgres password=pwdidc dbname=postgres port=5432" ,"gbk" )!=0 ) { printf ("connect database failed.\n%s\n" ,conn.m_cda.message); return -1 ; } sqlstatement stmt (&conn) ; if (stmt.execute ("delete from girls where id>=2 and id<=4" ) != 0 ) { printf ("stmt.execute() failed.\n%s\n%s\n" ,stmt.m_sql,stmt.m_cda.message); return -1 ; } printf ("本次从girls表中删除了%ld条记录。\n" ,stmt.m_cda.rpc); conn.commit (); }
运行效果
8、与Oracle的兼容性处理
在封装sqlstatement类的时候,为了与Oracle兼容,做了以下方面的处理:
1)在PostgreSQL中,绑定输入和输出变量采用的是"$“,Oracle采用的是”:n"(n表示变量的序号),在sqlstatement的prepare方法中,把":n"替换成了"$"。
2)在PostgreSQL中,把字符串输换为日期时间的函数是to_timestamp,Oracle是to_date,在sqlstatement的prepare方法中,把to_date替换成to_timestamp。
4)PostgreSQL的sqlstatement类绑定输入或输出变量的最大数量缺省是256,在"_mysql.h"头文件中定义了MAXPARAMS宏,您可以根据实际需求修改它。
5)sqlstatement类绑定输入或输出变量时,如果是字符串,最大长度缺省是2000,在"_postgresql.h"头文件中定义了MAXFIELDLENGTH宏,您可以根据实际需求修改它。
1 2 #define MAXFIELDLENGTH 2000
9、text和bytea字段的操作
PostgreSQL提供的库函数支持对text和bytea字段的操作,本人的技术水平有限,找不到这方面的资料和示例程序,所以还没有封装对text和bytea字段的操作,希望各位能提供技术帮助,通过C语言技术网与我联系,我们共同完善freecplus框架,非常感谢。
七、应用经验
本文提供的示例程序看上去简单,实则很精妙,希望大家多多思考,慢慢体会。
为了让大家完全掌握connection和sqlstatement类的用法,我将录制freecplus框架的专题视频,请大家多关注C语言技术网(www.freecplus.net )发布的内容。
八、版权声明
C语言技术网原创文章,转载请说明文章的来源、作者和原文的链接。
来源:C语言技术网(www.freecplus.net )
作者:码农有道