Skip to content

Commit

Permalink
Added support to use collation with field tag 'collation'
Browse files Browse the repository at this point in the history
  • Loading branch information
arknable committed Apr 20, 2024
1 parent 8744ea8 commit bf8c7ba
Show file tree
Hide file tree
Showing 3 changed files with 77 additions and 54 deletions.
31 changes: 27 additions & 4 deletions helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,11 @@ import (
"strings"
)

const nullSelectField = "ifnull(`%s`.`%s`, %s) as %s, "
const nullSelectField = "ifnull(`%s`.`%s`, %s)%s as %s, "
const selectField = "`%s`.`%s`, "
const selectFieldWithCollation = "`%s`.`%s`%s as %s, "
const selectFuncField = "ifnull(%s(`%s`.`%s`), %s) as %s, "
const selectFuncFieldWithCollation = "ifnull(%s(`%s`.`%s`), %s)%s as %s, "
const andPredicate = " AND `%s`.`%s`"
const orPredicate = " OR `%s`.`%s`"
const strComparison = " LIKE :%s"
Expand All @@ -18,6 +20,7 @@ const valComparison = " = :%s"
const notValComparison = " != :%s"
const queryCore = "%sFROM `%s`%s%s"
const isoDateFormat = "2006-01-02 15:04:05"
const defaultCollation = "utf8mb4_0900_ai_ci"

var matchFirstCap = regexp.MustCompile("(.)([A-Z][a-z]+)")
var matchAllCap = regexp.MustCompile("([a-z0-9])([A-Z])")
Expand All @@ -38,6 +41,7 @@ type field struct {
selectFunc *selectFuncData
isMultiValue bool
name string
collation string
}

type selectFuncData struct {
Expand All @@ -62,6 +66,11 @@ func parseReflection(val reflect.Value, i int, target string) *field {
argName: self.Tag.Get("func_arg_name"),
}

collation := self.Tag.Get("collation")
if collation == "default" {
collation = defaultCollation
}

return &field{
value: value,
table: target,
Expand All @@ -74,6 +83,7 @@ func parseReflection(val reflect.Value, i int, target string) *field {
isMultiValue: self.Tag.Get("multi_value") != "",
selectFunc: selectFunc,
name: name,
collation: collation,
}
}

Expand Down Expand Up @@ -107,15 +117,28 @@ type queryBuilder struct {
}

func (qb *queryBuilder) writeSelectField(f *field) {
collation := f.collation
if collation != "" {
collation = " COLLATE " + collation
}

if f.isNullable {
fmt.Fprintf(&qb.Fields, nullSelectField, f.table, f.name, getDefault(f.typeStr, f.name), f.name)
fmt.Fprintf(&qb.Fields, nullSelectField, f.table, f.name, getDefault(f.typeStr, f.name), collation, f.name)
} else {
fmt.Fprintf(&qb.Fields, selectField, f.table, f.name)
if collation != "" {
fmt.Fprintf(&qb.Fields, selectFieldWithCollation, f.table, f.name, collation, f.name)
} else {
fmt.Fprintf(&qb.Fields, selectField, f.table, f.name)
}
}
}

func (qb *queryBuilder) writeSelectFunc(f *field) {
fmt.Fprintf(&qb.Fields, selectFuncField, f.selectFunc.name, f.table, f.selectFunc.argName, getDefault(f.typeStr, f.name), f.name)
if f.collation != "" {
fmt.Fprintf(&qb.Fields, selectFuncFieldWithCollation, f.selectFunc.name, f.table, f.selectFunc.argName, getDefault(f.typeStr, f.name), " COLLATE "+f.collation, f.name)
} else {
fmt.Fprintf(&qb.Fields, selectFuncField, f.selectFunc.name, f.table, f.selectFunc.argName, getDefault(f.typeStr, f.name), f.name)
}
}

func (qb *queryBuilder) writePredicate(f *field, fieldMask []string, predicateStr string) {
Expand Down
4 changes: 2 additions & 2 deletions main_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ func TestBuildSearchQuery(t *testing.T) {
t.Fatal(err.Error())
}

assert.Equal(t, "SELECT ifnull(name_of_user(`task`.`external_id`), '') as owner_name, `task`.`task_id`, `task`.`external_id`, ifnull(`task`.`external_code`, '') as external_code, ifnull(`task`.`reference_number`, '') as reference_number, `task`.`creator_user_id`, `task`.`time_created`, ifnull(`task`.`time_due`, '') as time_due, ifnull(`task`.`brief_description`, '') as brief_description, ifnull(`task`.`details`, '') as details, ifnull(`task`.`notes`, '') as notes, `task`.`status_id`, `task`.`priority_id`, ifnull(`task`.`reference_url`, '') as reference_url, ifnull(`task`.`isActive`, 0) as isActive, `task`.`billable`, ifnull(`task`.`task_billable_type`, '') as task_billable_type, ifnull(`task`.`flat_rate`, 0.0) as flat_rate, ifnull(`task`.`hourly_start`, '') as hourly_start, ifnull(`task`.`hourly_end`, '') as hourly_end, ifnull(`task`.`address`, '') as address, ifnull(`task`.`order_num`, '') as order_num, `task`.`spiff_amount`, ifnull(`task`.`spiff_jobNumber`, '') as spiff_jobNumber, ifnull(`task`.`spiff_type_id`, 0) as spiff_type_id, ifnull(`task`.`spiff_address`, '') as spiff_address, ifnull(`task`.`toolpurchase_date`, '0001-01-01 00:00:00') as toolpurchase_date, `task`.`toolpurchase_cost`, ifnull(`task`.`toolpurchase_file`, '') as toolpurchase_file, ifnull(`task`.`admin_action_id`, 0) as admin_action_id, ifnull(`task`.`date_performed`, '0001-01-01 00:00:00') as date_performed, ifnull(`task`.`spiff_tool_id`, '') as spiff_tool_id, ifnull(`task`.`spiff_tool_closeout_date`, '0001-01-01 00:00:00') as spiff_tool_closeout_date FROM `task` WHERE true AND `task`.`external_code` LIKE ? AND `task`.`reference_number` LIKE ? AND `task`.`creator_user_id` = ? AND `task`.`brief_description` LIKE ? AND `task`.`details` LIKE ? AND `task`.`notes` LIKE ? AND ( `task`.`time_created` LIKE ? OR `task`.`time_due` LIKE ? OR `task`.`reference_url` LIKE ? OR `task`.`task_billable_type` LIKE ? OR `task`.`hourly_start` LIKE ? OR `task`.`hourly_end` LIKE ? OR `task`.`address` LIKE ? OR `task`.`order_num` LIKE ? OR `task`.`spiff_jobNumber` LIKE ? OR `task`.`spiff_address` LIKE ? OR `task`.`toolpurchase_date` LIKE ? OR `task`.`toolpurchase_file` LIKE ? OR `task`.`date_performed` LIKE ? OR `task`.`spiff_tool_id` LIKE ? OR `task`.`spiff_tool_closeout_date` LIKE ?)", query)
assert.Equal(t, "SELECT ifnull(name_of_user(`task`.`external_id`), '') COLLATE utf8mb4_0900_ai_ci as owner_name, `task`.`task_id`, `task`.`external_id`, ifnull(`task`.`external_code`, '') as external_code, ifnull(`task`.`reference_number`, '') as reference_number, `task`.`creator_user_id`, `task`.`time_created`, ifnull(`task`.`time_due`, '') as time_due, ifnull(`task`.`brief_description`, '') as brief_description, ifnull(`task`.`details`, '') as details, ifnull(`task`.`notes`, '') as notes, `task`.`status_id`, `task`.`priority_id`, ifnull(`task`.`reference_url`, '') as reference_url, ifnull(`task`.`isActive`, 0) as isActive, `task`.`billable`, ifnull(`task`.`task_billable_type`, '') as task_billable_type, ifnull(`task`.`flat_rate`, 0.0) as flat_rate, ifnull(`task`.`hourly_start`, '') as hourly_start, ifnull(`task`.`hourly_end`, '') as hourly_end, ifnull(`task`.`address`, '') as address, ifnull(`task`.`order_num`, '') as order_num, `task`.`spiff_amount`, ifnull(`task`.`spiff_jobNumber`, '') as spiff_jobNumber, ifnull(`task`.`spiff_type_id`, 0) as spiff_type_id, ifnull(`task`.`spiff_address`, '') as spiff_address, ifnull(`task`.`toolpurchase_date`, '0001-01-01 00:00:00') as toolpurchase_date, `task`.`toolpurchase_cost`, ifnull(`task`.`toolpurchase_file`, '') as toolpurchase_file, ifnull(`task`.`admin_action_id`, 0) as admin_action_id, ifnull(`task`.`date_performed`, '0001-01-01 00:00:00') COLLATE utf8mb4_0900_ai_ci as date_performed, ifnull(`task`.`spiff_tool_id`, '') as spiff_tool_id, ifnull(`task`.`spiff_tool_closeout_date`, '0001-01-01 00:00:00') as spiff_tool_closeout_date FROM `task` WHERE true AND `task`.`external_code` LIKE ? AND `task`.`reference_number` LIKE ? AND `task`.`creator_user_id` = ? AND `task`.`brief_description` LIKE ? AND `task`.`details` LIKE ? AND `task`.`notes` LIKE ? AND ( `task`.`time_created` LIKE ? OR `task`.`time_due` LIKE ? OR `task`.`reference_url` LIKE ? OR `task`.`task_billable_type` LIKE ? OR `task`.`hourly_start` LIKE ? OR `task`.`hourly_end` LIKE ? OR `task`.`address` LIKE ? OR `task`.`order_num` LIKE ? OR `task`.`spiff_jobNumber` LIKE ? OR `task`.`spiff_address` LIKE ? OR `task`.`toolpurchase_date` LIKE ? OR `task`.`toolpurchase_file` LIKE ? OR `task`.`date_performed` LIKE ? OR `task`.`spiff_tool_id` LIKE ? OR `task`.`spiff_tool_closeout_date` LIKE ?)", query)
require.Equal(t, 21, len(args))
assert.Equal(t, "ext_code", args[0])
assert.Equal(t, "ref_number", args[1])
Expand Down Expand Up @@ -189,7 +189,7 @@ func TestBuildReadQuery(t *testing.T) {
query, args, err := BuildReadQuery("tasks", task)
require.NoError(t, err)

assert.Equal(t, "SELECT `tasks`.`task_id`, `tasks`.`external_id`, ifnull(`tasks`.`external_code`, '') as external_code, ifnull(`tasks`.`reference_number`, '') as reference_number, `tasks`.`creator_user_id`, `tasks`.`time_created`, ifnull(`tasks`.`time_due`, '') as time_due, ifnull(`tasks`.`brief_description`, '') as brief_description, ifnull(`tasks`.`details`, '') as details, ifnull(`tasks`.`notes`, '') as notes, `tasks`.`status_id`, `tasks`.`priority_id`, ifnull(`tasks`.`reference_url`, '') as reference_url, ifnull(`tasks`.`isActive`, 0) as isActive, `tasks`.`billable`, ifnull(`tasks`.`task_billable_type`, '') as task_billable_type, ifnull(`tasks`.`flat_rate`, 0.0) as flat_rate, ifnull(`tasks`.`hourly_start`, '') as hourly_start, ifnull(`tasks`.`hourly_end`, '') as hourly_end, ifnull(`tasks`.`address`, '') as address, ifnull(`tasks`.`order_num`, '') as order_num, `tasks`.`spiff_amount`, ifnull(`tasks`.`spiff_jobNumber`, '') as spiff_jobNumber, ifnull(`tasks`.`spiff_type_id`, 0) as spiff_type_id, ifnull(`tasks`.`spiff_address`, '') as spiff_address, ifnull(`tasks`.`toolpurchase_date`, '0001-01-01 00:00:00') as toolpurchase_date, `tasks`.`toolpurchase_cost`, ifnull(`tasks`.`toolpurchase_file`, '') as toolpurchase_file, ifnull(`tasks`.`admin_action_id`, 0) as admin_action_id, ifnull(`tasks`.`date_performed`, '0001-01-01 00:00:00') as date_performed, ifnull(`tasks`.`spiff_tool_id`, '') as spiff_tool_id, ifnull(name_of_user(`tasks`.`external_id`), '') as owner_name, ifnull(`tasks`.`spiff_tool_closeout_date`, '0001-01-01 00:00:00') as spiff_tool_closeout_date FROM `tasks` WHERE true AND `tasks`.`external_code` LIKE ? AND `tasks`.`reference_number` LIKE ? AND `tasks`.`creator_user_id` = ? AND `tasks`.`brief_description` LIKE ? AND `tasks`.`details` LIKE ? AND `tasks`.`notes` LIKE ?", query)
assert.Equal(t, "SELECT `tasks`.`task_id`, `tasks`.`external_id`, ifnull(`tasks`.`external_code`, '') as external_code, ifnull(`tasks`.`reference_number`, '') as reference_number, `tasks`.`creator_user_id`, `tasks`.`time_created`, ifnull(`tasks`.`time_due`, '') as time_due, ifnull(`tasks`.`brief_description`, '') as brief_description, ifnull(`tasks`.`details`, '') as details, ifnull(`tasks`.`notes`, '') as notes, `tasks`.`status_id`, `tasks`.`priority_id`, ifnull(`tasks`.`reference_url`, '') as reference_url, ifnull(`tasks`.`isActive`, 0) as isActive, `tasks`.`billable`, ifnull(`tasks`.`task_billable_type`, '') as task_billable_type, ifnull(`tasks`.`flat_rate`, 0.0) as flat_rate, ifnull(`tasks`.`hourly_start`, '') as hourly_start, ifnull(`tasks`.`hourly_end`, '') as hourly_end, ifnull(`tasks`.`address`, '') as address, ifnull(`tasks`.`order_num`, '') as order_num, `tasks`.`spiff_amount`, ifnull(`tasks`.`spiff_jobNumber`, '') as spiff_jobNumber, ifnull(`tasks`.`spiff_type_id`, 0) as spiff_type_id, ifnull(`tasks`.`spiff_address`, '') as spiff_address, ifnull(`tasks`.`toolpurchase_date`, '0001-01-01 00:00:00') as toolpurchase_date, `tasks`.`toolpurchase_cost`, ifnull(`tasks`.`toolpurchase_file`, '') as toolpurchase_file, ifnull(`tasks`.`admin_action_id`, 0) as admin_action_id, ifnull(`tasks`.`date_performed`, '0001-01-01 00:00:00') COLLATE utf8mb4_0900_ai_ci as date_performed, ifnull(`tasks`.`spiff_tool_id`, '') as spiff_tool_id, ifnull(name_of_user(`tasks`.`external_id`), '') COLLATE utf8mb4_0900_ai_ci as owner_name, ifnull(`tasks`.`spiff_tool_closeout_date`, '0001-01-01 00:00:00') as spiff_tool_closeout_date FROM `tasks` WHERE true AND `tasks`.`external_code` LIKE ? AND `tasks`.`reference_number` LIKE ? AND `tasks`.`creator_user_id` = ? AND `tasks`.`brief_description` LIKE ? AND `tasks`.`details` LIKE ? AND `tasks`.`notes` LIKE ?", query)
require.Equal(t, 6, len(args))
assert.Equal(t, "ext_code", args[0])
assert.Equal(t, "ref_number", args[1])
Expand Down
Loading

0 comments on commit bf8c7ba

Please sign in to comment.