@pythia-software/query-table-core 0.9.9 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +459 -359
- package/dist/index.js +422 -116
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -1201,6 +1201,7 @@ function parseHostQueryState(input) {
|
|
|
1201
1201
|
if (new TextEncoder().encode(JSON.stringify(state)).length > MAX_HOST_QUERY_STATE_BYTES) fail("payload too large.");
|
|
1202
1202
|
return state;
|
|
1203
1203
|
}
|
|
1204
|
+
var FIT_MODES = /* @__PURE__ */ new Set(["content", "screen"]);
|
|
1204
1205
|
var EMPTY_QUERY = {
|
|
1205
1206
|
select: [],
|
|
1206
1207
|
where: [],
|
|
@@ -1390,7 +1391,11 @@ function normalizeQuery(input, fallback, links) {
|
|
|
1390
1391
|
limit: boundedInteger(raw.limit, fallback.limit, 1, MAX_QUERY_LIMIT),
|
|
1391
1392
|
offset: boundedInteger(raw.offset, fallback.offset, 0, MAX_QUERY_OFFSET)
|
|
1392
1393
|
};
|
|
1393
|
-
if (raw.
|
|
1394
|
+
if (typeof raw.fit === "string" && FIT_MODES.has(raw.fit)) out.fit = raw.fit;
|
|
1395
|
+
else if (raw.fitToScreen === true) {
|
|
1396
|
+
out.fit = "screen";
|
|
1397
|
+
for (const column of select) delete column.width;
|
|
1398
|
+
}
|
|
1394
1399
|
if (links && raw.hostState !== void 0) {
|
|
1395
1400
|
try {
|
|
1396
1401
|
out.hostState = parseHostQueryState(raw.hostState);
|
|
@@ -1561,7 +1566,7 @@ function normalizeGroupingState(value, links) {
|
|
|
1561
1566
|
return { keys, summaries, representative, source: normalizedSource, ...diagnostics.length ? { diagnostics: [...new Set(diagnostics)] } : {} };
|
|
1562
1567
|
}
|
|
1563
1568
|
var QUERY_FACETS = /* @__PURE__ */ new Set(["filters", "columns", "order", "grouping", "metrics", "limit"]);
|
|
1564
|
-
var PATCH_QUERY_KEYS = ["select", "where", "orderBy", "limit", "aggregations", "grouping", "
|
|
1569
|
+
var PATCH_QUERY_KEYS = ["select", "where", "orderBy", "limit", "aggregations", "grouping", "fit"];
|
|
1565
1570
|
var MAX_LINK_PARAMS = 50;
|
|
1566
1571
|
function normalizeLinkSpec(value) {
|
|
1567
1572
|
if (!isRecord(value) || !isRecord(value.to)) return void 0;
|
|
@@ -1613,14 +1618,15 @@ function normalizeLinkPatch(value) {
|
|
|
1613
1618
|
if (raw.aggregations !== void 0 && (!Array.isArray(raw.aggregations) || raw.aggregations.length !== (normalized.aggregations?.length ?? 0) || normalized.aggregations?.some((clause) => clause.diagnostics?.length))) return void 0;
|
|
1614
1619
|
const query = {};
|
|
1615
1620
|
for (const key of PATCH_QUERY_KEYS) {
|
|
1616
|
-
|
|
1621
|
+
const supplied = raw[key] !== void 0 || key === "fit" && raw.fitToScreen !== void 0;
|
|
1622
|
+
if (supplied && normalized[key] !== void 0) query[key] = normalized[key];
|
|
1617
1623
|
}
|
|
1618
1624
|
return { id: value.id, owns: [...new Set(value.owns)], query };
|
|
1619
1625
|
}
|
|
1620
1626
|
function queriesEqual(a, b) {
|
|
1621
1627
|
if (a === b) return true;
|
|
1622
1628
|
if (a.limit !== b.limit || a.offset !== b.offset) return false;
|
|
1623
|
-
if (a.
|
|
1629
|
+
if (a.fit !== b.fit) return false;
|
|
1624
1630
|
if (!sameArray(a.where, b.where)) return false;
|
|
1625
1631
|
if (!sameArray(a.orderBy, b.orderBy)) return false;
|
|
1626
1632
|
if (!sameArray(a.select, b.select)) return false;
|
|
@@ -1679,6 +1685,183 @@ function isGroupable(field) {
|
|
|
1679
1685
|
// src/encode.ts
|
|
1680
1686
|
import { deflateSync, Inflate } from "fflate";
|
|
1681
1687
|
|
|
1688
|
+
// src/wire.generated.ts
|
|
1689
|
+
var ERROR_CODES = [
|
|
1690
|
+
"invalid_json",
|
|
1691
|
+
"invalid_request",
|
|
1692
|
+
"method_not_allowed",
|
|
1693
|
+
"forbidden",
|
|
1694
|
+
"not_found",
|
|
1695
|
+
"request_too_large",
|
|
1696
|
+
"conflict",
|
|
1697
|
+
"quota_exceeded",
|
|
1698
|
+
"timeout",
|
|
1699
|
+
"cancelled",
|
|
1700
|
+
"unavailable",
|
|
1701
|
+
"internal",
|
|
1702
|
+
"version",
|
|
1703
|
+
"invalid_query",
|
|
1704
|
+
"profile_mismatch",
|
|
1705
|
+
"definition_changed",
|
|
1706
|
+
"execution_expired",
|
|
1707
|
+
"definition_unavailable",
|
|
1708
|
+
"definition_invalid",
|
|
1709
|
+
"datetime_value",
|
|
1710
|
+
"residual_query",
|
|
1711
|
+
"dataset",
|
|
1712
|
+
"schema",
|
|
1713
|
+
"source_required",
|
|
1714
|
+
"resolver_required",
|
|
1715
|
+
"resolver_scope",
|
|
1716
|
+
"snapshot_transaction_required",
|
|
1717
|
+
"snapshot_validation_required",
|
|
1718
|
+
"plan_token_validation_required",
|
|
1719
|
+
"numeric_domain_required",
|
|
1720
|
+
"unknown_field",
|
|
1721
|
+
"select_required",
|
|
1722
|
+
"filter",
|
|
1723
|
+
"filter_disabled",
|
|
1724
|
+
"sort",
|
|
1725
|
+
"collation_required",
|
|
1726
|
+
"unsupported_regex",
|
|
1727
|
+
"unsupported_literal",
|
|
1728
|
+
"unsupported_type",
|
|
1729
|
+
"invalid_source_value",
|
|
1730
|
+
"numeric_range",
|
|
1731
|
+
"encoding",
|
|
1732
|
+
"identity_required",
|
|
1733
|
+
"duplicate_identity",
|
|
1734
|
+
"scope",
|
|
1735
|
+
"window_required",
|
|
1736
|
+
"syntax",
|
|
1737
|
+
"unsupported_syntax",
|
|
1738
|
+
"unsupported_function",
|
|
1739
|
+
"arity",
|
|
1740
|
+
"type_error",
|
|
1741
|
+
"aggregate_required",
|
|
1742
|
+
"aggregate_context",
|
|
1743
|
+
"aggregate_disabled",
|
|
1744
|
+
"paired_expression_required",
|
|
1745
|
+
"dependency_cycle",
|
|
1746
|
+
"invalid_metric",
|
|
1747
|
+
"duplicate_metric",
|
|
1748
|
+
"metric_sort",
|
|
1749
|
+
"distribution",
|
|
1750
|
+
"date_grouping",
|
|
1751
|
+
"unsupported_date_grouping",
|
|
1752
|
+
"min_count_grouping",
|
|
1753
|
+
"min_count_field",
|
|
1754
|
+
"min_count_distribution",
|
|
1755
|
+
"group_disabled",
|
|
1756
|
+
"grouping",
|
|
1757
|
+
"representative",
|
|
1758
|
+
"resource_limit",
|
|
1759
|
+
"complexity_limit",
|
|
1760
|
+
"dependency_limit",
|
|
1761
|
+
"expansion_limit",
|
|
1762
|
+
"source_limit",
|
|
1763
|
+
"stage_limit",
|
|
1764
|
+
"schema_limit",
|
|
1765
|
+
"capability_unsupported",
|
|
1766
|
+
"unexpanded_series",
|
|
1767
|
+
"invalid",
|
|
1768
|
+
"too_large",
|
|
1769
|
+
"unsupported_version"
|
|
1770
|
+
];
|
|
1771
|
+
|
|
1772
|
+
// src/errors.ts
|
|
1773
|
+
var KNOWN = new Set(ERROR_CODES);
|
|
1774
|
+
var isErrorCode = (value) => typeof value === "string" && KNOWN.has(value);
|
|
1775
|
+
var QueryError = class extends Error {
|
|
1776
|
+
constructor(code, message, options2 = {}) {
|
|
1777
|
+
super(message);
|
|
1778
|
+
this.name = "QueryError";
|
|
1779
|
+
this.code = code;
|
|
1780
|
+
for (const key of ["field", "offset", "metricId", "status", "capability", "cause"])
|
|
1781
|
+
if (options2[key] !== void 0) this[key] = options2[key];
|
|
1782
|
+
}
|
|
1783
|
+
/** The QueryErrorDetail wire shape of this error. */
|
|
1784
|
+
toJSON() {
|
|
1785
|
+
return {
|
|
1786
|
+
code: this.code,
|
|
1787
|
+
message: this.message,
|
|
1788
|
+
...this.field !== void 0 ? { field: this.field } : {},
|
|
1789
|
+
...this.offset !== void 0 ? { offset: this.offset } : {},
|
|
1790
|
+
...this.metricId !== void 0 ? { metricId: this.metricId } : {}
|
|
1791
|
+
};
|
|
1792
|
+
}
|
|
1793
|
+
};
|
|
1794
|
+
var isQueryError = (error) => error instanceof QueryError;
|
|
1795
|
+
function errorCodeForStatus(status) {
|
|
1796
|
+
switch (status) {
|
|
1797
|
+
case 400:
|
|
1798
|
+
return "invalid_request";
|
|
1799
|
+
case 401:
|
|
1800
|
+
case 403:
|
|
1801
|
+
return "forbidden";
|
|
1802
|
+
case 404:
|
|
1803
|
+
return "not_found";
|
|
1804
|
+
case 405:
|
|
1805
|
+
return "method_not_allowed";
|
|
1806
|
+
case 408:
|
|
1807
|
+
return "cancelled";
|
|
1808
|
+
case 409:
|
|
1809
|
+
return "conflict";
|
|
1810
|
+
case 413:
|
|
1811
|
+
return "request_too_large";
|
|
1812
|
+
case 422:
|
|
1813
|
+
case 429:
|
|
1814
|
+
return "resource_limit";
|
|
1815
|
+
case 502:
|
|
1816
|
+
case 503:
|
|
1817
|
+
return "unavailable";
|
|
1818
|
+
case 504:
|
|
1819
|
+
return "timeout";
|
|
1820
|
+
case 507:
|
|
1821
|
+
return "quota_exceeded";
|
|
1822
|
+
}
|
|
1823
|
+
return "internal";
|
|
1824
|
+
}
|
|
1825
|
+
function parseQueryErrorBody(body) {
|
|
1826
|
+
const error = body?.error;
|
|
1827
|
+
if (!error || typeof error !== "object" || typeof error.code !== "string" || !error.code || typeof error.message !== "string") return null;
|
|
1828
|
+
return {
|
|
1829
|
+
code: error.code,
|
|
1830
|
+
message: error.message,
|
|
1831
|
+
...typeof error.field === "string" ? { field: error.field } : {},
|
|
1832
|
+
...Number.isSafeInteger(error.offset) ? { offset: error.offset } : {},
|
|
1833
|
+
...typeof error.metricId === "string" ? { metricId: error.metricId } : {}
|
|
1834
|
+
};
|
|
1835
|
+
}
|
|
1836
|
+
function queryErrorFromBody(body, status) {
|
|
1837
|
+
const detail = parseQueryErrorBody(body);
|
|
1838
|
+
if (!detail) return null;
|
|
1839
|
+
const { code, message, ...rest } = detail;
|
|
1840
|
+
return new QueryError(code, message, { ...rest, ...status !== void 0 ? { status } : {} });
|
|
1841
|
+
}
|
|
1842
|
+
async function queryErrorFromResponse(response) {
|
|
1843
|
+
let text2 = "";
|
|
1844
|
+
try {
|
|
1845
|
+
text2 = await response.text();
|
|
1846
|
+
} catch {
|
|
1847
|
+
}
|
|
1848
|
+
let body;
|
|
1849
|
+
try {
|
|
1850
|
+
body = JSON.parse(text2);
|
|
1851
|
+
} catch {
|
|
1852
|
+
body = void 0;
|
|
1853
|
+
}
|
|
1854
|
+
const parsed = queryErrorFromBody(body, response.status);
|
|
1855
|
+
if (parsed) return parsed;
|
|
1856
|
+
const legacy = typeof body?.error === "string" ? body.error : text2.trim();
|
|
1857
|
+
const message = legacy && legacy.length <= 500 && !/^\s*</.test(legacy) ? legacy : `Request failed with HTTP ${response.status}${response.statusText ? ` ${response.statusText}` : ""}.`;
|
|
1858
|
+
return new QueryError(errorCodeForStatus(response.status), message, { status: response.status });
|
|
1859
|
+
}
|
|
1860
|
+
function failedMetric(id, error, fallback) {
|
|
1861
|
+
const code = error instanceof QueryError ? error.code : fallback;
|
|
1862
|
+
return { id, buckets: [], error: error instanceof Error ? error.message : String(error), errorCode: code };
|
|
1863
|
+
}
|
|
1864
|
+
|
|
1682
1865
|
// src/setFilter.ts
|
|
1683
1866
|
function setFilterTarget(field) {
|
|
1684
1867
|
if (field.filter?.editor !== "set") return null;
|
|
@@ -1768,16 +1951,15 @@ function compactQuery(q) {
|
|
|
1768
1951
|
if (q.offset) c.f = q.offset;
|
|
1769
1952
|
if (q.aggregations?.length) c.g = q.aggregations;
|
|
1770
1953
|
if (q.itemDetails) c.d = q.itemDetails;
|
|
1771
|
-
if (q.
|
|
1954
|
+
if (q.fit) c.ft = q.fit;
|
|
1772
1955
|
if (q.grouping) c.b = q.grouping;
|
|
1773
1956
|
if (q.hostState) c.h = q.hostState;
|
|
1774
1957
|
return c;
|
|
1775
1958
|
}
|
|
1776
1959
|
var MAX_QUERY_STATE_BYTES = 4 * 1024 * 1024;
|
|
1777
|
-
var QueryDecodeError = class extends
|
|
1960
|
+
var QueryDecodeError = class extends QueryError {
|
|
1778
1961
|
constructor(code, message) {
|
|
1779
|
-
super(message);
|
|
1780
|
-
this.code = code;
|
|
1962
|
+
super(code, message);
|
|
1781
1963
|
this.name = "QueryDecodeError";
|
|
1782
1964
|
}
|
|
1783
1965
|
};
|
|
@@ -1911,6 +2093,7 @@ function decodeToken(token, strict) {
|
|
|
1911
2093
|
offset: c.f,
|
|
1912
2094
|
aggregations: c.g,
|
|
1913
2095
|
itemDetails: c.d,
|
|
2096
|
+
fit: c.ft,
|
|
1914
2097
|
fitToScreen: c.fs,
|
|
1915
2098
|
grouping: c.b,
|
|
1916
2099
|
hostState: c.h
|
|
@@ -1986,29 +2169,6 @@ function termIsPushdown(term, byName, resolveField2) {
|
|
|
1986
2169
|
return f2 != null && isPushdownFilter(f2);
|
|
1987
2170
|
});
|
|
1988
2171
|
}
|
|
1989
|
-
function toAggregationQuery(q, schema) {
|
|
1990
|
-
if (q.grouping) throw new Error("Metrics over grouped rows require grouped-query execution.");
|
|
1991
|
-
const suppliedCount = q.where.reduce((n, t) => n + predicatesOf(t).length, 0);
|
|
1992
|
-
q = normalizeQueryState(q);
|
|
1993
|
-
if (q.aggregations?.some((a) => a.dateGrouping)) throw new Error("Date grouping currently requires local metric execution.");
|
|
1994
|
-
if (q.aggregations?.some((a) => a.diagnostics?.length || a.expression !== void 0 || a.expressionY !== void 0 || a.distribution || a.scope === "shownRows" || a.sort?.length || a.groupLimit || a.where?.length || a.minCount !== void 0)) throw new Error("Metric definition requires the version 2 metric transport.");
|
|
1995
|
-
const byName = indexFields(schema);
|
|
1996
|
-
const resolveField2 = (name) => resolveFieldName(schema, name) ?? name;
|
|
1997
|
-
const where = q.where.filter((term) => termIsPushdown(term, byName, resolveField2)).map(stripSetFilter);
|
|
1998
|
-
const isBackend = (name) => {
|
|
1999
|
-
if (name == null) return true;
|
|
2000
|
-
const field = resolveField2(name);
|
|
2001
|
-
const f2 = byName.get(field);
|
|
2002
|
-
return f2 != null && f2.source.kind === "backend";
|
|
2003
|
-
};
|
|
2004
|
-
const aggregations = (q.aggregations ?? []).filter(
|
|
2005
|
-
(a) => isBackend(a.field) && a.groupBy.every((g) => isBackend(g))
|
|
2006
|
-
);
|
|
2007
|
-
const diagnostics = [];
|
|
2008
|
-
for (const c of q.aggregations ?? []) if (!aggregations.includes(c)) diagnostics.push({ metricId: c.id, code: "unsupported_metric", message: "Metric requires unavailable backend fields.", from: 0, to: 1 });
|
|
2009
|
-
if (where.length !== q.where.length || suppliedCount !== q.where.reduce((n, t) => n + predicatesOf(t).length, 0)) diagnostics.push({ code: "residual_filter", message: "All-matching metrics are unavailable with residual client-only filters.", from: 0, to: 1 });
|
|
2010
|
-
return { where, aggregations, ...diagnostics.length ? { diagnostics } : {} };
|
|
2011
|
-
}
|
|
2012
2172
|
|
|
2013
2173
|
// src/queryLinks.ts
|
|
2014
2174
|
var codePattern = /^[A-Za-z0-9_-]{22}$/;
|
|
@@ -2383,14 +2543,22 @@ function httpComputedColumnStore(base, fetcher = fetch) {
|
|
|
2383
2543
|
const listeners = /* @__PURE__ */ new Map();
|
|
2384
2544
|
const url = (dataset) => `${base}${base.includes("?") ? "&" : "?"}dataset=${encodeURIComponent(dataset)}`;
|
|
2385
2545
|
async function read(response) {
|
|
2386
|
-
if (response.
|
|
2387
|
-
|
|
2388
|
-
|
|
2389
|
-
|
|
2390
|
-
|
|
2391
|
-
|
|
2392
|
-
|
|
2546
|
+
if (!response.ok) {
|
|
2547
|
+
const error = await queryErrorFromResponse(response);
|
|
2548
|
+
if (response.status !== 409) throw error;
|
|
2549
|
+
const { code, field, offset, metricId, status } = error;
|
|
2550
|
+
throw new QueryError(
|
|
2551
|
+
code,
|
|
2552
|
+
"This definition changed. Reload it before saving again.",
|
|
2553
|
+
{
|
|
2554
|
+
...field !== void 0 ? { field } : {},
|
|
2555
|
+
...offset !== void 0 ? { offset } : {},
|
|
2556
|
+
...metricId !== void 0 ? { metricId } : {},
|
|
2557
|
+
...status !== void 0 ? { status } : {},
|
|
2558
|
+
cause: error
|
|
2559
|
+
}
|
|
2393
2560
|
);
|
|
2561
|
+
}
|
|
2394
2562
|
return response.json();
|
|
2395
2563
|
}
|
|
2396
2564
|
return {
|
|
@@ -4024,7 +4192,7 @@ function planMetricSeries(query, schema, options2 = {}) {
|
|
|
4024
4192
|
return prepared.map((item) => {
|
|
4025
4193
|
const c = item.clause;
|
|
4026
4194
|
if (item.kind === "error")
|
|
4027
|
-
return { id: c.id, buckets: [], error: item.error };
|
|
4195
|
+
return { id: c.id, buckets: [], error: item.error, errorCode: "invalid_metric" };
|
|
4028
4196
|
if (item.kind === "empty") {
|
|
4029
4197
|
const none = {
|
|
4030
4198
|
id: c.id,
|
|
@@ -4039,20 +4207,20 @@ function planMetricSeries(query, schema, options2 = {}) {
|
|
|
4039
4207
|
(part) => byId.get(part.id) ?? {
|
|
4040
4208
|
id: part.id,
|
|
4041
4209
|
buckets: [],
|
|
4042
|
-
error: "Server omitted this metric."
|
|
4210
|
+
error: "Server omitted this metric.",
|
|
4211
|
+
errorCode: "internal"
|
|
4043
4212
|
}
|
|
4044
4213
|
);
|
|
4045
4214
|
if (item.kind === "plain") return { ...entries[0], id: c.id };
|
|
4215
|
+
const failed = entries.find((entry) => entry.error);
|
|
4216
|
+
if (failed)
|
|
4217
|
+
return { id: c.id, buckets: [], error: failed.error, ...failed.errorCode ? { errorCode: failed.errorCode } : {} };
|
|
4046
4218
|
try {
|
|
4047
4219
|
if (item.kind === "occurrence")
|
|
4048
4220
|
return combineOccurrences(c, entries[0]);
|
|
4049
4221
|
return combineSeries(item, entries);
|
|
4050
4222
|
} catch (e) {
|
|
4051
|
-
return
|
|
4052
|
-
id: c.id,
|
|
4053
|
-
buckets: [],
|
|
4054
|
-
error: e instanceof Error ? e.message : String(e)
|
|
4055
|
-
};
|
|
4223
|
+
return failedMetric(c.id, e, "invalid_metric");
|
|
4056
4224
|
}
|
|
4057
4225
|
});
|
|
4058
4226
|
}
|
|
@@ -5205,7 +5373,7 @@ function distinctAccumulator(options2) {
|
|
|
5205
5373
|
if (v === null || values.has(v)) return;
|
|
5206
5374
|
const size = 64 + (typeof v === "string" ? v.length * 2 : 8);
|
|
5207
5375
|
if (values.size >= cardinality || size > byteLimit - bytes)
|
|
5208
|
-
throw
|
|
5376
|
+
throw new QueryError("resource_limit", "Metric distinct retained-memory budget exceeded.");
|
|
5209
5377
|
values.add(v);
|
|
5210
5378
|
bytes += size;
|
|
5211
5379
|
},
|
|
@@ -5756,15 +5924,15 @@ function assertMetricQueryState(q, schema) {
|
|
|
5756
5924
|
throw Error("Malformed metric query state cannot be executed exactly.");
|
|
5757
5925
|
}
|
|
5758
5926
|
function selectMetricShownRows(rows, q, schema, options2 = {}) {
|
|
5759
|
-
if (options2.signal?.aborted) throw
|
|
5927
|
+
if (options2.signal?.aborted) throw new QueryError("cancelled", "Metric evaluation cancelled.");
|
|
5760
5928
|
assertMetricQueryState(q, schema);
|
|
5761
5929
|
if (options2.shownRows) return options2.shownRows;
|
|
5762
5930
|
const orderBy = q.orderBy.length ? q.orderBy : schema.defaultSort ?? [];
|
|
5763
5931
|
assertMetricPopulation(q, schema, orderBy);
|
|
5764
5932
|
if (rows.length > (options2.maxRows ?? 1e6))
|
|
5765
|
-
throw
|
|
5933
|
+
throw new QueryError("resource_limit", "Metric row budget exceeded.");
|
|
5766
5934
|
if ((options2.maxOperations ?? 1e7) <= 0)
|
|
5767
|
-
throw
|
|
5935
|
+
throw new QueryError("resource_limit", "Metric operation budget exceeded.");
|
|
5768
5936
|
return applyQuery(
|
|
5769
5937
|
rows,
|
|
5770
5938
|
// Local tables use a stable sort: ties retain input order. An extra ID
|
|
@@ -5805,7 +5973,7 @@ var scalar = (v) => {
|
|
|
5805
5973
|
function groupStringLength(value, limit) {
|
|
5806
5974
|
let length = value.length + 2;
|
|
5807
5975
|
if (length > limit)
|
|
5808
|
-
throw
|
|
5976
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
5809
5977
|
for (let i = 0; i < value.length; i++) {
|
|
5810
5978
|
const code = value.charCodeAt(i);
|
|
5811
5979
|
if (code === 34 || code === 92 || code === 8 || code === 9 || code === 10 || code === 12 || code === 13)
|
|
@@ -5815,7 +5983,7 @@ function groupStringLength(value, limit) {
|
|
|
5815
5983
|
i++;
|
|
5816
5984
|
else if (code >= 55296 && code <= 57343) length += 5;
|
|
5817
5985
|
if (length > limit)
|
|
5818
|
-
throw
|
|
5986
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
5819
5987
|
}
|
|
5820
5988
|
return length;
|
|
5821
5989
|
}
|
|
@@ -5855,8 +6023,8 @@ function sortMetricBuckets(buckets, c) {
|
|
|
5855
6023
|
function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
5856
6024
|
if (q.grouping) {
|
|
5857
6025
|
try {
|
|
5858
|
-
if (options2.signal?.aborted) throw new
|
|
5859
|
-
if (rows.length > (options2.maxRows ?? 1e6)) throw new
|
|
6026
|
+
if (options2.signal?.aborted) throw new QueryError("cancelled", "Metric evaluation cancelled.");
|
|
6027
|
+
if (rows.length > (options2.maxRows ?? 1e6)) throw new QueryError("resource_limit", "Metric row budget exceeded.");
|
|
5860
6028
|
const now2 = evaluationTime(options2);
|
|
5861
6029
|
const prepared = prepareGrouping(rows, schema, q.grouping, { now: now2 });
|
|
5862
6030
|
return evaluateMetrics(prepared.rows, withoutGrouping(q), prepared.schema, {
|
|
@@ -5864,7 +6032,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5864
6032
|
now: now2
|
|
5865
6033
|
});
|
|
5866
6034
|
} catch (error) {
|
|
5867
|
-
return { metrics: (q.aggregations ?? []).map((metric) => (
|
|
6035
|
+
return { metrics: (q.aggregations ?? []).map((metric) => failedMetric(metric.id, error, "grouping")) };
|
|
5868
6036
|
}
|
|
5869
6037
|
}
|
|
5870
6038
|
const now = evaluationTime(options2), fields = indexFields(schema);
|
|
@@ -5886,19 +6054,19 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5886
6054
|
metrics: (q.aggregations ?? []).map((c) => {
|
|
5887
6055
|
let budget = options2.maxOperations ?? 1e7;
|
|
5888
6056
|
const tick = () => {
|
|
5889
|
-
if (--budget < 0) throw
|
|
6057
|
+
if (--budget < 0) throw new QueryError("resource_limit", "Metric operation budget exceeded.");
|
|
5890
6058
|
if (options2.signal?.aborted)
|
|
5891
|
-
throw
|
|
6059
|
+
throw new QueryError("cancelled", "Metric evaluation cancelled.");
|
|
5892
6060
|
};
|
|
5893
6061
|
try {
|
|
5894
6062
|
if (options2.signal?.aborted)
|
|
5895
|
-
throw
|
|
6063
|
+
throw new QueryError("cancelled", "Metric evaluation cancelled.");
|
|
5896
6064
|
assertMetricQueryState(q, schema);
|
|
5897
6065
|
if ((q.aggregations?.length ?? 0) > 20)
|
|
5898
|
-
throw
|
|
6066
|
+
throw new QueryError("resource_limit", "At most 20 metrics are allowed.");
|
|
5899
6067
|
const diagnostics = validateMetric(c, schema, options2.resolveComputed);
|
|
5900
6068
|
if (diagnostics.length)
|
|
5901
|
-
throw
|
|
6069
|
+
throw new QueryError("invalid_metric", diagnostics.map((d) => d.message).join(" "), { metricId: c.id });
|
|
5902
6070
|
const ps = metricPlans(c, schema, options2.resolveComputed);
|
|
5903
6071
|
if (ps.some(hasRegex) && !options2.evaluateInput)
|
|
5904
6072
|
throw Error(
|
|
@@ -5911,7 +6079,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5911
6079
|
if (!matching) {
|
|
5912
6080
|
assertMetricPopulation(q, schema, []);
|
|
5913
6081
|
if (rows.length > (options2.maxRows ?? 1e6))
|
|
5914
|
-
throw
|
|
6082
|
+
throw new QueryError("resource_limit", "Metric row budget exceeded.");
|
|
5915
6083
|
matching = applyQuery(
|
|
5916
6084
|
rows,
|
|
5917
6085
|
{ ...q, orderBy: [], offset: 0, limit: Number.MAX_SAFE_INTEGER },
|
|
@@ -5922,7 +6090,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5922
6090
|
inputRows = matching;
|
|
5923
6091
|
}
|
|
5924
6092
|
if (inputRows.length > (options2.maxRows ?? 1e6))
|
|
5925
|
-
throw
|
|
6093
|
+
throw new QueryError("resource_limit", "Metric row budget exceeded.");
|
|
5926
6094
|
const population = inputRows.length;
|
|
5927
6095
|
if (c.where?.length)
|
|
5928
6096
|
inputRows = applyQuery(
|
|
@@ -5942,11 +6110,11 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5942
6110
|
let groupBytes = 0;
|
|
5943
6111
|
const reserve = (bytes) => {
|
|
5944
6112
|
if (bytes > byteLimit - groupBytes)
|
|
5945
|
-
throw
|
|
6113
|
+
throw new QueryError("resource_limit", "Metric grouping retained-memory budget exceeded.");
|
|
5946
6114
|
};
|
|
5947
6115
|
if (!c.groupBy.length) {
|
|
5948
6116
|
if (keyLimit < 2)
|
|
5949
|
-
throw
|
|
6117
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
5950
6118
|
reserve(256);
|
|
5951
6119
|
groupBytes = 256;
|
|
5952
6120
|
groups.set("[]", { keys: [], rows: [] });
|
|
@@ -5995,7 +6163,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
5995
6163
|
keyLength += 1 + (typeof value === "string" ? groupStringLength(value, keyLimit - keyLength - 1) : value === null ? 4 : typeof value === "boolean" ? value ? 4 : 5 : String(value).length);
|
|
5996
6164
|
payloadBytes += typeof value === "string" ? 2 * value.length : 8;
|
|
5997
6165
|
if (keyLength > keyLimit)
|
|
5998
|
-
throw
|
|
6166
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
5999
6167
|
}
|
|
6000
6168
|
const retainedBytes = 256 + 16 * keys.length + payloadBytes + 2 * keyLength;
|
|
6001
6169
|
reserve(retainedBytes + 2 * keyLength + 16);
|
|
@@ -6003,7 +6171,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
6003
6171
|
let g = groups.get(key);
|
|
6004
6172
|
if (!g) {
|
|
6005
6173
|
if (groups.size >= (options2.maxGroups ?? 1e4))
|
|
6006
|
-
throw
|
|
6174
|
+
throw new QueryError("resource_limit", "Metric group budget exceeded.");
|
|
6007
6175
|
g = { keys, rows: [] };
|
|
6008
6176
|
groups.set(key, g);
|
|
6009
6177
|
groupBytes += retainedBytes;
|
|
@@ -6043,10 +6211,10 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
6043
6211
|
const keys = [slot, ...tuple];
|
|
6044
6212
|
const key = JSON.stringify(keys);
|
|
6045
6213
|
if (key.length > keyLimit)
|
|
6046
|
-
throw
|
|
6214
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
6047
6215
|
if (!groups.has(key)) {
|
|
6048
6216
|
if (groups.size >= (options2.maxGroups ?? 1e4))
|
|
6049
|
-
throw
|
|
6217
|
+
throw new QueryError("resource_limit", "Metric group budget exceeded.");
|
|
6050
6218
|
const bytes = 256 + 16 * keys.length + 4 * key.length;
|
|
6051
6219
|
reserve(bytes);
|
|
6052
6220
|
groupBytes += bytes;
|
|
@@ -6071,10 +6239,10 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
6071
6239
|
];
|
|
6072
6240
|
const key = JSON.stringify(keys);
|
|
6073
6241
|
if (key.length > keyLimit)
|
|
6074
|
-
throw
|
|
6242
|
+
throw new QueryError("resource_limit", "Metric grouping key length budget exceeded.");
|
|
6075
6243
|
if (!groups.has(key)) {
|
|
6076
6244
|
if (groups.size >= (options2.maxGroups ?? 1e4))
|
|
6077
|
-
throw
|
|
6245
|
+
throw new QueryError("resource_limit", "Metric group budget exceeded.");
|
|
6078
6246
|
const bytes = 256 + 16 * keys.length + 4 * key.length;
|
|
6079
6247
|
reserve(bytes);
|
|
6080
6248
|
groupBytes += bytes;
|
|
@@ -6087,7 +6255,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
6087
6255
|
config.timeZone
|
|
6088
6256
|
);
|
|
6089
6257
|
if (!Number.isFinite(next) || next <= time)
|
|
6090
|
-
throw
|
|
6258
|
+
throw new QueryError("resource_limit", "Date grouping exceeds calendar limits.");
|
|
6091
6259
|
time = next;
|
|
6092
6260
|
}
|
|
6093
6261
|
}
|
|
@@ -6258,11 +6426,7 @@ function evaluateMetrics(rows, q, schema, options2 = {}) {
|
|
|
6258
6426
|
...other ? { other } : {}
|
|
6259
6427
|
};
|
|
6260
6428
|
} catch (e) {
|
|
6261
|
-
return
|
|
6262
|
-
id: c.id,
|
|
6263
|
-
buckets: [],
|
|
6264
|
-
error: e instanceof Error ? e.message : String(e)
|
|
6265
|
-
};
|
|
6429
|
+
return failedMetric(c.id, e, "invalid_metric");
|
|
6266
6430
|
}
|
|
6267
6431
|
})
|
|
6268
6432
|
};
|
|
@@ -6377,7 +6541,8 @@ function applyAggregations(rows, q, schema, options2 = {}) {
|
|
|
6377
6541
|
return {
|
|
6378
6542
|
id: agg.id,
|
|
6379
6543
|
buckets: [],
|
|
6380
|
-
error: `Field ${field.name} does not allow ${agg.op}
|
|
6544
|
+
error: `Field ${field.name} does not allow ${agg.op}.`,
|
|
6545
|
+
errorCode: "aggregate_disabled"
|
|
6381
6546
|
};
|
|
6382
6547
|
return {
|
|
6383
6548
|
id: agg.id,
|
|
@@ -7205,6 +7370,8 @@ function resolveGroupLink(query, value, targets = {}) {
|
|
|
7205
7370
|
}
|
|
7206
7371
|
|
|
7207
7372
|
// src/queryV3.ts
|
|
7373
|
+
var MAX_DISTINCT_LIMIT = 500;
|
|
7374
|
+
var MAX_DISTINCT_SEARCH_LENGTH = 2e3;
|
|
7208
7375
|
function sourceExecutionSchema(schema, execution, catalogue = []) {
|
|
7209
7376
|
const fields = new Map(schema.fields.map((field) => [field.name, field]));
|
|
7210
7377
|
for (const [name, capability] of Object.entries(execution?.fields ?? {})) {
|
|
@@ -7225,73 +7392,97 @@ function sourceExecutionSchema(schema, execution, catalogue = []) {
|
|
|
7225
7392
|
}
|
|
7226
7393
|
return { ...schema, fields: [...fields.values()] };
|
|
7227
7394
|
}
|
|
7395
|
+
var profileUnavailable = (capabilities, execution) => capabilities.version !== 3 || !!execution && execution.profile !== capabilities.profile;
|
|
7396
|
+
var unsupported = (capability, message, field) => ({ code: "capability_unsupported", message, capability, ...field !== void 0 ? { field } : {} });
|
|
7397
|
+
function refuse(refusals) {
|
|
7398
|
+
const [first] = refusals;
|
|
7399
|
+
if (!first) return;
|
|
7400
|
+
const { code, message: _message, ...details } = first;
|
|
7401
|
+
throw new QueryError(code, [...new Set(refusals.map((refusal) => refusal.message))].join(" "), details);
|
|
7402
|
+
}
|
|
7403
|
+
function requestEnvelope(capabilities, execution, now, refusals) {
|
|
7404
|
+
const revisions = /* @__PURE__ */ Object.create(null);
|
|
7405
|
+
for (const [id, revision] of Object.entries(execution?.resolvedRevisions ?? {})) {
|
|
7406
|
+
if (!revision || id.length > 256 || revision.length > 256) refusals.push({ code: "definition_changed", message: "Invalid computed revision envelope." });
|
|
7407
|
+
else revisions[id.startsWith("@computed/") ? id.slice(10) : id] = revision;
|
|
7408
|
+
}
|
|
7409
|
+
return {
|
|
7410
|
+
version: 3,
|
|
7411
|
+
profile: capabilities.profile,
|
|
7412
|
+
now,
|
|
7413
|
+
expectedRevisions: revisions,
|
|
7414
|
+
...execution?.planToken ? { planToken: execution.planToken } : {},
|
|
7415
|
+
...execution?.snapshot !== void 0 ? { snapshot: execution.snapshot } : {}
|
|
7416
|
+
};
|
|
7417
|
+
}
|
|
7418
|
+
var profileRefusal = { code: "profile_mismatch", message: "The negotiated query profile is unavailable." };
|
|
7228
7419
|
function toQueryRequestV3(query, baseSchema, capabilities, execution, options2 = {}) {
|
|
7229
7420
|
const now = evaluationTime(options2), q = normalizeQueryState(query);
|
|
7230
7421
|
const sourceSchema = sourceExecutionSchema(baseSchema, execution);
|
|
7231
7422
|
const source = q.grouping?.source ?? withoutGrouping(q);
|
|
7232
|
-
const
|
|
7233
|
-
if ((query.aggregations?.length ?? 0) !== (q.aggregations?.length ?? 0))
|
|
7234
|
-
|
|
7423
|
+
const refusals = [];
|
|
7424
|
+
if ((query.aggregations?.length ?? 0) !== (q.aggregations?.length ?? 0))
|
|
7425
|
+
refusals.push({ code: "invalid_metric", message: "The query exceeds its metric budget or contains malformed metric state." });
|
|
7426
|
+
if (profileUnavailable(capabilities, execution)) refusals.push(profileRefusal);
|
|
7235
7427
|
let resultSchema = sourceSchema;
|
|
7236
7428
|
if (q.grouping) {
|
|
7237
|
-
if (!capabilities.groupedRows)
|
|
7429
|
+
if (!capabilities.groupedRows) refusals.push(unsupported("groupedRows", "This server does not support grouped rows."));
|
|
7238
7430
|
else if (q.grouping.keys.some(isCalendarGroupKey) && !capabilities.calendarGroupKeys)
|
|
7239
|
-
|
|
7431
|
+
refusals.push(unsupported("calendarGroupKeys", "This server does not support grouping keys by calendar interval."));
|
|
7432
|
+
for (const message of q.grouping.diagnostics ?? []) refusals.push({ code: "grouping", message });
|
|
7240
7433
|
try {
|
|
7241
7434
|
const model = createGroupingModel(sourceSchema);
|
|
7242
7435
|
model.validate(q.grouping);
|
|
7243
7436
|
resultSchema = model.resultSchema(q.grouping.keys, q.grouping.summaries);
|
|
7244
7437
|
} catch (error) {
|
|
7245
|
-
|
|
7438
|
+
refusals.push({ code: "grouping", message: error instanceof Error ? error.message : String(error) });
|
|
7246
7439
|
}
|
|
7247
7440
|
}
|
|
7248
7441
|
const sourceRequest = toServerQuery(withoutGrouping(source), sourceSchema);
|
|
7249
7442
|
for (const term of source.where) for (const predicate of predicatesOf(term)) if (["matches_regex", "not_matches_regex"].includes(predicate.op) && !capabilities.regexSourceFilters)
|
|
7250
|
-
|
|
7443
|
+
refusals.push(unsupported("regexSourceFilters", "This server profile does not support isolated regex source filtering.", predicate.field));
|
|
7251
7444
|
const result = toServerQuery(withoutGrouping(q), resultSchema);
|
|
7252
|
-
if (sourceRequest.where.length !== source.where.length || result.where.length !== q.where.length)
|
|
7253
|
-
|
|
7445
|
+
if (sourceRequest.where.length !== source.where.length || result.where.length !== q.where.length)
|
|
7446
|
+
refusals.push({ code: "residual_query", message: "The query contains unsupported source or result predicates." });
|
|
7447
|
+
if (result.orderBy.length !== q.orderBy.length) refusals.push({ code: "residual_query", message: "The query contains unsupported result ordering." });
|
|
7254
7448
|
if (q.grouping) {
|
|
7255
7449
|
for (const term of q.grouping.source.where) for (const predicate of predicatesOf(term))
|
|
7256
|
-
if (predicate.field.startsWith("@computed/") && !capabilities.computedSourceFilters)
|
|
7450
|
+
if (predicate.field.startsWith("@computed/") && !capabilities.computedSourceFilters)
|
|
7451
|
+
refusals.push(unsupported("computedSourceFilters", "This server does not support computed source predicates.", predicate.field));
|
|
7257
7452
|
}
|
|
7258
7453
|
const metricQuery = toMetricQuery(withoutGrouping(q), resultSchema, execution);
|
|
7259
|
-
|
|
7260
|
-
|
|
7261
|
-
|
|
7262
|
-
|
|
7263
|
-
})
|
|
7264
|
-
|
|
7265
|
-
|
|
7266
|
-
|
|
7267
|
-
|
|
7268
|
-
|
|
7269
|
-
|
|
7270
|
-
|
|
7271
|
-
|
|
7272
|
-
if (
|
|
7273
|
-
|
|
7274
|
-
|
|
7454
|
+
for (const issue of metricQuery.diagnostics)
|
|
7455
|
+
if (issue.code === "residual_metric_filter" || issue.code === "unexpanded_series")
|
|
7456
|
+
refusals.push({ code: issue.code === "unexpanded_series" ? "unexpanded_series" : "residual_query", message: issue.message, ...issue.metricId ? { metricId: issue.metricId } : {} });
|
|
7457
|
+
const metrics = [];
|
|
7458
|
+
for (const { unsupportedOp, ...clause } of metricQuery.metrics) {
|
|
7459
|
+
const { diagnostics: _diagnostics, ...projected } = clause;
|
|
7460
|
+
if (unsupportedOp)
|
|
7461
|
+
refusals.push({ code: "invalid_metric", message: `Metric ${projected.id} uses an unsupported operation: ${unsupportedOp}.`, metricId: projected.id });
|
|
7462
|
+
const dateGrouping = q.aggregations?.find((c) => c.id === projected.id)?.dateGrouping;
|
|
7463
|
+
for (const issue of validateMetric({ ...clause, ...dateGrouping ? { dateGrouping } : {} }, resultSchema))
|
|
7464
|
+
refusals.push({ code: "invalid_metric", message: issue.message, metricId: projected.id });
|
|
7465
|
+
if (dateGrouping && !capabilities.calendarGrouping)
|
|
7466
|
+
refusals.push({ ...unsupported("calendarGrouping", "This server does not support calendar grouping."), metricId: projected.id });
|
|
7467
|
+
else if (isMetricCycle(dateGrouping?.interval) && !capabilities.calendarCycles)
|
|
7468
|
+
refusals.push({ ...unsupported("calendarCycles", "This server does not support hour-of-day or day-of-week grouping."), metricId: projected.id });
|
|
7469
|
+
metrics.push(dateGrouping ? { ...projected, dateGrouping } : projected);
|
|
7470
|
+
}
|
|
7471
|
+
const envelope = requestEnvelope(capabilities, execution, now, refusals);
|
|
7472
|
+
refuse(refusals);
|
|
7275
7473
|
return {
|
|
7276
|
-
|
|
7277
|
-
profile: capabilities.profile,
|
|
7278
|
-
now,
|
|
7279
|
-
expectedRevisions: revisions,
|
|
7280
|
-
...execution?.planToken ? { planToken: execution.planToken } : {},
|
|
7281
|
-
...execution?.snapshot !== void 0 ? { snapshot: execution.snapshot } : {},
|
|
7474
|
+
...envelope,
|
|
7282
7475
|
source: sourceRequest,
|
|
7283
7476
|
result,
|
|
7284
7477
|
...q.grouping ? { grouping: {
|
|
7285
7478
|
keys: q.grouping.keys,
|
|
7286
7479
|
// Summary display is presentation only and link columns are computed on
|
|
7287
7480
|
// the client; the wire contract is unchanged.
|
|
7288
|
-
summaries: withoutLinkSummaries(q.grouping.summaries).map(({ display: _display, ...summary }) => summary),
|
|
7289
|
-
representative: q.grouping.representative
|
|
7290
|
-
...q.grouping.diagnostics ? { diagnostics: q.grouping.diagnostics } : {}
|
|
7481
|
+
summaries: withoutLinkSummaries(q.grouping.summaries).map(({ display: _display, link: _link, ...summary }) => summary),
|
|
7482
|
+
representative: q.grouping.representative
|
|
7291
7483
|
} } : {},
|
|
7292
7484
|
rows: options2.rows !== false,
|
|
7293
|
-
metrics
|
|
7294
|
-
diagnostics: [...new Set(diagnostics)]
|
|
7485
|
+
metrics
|
|
7295
7486
|
};
|
|
7296
7487
|
}
|
|
7297
7488
|
function validateQueryResultV3(result, request, schema) {
|
|
@@ -7301,6 +7492,11 @@ function validateQueryResultV3(result, request, schema) {
|
|
|
7301
7492
|
if (!Array.isArray(result.schema.fields) || result.schema.fields.some((field) => expected.get(field.name) !== field.type) || request.rows && request.result.select.some((name) => !result.schema.fields.some((field) => field.name === name))) throw Error("The server returned a different projected schema.");
|
|
7302
7493
|
for (const [id, revision] of Object.entries(request.expectedRevisions)) if (result.execution.resolvedRevisions[id] !== revision)
|
|
7303
7494
|
throw Error("Computed definitions changed. Refresh the server execution handshake.");
|
|
7495
|
+
if (request.distinct) {
|
|
7496
|
+
const distinct = result.distinct;
|
|
7497
|
+
if (!distinct || !Array.isArray(distinct.values) || distinct.values.length > request.distinct.limit || distinct.values.some((value) => typeof value !== "string") || typeof distinct.hasMore !== "boolean" || typeof distinct.hasNull !== "boolean")
|
|
7498
|
+
throw Error("Invalid distinct values response.");
|
|
7499
|
+
}
|
|
7304
7500
|
const seen = /* @__PURE__ */ new Set();
|
|
7305
7501
|
for (const row of result.rows) {
|
|
7306
7502
|
const id = row[schema.idField];
|
|
@@ -7308,6 +7504,98 @@ function validateQueryResultV3(result, request, schema) {
|
|
|
7308
7504
|
seen.add(id);
|
|
7309
7505
|
}
|
|
7310
7506
|
}
|
|
7507
|
+
var emptyStage = (where = []) => ({ select: [], where, orderBy: [], limit: 1, offset: 0 });
|
|
7508
|
+
function auxiliaryRequest(capabilities, source, result, options2, part, refusals = []) {
|
|
7509
|
+
if (profileUnavailable(capabilities, options2.execution)) refusals.unshift(profileRefusal);
|
|
7510
|
+
const envelope = requestEnvelope(capabilities, options2.execution, evaluationTime(options2), refusals);
|
|
7511
|
+
refuse(refusals);
|
|
7512
|
+
return {
|
|
7513
|
+
...envelope,
|
|
7514
|
+
source,
|
|
7515
|
+
result,
|
|
7516
|
+
...part,
|
|
7517
|
+
...options2.hostState ? { hostState: options2.hostState } : {}
|
|
7518
|
+
};
|
|
7519
|
+
}
|
|
7520
|
+
async function execute(transport, request, signal) {
|
|
7521
|
+
const result = await transport.fetchQueryV3(request, signal);
|
|
7522
|
+
if (signal?.aborted) throw new DOMException("Aborted", "AbortError");
|
|
7523
|
+
return result;
|
|
7524
|
+
}
|
|
7525
|
+
function toDistinctRequestV3(field, search, capabilities, options2 = {}) {
|
|
7526
|
+
const limit = options2.limit ?? 50, refusals = [];
|
|
7527
|
+
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_DISTINCT_LIMIT) refusals.push({ code: "invalid_query", message: `Distinct value limit must be between 1 and ${MAX_DISTINCT_LIMIT}.` });
|
|
7528
|
+
if (search.length > MAX_DISTINCT_SEARCH_LENGTH) refusals.push({ code: "invalid_query", message: "Distinct search exceeds its budget." });
|
|
7529
|
+
return auxiliaryRequest(capabilities, emptyStage(options2.where), emptyStage(), options2, { rows: false, metrics: [], distinct: { field, search, limit } }, refusals);
|
|
7530
|
+
}
|
|
7531
|
+
function readDistinctResultV3(result, request, schema) {
|
|
7532
|
+
validateQueryResultV3(result, request, schema);
|
|
7533
|
+
const { values, hasMore, hasNull } = result.distinct;
|
|
7534
|
+
return { values: [...values], hasMore, hasNull };
|
|
7535
|
+
}
|
|
7536
|
+
async function fetchDistinctValuesV3(transport, schema, field, search, options2 = {}, signal) {
|
|
7537
|
+
const request = toDistinctRequestV3(field, search, transport.queryCapabilities, options2);
|
|
7538
|
+
return readDistinctResultV3(await execute(transport, request, signal), request, schema);
|
|
7539
|
+
}
|
|
7540
|
+
function rowLookupFields(schema) {
|
|
7541
|
+
return [.../* @__PURE__ */ new Set([schema.idField, ...schema.fields.filter((field) => field.source.kind === "backend" && isSelectable(field) && !field.name.startsWith("@computed/")).map((field) => field.name)])].sort();
|
|
7542
|
+
}
|
|
7543
|
+
function toRowRequestV3(schema, id, capabilities, options2 = {}) {
|
|
7544
|
+
const stage = {
|
|
7545
|
+
select: options2.select ? [.../* @__PURE__ */ new Set([schema.idField, ...options2.select])].sort() : rowLookupFields(schema),
|
|
7546
|
+
where: [{ field: schema.idField, op: "=", value: String(id) }],
|
|
7547
|
+
orderBy: [],
|
|
7548
|
+
limit: Math.max(1, Math.min(2, capabilities.maxRows)),
|
|
7549
|
+
offset: 0
|
|
7550
|
+
};
|
|
7551
|
+
return auxiliaryRequest(capabilities, stage, stage, options2, { rows: true, metrics: [] });
|
|
7552
|
+
}
|
|
7553
|
+
function readRowResultV3(result, request, schema) {
|
|
7554
|
+
if (Array.isArray(result?.rows) && result.rows.length > 1 || result?.total > 1) throw Error("Row identity must be unique.");
|
|
7555
|
+
validateQueryResultV3(result, request, schema);
|
|
7556
|
+
const row = result.rows[0];
|
|
7557
|
+
if (row && String(row[schema.idField]) !== request.result.where[0]?.value)
|
|
7558
|
+
throw Error("The server returned a different row.");
|
|
7559
|
+
return row ?? null;
|
|
7560
|
+
}
|
|
7561
|
+
async function fetchRowV3(transport, schema, id, options2 = {}, signal) {
|
|
7562
|
+
const request = toRowRequestV3(schema, id, transport.queryCapabilities, options2);
|
|
7563
|
+
return readRowResultV3(await execute(transport, request, signal), request, schema);
|
|
7564
|
+
}
|
|
7565
|
+
var STAT_OPS = [{ op: "count_distinct", key: "distinct" }, { op: "min", key: "min" }, { op: "max", key: "max" }];
|
|
7566
|
+
function toFieldStatsRequestV3(schema, fields, capabilities, options2 = {}) {
|
|
7567
|
+
const metrics = [];
|
|
7568
|
+
for (const name of new Set(fields)) {
|
|
7569
|
+
const field = schema.fields.find((f2) => f2.name === name);
|
|
7570
|
+
if (!field || field.source.kind !== "backend" || !isMeasurable(field)) continue;
|
|
7571
|
+
const ops = aggOpsForField(field);
|
|
7572
|
+
for (const { op, key } of STAT_OPS) if (ops.includes(op)) metrics.push({ id: `${name}:${key}`, op, field: name, groupBy: [] });
|
|
7573
|
+
}
|
|
7574
|
+
return auxiliaryRequest(capabilities, emptyStage(), emptyStage(), options2, { rows: false, metrics });
|
|
7575
|
+
}
|
|
7576
|
+
function readFieldStatsResultV3(result, request, schema) {
|
|
7577
|
+
validateQueryResultV3(result, request, schema);
|
|
7578
|
+
const byId = new Map(result.metrics.map((metric) => [metric.id, metric]));
|
|
7579
|
+
const stats = /* @__PURE__ */ new Map();
|
|
7580
|
+
for (const metric of request.metrics) {
|
|
7581
|
+
const entry = byId.get(metric.id), value = entry && !entry.error ? entry.buckets[0]?.value : void 0;
|
|
7582
|
+
const key = metric.id.slice(metric.field.length + 1);
|
|
7583
|
+
const item = stats.get(metric.field) ?? {};
|
|
7584
|
+
stats.set(metric.field, item);
|
|
7585
|
+
if (key === "distinct" ? typeof value === "number" : typeof value === "number" || typeof value === "string") item[key] = value;
|
|
7586
|
+
}
|
|
7587
|
+
return Object.fromEntries(stats);
|
|
7588
|
+
}
|
|
7589
|
+
async function fetchFieldStatsV3(transport, schema, fields, options2 = {}, signal) {
|
|
7590
|
+
const all = toFieldStatsRequestV3(schema, fields, transport.queryCapabilities, options2);
|
|
7591
|
+
const stats = /* @__PURE__ */ new Map();
|
|
7592
|
+
for (let i = 0; i < all.metrics.length; i += MAX_AGGREGATIONS) {
|
|
7593
|
+
const request = { ...all, metrics: all.metrics.slice(i, i + MAX_AGGREGATIONS) };
|
|
7594
|
+
for (const [name, item] of Object.entries(readFieldStatsResultV3(await execute(transport, request, signal), request, schema)))
|
|
7595
|
+
stats.set(name, { ...stats.get(name), ...item });
|
|
7596
|
+
}
|
|
7597
|
+
return Object.fromEntries(stats);
|
|
7598
|
+
}
|
|
7311
7599
|
|
|
7312
7600
|
// src/drilldown.ts
|
|
7313
7601
|
function metricSchema(query, schema) {
|
|
@@ -7551,6 +7839,7 @@ export {
|
|
|
7551
7839
|
AGG_OPS_NEEDING_FIELD,
|
|
7552
7840
|
COMPUTED_PREFIX,
|
|
7553
7841
|
EMPTY_QUERY,
|
|
7842
|
+
ERROR_CODES,
|
|
7554
7843
|
FORMULA_FUNCTIONS,
|
|
7555
7844
|
FormulaError,
|
|
7556
7845
|
GROUP_ID_FIELD,
|
|
@@ -7558,6 +7847,8 @@ export {
|
|
|
7558
7847
|
GROUP_LINK_RENDER,
|
|
7559
7848
|
ITEM_DETAILS_COLUMN,
|
|
7560
7849
|
MAX_AGGREGATIONS,
|
|
7850
|
+
MAX_DISTINCT_LIMIT,
|
|
7851
|
+
MAX_DISTINCT_SEARCH_LENGTH,
|
|
7561
7852
|
MAX_GROUP_BY_FIELDS,
|
|
7562
7853
|
MAX_HOST_QUERY_PARAMS,
|
|
7563
7854
|
MAX_HOST_QUERY_STATE_BYTES,
|
|
@@ -7585,6 +7876,7 @@ export {
|
|
|
7585
7876
|
OPS_BY_TYPE,
|
|
7586
7877
|
OP_LABEL,
|
|
7587
7878
|
QueryDecodeError,
|
|
7879
|
+
QueryError,
|
|
7588
7880
|
QuerySnapshotHttpError,
|
|
7589
7881
|
aggOpAllowedForType,
|
|
7590
7882
|
aggOpNeedsField,
|
|
@@ -7610,9 +7902,14 @@ export {
|
|
|
7610
7902
|
decodeQueryStrict,
|
|
7611
7903
|
discoverFieldRelationships,
|
|
7612
7904
|
encodeQuery,
|
|
7905
|
+
errorCodeForStatus,
|
|
7613
7906
|
evaluateMetricReductions,
|
|
7614
7907
|
evaluateMetrics,
|
|
7615
7908
|
exitGrouping,
|
|
7909
|
+
failedMetric,
|
|
7910
|
+
fetchDistinctValuesV3,
|
|
7911
|
+
fetchFieldStatsV3,
|
|
7912
|
+
fetchRowV3,
|
|
7616
7913
|
filterValues,
|
|
7617
7914
|
formulaRuntime,
|
|
7618
7915
|
groupKeyField,
|
|
@@ -7632,6 +7929,7 @@ export {
|
|
|
7632
7929
|
isCalendarGroupKey,
|
|
7633
7930
|
isComputedCellError,
|
|
7634
7931
|
isComputedField,
|
|
7932
|
+
isErrorCode,
|
|
7635
7933
|
isFilterable,
|
|
7636
7934
|
isGroupLinkField,
|
|
7637
7935
|
isGroupLinkValue,
|
|
@@ -7646,6 +7944,7 @@ export {
|
|
|
7646
7944
|
isOrOfAndsGroup,
|
|
7647
7945
|
isPropertyFieldLoaded,
|
|
7648
7946
|
isPushdownFilter,
|
|
7947
|
+
isQueryError,
|
|
7649
7948
|
isSelectable,
|
|
7650
7949
|
isSortable,
|
|
7651
7950
|
isWhereConjunction,
|
|
@@ -7696,20 +7995,27 @@ export {
|
|
|
7696
7995
|
parseHostQueryState,
|
|
7697
7996
|
parseJsonInspectionState,
|
|
7698
7997
|
parseJsonInspections,
|
|
7998
|
+
parseQueryErrorBody,
|
|
7699
7999
|
parseRelativeDuration,
|
|
7700
8000
|
percentile,
|
|
7701
8001
|
planMetricSeries,
|
|
7702
8002
|
predicatesOf,
|
|
7703
8003
|
prepareGrouping,
|
|
7704
8004
|
queriesEqual,
|
|
8005
|
+
queryErrorFromBody,
|
|
8006
|
+
queryErrorFromResponse,
|
|
8007
|
+
readDistinctResultV3,
|
|
8008
|
+
readFieldStatsResultV3,
|
|
7705
8009
|
readFieldValue,
|
|
7706
8010
|
readObjectReference,
|
|
8011
|
+
readRowResultV3,
|
|
7707
8012
|
readSetFilter,
|
|
7708
8013
|
reduceMetricValues,
|
|
7709
8014
|
removePatch,
|
|
7710
8015
|
renderItemPageTemplate,
|
|
7711
8016
|
resolveFieldName,
|
|
7712
8017
|
resolveGroupLink,
|
|
8018
|
+
rowLookupFields,
|
|
7713
8019
|
selectMetricShownRows,
|
|
7714
8020
|
selectedFields,
|
|
7715
8021
|
setFilterModes,
|
|
@@ -7718,11 +8024,11 @@ export {
|
|
|
7718
8024
|
sourceDependencies,
|
|
7719
8025
|
sourceExecutionSchema,
|
|
7720
8026
|
stripSetFilter,
|
|
7721
|
-
|
|
7722
|
-
|
|
8027
|
+
toDistinctRequestV3,
|
|
8028
|
+
toFieldStatsRequestV3,
|
|
7723
8029
|
toQueryRequestV3,
|
|
8030
|
+
toRowRequestV3,
|
|
7724
8031
|
toServerQuery,
|
|
7725
|
-
toServerQueryV2,
|
|
7726
8032
|
validateComputedColumn,
|
|
7727
8033
|
validateDateGrouping,
|
|
7728
8034
|
validateItemPageTemplate,
|