oracle_nosql_rust_sdk/prepared_statement.rs
1//
2// Copyright (c) 2024, 2025 Oracle and/or its affiliates. All rights reserved.
3//
4// Licensed under the Universal Permissive License v 1.0 as shown at
5// https://oss.oracle.com/licenses/upl/
6//
7use crate::error::NoSQLError;
8use crate::plan_iter::{PlanIter, PlanIterKind};
9use crate::types::{FieldValue, TopologyInfo};
10
11use std::collections::HashMap;
12use std::result::Result;
13
14/// A prepared query statement for use in a [`QueryRequest`](crate::QueryRequest).
15///
16/// PreparedStatement encapsulates a prepared query statement. It includes state
17/// that can be sent to a server and executed without re-parsing the query.
18///
19/// The details of a prepared query are purposefully opaque, as its internal
20/// data and implementation may change over time.
21///
22/// PreparedStatement is only created by calling [`QueryRequest::execute()`](crate::QueryRequest::execute()) followed by
23/// [`QueryResult::prepared_statement()`](crate::QueryResult::prepared_statement()).
24///
25/// The main purpose of a prepared statement is to parse a query for execution many times,
26/// with different variables. Here is a simple example using `QueryRequest` and `PreparedStatement` to
27/// insert a set of rows into a table (note: it may be more optimal to use [`WriteMultipleRequest`](crate::WriteMultipleRequest)
28/// for this specific case, this example is just to show the use of variables in prepared statetments):
29/// ```no_run
30/// # use oracle_nosql_rust_sdk::{Handle, QueryRequest, NoSQLColumnToFieldValue};
31/// # #[tokio::main]
32/// # pub async fn main() -> Result<(), Box<dyn std::error::Error>> {
33/// let handle = Handle::builder().build().await?;
34/// let prep_result = QueryRequest::new(
35/// "declare $id integer; $name string; insert into testusers(id, name) values($id, $name)",
36/// )
37/// .prepare_only()
38/// .execute(&handle)
39/// .await?;
40/// let data = vec!["jane", "john", "jasper"];
41/// let mut qreq = QueryRequest::new_prepared(&prep_result.prepared_statement());
42/// for i in 0..data.len() {
43/// let id = (i as i32) + 1;
44/// qreq.set_variable("$id", &id)?;
45/// qreq.set_variable("$name", &data[i])?;
46/// let result = qreq.execute(&handle).await?;
47/// println!("Insert result = {:?}", result);
48/// }
49/// # Ok(())
50/// # }
51#[derive(Default, Clone)]
52pub struct PreparedStatement {
53 // sql_text represents the application provided SQL text.
54 #[allow(dead_code)]
55 pub(crate) sql_text: String,
56
57 // query_plan is the string representation of query plan.
58 pub(crate) query_plan: String,
59
60 // query_schema is the string representation of query schema.
61 pub(crate) query_schema: String,
62
63 // table_name is the table name returned from a prepared query result, if any.
64 pub(crate) table_name: Option<String>,
65
66 // namespace is the namespace returned from a prepared query result, if any.
67 pub(crate) namespace: Option<String>,
68
69 // operation is the operation code for the query.
70 pub(crate) operation: u8,
71
72 // driver_query_plan represents the part of query plan that must be executed at the driver.
73 // It is received from the NoSQL database proxy when the query is prepared there.
74 // It is deserialized by the driver and not sent back to the database proxy.
75 // This is only used for advanced queries.
76 pub(crate) driver_query_plan: Box<PlanIter>,
77
78 // topology_info represents the NoSQL database topology information that
79 // are required for query execution.
80 // This is only used for advanced queries.
81 pub(crate) topology_info: Option<TopologyInfo>,
82
83 // statement represents the serialized PreparedStatement created at the backend store.
84 // It is opaque for the driver.
85 // It is received from the NoSQL database proxy and sent back to the proxy
86 // every time a new batch of results is needed.
87 pub(crate) statement: Vec<u8>,
88
89 // variable_to_ids maps the name of each external variable to its id, which is
90 // a position in a FieldValue array stored in the QueryRequest and
91 // holding the values of the variables.
92 // This is only used for advanced queries.
93 // It is only created when deserializing a prepared statement from a query response.
94 pub(crate) variable_to_ids: Option<HashMap<String, i32>>,
95
96 // for driver plans
97 pub(crate) num_registers: i32,
98 pub(crate) num_iterators: i32,
99
100 pub(crate) data: PreparedStatementData,
101}
102
103impl std::fmt::Debug for PreparedStatement {
104 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105 write!(
106 f,
107 "PreparedStatement: size={}, data={:?}",
108 self.statement.len(),
109 self.data
110 )
111 }
112}
113
114#[derive(Debug, Default)]
115pub struct PreparedStatementData {
116 // bind_variables is a map that associates the name to the value for external
117 // variables used in the query.
118 //
119 // This map is populated by the application using the SetVariable() method.
120 // It is sent to the NoSQL database proxy every time a new batch of results is needed.
121 // The values in this map are also placed in the runtimeControlBlock
122 // FieldValue array, just before the query starts its execution at the driver.
123 pub bind_variables: HashMap<String, FieldValue>,
124}
125
126// We do this manually because we do not want to clone the bind variables
127impl Clone for PreparedStatementData {
128 fn clone(&self) -> Self {
129 //println!("ps.data.clone(): clearing");
130 PreparedStatementData {
131 bind_variables: Default::default(),
132 }
133 }
134 fn clone_from(&mut self, _source: &Self) {
135 //println!("ps.data.clone_from(): clearing");
136 self.bind_variables.clear();
137 }
138}
139
140impl PreparedStatement {
141 pub(crate) fn is_simple(&self) -> bool {
142 self.driver_query_plan.get_kind() == PlanIterKind::Empty
143 }
144 pub(crate) fn is_empty(&self) -> bool {
145 self.statement.len() == 0
146 }
147 // set iterators/etc to their initial values, as if
148 // they had just been deserialized
149 pub(crate) fn reset(&mut self) -> Result<(), NoSQLError> {
150 self.driver_query_plan.reset()?;
151 // Do not clear bound variables: that's in a different call
152 //self.data = PreparedStatementData::default();
153 Ok(())
154 }
155 pub(crate) fn copy_for_internal(&self) -> Self {
156 let mut data = PreparedStatementData::default();
157 for (k, v) in &self.data.bind_variables {
158 data.bind_variables.insert(k.clone(), v.clone_internal());
159 }
160 PreparedStatement {
161 // we only keep the actual binary prepared statement, all other
162 // fields get their defaults
163 statement: self.statement.clone(),
164 data: data,
165 ..Default::default()
166 }
167 }
168
169 pub(crate) fn set_variable(
170 &mut self,
171 name: &str,
172 value: &FieldValue,
173 ) -> Result<(), NoSQLError> {
174 // TODO: verify variable should start with '$'
175 self.data
176 .bind_variables
177 .insert(name.to_string(), value.clone_internal());
178 Ok(())
179 }
180
181 pub(crate) fn set_variable_by_id(
182 &mut self,
183 id: i32,
184 value: &FieldValue,
185 ) -> Result<(), NoSQLError> {
186 /*
187 if let Some(vars) = &self.variable_to_ids {
188 for (k, v) in vars {
189 if *v == id {
190 self.data
191 .bind_variables
192 .insert(k.clone(), value.clone_internal());
193 return Ok(());
194 }
195 }
196 return Err(format!(
197 "prepared statement does not have variable at position {}",
198 id
199 )
200 .as_str()
201 .into());
202 }
203 Err("prepared statement does not have positional variables".into())
204 */
205 self.data
206 .bind_variables
207 .insert(format!("#{}", id), value.clone_internal());
208 Ok(())
209 }
210
211 pub(crate) fn get_variable_by_id(&self, id: i32) -> Option<&FieldValue> {
212 if let Some(vars) = &self.variable_to_ids {
213 for (k, v) in vars {
214 if *v == id {
215 return self.data.bind_variables.get(k);
216 }
217 }
218 }
219 None
220 }
221}