mirror of
https://github.com/ddworken/hishtory.git
synced 2025-06-18 19:16:44 +02:00
Fix bug in searching logic that caused gorm to template in NULL for non-null values
This commit is contained in:
parent
8e7dc41ed7
commit
134061085b
@ -620,6 +620,20 @@ func parseTimeGenerously(input string) (time.Time, error) {
|
|||||||
return dateparse.ParseLocal(input)
|
return dateparse.ParseLocal(input)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A wrapper around tx.Where(...) that filters out nil-values
|
||||||
|
func where(tx *gorm.DB, s string, v1 any, v2 any) *gorm.DB {
|
||||||
|
if v1 == nil && v2 == nil {
|
||||||
|
return tx.Where(s)
|
||||||
|
}
|
||||||
|
if v1 != nil && v2 == nil {
|
||||||
|
return tx.Where(s, v1)
|
||||||
|
}
|
||||||
|
if v1 != nil && v2 != nil {
|
||||||
|
return tx.Where(s, v1, v2)
|
||||||
|
}
|
||||||
|
panic(fmt.Sprintf("Impossible state: v1=%#v, v2=%#v", v1, v2))
|
||||||
|
}
|
||||||
|
|
||||||
func MakeWhereQueryFromSearch(ctx context.Context, db *gorm.DB, query string) (*gorm.DB, error) {
|
func MakeWhereQueryFromSearch(ctx context.Context, db *gorm.DB, query string) (*gorm.DB, error) {
|
||||||
tokens, err := tokenize(query)
|
tokens, err := tokenize(query)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -638,7 +652,7 @@ func MakeWhereQueryFromSearch(ctx context.Context, db *gorm.DB, query string) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tx = tx.Where("NOT "+query, v1, v2)
|
tx = where(tx, "NOT "+query, v1, v2)
|
||||||
} else {
|
} else {
|
||||||
query, v1, v2, v3, err := parseNonAtomizedToken(token[1:])
|
query, v1, v2, v3, err := parseNonAtomizedToken(token[1:])
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -651,7 +665,7 @@ func MakeWhereQueryFromSearch(ctx context.Context, db *gorm.DB, query string) (*
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
tx = tx.Where(query, v1, v2)
|
tx = where(tx, query, v1, v2)
|
||||||
} else {
|
} else {
|
||||||
query, v1, v2, v3, err := parseNonAtomizedToken(token)
|
query, v1, v2, v3, err := parseNonAtomizedToken(token)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -727,11 +741,7 @@ func parseAtomizedToken(ctx context.Context, token string) (string, interface{},
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to parse start_time:%s as a timestamp: %w", val, err)
|
return "", nil, nil, fmt.Errorf("failed to parse start_time:%s as a timestamp: %w", val, err)
|
||||||
}
|
}
|
||||||
// Note: We are bypassing Gorm's templating here and directly string substituting in the timestamp. This is because
|
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) = ?)", strconv.FormatInt(t.Unix(), 10), nil, nil
|
||||||
// Gorm treats zero and NULL as identical, so if the timestamp we're search for is 0 (aka the beginning of time) then
|
|
||||||
// Gorm will make a query that actually looks for `start_time = NULL`, which will of course never return true. So we
|
|
||||||
// directly template in our string. This is safe from SQL injection since it is just a number.
|
|
||||||
return "(CAST(strftime(\"%s\",start_time) AS INTEGER) = " + strconv.FormatInt(t.Unix(), 10) + ")", nil, nil, nil
|
|
||||||
case "end_time":
|
case "end_time":
|
||||||
// Note that this atom probably isn't useful for interactive usage since it does exact matching, but we use it
|
// Note that this atom probably isn't useful for interactive usage since it does exact matching, but we use it
|
||||||
// internally for pre-saving history entries.
|
// internally for pre-saving history entries.
|
||||||
@ -739,8 +749,7 @@ func parseAtomizedToken(ctx context.Context, token string) (string, interface{},
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return "", nil, nil, fmt.Errorf("failed to parse end_time:%s as a timestamp: %w", val, err)
|
return "", nil, nil, fmt.Errorf("failed to parse end_time:%s as a timestamp: %w", val, err)
|
||||||
}
|
}
|
||||||
// See note above about why we're directly templating in here rather than using parameterized queries
|
return "(CAST(strftime(\"%s\",end_time) AS INTEGER) = ?)", strconv.FormatInt(t.Unix(), 10), nil, nil
|
||||||
return "(CAST(strftime(\"%s\",end_time) AS INTEGER) = " + strconv.FormatInt(t.Unix(), 10) + ")", nil, nil, nil
|
|
||||||
case "command":
|
case "command":
|
||||||
return "(instr(command, ?) > 0)", val, nil, nil
|
return "(instr(command, ?) > 0)", val, nil, nil
|
||||||
default:
|
default:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user