fix(09): WR-08 search non-text list columns as text instead of failing in SQL

This commit is contained in:
Jakub Zych
2026-10-01 21:11:38 +02:00
parent b026aec162
commit 8b48eea4e1
2 changed files with 45 additions and 1 deletions

View File

@@ -291,7 +291,9 @@ func applyListSearch(db *gorm.DB, controller pact.AdminController, schema *ListS
} else if !identifier(column) { } else if !identifier(column) {
return nil, listInvalid("search", "is not searchable") return nil, listInvalid("search", "is not searchable")
} }
parts = append(parts, fmt.Sprintf("LOWER(%s) LIKE ? ESCAPE '\\'", qualifiedColumn(db, table, column))) // CAST makes a numeric, date or boolean column searchable as its text
// form (Winter casts to TEXT too); PostgreSQL has no lower(integer).
parts = append(parts, fmt.Sprintf("LOWER(CAST(%s AS TEXT)) LIKE ? ESCAPE '\\'", qualifiedColumn(db, table, column)))
args = append(args, pattern) args = append(args, pattern)
} }
return db.Where(strings.Join(parts, " OR "), args...), nil return db.Where(strings.Join(parts, " OR "), args...), nil

View File

@@ -435,3 +435,45 @@ var _ pact.AdminRecordSource = queryController{}
var _ pact.AdminPermissioned = queryController{} var _ pact.AdminPermissioned = queryController{}
var _ pact.FilterScope = queryRow{} var _ pact.FilterScope = queryRow{}
var _ pact.FilterOptions = queryRow{} var _ pact.FilterOptions = queryRow{}
// TestListSearchNonTextColumns pins WR-08: a searchable numeric or date column
// is searched as text instead of failing with an SQL error (PostgreSQL has no
// lower(integer) or lower(timestamp)).
func TestListSearchNonTextColumns(t *testing.T) {
svc, db := newListService(t)
seedListRows(t, db)
const columns = `columns:
name:
label: Name
searchable: true
group_id:
label: Group
searchable: true
created_at:
label: Created
type: datetime
searchable: true
active:
label: Active
type: switch
searchable: true
`
schema, err := CompileList("acme.demo", queryController{}, filterFS(queryListConfig, columns, queryFilters))
if err != nil {
t.Fatal(err)
}
svc.reg.byID["acme.demo.widgets"].List = schema
byGroup := decodeList(t, getList(t, svc, "search=9"), http.StatusOK)
if ids := rowIDs(byGroup.Data); len(ids) != 1 || ids[0] != 3 {
t.Fatalf("integer column search = %v, want [3]", ids)
}
byDate := decodeList(t, getList(t, svc, "search=2024-01"), http.StatusOK)
if byDate.Meta.Total != 2 {
t.Fatalf("timestamp column search total = %d, want 2", byDate.Meta.Total)
}
byName := decodeList(t, getList(t, svc, "search=other"), http.StatusOK)
if ids := rowIDs(byName.Data); len(ids) != 1 || ids[0] != 4 {
t.Fatalf("text column search = %v, want [4]", ids)
}
}