spellsql 高性能sql拼接器

xuesongtao · 2022-01-12 09:32:51 · 2725 次点击    
这是一个分享于 2022-01-12 09:32:51 的资源,其中的信息可能已经有所发展或是发生改变。

1. 介绍

  • 通过 sync.Pool, strings.Builder 等实现的较高性能 sql 拼接工具, 如果使用 orm 的此项目不满足使用(但还是希望大佬阅读, 指正下问题😃)
  • 自动打印 sql 最终的 log
  • 非法字符会自动转义,防注入
  • 安装:
      go get -u gitee.com/xuesongtao/spellsql
    

2. 占位符

  • 目前支持占位符 ?, ?d, ?v, 说明如下:
    2.1 占位符 ?
  • 直接根据 args 中类型来自动推动 arg 的类型, 使用如下: 1.第一种用法: 根据 args 中类型来自动推动 arg 的类型

          如: NewCacheSql("SELECT username, password FROM sys_user WHERE username = ? AND password = ?", "test", 123).GetSqlStr()
          => SELECT username, password FROM sys_user WHERE username = "test" AND password = 123
    

    2.第二种用法: 当 arg 为 []int, 暂时支持 []int, []int32, []int64

          如: NewCacheSql("SELECT username, password FROM sys_user WHERE id IN (?)", []int{1, 2, 3}).GetSqlStr()
          => SELECT username, password FROM sys_user WHERE id IN (1,2,3)
    
2.2 占位符 ?d
  • 只会把数字型的字符串转为数字型, 如果是字母的话会被转义为 0, 如: "123" => 123; []string{"1", "2", "3"} => 1,2,3, 如下: 第一种用法: 当 arg 为字符串时, 又想不加双引号就用这个

          如: NewCacheSql("SELECT username, password FROM sys_user WHERE id = ?d", "123").GetSqlStr()
          => SELECT username, password FROM sys_user WHERE id = 123
    

    第二种用法: 当 arg 为 []string, 又想把解析后的单个元素不加引号

          如: NewCacheSql("SELECT username, password FROM sys_user WHERE id IN (?d)", []string{"1", "2", "3"}).GetSqlStr()
          => SELECT username, password FROM sys_user WHERE id IN (1,2,3)
    
2.3 占位符为: ?v
  • 这样会让字符串类型不加引号, 原样输出, 如: "test" => test; 第一种用法: 当 arg 为字符串时, 又想不加双引号就用这个, 注: 只支持 arg 为字符串类型

          如: NewCacheSql("SELECT username, password FROM ?v WHERE id = ?d", "sys_user", "123").GetSqlStr()
          => SELECT username, password FROM sys_user WHERE id = 123
    

    第二种用法: 子查询

          如: NewCacheSql("SELECT u.username, u.password FROM sys_user su LEFT JOIN user u ON su.id = u.id WHERE u.id IN (?v)", FmtSqlStr("SELECT id FROM user WHERE name=?", "test").GetSqlStr()
          => SELECT u.username, u.password FROM sys_user su LEFT JOIN user u ON su.id = u.id WHERE u.id IN (SELECT id FROM user WHERE name="test");
    
  • 注: 由于这种不会进行转义处理, 所有这种不推荐用于请求输入(外部非法输入)的内容, 会出现 SQL 注入风险; 当我们明确知道参数是干什么的可以使用会简化我们代码, 这里就不进行演示.

3. 使用

  • 可以参考 getsqlstr_test.go 里的测试方法

    3.1 新增
      s := NewCacheSql("INSERT INTO sys_user (username, password, name)")
      s.SetInsertValues("xuesongtao", "123456", "阿桃")
      s.SetInsertValues("xuesongtao", "123456", "阿桃")
      s.GetSqlStr()
    
      // Output:
      // INSERT INTO sys_user (username, password, name) VALUES ("test", 123456, "阿涛"), ("xuesongtao", "123456", "阿桃"), ("xuesongtao", "123456", "阿桃");
    
3.2 删除
    s := NewCacheSql("DELETE FROM sys_user WHERE id = ?", 123)
    if true {
        s.SetWhere("name", "test")
    }
    s.GetSqlStr()

    // Output:
    // DELETE FROM sys_user WHERE id = 123 AND name = "test";
3.3 查询
    s := NewCacheSql("SELECT * FROM user u LEFT JOIN role r ON u.id = r.user_id")
    s.SetOrWhere("u.name", "xue")
    s.SetOrWhereArgs("(r.id IN (?d))", []string{"1", "2"})
    s.SetWhere("u.age", ">", 20)
    s.SetWhereArgs("u.addr = ?", "南部")
    s.GetTotalSqlStr()
    s.SetLimit(1, 10)
    s.GetSqlStr()

    // Output:
    // sqlTotalStr: SELECT COUNT(*) FROM user u LEFT JOIN role r ON u.id = r.user_id WHERE u.name = "xue" OR (r.id IN (1,2)) AND u.age > 20 AND u.addr = "南部";
    // sqlStr: SELECT * FROM user u LEFT JOIN role r ON u.id = r.user_id WHERE u.name = "xue" OR (r.id IN (1,2)) AND u.age > 20 AND u.addr = "南部" LIMIT 0, 10;
3.4 修改
    s := NewCacheSql("UPDATE sys_user SET")
    idsStr := []string{"1", "2", "3", "4", "5"}
    s.SetUpdateValue("name", "xue")
    s.SetUpdateValueArgs("age = ?, score = ?", 18, 90.5)
    s.SetWhereArgs("id IN (?d) AND name = ?", idsStr, "tao")
    s.GetSqlStr()

    // Output:
    // UPDATE sys_user SET name = "xue", age = 18, score = 90.50 WHERE id IN (1,2,3,4,5) AND name = "tao";
3.5 复用
    1. NewCacheSql() 获取的对象在调用 GetSqlStr() 后会重置并放入内存池, 是不能对结果进行再进行 GetSqlStr(), 当然你是可以对结果作为 NewCacheSql() 的入参进行使用以此达到复用, 这样代码看起来不是多优雅, 分页处理案例如下:

      sqlObj := NewCacheSql("SELECT * FROM user_info WHERE status = 1")
      handleFn := func(obj *SqlStrObj, page, size int32) {
         // 业务代码
         fmt.Println(obj.SetLimit(page, size).SetPrintLog(false).GetSqlStr())
      }
      
      // 每次同步大小
      var (
         totalNum  int32 = 30
         page      int32 = 1
         size      int32 = 10
         totalPage int32 = int32(math.Ceil(float64(totalNum / size)))
      )
      sqlStr := sqlObj.SetPrintLog(false).GetSqlStr("", "")
      for page <= totalPage {
         handleFn(NewCacheSql(sqlStr), page, size)
         page++
      }
      
      // Output:
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 0, 10;
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 10, 10;
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 20, 10;
      
  • NewSql() 的产生的对象不会放入内存池, 可以进行多次调用 GetSqlStr(), 对应上面的示例可以使用 NewSql() 再调用 Clone() 进行处理, 如下:

      sqlObj := NewSql("SELECT u_name, phone, account_id FROM user_info WHERE u_status = 1")
      handleFn := func(obj *SqlStrObj, page, size int32) {
          // 业务代码
          fmt.Println(obj.SetLimit(page, size).SetPrintLog(false).GetSqlStr())
      }
    
      // 每次同步大小
      var (
          totalNum  int32 = 30
          page      int32 = 1
          size      int32 = 10
          totalPage int32 = int32(math.Ceil(float64(totalNum / size)))
      )
      for page <= totalPage {
          handleFn(sqlObj.Clone(), page, size)
          page++
      }
    
      // Output:
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 0, 10;
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 10, 10;
      // SELECT * FROM user_info WHERE u_status = 1 LIMIT 20, 10;
    

其他

  • 欢迎大佬们指正, 希望大佬给 star, to gitee

入群交流(和以上内容无关):加入Go大咖交流群,或添加微信:liuxiaoyan-s 备注:入群;或加QQ群:692541889

2725 次点击  ∙  1 赞  
加入收藏 微博
4 回复  |  直到
暂无回复
添加一条新回复 (您需要 登录 后才能回复 没有账号 ?)
  • 请尽量让自己的回复能够对别人有帮助
  • 支持 Markdown 格式, **粗体**、~~删除线~~、`单行代码`
  • 支持 @ 本站用户;支持表情(输入 : 提示),见 Emoji cheat sheet
  • 图片支持拖拽、截图粘贴等方式上传