diff --git a/Cargo.lock b/Cargo.lock index 0bf87137384..45abb86496b 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3955,6 +3955,7 @@ dependencies = [ "prisma-metrics", "psl", "quaint", + "query-compiler", "query-connector", "query-core", "query-engine-common", diff --git a/query-compiler/query-compiler-wasm/src/compiler.rs b/query-compiler/query-compiler-wasm/src/compiler.rs index 3af109c9ef3..1095d630721 100644 --- a/query-compiler/query-compiler-wasm/src/compiler.rs +++ b/query-compiler/query-compiler-wasm/src/compiler.rs @@ -85,11 +85,7 @@ impl QueryCompiler { } #[wasm_bindgen] - pub fn compile( - &self, - request: String, - _human_readable: bool, // ignored on wasm to not compile it in - ) -> Result { + pub fn compile(&self, request: String) -> Result { let request = RequestBody::try_from_str(&request, self.protocol)?; let query_doc = request.into_doc(&self.schema)?; let plan = query_compiler::compile(&self.schema, query_doc, &self.connection_info)?; diff --git a/query-engine/driver-adapters/executor/src/demo-qc.ts b/query-engine/driver-adapters/executor/src/demo-qc.ts index 2e257608258..eeffe35a4c2 100644 --- a/query-engine/driver-adapters/executor/src/demo-qc.ts +++ b/query-engine/driver-adapters/executor/src/demo-qc.ts @@ -8,7 +8,7 @@ import { err } from "./utils"; import { setupDriverAdaptersManager } from "./setup"; /** - * Example run: `DRIVER_ADAPTER="libsql" pnpm demo:se` + * Example run: `DRIVER_ADAPTER="libsql" pnpm demo:qc` */ async function main(): Promise { const env = S.decodeUnknownSync(Env)(process.env); diff --git a/query-engine/query-engine-node-api/Cargo.toml b/query-engine/query-engine-node-api/Cargo.toml index a12e7a28675..c042e1f3042 100644 --- a/query-engine/query-engine-node-api/Cargo.toml +++ b/query-engine/query-engine-node-api/Cargo.toml @@ -23,6 +23,7 @@ query-core = { path = "../core", features = ["metrics"] } request-handlers = { path = "../request-handlers", features = ["all"] } query-connector = { path = "../connectors/query-connector" } query-engine-common = { path = "../../libs/query-engine-common" } +query-compiler = { path = "../../query-compiler/query-compiler" } user-facing-errors = { path = "../../libs/user-facing-errors" } telemetry = { path = "../../libs/telemetry" } psl = { workspace = true, features = ["all"] } diff --git a/query-engine/query-engine-node-api/src/engine.rs b/query-engine/query-engine-node-api/src/engine.rs index 9e6bf180171..0e8169253e5 100644 --- a/query-engine/query-engine-node-api/src/engine.rs +++ b/query-engine/query-engine-node-api/src/engine.rs @@ -4,7 +4,7 @@ use napi::{threadsafe_function::ThreadSafeCallContext, Env, JsFunction, JsObject use napi_derive::napi; use prisma_metrics::{MetricFormat, WithMetricsInstrumentation}; use psl::PreviewFeature; -use quaint::connector::ExternalConnector; +use quaint::{connector::ExternalConnector, prelude::ConnectionInfo}; use query_core::{protocol::EngineProtocol, relation_load_strategy, schema, TransactionOptions, TxId}; use query_engine_common::{ engine::{ @@ -354,6 +354,50 @@ impl QueryEngine { .await } + #[napi] + pub async fn compile(&self, request: String, human_readable: bool) -> napi::Result { + let dispatcher = self.logger.dispatcher(); + let recorder = self.logger.recorder(); + + async_panic_to_js_error(async { + let inner = self.inner.read().await; + let engine = inner.as_engine()?; + + let request = RequestBody::try_from_str(&request, engine.engine_protocol())?; + let query_doc = request + .into_doc(engine.query_schema()) + .map_err(|err| napi::Error::from_reason(err.to_string()))?; + + let connection_info = match self.connector_mode { + ConnectorMode::Js { ref adapter } => ConnectionInfo::External( + adapter + .get_connection_info() + .await + .map_err(|err| napi::Error::from_reason(err.to_string()))?, + ), + ConnectorMode::Rust => { + return Err(napi::Error::from_reason( + "Query compiler requires JS driver adapter".to_string(), + )) + } + }; + + let plan = query_compiler::compile(engine.query_schema(), query_doc, &connection_info) + .map_err(|err| napi::Error::from_reason(err.to_string()))?; + + let response = if human_readable { + plan.to_string() + } else { + serde_json::to_string(&plan)? + }; + + Ok(response) + }) + .with_subscriber(dispatcher) + .with_optional_recorder(recorder) + .await + } + /// If connected, attempts to start a transaction in the core and returns its ID. #[napi] pub async fn start_transaction(&self, input: String, trace: String, request_id: String) -> napi::Result {