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
import { promisify } from "node:util";
import { gzip } from "node:zlib";
import { encode } from "npm:base64-arraybuffer";
import { parse } from "npm:json2csv";
import mysql from "npm:mysql2/promise";
import pg from "npm:pg";
type DatabaseType = "postgres" | "mysql" | "duckdb";
interface QueryRequest {
databaseType: DatabaseType;
url?: string;
query?: string;
introspect?: boolean;
gzip?: boolean;
}
const gzipAsync = promisify(gzip);
const MAX_FILE_SIZE_MB = 10;
const MAX_FILE_SIZE_BYTES = MAX_FILE_SIZE_MB * 1024 * 1024;
const ESTIMATED_COMPRESSION_RATIO = 0.3;
const MAX_ROWS_PER_CHUNK = 10000;
export default async function(req: Request): Promise<Response> {
console.log("Received request");
let requestBody: QueryRequest;
try {
requestBody = await req.json();
console.log("Request body parsed successfully");
} catch (error) {
console.error("Error parsing request body:", error);
return new Response("Invalid JSON body", { status: 400 });
}
const { databaseType, url, query, introspect, gzip = false } = requestBody;
console.log(`Database type: ${databaseType}`);
console.log(`Query: ${query}`);
console.log(`Introspect: ${introspect}`);
console.log(`Gzip: ${gzip}`);
if (!databaseType) {
console.error("Missing database type");
return new Response("databaseType is required", { status: 400 });
}
if (!introspect && !query) {
console.error("Missing query when introspect is false");
return new Response("query is required when introspect is false", { status: 400 });
}
if (databaseType !== "duckdb" && !url) {
console.error("Missing URL for non-DuckDB query");
return new Response("URL is required for postgres and mysql", { status: 400 });
}
let results;
try {
if (introspect) {
console.log(`Introspecting ${databaseType} schema`);
if (databaseType === "postgres") {
results = await introspectPostgresSchema(url!);
} else if (databaseType === "mysql") {
results = await introspectMysqlSchema(url!);
} else {
console.error(`Introspection not supported for ${databaseType}`);
return new Response(`Introspection not supported for ${databaseType}`, { status: 400 });
}
} else {
console.log(`Executing ${databaseType} query`);
if (databaseType === "postgres") {
results = await executePostgresQuery(url!, query!);
} else if (databaseType === "mysql") {
results = await executeMysqlQuery(url!, query!);
} else if (databaseType === "duckdb") {
console.log("DuckDB is not supported");
return new Response("DuckDB is not supported", { status: 400 });
} else {
console.error(`Invalid database type: ${databaseType}`);
return new Response("Invalid databaseType", { status: 400 });
}
}
console.log(`Operation completed successfully. Row count: ${results.length}`);
const chunks = splitIntoChunks(results, MAX_ROWS_PER_CHUNK);
console.log(`Split results into ${chunks.length} chunks`);
const fileResponses = await Promise.all(chunks.map((chunk, index) => createGzipFile(chunk, index, gzip, introspect)));
console.log(`Created ${fileResponses.length} ${gzip ? "gzip" : ""} files`);
return new Response(JSON.stringify({ openaiFileResponse: fileResponses }), {
status: 200,
headers: { "Content-Type": "application/json" },
});
} catch (error) {
console.error("Error executing operation:", error);
return new Response(`Error: ${error.message}`, { status: 500 });
}
}