@gscdump/cloudflare 0.25.10 → 0.25.12
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.
|
@@ -86,8 +86,8 @@ declare function createDuckDbIcebergExecutor(config: DuckDbIcebergExecutorConfig
|
|
|
86
86
|
interface R2SqlClientConfig {
|
|
87
87
|
/** Cloudflare account id. */
|
|
88
88
|
accountId: string;
|
|
89
|
-
/** R2
|
|
90
|
-
|
|
89
|
+
/** R2 bucket backing the Iceberg catalog — R2 SQL addresses the catalog by bucket. */
|
|
90
|
+
bucket: string;
|
|
91
91
|
/** Iceberg namespace the 5 fact tables live in. */
|
|
92
92
|
namespace: string;
|
|
93
93
|
/** Cloudflare API token with R2 Data Catalog read scope. */
|
|
@@ -14,14 +14,6 @@ function metricExpr(metric) {
|
|
|
14
14
|
case "position": return "SUM(sum_position) / NULLIF(SUM(impressions), 0) AS position";
|
|
15
15
|
}
|
|
16
16
|
}
|
|
17
|
-
function orderMetricExpr(metric) {
|
|
18
|
-
switch (metric) {
|
|
19
|
-
case "clicks": return "SUM(clicks)";
|
|
20
|
-
case "impressions": return "SUM(impressions)";
|
|
21
|
-
case "ctr": return "SUM(clicks) / NULLIF(SUM(impressions), 0)";
|
|
22
|
-
case "position": return "SUM(sum_position) / NULLIF(SUM(impressions), 0)";
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
17
|
const DEVICE_SUFFIXES = [
|
|
26
18
|
"desktop",
|
|
27
19
|
"mobile",
|
|
@@ -138,8 +130,8 @@ function buildTopNBreakdown(q) {
|
|
|
138
130
|
};
|
|
139
131
|
}
|
|
140
132
|
const col = dimColumn(q.dimension);
|
|
141
|
-
const metrics = q.metrics.map(metricExpr).join(", ");
|
|
142
|
-
const order = `${
|
|
133
|
+
const metrics = (q.metrics.includes(q.orderBy.metric) ? q.metrics : [...q.metrics, q.orderBy.metric]).map(metricExpr).join(", ");
|
|
134
|
+
const order = `${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
|
|
143
135
|
const facet = facetPredicate(q);
|
|
144
136
|
let sql = `SELECT ${col}, ${metrics} FROM ${TABLE_PLACEHOLDER} WHERE ${w.clause}${facet.sql} GROUP BY ${col} ORDER BY ${order} LIMIT ${Math.max(0, Math.floor(q.limit))}`;
|
|
145
137
|
if (q.offset && q.offset > 0) sql += ` OFFSET ${Math.floor(q.offset)}`;
|
|
@@ -203,8 +195,8 @@ function buildTwoDimensionDetail(q) {
|
|
|
203
195
|
const facet = facetPredicate(q);
|
|
204
196
|
clause += facet.sql;
|
|
205
197
|
params.push(...facet.params);
|
|
206
|
-
let sql = `SELECT url, query, ${q.metrics.map(metricExpr).join(", ")} FROM ${TABLE_PLACEHOLDER} WHERE ${clause} GROUP BY url, query`;
|
|
207
|
-
if (q.orderBy) sql += ` ORDER BY ${
|
|
198
|
+
let sql = `SELECT url, query, ${(q.orderBy && !q.metrics.includes(q.orderBy.metric) ? [...q.metrics, q.orderBy.metric] : q.metrics).map(metricExpr).join(", ")} FROM ${TABLE_PLACEHOLDER} WHERE ${clause} GROUP BY url, query`;
|
|
199
|
+
if (q.orderBy) sql += ` ORDER BY ${q.orderBy.metric} ${q.orderBy.dir.toUpperCase()}`;
|
|
208
200
|
if (q.limit && q.limit > 0) sql += ` LIMIT ${Math.floor(q.limit)}`;
|
|
209
201
|
return {
|
|
210
202
|
table: "page_queries",
|
|
@@ -349,8 +341,12 @@ var R2SqlTimeoutError = class extends Error {
|
|
|
349
341
|
super(`R2 SQL query exceeded ${timeoutMs}ms deadline`);
|
|
350
342
|
}
|
|
351
343
|
};
|
|
352
|
-
const DEFAULT_API_BASE = "https://api.
|
|
344
|
+
const DEFAULT_API_BASE = "https://api.sql.cloudflarestorage.com/api/v1";
|
|
353
345
|
const DEFAULT_TIMEOUT_MS = 25e3;
|
|
346
|
+
const PARTITION_PREDICATE_RE = /\b(site_id|search_type)(\s*=)/g;
|
|
347
|
+
function workaroundPartitionEquality(sql) {
|
|
348
|
+
return sql.replace(PARTITION_PREDICATE_RE, (_m, col, eq) => `CONCAT(${col}, '')${eq}`);
|
|
349
|
+
}
|
|
354
350
|
function escapeSqlValue(value) {
|
|
355
351
|
if (value === null || value === void 0) return "NULL";
|
|
356
352
|
if (typeof value === "number") {
|
|
@@ -406,7 +402,7 @@ function createR2SqlClient(config) {
|
|
|
406
402
|
const fetchImpl = config.fetchImpl ?? globalThis.fetch;
|
|
407
403
|
const apiBase = config.apiBase ?? DEFAULT_API_BASE;
|
|
408
404
|
const timeoutMs = config.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
409
|
-
const endpoint = `${apiBase}/accounts/${config.accountId}/r2-
|
|
405
|
+
const endpoint = `${apiBase}/accounts/${config.accountId}/r2-sql/query/${config.bucket}`;
|
|
410
406
|
async function query(sql) {
|
|
411
407
|
const started = Date.now();
|
|
412
408
|
const controller = new AbortController();
|
|
@@ -417,7 +413,8 @@ function createR2SqlClient(config) {
|
|
|
417
413
|
method: "POST",
|
|
418
414
|
headers: {
|
|
419
415
|
"authorization": `Bearer ${config.token}`,
|
|
420
|
-
"content-type": "application/json"
|
|
416
|
+
"content-type": "application/json",
|
|
417
|
+
"user-agent": "gscdump-cloudflare-r2sql/1.0"
|
|
421
418
|
},
|
|
422
419
|
body: JSON.stringify({ query: sql }),
|
|
423
420
|
signal: controller.signal
|
|
@@ -442,7 +439,7 @@ function createR2SqlClient(config) {
|
|
|
442
439
|
}
|
|
443
440
|
function runPlan(plan) {
|
|
444
441
|
const tableRef = r2TableRef(config.namespace, plan.table);
|
|
445
|
-
return query(inlineParams(plan.sql.split(TABLE_PLACEHOLDER).join(tableRef), plan.params));
|
|
442
|
+
return query(workaroundPartitionEquality(inlineParams(plan.sql.split(TABLE_PLACEHOLDER).join(tableRef), plan.params)));
|
|
446
443
|
}
|
|
447
444
|
function runArchetype(archetypeQuery) {
|
|
448
445
|
return runPlan(buildArchetypeSql(archetypeQuery));
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gscdump/cloudflare",
|
|
3
3
|
"type": "module",
|
|
4
|
-
"version": "0.25.
|
|
4
|
+
"version": "0.25.12",
|
|
5
5
|
"description": "Cloudflare-Workers-flavored helpers for the gscdump analytics stack: AnalyticsEnv binding contract, R2 SigV4 presigner, size-hint HMAC, DuckDB Workers shims, engine factory.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "Harlan Wilton",
|
|
@@ -46,11 +46,11 @@
|
|
|
46
46
|
"dependencies": {
|
|
47
47
|
"@uwdata/flechette": "^2.5.0",
|
|
48
48
|
"aws4fetch": "^1.0.20",
|
|
49
|
-
"@gscdump/
|
|
50
|
-
"@gscdump/engine-sqlite": "0.25.
|
|
51
|
-
"@gscdump/sdk": "0.25.
|
|
52
|
-
"gscdump": "0.25.
|
|
53
|
-
"
|
|
49
|
+
"@gscdump/engine": "0.25.12",
|
|
50
|
+
"@gscdump/engine-sqlite": "0.25.12",
|
|
51
|
+
"@gscdump/sdk": "0.25.12",
|
|
52
|
+
"@gscdump/contracts": "0.25.12",
|
|
53
|
+
"gscdump": "0.25.12"
|
|
54
54
|
},
|
|
55
55
|
"devDependencies": {
|
|
56
56
|
"@cloudflare/vitest-pool-workers": "^0.16.10",
|