Refuse duplicate aggregate columns and page aggregates after the fold
A repeated extract function names one response column twice, which openvoxdb aliases as <name>_2: unknown to the merge spec, it froze at the first backend's value. A limit pushed upstream truncated each backend's groups before the cross-backend fold, so a group could be partly counted or missed. - Refuses any extract projecting one response column twice, naming the clash - Fetches every group and applies limit/offset after the fold - Documents the float64 avg divergence from Postgres numeric
This commit is contained in:
+22
-11
@@ -93,9 +93,18 @@ func parseAggregate(query string) (*aggregateSpec, error) {
|
||||
spec := &aggregateSpec{}
|
||||
var avgArgs []json.RawMessage
|
||||
sawFunction := false
|
||||
projected := map[string]bool{}
|
||||
fnColumns := map[string]bool{}
|
||||
for _, col := range cols {
|
||||
var name string
|
||||
if json.Unmarshal(col, &name) == nil {
|
||||
// A field repeating another field is harmless — the copy holds the same
|
||||
// value as the key it duplicates — but one taking a function's name is
|
||||
// the same clash the other way round.
|
||||
if fnColumns[name] {
|
||||
return nil, columnClash(name)
|
||||
}
|
||||
projected[name] = true
|
||||
spec.keys = appendUnique(spec.keys, name)
|
||||
continue
|
||||
}
|
||||
@@ -104,13 +113,14 @@ func parseAggregate(query string) (*aggregateSpec, error) {
|
||||
continue
|
||||
}
|
||||
sawFunction = true
|
||||
if projected[fn] {
|
||||
return nil, columnClash(fn)
|
||||
}
|
||||
projected[fn], fnColumns[fn] = true, true
|
||||
switch {
|
||||
case rowFns[fn]:
|
||||
spec.keys = appendUnique(spec.keys, fn)
|
||||
case fn == avgColumn:
|
||||
if spec.avg {
|
||||
return nil, fmt.Errorf("extract projects %q more than once, which openvoxdb answers with order-dependent columns", avgColumn)
|
||||
}
|
||||
if len(args) == 0 {
|
||||
return nil, fmt.Errorf("extract function avg needs a column to average")
|
||||
}
|
||||
@@ -120,7 +130,7 @@ func parseAggregate(query string) (*aggregateSpec, error) {
|
||||
if !known {
|
||||
return nil, fmt.Errorf("extract function %q cannot be merged across backends", fn)
|
||||
}
|
||||
spec.aggs = appendUniqueAgg(spec.aggs, aggColumn{name: fn, op: combine})
|
||||
spec.aggs = append(spec.aggs, aggColumn{name: fn, op: combine})
|
||||
}
|
||||
}
|
||||
if !sawFunction {
|
||||
@@ -139,6 +149,14 @@ func parseAggregate(query string) (*aggregateSpec, error) {
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
// columnClash refuses a projection naming one response column twice. openvoxdb
|
||||
// aliases every extract column after its function, so a repeat comes back as an
|
||||
// order-dependent "<name>_2" that is neither a grouping key nor a folded
|
||||
// aggregate, leaving the first backend's value frozen in the merged row.
|
||||
func columnClash(name string) error {
|
||||
return fmt.Errorf("extract projects the column %q more than once: openvoxdb returns the repeat as %q, which pdbmux can neither key on nor fold", name, name+"_2")
|
||||
}
|
||||
|
||||
// rewriteAvg replaces the client's avg column with the sum and count of the same
|
||||
// expression, so the true weighted average can be computed from the shards:
|
||||
// Postgres avg(x) is sum(x)/count(x), and both of those do combine.
|
||||
@@ -242,13 +260,6 @@ func appendUnique(s []string, v string) []string {
|
||||
return append(s, v)
|
||||
}
|
||||
|
||||
func appendUniqueAgg(s []aggColumn, v aggColumn) []aggColumn {
|
||||
if hasAgg(s, v.name) {
|
||||
return s
|
||||
}
|
||||
return append(s, v)
|
||||
}
|
||||
|
||||
func hasAgg(s []aggColumn, name string) bool {
|
||||
for _, x := range s {
|
||||
if x.name == name {
|
||||
|
||||
Reference in New Issue
Block a user