Skip to main content

hyperdb_api_core/client/
prepare.rs

1// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4//! Prepared statement support using extended query protocol.
5//!
6//! # Parameter Encoding
7//!
8//! Use the \[`params!`\] macro for ergonomic parameter encoding:
9//!
10//! ```no_run
11//! # use hyperdb_api_core::{params, client::{Client, Config}};
12//! # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
13//! let stmt = client.prepare("SELECT * FROM users WHERE id = $1 AND name = $2")?;
14//! let rows = client.execute(&stmt, params![42_i32, "Alice"])?;
15//! # Ok(())
16//! # }
17//! ```
18
19use std::sync::atomic::{AtomicU64, Ordering};
20use std::sync::{Arc, Mutex, Weak};
21
22use crate::protocol::message::{backend::Message, frontend};
23use crate::types::Oid;
24use tracing::{trace, warn};
25
26use super::connection::RawConnection;
27use super::error::{Error, Result};
28use super::row::Row;
29use super::statement::{Column, ParamFormat, bind_format_codes};
30use super::sync_stream::SyncStream;
31// =============================================================================
32// SqlParam trait - Zero-cost parameter encoding
33// =============================================================================
34
35/// Trait for types that can be encoded as SQL prepared statement parameters.
36///
37/// This trait enables the \[`params!`\] macro to automatically encode values.
38/// All implementations use `#[inline]` for zero-cost abstraction.
39pub trait SqlParam {
40    /// Encodes the value as binary bytes.
41    fn encode(&self) -> Vec<u8>;
42}
43
44impl SqlParam for i16 {
45    #[inline]
46    fn encode(&self) -> Vec<u8> {
47        self.to_le_bytes().to_vec()
48    }
49}
50
51impl SqlParam for i32 {
52    #[inline]
53    fn encode(&self) -> Vec<u8> {
54        self.to_le_bytes().to_vec()
55    }
56}
57
58impl SqlParam for i64 {
59    #[inline]
60    fn encode(&self) -> Vec<u8> {
61        self.to_le_bytes().to_vec()
62    }
63}
64
65impl SqlParam for f32 {
66    #[inline]
67    fn encode(&self) -> Vec<u8> {
68        self.to_le_bytes().to_vec()
69    }
70}
71
72impl SqlParam for f64 {
73    #[inline]
74    fn encode(&self) -> Vec<u8> {
75        self.to_le_bytes().to_vec()
76    }
77}
78
79impl SqlParam for bool {
80    #[inline]
81    fn encode(&self) -> Vec<u8> {
82        vec![u8::from(*self)]
83    }
84}
85
86impl SqlParam for &str {
87    #[inline]
88    fn encode(&self) -> Vec<u8> {
89        self.as_bytes().to_vec()
90    }
91}
92
93impl SqlParam for String {
94    #[inline]
95    fn encode(&self) -> Vec<u8> {
96        self.as_bytes().to_vec()
97    }
98}
99
100impl SqlParam for &String {
101    #[inline]
102    fn encode(&self) -> Vec<u8> {
103        self.as_bytes().to_vec()
104    }
105}
106
107impl SqlParam for Vec<u8> {
108    #[inline]
109    fn encode(&self) -> Vec<u8> {
110        self.clone()
111    }
112}
113
114impl SqlParam for &[u8] {
115    #[inline]
116    fn encode(&self) -> Vec<u8> {
117        self.to_vec()
118    }
119}
120
121/// Macro for building prepared statement parameters with automatic encoding.
122///
123/// # Examples
124///
125/// ```no_run
126/// # use hyperdb_api_core::{params, client::{Client, Config, SqlParam}};
127/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
128/// let stmt = client.prepare("SELECT * FROM t WHERE id = $1 AND name = $2")?;
129///
130/// // Pass typed values directly
131/// let rows = client.execute(&stmt, params![42_i32, "Alice"])?;
132///
133/// // For NULL values, use None explicitly
134/// let rows = client.execute(&stmt, &[Some(42_i32.encode()), None])?;
135/// # Ok(())
136/// # }
137/// ```
138#[macro_export]
139macro_rules! params {
140    () => {
141        &[] as &[Option<Vec<u8>>]
142    };
143    ($($val:expr),+ $(,)?) => {{
144        use $crate::client::prepare::SqlParam;
145        vec![$(Some($val.encode())),+]
146    }};
147}
148
149/// Counter for generating unique statement names.
150static STATEMENT_COUNTER: AtomicU64 = AtomicU64::new(0);
151
152/// Generates a unique statement name.
153fn generate_statement_name() -> String {
154    let id = STATEMENT_COUNTER.fetch_add(1, Ordering::Relaxed);
155    format!("__hyper_stmt_{id}")
156}
157
158/// A prepared statement.
159///
160/// Prepared statements allow you to execute the same query multiple times
161/// with different parameters efficiently. The statement is prepared once on
162/// the server and can be executed many times with different parameter values.
163///
164/// For automatic cleanup, use \[`OwnedPreparedStatement`\] via \[`crate::Client::prepare`\].
165///
166/// # Example
167///
168/// ```no_run
169/// # use hyperdb_api_core::{params, client::{Client, Config}};
170/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
171/// let stmt = client.prepare("SELECT * FROM users WHERE id = $1")?;
172/// let rows1 = client.execute(&stmt, params![42_i32])?;
173/// let rows2 = client.execute(&stmt, params![100_i32])?;
174/// # Ok(())
175/// # }
176/// ```
177#[derive(Debug)]
178pub struct PreparedStatement {
179    /// Statement name on the server (used for Bind/Execute messages).
180    name: String,
181    /// Original SQL query string.
182    query: String,
183    /// Parameter type OIDs (empty if types were inferred by the server).
184    param_types: Vec<Oid>,
185    /// Result column descriptions (populated after first execution).
186    columns: Vec<Column>,
187}
188
189/// A prepared statement that automatically closes itself when dropped.
190///
191/// This is the recommended way to use prepared statements. It holds a weak
192/// reference to the connection and automatically closes the statement when dropped.
193///
194/// # Example
195///
196/// ```no_run
197/// # use hyperdb_api_core::{params, client::{Client, Config}};
198/// # fn example(client: &Client) -> hyperdb_api_core::client::Result<()> {
199/// // Statement automatically closes when it goes out of scope
200/// {
201///     let stmt = client.prepare("SELECT * FROM users WHERE id = $1")?;
202///     let rows = client.execute(&stmt, params![42_i32])?;
203/// } // Statement is automatically closed here
204/// # Ok(())
205/// # }
206/// ```
207#[derive(Debug)]
208pub struct OwnedPreparedStatement {
209    /// The underlying prepared statement.
210    statement: PreparedStatement,
211    /// Weak reference to the connection for cleanup.
212    connection: Weak<Mutex<RawConnection<SyncStream>>>,
213}
214
215impl OwnedPreparedStatement {
216    /// Creates a new owned prepared statement.
217    pub(crate) fn new(
218        statement: PreparedStatement,
219        connection: &Arc<Mutex<RawConnection<SyncStream>>>,
220    ) -> Self {
221        OwnedPreparedStatement {
222            statement,
223            connection: Arc::downgrade(connection),
224        }
225    }
226
227    /// Returns the statement name.
228    #[must_use]
229    pub fn name(&self) -> &str {
230        self.statement.name()
231    }
232
233    /// Returns the original query.
234    #[must_use]
235    pub fn query(&self) -> &str {
236        self.statement.query()
237    }
238
239    /// Returns the parameter types.
240    #[must_use]
241    pub fn param_types(&self) -> &[Oid] {
242        self.statement.param_types()
243    }
244
245    /// Returns the number of parameters.
246    #[must_use]
247    pub fn param_count(&self) -> usize {
248        self.statement.param_count()
249    }
250
251    /// Returns the result column descriptions.
252    #[must_use]
253    pub fn columns(&self) -> &[Column] {
254        self.statement.columns()
255    }
256
257    /// Returns the number of result columns.
258    #[must_use]
259    pub fn column_count(&self) -> usize {
260        self.statement.column_count()
261    }
262
263    /// Returns a reference to the underlying `PreparedStatement`.
264    #[must_use]
265    pub fn statement(&self) -> &PreparedStatement {
266        &self.statement
267    }
268
269    /// Explicitly closes the statement, returning any error.
270    ///
271    /// This is called automatically when the `OwnedPreparedStatement` is
272    /// dropped, but errors are silently ignored in that case. Use this
273    /// method if you need to handle close errors.
274    ///
275    /// # Errors
276    ///
277    /// Propagates any error from [`close_statement`] — connection
278    /// mutex poisoning, server-side error during `Close`/`Sync`, or
279    /// wire I/O failure. Returns `Ok(())` without contacting the server
280    /// when the connection has already been dropped.
281    pub fn close(self) -> Result<()> {
282        if let Some(conn) = self.connection.upgrade() {
283            close_statement(&conn, &self.statement)?;
284        }
285        // Don't run Drop since we've already closed
286        std::mem::forget(self);
287        Ok(())
288    }
289}
290
291impl Drop for OwnedPreparedStatement {
292    fn drop(&mut self) {
293        // Best-effort cleanup - log errors but don't panic during drop
294        if let Some(conn) = self.connection.upgrade()
295            && let Err(e) = close_statement_internal(&conn, &self.statement)
296        {
297            warn!(
298                target: "hyperdb_api",
299                statement_name = %self.statement.name,
300                error = %e,
301                "failed-to-close-prepared-statement-during-drop"
302            );
303        }
304        // If the connection is already dropped, we can't close the statement
305        // but that's okay - the server will clean it up when the connection closes
306    }
307}
308
309impl PreparedStatement {
310    /// Returns the statement name.
311    #[must_use]
312    pub fn name(&self) -> &str {
313        &self.name
314    }
315
316    /// Returns the original query.
317    #[must_use]
318    pub fn query(&self) -> &str {
319        &self.query
320    }
321
322    /// Returns the parameter types.
323    #[must_use]
324    pub fn param_types(&self) -> &[Oid] {
325        &self.param_types
326    }
327
328    /// Returns the number of parameters.
329    #[must_use]
330    pub fn param_count(&self) -> usize {
331        self.param_types.len()
332    }
333
334    /// Returns the result column descriptions.
335    #[must_use]
336    pub fn columns(&self) -> &[Column] {
337        &self.columns
338    }
339
340    /// Returns the number of result columns.
341    #[must_use]
342    pub fn column_count(&self) -> usize {
343        self.columns.len()
344    }
345}
346
347/// Prepares a statement using the extended query protocol.
348///
349/// # Errors
350///
351/// - Returns [`Error`] (connection) if the connection mutex is poisoned.
352/// - Returns [`Error`] (server) if the server rejects the `Parse` request
353///   (SQL syntax error, unknown parameter OIDs, etc.).
354/// - Returns [`Error`] (I/O) / [`Error`] (closed) on wire-protocol I/O
355///   failure.
356pub fn prepare(
357    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
358    query: &str,
359    param_types: &[Oid],
360) -> Result<PreparedStatement> {
361    let name = generate_statement_name();
362    let mut conn = connection
363        .lock()
364        .map_err(|_| Error::connection("connection mutex poisoned"))?;
365
366    // Send Parse message
367    frontend::parse(&name, query, param_types, conn.write_buf())?;
368
369    // Send Describe message for the statement
370    frontend::describe(b'S', &name, conn.write_buf())?;
371
372    // Send Sync to get responses
373    frontend::sync(conn.write_buf());
374    conn.flush()?;
375
376    // Process responses
377    let mut parsed_params = Vec::new();
378    let mut parsed_columns = Vec::new();
379
380    loop {
381        let msg = conn.read_message()?;
382        match msg {
383            Message::ParseComplete => {
384                // Statement parsed successfully
385            }
386            Message::ParameterDescription(desc) => {
387                for oid in desc.parameters().filter_map(|r| {
388                    r.map_err(|e| trace!(target: "hyperdb_api_core::client", error = %e, "dropped error parsing parameter OID")).ok()
389                }) {
390                    parsed_params.push(oid);
391                }
392            }
393            Message::RowDescription(desc) => {
394                for f in desc.fields().filter_map(|r| {
395                    r.map_err(|e| trace!(target: "hyperdb_api_core::client", error = %e, "dropped error parsing row description field")).ok()
396                }) {
397                    parsed_columns.push(Column::new(
398                        f.name().to_string(),
399                        f.type_oid(),
400                        f.type_modifier(),
401                        super::statement::ColumnFormat::from_code(f.format()),
402                    ));
403                }
404            }
405            Message::NoData => {
406                // Statement returns no data (e.g., INSERT)
407            }
408            Message::ReadyForQuery(_) => {
409                break;
410            }
411            Message::ErrorResponse(body) => {
412                return Err(conn.consume_error(&body));
413            }
414            _ => {}
415        }
416    }
417
418    Ok(PreparedStatement {
419        name,
420        query: query.to_string(),
421        param_types: parsed_params,
422        columns: parsed_columns,
423    })
424}
425
426/// Executes a prepared statement with parameters, collecting all rows.
427///
428/// **Every parameter is bound as PostgreSQL binary.** Values whose
429/// [`ParamFormat`] is [`ParamFormat::Text`] — a scaled `NUMERIC`, a
430/// `geography` — will be rejected by the server, because the bytes
431/// `ToSqlParam::encode_param` produced for them are text. That combination is
432/// unreachable from `hyperdb-api`, which streams through
433/// [`RawConnection::start_execute_prepared_with_formats`] instead; if you are
434/// calling this directly and need mixed formats, use
435/// [`execute_prepared_no_result_with_formats`] or the streaming path.
436///
437/// # Errors
438///
439/// - Returns [`Error`] (connection) if the connection mutex is poisoned.
440/// - Returns [`Error`] (server) if the server rejects `Bind` / `Execute`
441///   (parameter type mismatch, constraint violation, etc.).
442/// - Returns [`Error`] (I/O) / [`Error`] (closed) on wire-protocol I/O
443///   failure.
444/// - Propagates row-construction errors from `Row::new` if a
445///   `DataRow` cannot be decoded against the prepared columns.
446pub fn execute_prepared(
447    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
448    statement: &PreparedStatement,
449    params: &[Option<&[u8]>],
450) -> Result<Vec<Row>> {
451    // `&[]` means all-binary; see `bind_format_codes`. Infallible here.
452    let param_formats = bind_format_codes(&[], params.len())?;
453    let result_formats: Vec<i16> = vec![1; statement.columns.len()]; // 1 = binary
454
455    let mut conn = connection
456        .lock()
457        .map_err(|_| Error::connection("connection mutex poisoned"))?;
458
459    frontend::bind(
460        "", // unnamed portal
461        &statement.name,
462        &param_formats,
463        params,
464        &result_formats,
465        conn.write_buf(),
466    )?;
467
468    // Execute
469    frontend::execute("", 0, conn.write_buf())?; // 0 = fetch all rows
470
471    // Sync
472    frontend::sync(conn.write_buf());
473    conn.flush()?;
474
475    // Process responses
476    let mut rows = Vec::new();
477    let columns = Arc::new(statement.columns.clone());
478
479    loop {
480        let msg = conn.read_message()?;
481        match msg {
482            Message::BindComplete => {
483                // Bind succeeded
484            }
485            Message::DataRow(data) => {
486                rows.push(Row::new(Arc::clone(&columns), data)?);
487            }
488            Message::CommandComplete(_) => {
489                // Execution complete
490            }
491            Message::EmptyQueryResponse => {
492                // Empty query
493            }
494            Message::ReadyForQuery(_) => {
495                break;
496            }
497            Message::ErrorResponse(body) => {
498                return Err(conn.consume_error(&body));
499            }
500            _ => {}
501        }
502    }
503
504    Ok(rows)
505}
506
507/// Executes a prepared statement that doesn't return rows.
508///
509/// # Errors
510///
511/// Same failure modes as [`execute_prepared`] (minus row-construction
512/// errors — this path never builds rows).
513pub fn execute_prepared_no_result(
514    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
515    statement: &PreparedStatement,
516    params: &[Option<&[u8]>],
517) -> Result<u64> {
518    execute_prepared_no_result_with_formats(connection, statement, params, &[])
519}
520
521/// Executes a prepared statement that doesn't return rows, with a
522/// caller-chosen wire format per parameter.
523///
524/// `param_formats` must be the same length as `params`, or empty to mean
525/// "every parameter is binary".
526///
527/// # Errors
528///
529/// - Returns [`Error`] (protocol) if `param_formats` is non-empty and its
530///   length differs from `params`.
531/// - Otherwise the same failure modes as [`execute_prepared_no_result`].
532pub fn execute_prepared_no_result_with_formats(
533    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
534    statement: &PreparedStatement,
535    params: &[Option<&[u8]>],
536    param_formats: &[ParamFormat],
537) -> Result<u64> {
538    let param_format_codes = bind_format_codes(param_formats, params.len())?;
539
540    let mut conn = connection
541        .lock()
542        .map_err(|_| Error::connection("connection mutex poisoned"))?;
543
544    let result_formats: Vec<i16> = vec![];
545
546    frontend::bind(
547        "",
548        &statement.name,
549        &param_format_codes,
550        params,
551        &result_formats,
552        conn.write_buf(),
553    )?;
554
555    // Execute
556    frontend::execute("", 0, conn.write_buf())?;
557
558    // Sync
559    frontend::sync(conn.write_buf());
560    conn.flush()?;
561
562    // Process responses
563    let mut affected_rows = 0u64;
564
565    loop {
566        let msg = conn.read_message()?;
567        match msg {
568            Message::BindComplete => {}
569            Message::CommandComplete(body) => {
570                if let Ok(tag) = body.tag() {
571                    affected_rows = parse_affected_rows(tag);
572                }
573            }
574            Message::EmptyQueryResponse => {}
575            Message::ReadyForQuery(_) => {
576                break;
577            }
578            Message::ErrorResponse(body) => {
579                return Err(conn.consume_error(&body));
580            }
581            _ => {}
582        }
583    }
584
585    Ok(affected_rows)
586}
587
588/// Closes a prepared statement on the server.
589///
590/// # Errors
591///
592/// - Returns [`Error`] (connection) if the connection mutex is poisoned.
593/// - Returns [`Error`] (server) if the server reports an `ErrorResponse`
594///   during `Close`/`Sync`.
595/// - Returns [`Error`] (I/O) / [`Error`] (closed) on wire-protocol I/O
596///   failure.
597pub fn close_statement(
598    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
599    statement: &PreparedStatement,
600) -> Result<()> {
601    close_statement_internal(connection, statement)
602}
603
604/// Internal close function that can be used from Drop.
605fn close_statement_internal(
606    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
607    statement: &PreparedStatement,
608) -> Result<()> {
609    let mut conn = connection
610        .lock()
611        .map_err(|_| Error::connection("connection mutex poisoned"))?;
612
613    // Send Close message for the statement
614    frontend::close(b'S', &statement.name, conn.write_buf())?;
615
616    // Sync
617    frontend::sync(conn.write_buf());
618    conn.flush()?;
619
620    // Process responses
621    loop {
622        let msg = conn.read_message()?;
623        match msg {
624            Message::CloseComplete => {}
625            Message::ReadyForQuery(_) => {
626                break;
627            }
628            Message::ErrorResponse(body) => {
629                return Err(conn.consume_error(&body));
630            }
631            _ => {}
632        }
633    }
634
635    Ok(())
636}
637
638/// Creates an owned prepared statement that automatically closes when dropped.
639///
640/// # Errors
641///
642/// Propagates any error from [`prepare`].
643pub fn prepare_owned(
644    connection: &Arc<Mutex<RawConnection<SyncStream>>>,
645    query: &str,
646    param_types: &[Oid],
647) -> Result<OwnedPreparedStatement> {
648    let statement = prepare(connection, query, param_types)?;
649    Ok(OwnedPreparedStatement::new(statement, connection))
650}
651
652/// Parses affected row count from a command tag.
653fn parse_affected_rows(tag: &str) -> u64 {
654    let parts: Vec<&str> = tag.split_whitespace().collect();
655
656    match parts.first() {
657        Some(&"INSERT") => parts.get(2).and_then(|s| s.parse().ok()).unwrap_or(0),
658        Some(&"UPDATE" | &"DELETE" | &"SELECT" | &"COPY") => {
659            parts.get(1).and_then(|s| s.parse().ok()).unwrap_or(0)
660        }
661        _ => 0,
662    }
663}
664
665#[cfg(test)]
666mod tests {
667    use super::*;
668
669    #[test]
670    fn test_sql_param_i16() {
671        assert_eq!(0_i16.encode(), vec![0, 0]);
672        assert_eq!(1_i16.encode(), vec![1, 0]);
673        assert_eq!((-1_i16).encode(), vec![255, 255]);
674    }
675
676    #[test]
677    fn test_sql_param_i32() {
678        assert_eq!(0_i32.encode(), vec![0, 0, 0, 0]);
679        assert_eq!(1_i32.encode(), vec![1, 0, 0, 0]);
680        assert_eq!((-1_i32).encode(), vec![255, 255, 255, 255]);
681        assert_eq!(256_i32.encode(), vec![0, 1, 0, 0]);
682    }
683
684    #[test]
685    fn test_sql_param_i64() {
686        assert_eq!(0_i64.encode(), vec![0, 0, 0, 0, 0, 0, 0, 0]);
687        assert_eq!(1_i64.encode(), vec![1, 0, 0, 0, 0, 0, 0, 0]);
688        assert_eq!(
689            (-1_i64).encode(),
690            vec![255, 255, 255, 255, 255, 255, 255, 255]
691        );
692    }
693
694    #[test]
695    #[expect(
696        clippy::float_cmp,
697        reason = "1.5 is exactly representable; encode/decode must round-trip bit-for-bit"
698    )]
699    fn test_sql_param_f32() {
700        let encoded = 1.5_f32.encode();
701        assert_eq!(encoded.len(), 4);
702        let decoded = f32::from_le_bytes([encoded[0], encoded[1], encoded[2], encoded[3]]);
703        assert_eq!(decoded, 1.5);
704    }
705
706    #[test]
707    #[expect(
708        clippy::float_cmp,
709        reason = "1.5 is exactly representable; encode/decode must round-trip bit-for-bit"
710    )]
711    fn test_sql_param_f64() {
712        let encoded = 1.5_f64.encode();
713        assert_eq!(encoded.len(), 8);
714        let decoded = f64::from_le_bytes([
715            encoded[0], encoded[1], encoded[2], encoded[3], encoded[4], encoded[5], encoded[6],
716            encoded[7],
717        ]);
718        assert_eq!(decoded, 1.5);
719    }
720
721    #[test]
722    fn test_sql_param_bool() {
723        assert_eq!(true.encode(), vec![1]);
724        assert_eq!(false.encode(), vec![0]);
725    }
726
727    #[test]
728    fn test_sql_param_str() {
729        assert_eq!("hello".encode(), b"hello".to_vec());
730        assert_eq!("".encode(), Vec::<u8>::new());
731        assert_eq!("héllo".encode(), "héllo".as_bytes().to_vec());
732    }
733
734    #[test]
735    fn test_sql_param_string() {
736        let s = String::from("hello");
737        assert_eq!(s.encode(), b"hello".to_vec());
738        assert_eq!(s.encode(), b"hello".to_vec());
739    }
740
741    #[test]
742    fn test_sql_param_bytes() {
743        let bytes: Vec<u8> = vec![1, 2, 3, 4];
744        assert_eq!(bytes.encode(), vec![1, 2, 3, 4]);
745        assert_eq!(bytes.as_slice().encode(), vec![1, 2, 3, 4]);
746    }
747
748    #[test]
749    fn test_params_macro_empty() {
750        let p = params![];
751        assert!(p.is_empty());
752    }
753
754    #[test]
755    fn test_params_macro_single() {
756        let p = params![42_i32];
757        assert_eq!(p.len(), 1);
758        assert_eq!(p[0], Some(vec![42, 0, 0, 0]));
759    }
760
761    #[test]
762    fn test_params_macro_multiple() {
763        let p = params![42_i32, "hello", true];
764        assert_eq!(p.len(), 3);
765        assert_eq!(p[0], Some(vec![42, 0, 0, 0]));
766        assert_eq!(p[1], Some(b"hello".to_vec()));
767        assert_eq!(p[2], Some(vec![1]));
768    }
769}