diff --git a/Makefile b/Makefile index 0f467a0b..6d6f3308 100644 --- a/Makefile +++ b/Makefile @@ -55,7 +55,7 @@ ifdef EMSCRIPTEN $(CXXFLAGS) \ -I$(LIBPG_QUERY_DIR) \ -L$(LIBPG_QUERY_DIR) \ - -sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_parse_query_protobuf','_wasm_get_protobuf_len','_wasm_deparse_protobuf','_wasm_parse_plpgsql','_wasm_fingerprint','_wasm_normalize_query','_wasm_parse_query_detailed','_wasm_free_detailed_result','_wasm_free_string']" \ + -sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_parse_query_protobuf','_wasm_get_protobuf_len','_wasm_deparse_protobuf','_wasm_deparse_json','_wasm_parse_plpgsql','_wasm_fingerprint','_wasm_normalize_query','_wasm_parse_query_detailed','_wasm_free_detailed_result','_wasm_free_string']" \ -sEXPORTED_RUNTIME_METHODS="['lengthBytesUTF8','stringToUTF8','UTF8ToString','HEAPU8','HEAPU32']" \ -sEXPORT_NAME="$(WASM_MODULE_NAME)" \ -sENVIRONMENT="web,node" \ diff --git a/src/wasm_wrapper.c b/src/wasm_wrapper.c index 9e1b02c6..53937c11 100644 --- a/src/wasm_wrapper.c +++ b/src/wasm_wrapper.c @@ -281,6 +281,38 @@ void wasm_free_detailed_result(WasmDetailedResult* result) { } } +EMSCRIPTEN_KEEPALIVE +char* wasm_deparse_json(const char* json_input) { + if (!validate_input(json_input)) { + return safe_strdup("Invalid input: JSON cannot be null or empty"); + } + + PgQueryProtobuf protobuf = pg_query_json_to_protobuf(json_input); + + if (!protobuf.data || protobuf.len <= 0) { + return safe_strdup("Failed to convert JSON to protobuf"); + } + + PgQueryDeparseResult deparse_result = pg_query_deparse_protobuf(protobuf); + + free(protobuf.data); + + if (deparse_result.error) { + char* error_msg = safe_strdup(deparse_result.error->message); + pg_query_free_deparse_result(deparse_result); + return error_msg; + } + + if (!deparse_result.query) { + pg_query_free_deparse_result(deparse_result); + return safe_strdup("Failed to deparse query"); + } + + char* query_copy = safe_strdup(deparse_result.query); + pg_query_free_deparse_result(deparse_result); + return query_copy; +} + EMSCRIPTEN_KEEPALIVE void wasm_free_string(char* str) { free(str); diff --git a/wasm/index.cjs b/wasm/index.cjs index e3479dde..b9503b5d 100644 --- a/wasm/index.cjs +++ b/wasm/index.cjs @@ -48,7 +48,9 @@ const parseQuery = awaitInit(async (query) => { throw new Error(resultStr); } - return JSON.parse(resultStr); + const parsed = JSON.parse(resultStr); + parsed.query = query; + return parsed; } finally { wasmModule._free(queryPtr); if (resultPtr) { @@ -58,23 +60,25 @@ const parseQuery = awaitInit(async (query) => { }); const deparse = awaitInit(async (parseTree) => { - const queryPtr = stringToPtr(JSON.stringify(parseTree)); - let resultPtr; + if (!parseTree || !parseTree.stmts || parseTree.stmts.length === 0) { + throw new Error('No protobuf data found in parse tree'); + } + + const jsonPtr = stringToPtr(JSON.stringify(parseTree)); try { - resultPtr = wasmModule._wasm_deparse_protobuf(queryPtr, 0); + const resultPtr = wasmModule._wasm_deparse_json(jsonPtr); const resultStr = ptrToString(resultPtr); + wasmModule._wasm_free_string(resultPtr); + if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) { throw new Error(resultStr); } return resultStr; } finally { - wasmModule._free(queryPtr); - if (resultPtr) { - wasmModule._wasm_free_string(resultPtr); - } + wasmModule._free(jsonPtr); } }); @@ -196,7 +200,9 @@ function parseQuerySync(query) { throw new Error(resultStr); } - return JSON.parse(resultStr); + const parsed = JSON.parse(resultStr); + parsed.query = query; + return parsed; } finally { wasmModule._free(queryPtr); if (resultPtr) { @@ -209,23 +215,26 @@ function deparseSync(parseTree) { if (!wasmModule) { throw new Error('WASM module not initialized. Call loadModule() first.'); } - const queryPtr = stringToPtr(JSON.stringify(parseTree)); - let resultPtr; + + if (!parseTree || !parseTree.stmts || parseTree.stmts.length === 0) { + throw new Error('No protobuf data found in parse tree'); + } + + const jsonPtr = stringToPtr(JSON.stringify(parseTree)); try { - resultPtr = wasmModule._wasm_deparse_protobuf(queryPtr, 0); + const resultPtr = wasmModule._wasm_deparse_json(jsonPtr); const resultStr = ptrToString(resultPtr); + wasmModule._wasm_free_string(resultPtr); + if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) { throw new Error(resultStr); } return resultStr; } finally { - wasmModule._free(queryPtr); - if (resultPtr) { - wasmModule._wasm_free_string(resultPtr); - } + wasmModule._free(jsonPtr); } } diff --git a/wasm/index.js b/wasm/index.js index 8396e15d..0d8505ee 100644 --- a/wasm/index.js +++ b/wasm/index.js @@ -48,7 +48,9 @@ export const parseQuery = awaitInit(async (query) => { throw new Error(resultStr); } - return JSON.parse(resultStr); + const parsed = JSON.parse(resultStr); + parsed.query = query; + return parsed; } finally { wasmModule._free(queryPtr); if (resultPtr) { @@ -58,23 +60,25 @@ export const parseQuery = awaitInit(async (query) => { }); export const deparse = awaitInit(async (parseTree) => { - const queryPtr = stringToPtr(JSON.stringify(parseTree)); - let resultPtr; + if (!parseTree || !parseTree.stmts || parseTree.stmts.length === 0) { + throw new Error('No protobuf data found in parse tree'); + } + + const jsonPtr = stringToPtr(JSON.stringify(parseTree)); try { - resultPtr = wasmModule._wasm_deparse_protobuf(queryPtr, 0); + const resultPtr = wasmModule._wasm_deparse_json(jsonPtr); const resultStr = ptrToString(resultPtr); + wasmModule._wasm_free_string(resultPtr); + if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) { throw new Error(resultStr); } return resultStr; } finally { - wasmModule._free(queryPtr); - if (resultPtr) { - wasmModule._wasm_free_string(resultPtr); - } + wasmModule._free(jsonPtr); } }); @@ -196,7 +200,9 @@ export function parseQuerySync(query) { throw new Error(resultStr); } - return JSON.parse(resultStr); + const parsed = JSON.parse(resultStr); + parsed.query = query; + return parsed; } finally { wasmModule._free(queryPtr); if (resultPtr) { @@ -209,23 +215,26 @@ export function deparseSync(parseTree) { if (!wasmModule) { throw new Error('WASM module not initialized. Call loadModule() first.'); } - const queryPtr = stringToPtr(JSON.stringify(parseTree)); - let resultPtr; + + if (!parseTree || !parseTree.stmts || parseTree.stmts.length === 0) { + throw new Error('No protobuf data found in parse tree'); + } + + const jsonPtr = stringToPtr(JSON.stringify(parseTree)); try { - resultPtr = wasmModule._wasm_deparse_protobuf(queryPtr, 0); + const resultPtr = wasmModule._wasm_deparse_json(jsonPtr); const resultStr = ptrToString(resultPtr); + wasmModule._wasm_free_string(resultPtr); + if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) { throw new Error(resultStr); } return resultStr; } finally { - wasmModule._free(queryPtr); - if (resultPtr) { - wasmModule._wasm_free_string(resultPtr); - } + wasmModule._free(jsonPtr); } }