mirror of
https://github.com/bitechdev/ResolveSpec.git
synced 2026-09-15 12:52:35 +00:00
fix(restheadspec,resolvespec): stop casting SqlNull-wrapped and citext columns to TEXT in filters
reflect.Type.Kind() on spectypes.SqlNull[T] wrappers (SqlInt16/32/64, SqlFloat64, SqlBool, SqlString, and embedders like SqlTimeStamp) always reports reflect.Struct, never the wrapped T. ValidateAndAdjustFilterForColumnType treated those as "complex" columns and forced CAST(col AS TEXT) on eq/gt/lt filters, e.g. CAST(atdetail.rid_parent AS TEXT) = '90446096', which can't use the index on rid_parent. Add spectypes.UnwrapKind to see through SqlNull wrappers to the underlying Kind, and use it in GetColumnTypeFromModel so numeric/string SqlNull columns are recognized correctly and compared natively. Also stop unconditionally casting to TEXT for LIKE/ILIKE and add reflection.IsCitextColumn: citext columns are already case-insensitive, so casting them to TEXT flips to case-sensitive matching and defeats a citext index.
This commit is contained in:
@@ -1937,10 +1937,10 @@ func (h *Handler) buildFilterCondition(filter common.FilterOption, model interfa
|
||||
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
||||
args = []interface{}{filter.Value}
|
||||
case "like":
|
||||
condition = fmt.Sprintf("CAST(%s AS TEXT) LIKE ?", filter.Column)
|
||||
condition = fmt.Sprintf("%s LIKE ?", likeColumn(filter.Column, model))
|
||||
args = []interface{}{filter.Value}
|
||||
case "ilike":
|
||||
condition = fmt.Sprintf("CAST(%s AS TEXT) ILIKE ?", filter.Column)
|
||||
condition = fmt.Sprintf("%s ILIKE ?", likeColumn(filter.Column, model))
|
||||
args = []interface{}{filter.Value}
|
||||
case "in":
|
||||
condition, args = common.BuildInCondition(filter.Column, filter.Value)
|
||||
@@ -1973,6 +1973,18 @@ func (h *Handler) buildFilterCondition(filter common.FilterOption, model interfa
|
||||
return condition, args
|
||||
}
|
||||
|
||||
// likeColumn returns the column expression to use for LIKE/ILIKE. citext
|
||||
// columns are compared natively — they're already case-insensitive, and
|
||||
// CAST(... AS TEXT) would switch to case-sensitive matching and defeat a
|
||||
// citext index. Every other column is cast to TEXT so LIKE/ILIKE also works
|
||||
// against date/time/timestamp and numeric columns.
|
||||
func likeColumn(column string, model interface{}) string {
|
||||
if reflection.IsCitextColumn(model, column) {
|
||||
return column
|
||||
}
|
||||
return fmt.Sprintf("CAST(%s AS TEXT)", column)
|
||||
}
|
||||
|
||||
func (h *Handler) applyFilter(query common.SelectQuery, filter common.FilterOption, model interface{}) common.SelectQuery {
|
||||
// Determine which method to use based on LogicOperator
|
||||
useOrLogic := strings.EqualFold(filter.LogicOperator, "OR")
|
||||
@@ -2007,10 +2019,10 @@ func (h *Handler) applyFilter(query common.SelectQuery, filter common.FilterOpti
|
||||
condition = fmt.Sprintf("%s <= ?", filter.Column)
|
||||
args = []interface{}{filter.Value}
|
||||
case "like":
|
||||
condition = fmt.Sprintf("CAST(%s AS TEXT) LIKE ?", filter.Column)
|
||||
condition = fmt.Sprintf("%s LIKE ?", likeColumn(filter.Column, model))
|
||||
args = []interface{}{filter.Value}
|
||||
case "ilike":
|
||||
condition = fmt.Sprintf("CAST(%s AS TEXT) ILIKE ?", filter.Column)
|
||||
condition = fmt.Sprintf("%s ILIKE ?", likeColumn(filter.Column, model))
|
||||
args = []interface{}{filter.Value}
|
||||
case "in":
|
||||
condition, args = common.BuildInCondition(filter.Column, filter.Value)
|
||||
|
||||
Reference in New Issue
Block a user