Repository navigation
Expand file tree
/
Copy pathhistogram_sql.go
More file actions
176 lines (149 loc) · 4.47 KB
/
Copy pathhistogram_sql.go
File metadata and controls
176 lines (149 loc) · 4.47 KB
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
// Copyright © 2025 Kaleido, Inc.
//
// SPDX-License-Identifier: Apache-2.0
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dbsql
import (
"context"
"database/sql"
"strconv"
sq "github.com/Masterminds/squirrel"
"github.com/hyperledger/firefly-common/pkg/config"
"github.com/hyperledger/firefly-common/pkg/fftypes"
"github.com/hyperledger/firefly-common/pkg/i18n"
)
// GetChartHistogram executes a collection of queries (one per interval) and builds
// a histogram response for the specified table and time intervals.
func (s *Database) GetChartHistogram(
ctx context.Context,
tableName string,
timestampColumn string,
typeColumn string,
namespaceColumn string,
namespaceValue string,
intervals []fftypes.ChartHistogramInterval,
) ([]*fftypes.ChartHistogram, error) {
maxRows := config.GetUint64(SQLConfHistogramsMaxChartRows)
// check if we have a type column for grouping
hasTypeColumn := typeColumn != ""
// Build qs for each interval
queries := s.buildHistogramQueries(
tableName, timestampColumn, typeColumn,
namespaceColumn, namespaceValue,
intervals, maxRows,
)
histogramList := []*fftypes.ChartHistogram{}
for i, query := range queries {
rows, _, err := s.Query(ctx, tableName, query)
if err != nil {
return nil, err
}
defer rows.Close()
data, total, err := s.processHistogramRows(ctx, tableName, rows, hasTypeColumn)
if err != nil {
return nil, err
}
// Build hist bucket
histBucket := &fftypes.ChartHistogram{
Count: strconv.FormatUint(total, 10),
Timestamp: intervals[i].StartTime,
Types: []*fftypes.ChartHistogramType{},
IsCapped: total == maxRows,
}
// Add type counts if applicable
if hasTypeColumn {
for t, c := range data {
histBucket.Types = append(histBucket.Types,
&fftypes.ChartHistogramType{
Count: strconv.Itoa(c),
Type: t,
})
}
}
histogramList = append(histogramList, histBucket)
}
return histogramList, nil
}
// buildHistogramQueries constructs SQL queries for each time interval.
// each query selects data within the interval's time range, optionally
// filtered by namespace, and limited to maxRows.
func (s *Database) buildHistogramQueries(
tableName string,
timestampColumn string,
typeColumn string,
namespaceColumn string,
namespaceValue string,
intervals []fftypes.ChartHistogramInterval,
maxRows uint64,
) []sq.SelectBuilder {
queries := []sq.SelectBuilder{}
// Determine columns to select
cols := []string{timestampColumn}
if typeColumn != "" {
cols = append(cols, typeColumn)
}
for _, i := range intervals {
whereClause := sq.And{
sq.GtOrEq{timestampColumn: i.StartTime},
sq.Lt{timestampColumn: i.EndTime},
}
// namespace filter
if namespaceColumn != "" && namespaceValue != "" {
whereClause = append(whereClause, sq.Eq{namespaceColumn: namespaceValue})
}
// Build query with PlaceholderFormat applied
query := sq.Select(cols...).
From(tableName).
Where(whereClause).
OrderBy(timestampColumn).
Limit(maxRows).
PlaceholderFormat(s.features.PlaceholderFormat)
queries = append(queries, query)
}
return queries
}
// processHistogramRows scans SQL result rows and builds histogram data
// If hasTypeColumn is true, it groups counts by type else it just
// counts total rows.
func (s *Database) processHistogramRows(
ctx context.Context,
tableName string,
rows *sql.Rows,
hasTypeColumn bool,
) (map[string]int, uint64, error) {
total := uint64(0)
if !hasTypeColumn {
// counting rows
for rows.Next() {
var timestamp interface{}
if err := rows.Scan(×tamp); err != nil {
return nil, 0, i18n.NewError(ctx, i18n.MsgDBReadErr, tableName)
}
total++
}
return map[string]int{}, total, nil
}
// Count by type
typeMap := map[string]int{}
for rows.Next() {
var timestamp interface{}
var typeStr string
if err := rows.Scan(×tamp, &typeStr); err != nil {
return nil, 0, i18n.NewError(ctx, i18n.MsgDBReadErr, tableName)
}
typeMap[typeStr]++
total++
}
return typeMap, total, nil
}