github.com/kaydxh/golang@v0.0.131/pkg/database/mysql/sql.go (about) 1 /* 2 *Copyright (c) 2022, kaydxh 3 * 4 *Permission is hereby granted, free of charge, to any person obtaining a copy 5 *of this software and associated documentation files (the "Software"), to deal 6 *in the Software without restriction, including without limitation the rights 7 *to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 *copies of the Software, and to permit persons to whom the Software is 9 *furnished to do so, subject to the following conditions: 10 * 11 *The above copyright notice and this permission notice shall be included in all 12 *copies or substantial portions of the Software. 13 * 14 *THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 *IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 *FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 *AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 *LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 *OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 20 *SOFTWARE. 21 */ 22 package mysql 23 24 import ( 25 "fmt" 26 "strings" 27 28 "github.com/jmoiron/sqlx" 29 reflect_ "github.com/kaydxh/golang/go/reflect" 30 strings_ "github.com/kaydxh/golang/go/strings" 31 ) 32 33 const dbTag = "db" 34 35 type SqlCompare string 36 37 const ( 38 SqlCompareEqual SqlCompare = "=" 39 SqlCompareNotEqual SqlCompare = "!=" 40 SqlCompareGreater SqlCompare = ">" 41 SqlCompareLessThan SqlCompare = "<" 42 SqlCompareGreatEqual SqlCompare = ">=" 43 SqlCompareLessEqual SqlCompare = "<=" 44 SqlCompareLike SqlCompare = "LIKE" 45 SqlCompareIn SqlCompare = "IN" 46 ) 47 48 type SqlOperator string 49 50 const ( 51 SqlOperatorAnd SqlOperator = "AND" 52 SqlOperatorOr SqlOperator = "OR" 53 SqlOperatorNot SqlOperator = "NOT" 54 ) 55 56 // "foo=:foo AND bar=:bar" 57 func NonzeroCondition(cmp SqlCompare, oper SqlOperator, arg interface{}) string { 58 condFields := reflect_.NonzeroFieldTags(arg, dbTag) 59 return fmt.Sprintf(" %s ", func() string { 60 if len(condFields) == 0 { 61 return "TRUE" 62 } 63 return fmt.Sprintf("%s", JoinNamedColumnsValuesWithOperator(cmp, oper, condFields...)) 64 }()) 65 } 66 67 func NonzeroFields(arg interface{}) []string { 68 return reflect_.NonzeroFieldTags(arg, dbTag) 69 } 70 71 func ConditionWithEqualAnd(condFields ...string) string { 72 return JoinNamedColumnsValuesWithOperator(SqlCompareEqual, SqlOperatorAnd, condFields...) 73 } 74 75 // "ORDER BY create_time DESC, id DESC" 76 func OrderCondition(orders map[string]bool) string { 77 if len(orders) == 0 { 78 return "" 79 } 80 81 return fmt.Sprintf(" ORDER BY %s", func() string { 82 var msg string 83 for k, v := range orders { 84 msg += fmt.Sprintf("%s %s,", k, func() string { 85 if v { 86 return "DESC" 87 } 88 return "ASC" 89 }()) 90 } 91 92 msg = strings.TrimRight(msg, ",") 93 return msg 94 }()) 95 96 } 97 98 func InCondition(cond string, values ...string) string { 99 if cond == "" || len(values) == 0 { 100 return "TRUE" 101 } 102 103 return fmt.Sprintf(`%s IN (%s)`, cond, func() string { 104 var msg string 105 for _, v := range values { 106 msg += fmt.Sprintf(`"%s",`, v) 107 } 108 msg = strings.TrimRight(msg, ",") 109 return msg 110 }()) 111 } 112 113 func NamedInCondition(oper SqlOperator, cols []string, arg interface{}) (string, error) { 114 query := JoinNamedColumnsValuesWithOperator(SqlCompareIn, oper, cols...) 115 query, args, err := sqlx.Named(query, arg) 116 if err != nil { 117 return "", err 118 } 119 query, args, err = sqlx.In(query, args...) 120 if err != nil { 121 return "", err 122 } 123 124 return strings_.ReplaceAll(query, "?", args, true), nil 125 } 126 127 // JoinNamedColumnsValues foo=:foo,bar=:bar, for update set 128 func JoinNamedColumnsValues(cols ...string) string { 129 return strings.Join(namedTableColumnsValues(SqlCompareEqual, cols...), ",") 130 } 131 132 // JoinNamedColumnsValuesWithOperator "foo=:foo AND bar=:bar" , for where condition 133 func JoinNamedColumnsValuesWithOperator(cmp SqlCompare, oper SqlOperator, cols ...string) string { 134 conds := strings.Join(namedTableColumnsValues(cmp, cols...), fmt.Sprintf(" %s ", oper)) 135 if len(cols) == 0 || conds == "" { 136 return "TRUE" 137 } 138 139 return conds 140 } 141 142 // []string{"foo=:foo", "bar=:bar"} 143 func namedTableColumnsValues(cmp SqlCompare, cols ...string) []string { 144 var namedCols []string 145 for _, col := range cols { 146 if col != "" { 147 switch cmp { 148 case SqlCompareLike: 149 namedCols = append(namedCols, fmt.Sprintf(`%[1]s %[2]s concat("%%",:%[1]s,"%%")`, col, cmp)) 150 case SqlCompareIn: 151 namedCols = append(namedCols, fmt.Sprintf("%[1]s %[2]s (:%[1]s)", col, cmp)) 152 default: 153 namedCols = append(namedCols, fmt.Sprintf("%[1]s %[2]s :%[1]s", col, cmp)) 154 } 155 } 156 } 157 return namedCols 158 } 159 160 /* 161 used for batch insert 162 ( 163 :group_id_1, 164 :page_id_1, 165 :fea_id_1, 166 :entity_id_1, 167 :feature0_1, 168 :feature1_1, 169 :extend_info_1 170 ), 171 ( 172 :group_id_2, 173 :page_id_2, 174 :fea_id_2, 175 :entity_id_2, 176 :feature0_2, 177 :feature1_2, 178 :extend_info_2 179 ) 180 */ 181 func JoinNamedColumnsValuesBatch(cols []string, batch int) string { 182 183 var batchNamedCols []string 184 for i := 0; i < batch; i++ { 185 var namedCols []string 186 for _, col := range cols { 187 namedCols = append(namedCols, fmt.Sprintf(":%s_%d", col, i)) 188 } 189 batchNamedCols = append(batchNamedCols, fmt.Sprintf("(%v)", strings.Join(namedCols, ","))) 190 } 191 192 return strings.Join(batchNamedCols, ",") 193 } 194 195 func BuildNamedInsertSql(table string, cols []string, batch int) string { 196 if table == "" || len(cols) == 0 { 197 return "" 198 } 199 sql := fmt.Sprintf(`INSERT INTO %s 200 ( 201 %s 202 ) 203 VALUES %s`, table, 204 strings.Join(cols, ","), 205 JoinNamedColumnsValuesBatch(cols, batch)) 206 return sql 207 } 208 209 // used for batch insert 210 func TransferToNamedColumnsValuesBatch(req []map[string]interface{}) map[string]interface{} { 211 212 valuesMap := make(map[string]interface{}, 0) 213 for i, values := range req { 214 for k, v := range values { 215 valuesMap[fmt.Sprintf("%s_%d", k, i)] = v 216 } 217 } 218 219 return valuesMap 220 } 221 222 // req is slice of struct or pointer struct 223 func BuildNamedColumnsValuesBatch(req interface{}) map[string]interface{} { 224 return TransferToNamedColumnsValuesBatch(reflect_.ArrayAllTagsVaules(req, dbTag)) 225 }