aboutsummaryrefslogtreecommitdiff
path: root/syncapi/storage/sqlite3/filtering.go
blob: 17a37a2df10e329905fb2ec80a78dee363c931d6 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
package sqlite3

import (
	"database/sql"
	"fmt"

	"github.com/matrix-org/dendrite/internal/sqlutil"
)

type FilterOrder int

const (
	FilterOrderNone = iota
	FilterOrderAsc
	FilterOrderDesc
)

// prepareWithFilters returns a prepared statement with the
// relevant filters included. It also includes an []interface{}
// list of all the relevant parameters to pass straight to
// QueryContext, QueryRowContext etc.
// We don't take the filter object directly here because the
// fields might come from either a StateFilter or an EventFilter,
// and it's easier just to have the caller extract the relevant
// parts.
func prepareWithFilters(
	db *sql.DB, txn *sql.Tx, query string, params []interface{},
	senders, notsenders, types, nottypes *[]string, excludeEventIDs []string,
	containsURL *bool, limit int, order FilterOrder,
) (*sql.Stmt, []interface{}, error) {
	offset := len(params)
	if senders != nil {
		if count := len(*senders); count > 0 {
			query += " AND sender IN " + sqlutil.QueryVariadicOffset(count, offset)
			for _, v := range *senders {
				params, offset = append(params, v), offset+1
			}
		} else {
			query += ` AND sender = ""`
		}
	}
	if notsenders != nil {
		if count := len(*notsenders); count > 0 {
			query += " AND sender NOT IN " + sqlutil.QueryVariadicOffset(count, offset)
			for _, v := range *notsenders {
				params, offset = append(params, v), offset+1
			}
		} else {
			query += ` AND sender NOT = ""`
		}
	}
	if types != nil {
		if count := len(*types); count > 0 {
			query += " AND type IN " + sqlutil.QueryVariadicOffset(count, offset)
			for _, v := range *types {
				params, offset = append(params, v), offset+1
			}
		} else {
			query += ` AND type = ""`
		}
	}
	if nottypes != nil {
		if count := len(*nottypes); count > 0 {
			query += " AND type NOT IN " + sqlutil.QueryVariadicOffset(count, offset)
			for _, v := range *nottypes {
				params, offset = append(params, v), offset+1
			}
		} else {
			query += ` AND type NOT = ""`
		}
	}
	if containsURL != nil {
		query += fmt.Sprintf(" AND contains_url = %v", *containsURL)
	}
	if count := len(excludeEventIDs); count > 0 {
		query += " AND event_id NOT IN " + sqlutil.QueryVariadicOffset(count, offset)
		for _, v := range excludeEventIDs {
			params, offset = append(params, v), offset+1
		}
	}
	switch order {
	case FilterOrderAsc:
		query += " ORDER BY id ASC"
	case FilterOrderDesc:
		query += " ORDER BY id DESC"
	}
	if limit > 0 {
		query += fmt.Sprintf(" LIMIT $%d", offset+1)
		params = append(params, limit)
	}

	var stmt *sql.Stmt
	var err error
	if txn != nil {
		stmt, err = txn.Prepare(query)
	} else {
		stmt, err = db.Prepare(query)
	}
	if err != nil {
		return nil, nil, fmt.Errorf("s.db.Prepare: %w", err)
	}
	return stmt, params, nil
}