Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 19 additions & 30 deletions templates/full/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,6 +244,19 @@ function ptrToString(ptr: number): string {
return wasmModule.UTF8ToString(ptr);
}

// fingerprint, normalize and scan return "ERROR: <message>" on failure and a
// null pointer when the WASM heap is exhausted.
function resultOrThrow(resultPtr: number): string {
if (!resultPtr) {
throw new Error('libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); pass large inputs in smaller pieces');
}
const resultStr = ptrToString(resultPtr);
if (resultStr.startsWith('ERROR: ')) {
throw new Error(resultStr.slice('ERROR: '.length));
}
return resultStr;
}

export const parse = awaitInit(async (query: string): Promise<ParseResult> => {
// Input validation
if (query === null || query === undefined) {
Expand Down Expand Up @@ -338,11 +351,7 @@ export const fingerprint = awaitInit(async (query: string): Promise<string> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_fingerprint(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -362,11 +371,7 @@ export const normalize = awaitInit(async (query: string): Promise<string> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_normalize_query(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand Down Expand Up @@ -484,11 +489,7 @@ export function fingerprintSync(query: string): string {

try {
resultPtr = callWasm(() => wasmModule._wasm_fingerprint(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -511,11 +512,7 @@ export function normalizeSync(query: string): string {

try {
resultPtr = callWasm(() => wasmModule._wasm_normalize_query(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -535,11 +532,7 @@ export const scan = awaitInit(async (query: string): Promise<ScanResult> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_scan(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return JSON.parse(resultStr);
} finally {
Expand All @@ -562,11 +555,7 @@ export function scanSync(query: string): ScanResult {

try {
resultPtr = callWasm(() => wasmModule._wasm_scan(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return JSON.parse(resultStr);
} finally {
Expand Down
45 changes: 27 additions & 18 deletions templates/full/wasm_wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,15 @@ static void* safe_malloc(size_t size) {
return ptr;
}

// fingerprint, normalize and scan return errors as "ERROR: <message>" so the
// caller can tell them from a result, and NULL when out of memory.
static char* error_result(const char* message) {
size_t len = strlen(message) + sizeof("ERROR: ");
char* result = safe_malloc(len);
if (result) snprintf(result, len, "ERROR: %s", message);
return result;
}

EMSCRIPTEN_KEEPALIVE
char* wasm_parse_query(const char* input) {
if (!validate_input(input)) {
Expand Down Expand Up @@ -107,15 +116,15 @@ char* wasm_parse_plpgsql(const char* input) {
EMSCRIPTEN_KEEPALIVE
char* wasm_fingerprint(const char* input) {
if (!validate_input(input)) {
return safe_strdup("Invalid input: query cannot be null or empty");
return error_result("Invalid input: query cannot be null or empty");
}

PgQueryFingerprintResult result = pg_query_fingerprint(input);

if (result.error) {
char* error_msg = safe_strdup(result.error->message);
char* error_msg = error_result(result.error->message);
pg_query_free_fingerprint_result(result);
return error_msg ? error_msg : safe_strdup("Memory allocation failed");
return error_msg;
}

char* fingerprint_str = safe_strdup(result.fingerprint_str);
Expand All @@ -126,24 +135,20 @@ char* wasm_fingerprint(const char* input) {
EMSCRIPTEN_KEEPALIVE
char* wasm_normalize_query(const char* input) {
if (!validate_input(input)) {
return safe_strdup("Invalid input: query cannot be null or empty");
return error_result("Invalid input: query cannot be null or empty");
}

PgQueryNormalizeResult result = pg_query_normalize(input);

if (result.error) {
char* error_msg = safe_strdup(result.error->message);
char* error_msg = error_result(result.error->message);
pg_query_free_normalize_result(result);
return error_msg ? error_msg : safe_strdup("Memory allocation failed");
return error_msg;
}

char* normalized = safe_strdup(result.normalized_query);
pg_query_free_normalize_result(result);

if (!normalized) {
return safe_strdup("Memory allocation failed");
}

return normalized;
}

Expand Down Expand Up @@ -283,7 +288,7 @@ static char* build_scan_json(PgQuery__ScanResult *scan_result, const char* origi
}
char* json = safe_malloc(estimated_size);
if (!json) {
return safe_strdup("{\"version\":0,\"tokens\":[]}");
return NULL;
}

// Start building JSON
Expand All @@ -297,7 +302,10 @@ static char* build_scan_json(PgQuery__ScanResult *scan_result, const char* origi
if (token_length < 0) token_length = 0; // Safety check

char* token_text = safe_malloc(token_length + 1);
if (!token_text) continue;
if (!token_text) {
free(json);
return NULL;
}

if (token_length > 0) {
strncpy(token_text, &original_sql[token->start], token_length);
Expand All @@ -308,7 +316,8 @@ static char* build_scan_json(PgQuery__ScanResult *scan_result, const char* origi
char* escaped_text = safe_malloc(token_length * 2 + 1);
if (!escaped_text) {
free(token_text);
continue;
free(json);
return NULL;
}

int escaped_pos = 0;
Expand Down Expand Up @@ -366,15 +375,15 @@ static char* build_scan_json(PgQuery__ScanResult *scan_result, const char* origi
EMSCRIPTEN_KEEPALIVE
char* wasm_scan(const char* input) {
if (!validate_input(input)) {
return safe_strdup("Invalid input: query cannot be null or empty");
return error_result("Invalid input: query cannot be null or empty");
}

PgQueryScanResult result = pg_query_scan(input);

if (result.error) {
char* error_msg = safe_strdup(result.error->message);
char* error_msg = error_result(result.error->message);
pg_query_free_scan_result(result);
return error_msg ? error_msg : safe_strdup("Memory allocation failed");
return error_msg;
}

// Unpack protobuf data
Expand All @@ -383,7 +392,7 @@ char* wasm_scan(const char* input) {

if (!scan_result) {
pg_query_free_scan_result(result);
return safe_strdup("Failed to unpack scan result");
return NULL;
}

// Convert to JSON
Expand All @@ -393,7 +402,7 @@ char* wasm_scan(const char* input) {
pg_query__scan_result__free_unpacked(scan_result, NULL);
pg_query_free_scan_result(result);

return json_result ? json_result : safe_strdup("{\"version\":0,\"tokens\":[]}");
return json_result;
}

EMSCRIPTEN_KEEPALIVE
Expand Down
49 changes: 19 additions & 30 deletions versions/18/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -251,6 +251,19 @@ function ptrToString(ptr: number): string {
return wasmModule.UTF8ToString(ptr);
}

// fingerprint, normalize and scan return "ERROR: <message>" on failure and a
// null pointer when the WASM heap is exhausted.
function resultOrThrow(resultPtr: number): string {
if (!resultPtr) {
throw new Error('libpg_query returned no result: out of WASM memory (heap limit is WASM_MAXIMUM_MEMORY, 1 GiB); pass large inputs in smaller pieces');
}
const resultStr = ptrToString(resultPtr);
if (resultStr.startsWith('ERROR: ')) {
throw new Error(resultStr.slice('ERROR: '.length));
}
return resultStr;
}

export const parse = awaitInit(async (query: string): Promise<ParseResult> => {
// Input validation
if (query === null || query === undefined) {
Expand Down Expand Up @@ -345,11 +358,7 @@ export const fingerprint = awaitInit(async (query: string): Promise<string> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_fingerprint(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -369,11 +378,7 @@ export const normalize = awaitInit(async (query: string): Promise<string> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_normalize_query(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand Down Expand Up @@ -491,11 +496,7 @@ export function fingerprintSync(query: string): string {

try {
resultPtr = callWasm(() => wasmModule._wasm_fingerprint(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -518,11 +519,7 @@ export function normalizeSync(query: string): string {

try {
resultPtr = callWasm(() => wasmModule._wasm_normalize_query(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return resultStr;
} finally {
Expand All @@ -542,11 +539,7 @@ export const scan = awaitInit(async (query: string): Promise<ScanResult> => {

try {
resultPtr = callWasm(() => wasmModule._wasm_scan(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return JSON.parse(resultStr);
} finally {
Expand All @@ -569,11 +562,7 @@ export function scanSync(query: string): ScanResult {

try {
resultPtr = callWasm(() => wasmModule._wasm_scan(queryPtr));
const resultStr = ptrToString(resultPtr);

if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.startsWith('ERROR')) {
throw new Error(resultStr);
}
const resultStr = resultOrThrow(resultPtr);

return JSON.parse(resultStr);
} finally {
Expand Down
Loading
Loading