- 主题:我们很多同事都是完全不用unique_ptr,一律shared_ptr
发一个,欣赏欣赏。
我也是,读一下表结构,生成模板。
【 在 hgoldfish 的大作中提到: 】
: 你们也太折腾了。
: 要我就写个预处理器读 sql 表结构,让 cmake 在编译之前跑一下。
: 最终上层的代码一定非常漂亮。
: ...................
--
FROM 221.221.52.*
f.ptr,理论上可以,实际上,难。
因为实际上要面对一大堆数据,结构数组,不需要每个记录都有模板,一个模板即可。
所以,模板里,ptr要变成offset。与数据分离,一个模板处理一大堆数据,各个来源各个去处的,不同函数里的。
最早是在informix数据库里的dbview,名字类型长度位置。位置就是ptr,好处是即可以用离散变量又可以用结构。模板数据分离的。
但是太死板。我给改成offset了,立刻天地宽了,可以配合各个数据。处理数组也方便。
用时就是一个指针运算,p+offset。pascal干不来。
【 在 z16166 的大作中提到: 】
: f.ptr就是绑定的字段成员的地址啊
: f.name是字段的名字
: cout那些打印是可以无视的,那只是演示怎么用那些数据。
: ...................
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
哦,你给引申了,有那么点意思。
【 在 z16166 的大作中提到: 】
: 你这就是TLV(type + length + value),以前你说过的
:
--
FROM 221.221.52.*
外部给的一切数据,都是数据,不会成为sql语句的一部分。
绑定变量的原理你要了解一下。
【 在 z16166 的大作中提到: 】
: sql注入就是外部给的畸形数据导致的。用字符串拼接出sql语句,是大忌
: 不过拼接sql语句的代码,改成调用数据库提供的prepared statement API,也很方便。
: 你之前说的绑定,就是把f.name、f.ptr喂给prepared statement?
: ...................
--
FROM 221.221.52.*
所以,即使外部给了一个歪sql,也只会被当做数据存进数据库,绝不可能成为语句被执行。
我的这个中间件全名叫SDBC,安全数据库连接,哪能给你这个漏子。
所以,这个模板系统,是兼顾数据库与网络通信的,有DB,有CONNECT。
既要序列化反序列化,又要做SQL。
protobuf只做前者。
【 在 z16166 的大作中提到: 】
: 知道啊,prepared statement的bind操作
:
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
绑定变量的意思是,语句是语句,变量是变量,在语句里只有占位符,没有数据。就是说,任何数据不会成为语句,这就彻底杜绝了sql注入。
一般人写数据库程序,懒得用绑定,都是把值写在sql语句里,这就会造成SQL注入。这样的sql不仅不安全而且效率低。
在数据库引擎里,有个语句解析器,把一个sql语句的文本解析成执行计划。如果遇到相同语句,就会避免解析,直接引用执行计划,叫做软解析,这可以提高执行效率。直接写值,造成每个sql都不同,无法进行软解析。
还有游标,有人认为游标开销大。但是必须要先打开游标才能绑定变量(这时sql语句已经没用了)。就是打开游标时有开销。
如果在后续操作中保持打开状态,就可以无代价的连续操作。
数据库包装器帮你做了这一切,所以省事,高效。
之所以可行,就是靠模板,生成sql语句,开游标,绑定变量,,,,,全靠模板。
全套泛型操作,都是运行时泛型,编辑器期没法办。程序规模比较大,而且编译时不知道将来来的是啥数据。
运行时泛型,操作的全是指针。
【 在 z16166 的大作中提到: 】
: 知道啊,prepared statement的bind操作
:
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
再来看看DAU_insert是干啥的:
经过环境检查后,调用:
int bind_ins(register DAU *DP,char *buf)
{
int ret=0;
register char *p;
p=buf;
if(DP->ins_sth==SQLO_STH_INIT) {//游标未打开,只要不执行DAU_free,游标就不会被关闭。
生成语句,干一大堆活;
}
//bind
DP->srm.hint=0;
DP->tail=p;
BB_Tree_Scan(DP->bt_ins,bind_proc);//绑定变量,泛型,遍历二叉树,第二个参数是回调函数。
//exec
DP->SQL_Connect->Errno = 0;
if(0!=(ret = sqlo_execute(DP->ins_sth, 1))) { //执行插入
错误处理;
}
//收集RETURNING变量值
BB_Tree_Scan(DP->bt_ins,bind_returning);
return ret;
}
【 在 ylh1969 的大作中提到: 】
: 绑定变量的意思是,语句是语句,变量是变量,在语句里只有占位符,没有数据。就是说,任何数据不会成为语句,这就彻底杜绝了sql注入。
: 一般人写数据库程序,懒得用绑定,都是把值写在sql语句里,这就会造成SQL注入。这样的sql不仅不安全而且效率低。
: 在数据库引擎里,有个语句解析器,把一个sql语句的文本解析成执行计划。如果遇到相同语句,就会避免解析,直接引用执行计划,叫做软解析,这可以提高执行效率。直接写值,造成每个sql都不同,无法进行软解析。
: ...................
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
T是由数据库的表结构自动生成的。也可以写meta,再转换成C源码。也可以手写。
类型是数据库定义的,按照编译器的规则形成的,类型错不了。
//这些结构用来测试各种数据的边界对齐规则,不要猜,要让编译器自己说。
typedef struct {
char a;
int b;
} align;
typedef struct {
char a;
double b;
} dalign;
typedef struct {
char a;
long double b;
} dfali;
typedef struct {
char a;
INT63+1 b;
} ll_ali;
typedef struct {
char a;
long b;
} l_ali;
产生的struct成员边界都错不了。
【 在 z16166 的大作中提到: 】
: 这种TLV应该是没有编译期的类型检查,T是手动填写的,不是编译器自动推导出来的。
:
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
哈,说对了。
还要回答大家一个问题,泛型编程效率高吗?是不是因为需要一些语句的组装和类型的判断就牺牲了一定的性能呢?下面举个例子,这是一个可以加载任何单表的程序,为了逻辑清晰,略去了错误处理:
int loadfile(T_SQL_Connect *SQL_Connect,char *tablename,FILE *ifd,
FILE *ofd,int Pflg,char *buf,int buflen,char dlmt)
{
char *p,tabn[512];
DAU _DAU;
int rows,ret;
int upd,loss;
int num;
INT64 now;
ret=DAU_init(&_DAU,SQL_Connect,tablename,0,0);//依据tablename,从数据库建立模板
upd=loss=0;
num=0;
now=now_usec();
for(rows=0;!ferror(ifd);rows++) {
fgets(buf,buflen,ifd);
if(feof(ifd)) break;
if(dlmt=='J') {//JSON格式
JSON_OBJECT json=json_tokener_parse(buf);
DAU_fromJSON(&_DAU,json); //泛型,JSON反序列化到DAU里边的struct。
json_object_put(json);
} else {//CSV格式
ret=DAU_pkg_dispack(&_DAU,buf,dlmt);//泛型,CSV反序列化到DAU里边的struct
}
*buf=0;
ret=DAU_insert(&_DAU,buf);//虽然在循环体里,但只有第一次会生成语句,打开游标,绑定变量。之后,只进行bind和exec,所以效率是非常高的。
。。。。。。。。。
}
【 在 z16166 的大作中提到: 】
: 现阶段反射的实现,处理meta信息,只能是要么用宏,要么用预处理了
: 用预处理的库也有现成的,比如开源的ODB,支持ORM
: ylh1969老哥说的是他在pure C时代的实现,他跟我们讨论C++的实现那是财务自由退休了放不下技术讨论着玩
: ...................
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*
89楼的那个回调函数,绑定变量操作,不过是一堆switch....case..
效率不会太低吧!
static int bind_proc(void *content) //bind处理
{
register struct bindnod *bp;
T_PkgType *tp;
char *p,*bindp;
int ret,n;
short nf;
bp=(struct bindnod *)content;
p=*bp->tail;
tp=bp->tp;
bindp=*(bp->rec)+tp->offset;//要绑定的位置
//bind NULL
nf=isnull(bindp,tp->type)?-1:0;
if(1&bp->flg) bp->flg=!(nf ^ bp->ind); //如果NULL状态有变化,清空免bind标志
bp->ind=nf;
ret=0;
if(!(1&bp->flg)) {
//ShowLog(5,"bind_proc sth=%d,%s:%d",*bp->sth,tp->name,bp->bindnum);
switch(tp->type) {
#ifdef SDBC_PTR_63+1
case CH_INT63+1:
#endif
case CH_LONG:
case CH_TINY:
case CH_SHORT:
case CH_INT:
ret=sqlo_bind_by_pos(*bp->sth, bp->bindnum, SQLOT_INT, bindp, tp->len, &bp->ind, 0);
bp->flg=1; //下次免bind
break;
case CH_FLOAT:
case CH_DOUBLE:
ret=sqlo_bind_by_pos(*bp->sth, bp->bindnum, SQLOT_FLT, bindp, tp->len, &bp->ind, 0);
bp->flg=1; //下次免bind
break;
case CH_BYTE:
ret=sqlo_bind_by_pos(*bp->sth, bp->bindnum, SQLOT_BIN, bindp, tp->len, &bp->ind, 0);
bp->flg=1; //下次免bind
break;
case CH_CHAR:
case CH_CNUM:
case CH_DATE:
ret=sqlo_bind_by_pos(*bp->sth, bp->bindnum, SQLOT_STR, bindp, tp->len, &bp->ind, 0);
bp->flg=1; //下次免bind
break;
default: //类型与ORACLE不符,需变换
bindp=p;
if(tp->bindtype & RETURNING) { //bind RETURNING
n=40;
*bindp=0;
p=bindp+n; //CH_JUL...CH_USEC,.....
bp->flg=2;
} else {
p+=get_one_str(p,*bp->rec,tp,0);
n=strlen(bindp)+1;
if(n<40) n=40;
bp->flg=0;
}
ret=0;
if(bindp != bp->last_bindp) {
ret=sqlo_bind_by_pos(*bp->sth, bp->bindnum, SQLOT_STR, bindp,n,&bp->ind, 0);
bp->last_bindp=bindp;
}
*(++p)=0;
break;
}
}
*bp->tail=p;
return 0;
}
【 在 ylh1969 的大作中提到: 】
: 再来看看DAU_insert是干啥的:
: 经过环境检查后,调用:
: int bind_ins(register DAU *DP,char *buf)
: ...................
--
修改:ylh1969 FROM 221.221.52.*
FROM 221.221.52.*