github.com/zhongdalu/gf@v1.0.0/g/database/gdb/gdb_oracle.go (about)

     1  // Copyright 2017 gf Author(https://github.com/zhongdalu/gf). All Rights Reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the MIT License.
     4  // If a copy of the MIT was not distributed with this file,
     5  // You can obtain one at https://github.com/zhongdalu/gf.
     6  // 说明:
     7  //    1.需要导入oracle驱动: github.com/mattn/go-oci8
     8  //    2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
     9  //    3.不支持LastInsertId方法
    10  
    11  package gdb
    12  
    13  import (
    14  	"database/sql"
    15  	"fmt"
    16  	"strconv"
    17  	"strings"
    18  
    19  	"github.com/zhongdalu/gf/g/text/gregex"
    20  )
    21  
    22  // 数据库链接对象
    23  type dbOracle struct {
    24  	*dbBase
    25  }
    26  
    27  // 创建SQL操作对象
    28  func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
    29  	var source string
    30  	if config.LinkInfo != "" {
    31  		source = config.LinkInfo
    32  	} else {
    33  		source = fmt.Sprintf("%s/%s@%s", config.User, config.Pass, config.Name)
    34  	}
    35  	if db, err := sql.Open("oci8", source); err == nil {
    36  		return db, nil
    37  	} else {
    38  		return nil, err
    39  	}
    40  }
    41  
    42  // 获得关键字操作符
    43  func (db *dbOracle) getChars() (charLeft string, charRight string) {
    44  	return "\"", "\""
    45  }
    46  
    47  // 在执行sql之前对sql进行进一步处理
    48  func (db *dbOracle) handleSqlBeforeExec(query string) string {
    49  	index := 0
    50  	str, _ := gregex.ReplaceStringFunc("\\?", query, func(s string) string {
    51  		index++
    52  		return fmt.Sprintf(":%d", index)
    53  	})
    54  
    55  	str, _ = gregex.ReplaceString("\"", "", str)
    56  
    57  	return db.parseSql(str)
    58  }
    59  
    60  //由于ORACLE中对LIMIT和批量插入的语法与MYSQL不一致,所以这里需要对LIMIT和批量插入做语法上的转换
    61  func (db *dbOracle) parseSql(sql string) string {
    62  	//下面的正则表达式匹配出SELECT和INSERT的关键字后分别做不同的处理,如有LIMIT则将LIMIT的关键字也匹配出
    63  	patten := `^\s*(?i)(SELECT)|(INSERT)|(LIMIT\s*(\d+)\s*,\s*(\d+))`
    64  	if gregex.IsMatchString(patten, sql) == false {
    65  		//fmt.Println("not matched..")
    66  		return sql
    67  	}
    68  
    69  	res, err := gregex.MatchAllString(patten, sql)
    70  	if err != nil {
    71  		//fmt.Println("MatchString error.", err)
    72  		return ""
    73  	}
    74  
    75  	index := 0
    76  	keyword := strings.TrimSpace(res[index][0])
    77  	keyword = strings.ToUpper(keyword)
    78  
    79  	index++
    80  	switch keyword {
    81  	case "SELECT":
    82  		//不含LIMIT关键字则不处理
    83  		if len(res) < 2 || (strings.HasPrefix(res[index][0], "LIMIT") == false && strings.HasPrefix(res[index][0], "limit") == false) {
    84  			break
    85  		}
    86  
    87  		//取limit前面的字符串
    88  		if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
    89  			break
    90  		}
    91  
    92  		queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
    93  		if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
    94  			break
    95  		}
    96  
    97  		//取limit后面的取值范围
    98  		first, limit := 0, 0
    99  		for i := 1; i < len(res[index]); i++ {
   100  			if len(strings.TrimSpace(res[index][i])) == 0 {
   101  				continue
   102  			}
   103  
   104  			if strings.HasPrefix(res[index][i], "LIMIT") || strings.HasPrefix(res[index][i], "limit") {
   105  				first, _ = strconv.Atoi(res[index][i+1])
   106  				limit, _ = strconv.Atoi(res[index][i+2])
   107  				break
   108  			}
   109  		}
   110  
   111  		//也可以使用between,据说这种写法的性能会比between好点,里层SQL中的ROWNUM_ >= limit可以缩小查询后的数据集规模
   112  		sql = fmt.Sprintf("SELECT * FROM (SELECT GFORM.*, ROWNUM ROWNUM_ FROM (%s %s) GFORM WHERE ROWNUM <= %d) WHERE ROWNUM_ >= %d", queryExpr[1], queryExpr[2], limit, first)
   113  	case "INSERT":
   114  		//获取VALUE的值,匹配所有带括号的值,会将INSERT INTO后的值匹配到,所以下面的判断语句会判断数组长度是否小于3
   115  		valueExpr, err := gregex.MatchAllString(`(\s*\(([^\(\)]*)\))`, sql)
   116  		if err != nil {
   117  			return sql
   118  		}
   119  
   120  		//判断VALUE后的值是否有多个,只有在批量插入的时候才需要做转换,如只有1个VALUE则不需要做转换
   121  		if len(valueExpr) < 3 {
   122  			break
   123  		}
   124  
   125  		//获取INTO后面的值
   126  		tableExpr, err := gregex.MatchString(`(?i)\s*(INTO\s+\w+\(([^\(\)]*)\))`, sql)
   127  		if err != nil {
   128  			return sql
   129  		}
   130  		tableExpr[0] = strings.TrimSpace(tableExpr[0])
   131  
   132  		sql = "INSERT ALL"
   133  		for i := 1; i < len(valueExpr); i++ {
   134  			sql += fmt.Sprintf(" %s VALUES%s", tableExpr[0], strings.TrimSpace(valueExpr[i][0]))
   135  		}
   136  		sql += " SELECT 1 FROM DUAL"
   137  
   138  	default:
   139  	}
   140  	return sql
   141  }
   142  
   143  // 获得指定表表的数据结构,构造成map哈希表返回,其中键名为表字段名称,键值暂无用途(默认为字段数据类型).
   144  func (db *dbOracle) getTableFields(table string) (fields map[string]string, err error) {
   145  	// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
   146  	v := db.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
   147  		result := (Result)(nil)
   148  		result, err = db.GetAll(fmt.Sprintf(`
   149  		SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE 
   150  		    WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')' 
   151  			WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')' 
   152  			ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE  
   153  		FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table)))
   154  		if err != nil {
   155  			return nil
   156  		}
   157  
   158  		fields = make(map[string]string)
   159  		for _, m := range result {
   160  			fields[strings.ToLower(m["FIELD"].String())] = strings.ToLower(m["TYPE"].String()) //ORACLE返回的值默认都是大写的,需要转为小写
   161  		}
   162  		return fields
   163  	}, 0)
   164  	if err == nil {
   165  		fields = v.(map[string]string)
   166  	}
   167  	return
   168  }