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  }