Skip to content

Commit d361418

Browse files
committed
Add API documentation and make numerous items non-pub
1 parent aa282dd commit d361418

File tree

15 files changed

+508
-85
lines changed

15 files changed

+508
-85
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ edition = "2021"
55
license = "MPL-2.0"
66

77
[lib]
8-
doctest = false
8+
# doctest = false
99

1010
[features]
1111
default = [] # doesn't yet include "rustls"

examples/testconnect.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use std::fmt::Write;
1111
use anyhow::{bail, Result as AResult};
1212
use log::info;
1313

14-
use monetdb::{conn::Connection, cursor::Cursor, parms::Parameters};
14+
use monetdb::{parms::Parameters, Connection, Cursor};
1515

1616
const DEFAULT_QUERY: &str = r##"
1717
DROP TABLE IF EXISTS foo;

src/conn.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@
55
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
//
77
// Copyright 2024 MonetDB Foundation
8-
#![allow(dead_code)]
98

109
use std::sync::{
1110
atomic::{self, AtomicBool},
@@ -15,12 +14,19 @@ use std::sync::{
1514
use crate::{
1615
cursor::{delayed::DelayedCommands, Cursor, CursorError, CursorResult},
1716
framing::{
18-
connecting::{establish_connection, ConnResult},
17+
connecting::{establish_connection, ConnectResult},
1918
ServerSock, ServerState,
2019
},
2120
parms::Parameters,
2221
};
2322

23+
/// A connection to MonetDB.
24+
///
25+
/// The [top-level documentation](`super#examples`) contains some examples of how a
26+
/// connection can be created.
27+
///
28+
/// Executing queries on a connection is done with a [`Cursor`] object, which
29+
/// can be obtained using the [`cursor()`](`Connection::cursor`) method.
2430
pub struct Connection(Arc<Conn>);
2531

2632
pub(crate) struct Conn {
@@ -36,7 +42,8 @@ struct Locked {
3642
}
3743

3844
impl Connection {
39-
pub fn new(parameters: Parameters) -> ConnResult<Connection> {
45+
/// Create a new connection based on the given [`Parameters`] object.
46+
pub fn new(parameters: Parameters) -> ConnectResult<Connection> {
4047
let (sock, state, delayed) = establish_connection(parameters)?;
4148

4249
let reply_size = state.reply_size;
@@ -56,15 +63,22 @@ impl Connection {
5663
Ok(connection)
5764
}
5865

59-
pub fn connect_url(url: impl AsRef<str>) -> ConnResult<Connection> {
66+
/// Create a new connection based on the given URL.
67+
pub fn connect_url(url: impl AsRef<str>) -> ConnectResult<Connection> {
6068
let parms = Parameters::from_url(url.as_ref())?;
6169
Self::new(parms)
6270
}
6371

72+
/// Create a new [`Cursor`] for this connection
6473
pub fn cursor(&self) -> Cursor {
6574
Cursor::new(Arc::clone(&self.0))
6675
}
6776

77+
/// Close the connection.
78+
///
79+
/// Any remaining cursors will not be able to fetch new data.
80+
/// They may still be able to return some already retrieved data but
81+
/// you shouldn't count on that.
6882
pub fn close(self) {
6983
drop(self);
7084
}

src/cursor/mod.rs

Lines changed: 96 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -7,9 +7,9 @@
77
// Copyright 2024 MonetDB Foundation
88
#![allow(dead_code)]
99

10-
pub mod delayed;
11-
pub mod replies;
12-
pub mod rowset;
10+
pub(crate) mod delayed;
11+
pub(crate) mod replies;
12+
pub(crate) mod rowset;
1313

1414
use std::mem;
1515
use std::{io, sync::Arc};
@@ -21,27 +21,40 @@ use rowset::RowSet;
2121
use crate::conn::Conn;
2222
use crate::framing::reading::MapiReader;
2323
use crate::framing::writing::MapiBuf;
24+
use crate::framing::FramingError;
2425
use crate::framing::{ServerSock, ServerState};
25-
use crate::{framing::FramingError, IoError};
26+
use crate::util::ioerror::IoError;
2627

28+
/// An error that occurs while accessing data with a [`Cursor`].
2729
#[derive(Debug, PartialEq, Eq, Clone, thiserror::Error)]
2830
pub enum CursorError {
31+
/// The server returned an error.
2932
#[error("{0}")]
3033
Server(String),
34+
/// The connection has been closed.
3135
#[error("connection has been closed")]
3236
Closed,
37+
/// An IO Error occurred.
3338
#[error(transparent)]
3439
IO(#[from] IoError),
3540
#[error(transparent)]
41+
/// Something went wrong in the communication with the server.
3642
Framing(#[from] FramingError),
43+
/// The server sent a response that we do not understand.
3744
#[error(transparent)]
3845
BadReply(#[from] BadReply),
46+
/// [`next_row()`](`Cursor::next_row`) or [`next_reply()`](`Cursor::next_reply`)
47+
/// was called but the server did not send a result set.
3948
#[error("there is no result set")]
4049
NoResultSet,
41-
#[error("could not convert column {0} to {1}: {2}")]
42-
Conversion(usize, &'static str, String),
43-
#[error("server unexpectedly returned no rows")]
44-
NoRows,
50+
/// The user called the wrong typed getter, for example
51+
/// [`get_bool()`](`Cursor::get_bool`) on an INT column.
52+
#[error("could not convert column {colnr} to {expected_type}: {message}")]
53+
Conversion {
54+
colnr: usize,
55+
expected_type: &'static str,
56+
message: String,
57+
},
4558
}
4659

4760
pub type CursorResult<T> = Result<T, CursorError>;
@@ -52,6 +65,54 @@ impl From<io::Error> for CursorError {
5265
}
5366
}
5467

68+
/// Executes queries on a connection and manages retrieval of the
69+
/// results. It can be obtained using the
70+
/// [`cursor()`](`super::conn::Connection::cursor`) method on the connection.
71+
///
72+
/// The method [`execute()`][`Cursor::execute`] can be used to send SQL
73+
/// statements to the server. The server will return zero or more replies,
74+
/// usually one per statement. A reply may be an error, an acknowledgement such
75+
/// as "your UPDATE statement affected 1001 rows", or a result set. This method
76+
/// will immediately abort with `Err(CursorError::Server(_))` if *any* of the
77+
/// replies is an error message, not just the first reply.
78+
///
79+
/// Most retrieval methods on a cursor operate on the *current reply*. To move
80+
/// on to the next reply, call [`next_reply()`][`Cursor::next_reply`]. The only
81+
/// exception is [`next_row()`][`Cursor::next_row`], which will automatically
82+
/// try to skip to the next result set reply if the current reply is not a
83+
/// result set. This is useful because people often write things like
84+
/// ```sql
85+
/// CREATE TABLE foo(..);
86+
/// INSERT INTO foo SELECT .. FROM other_table;
87+
/// INSERT INTO foo SELECT .. FROM yet_another_table;
88+
/// SELECT COUNT(*) FROM foo;
89+
/// ```
90+
/// and they expect to be able to directly retrieve the count, not get an error
91+
/// message "CREATE TABLE did not return a result set". Note that
92+
/// [`next_row()`][`Cursor::next_row`] will *not* automatically skip to the next
93+
/// result set if the current result set is exhausted.
94+
///
95+
/// To retrieve data from a result set, first call
96+
/// [`next_row()`][`Cursor::next_row`]. This tries to move the cursor to the
97+
/// next row and returns a boolean indicating if a new row was found. if so,
98+
/// methods like [`get_str(colnr)`][`Cursor::get_str`] and
99+
/// [`get_i32(colnr)`][`Cursor::get_i32`] can be used to retrieve individual
100+
/// fields from this row.
101+
/// Note that you **must** call [`next_row()`][`Cursor::next_row`] before you
102+
/// call a getter. Before the first call to [`next_row()`][`Cursor::next_row`],
103+
/// the cursor is *before* the first row, not *at* the first row. This behaviour
104+
/// is convenient because it allows to write things like
105+
/// ```no_run
106+
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
107+
/// # let mut cursor: monetdb::Cursor = todo!();
108+
/// cursor.execute("SELECT * FROM mytable")?;
109+
/// while cursor.next_row()? {
110+
/// let value: Option<&str> = cursor.get_str(0)?;
111+
/// println!("{}", value.unwrap());
112+
/// }
113+
/// # Ok(())
114+
/// # }
115+
/// ```
55116
pub struct Cursor {
56117
conn: Arc<Conn>,
57118
buf: MapiBuf,
@@ -69,6 +130,9 @@ impl Cursor {
69130
}
70131
}
71132

133+
/// Execute the given SQL statements and place the cursor at the first
134+
/// reply. The results of any earlier queries on this cursor are discarded.
135+
72136
pub fn execute(&mut self, statements: &str) -> CursorResult<()> {
73137
self.exhaust()?;
74138

@@ -107,14 +171,23 @@ impl Cursor {
107171
Ok(())
108172
}
109173

174+
/// Retrieve the number of affected rows from the current reply. INSERT,
175+
/// UPDATE and SELECT statements provide the number of affected rows, but
176+
/// for example CREATE TABLE doesn't. Returns a signed value because we're
177+
/// not entirely sure whether the server ever sends negative values to indicate
178+
/// exceptional conditions.
179+
///
180+
/// TODO figure this out and deal with it.
110181
pub fn affected_rows(&self) -> Option<i64> {
111182
self.replies.affected_rows()
112183
}
113184

185+
/// Return `true` if the current reply is a result set.
114186
pub fn has_result_set(&self) -> bool {
115187
self.replies.at_result_set()
116188
}
117189

190+
/// Try to move the cursor to the next reply.
118191
pub fn next_reply(&mut self) -> CursorResult<bool> {
119192
// todo: close server side result set if necessary
120193
let old = mem::take(&mut self.replies);
@@ -148,6 +221,8 @@ impl Cursor {
148221
}
149222
}
150223

224+
/// Destroy the cursor, discarding all results. This may need to communicate with the server
225+
/// to release resources there.
151226
pub fn close(mut self) -> CursorResult<()> {
152227
self.do_close()?;
153228
Ok(())
@@ -165,6 +240,7 @@ impl Cursor {
165240
})
166241
}
167242

243+
/// Return information about the columns of the current result set.
168244
pub fn column_metadata(&self) -> &[ResultColumn] {
169245
if let ReplyParser::Data(ResultSet { columns, .. }) = &self.replies {
170246
&columns[..]
@@ -173,6 +249,15 @@ impl Cursor {
173249
}
174250
}
175251

252+
/// Advance the cursor to the next available row in the result set,
253+
/// returning a boolean that indicates whether such a row was present.
254+
///
255+
/// When the cursor enters a new result set after
256+
/// [`execute()`][`Cursor::execute`] or
257+
/// [`next_reply()`][`Cursor::next_reply`], it is initially positioned
258+
/// *before* the first row, and the first call to this method will advance
259+
/// it to be *at* the first row. This means you always have to call this method
260+
/// before calling getters.
176261
pub fn next_row(&mut self) -> CursorResult<bool> {
177262
self.skip_to_result_set()?;
178263

@@ -284,6 +369,9 @@ macro_rules! getter {
284369
};
285370
}
286371

372+
/// These getters can be called to retrieve values from the current row, after
373+
/// [`next_row()`][`Cursor::next_row`] has confirmed that that row exists.
374+
/// They return None if the value is NULL.
287375
impl Cursor {
288376
getter!(get_str, &str);
289377
getter!(get_bool, bool);

src/cursor/replies.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -538,6 +538,7 @@ impl ReplyParser {
538538
}
539539
}
540540

541+
/// Holds information about a column of a result set.
541542
#[derive(Debug, PartialEq, Eq, Clone)]
542543
pub struct ResultColumn {
543544
name: String,
@@ -552,10 +553,12 @@ impl ResultColumn {
552553
}
553554
}
554555

556+
/// Return the name of the column.
555557
pub fn name(&self) -> &str {
556558
&self.name
557559
}
558560

561+
/// Return the type of the column.
559562
pub fn sql_type(&self) -> &MonetType {
560563
&self.typ
561564
}

src/cursor/rowset.rs

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -293,11 +293,11 @@ impl RowSet {
293293
};
294294
match f(s) {
295295
Ok(t) => Ok(Some(t)),
296-
Err(e) => Err(CursorError::Conversion(
297-
col,
298-
type_name::<T>(),
299-
e.to_string(),
300-
)),
296+
Err(e) => Err(CursorError::Conversion {
297+
colnr: col,
298+
expected_type: type_name::<T>(),
299+
message: e.to_string(),
300+
}),
301301
}
302302
}
303303

@@ -355,23 +355,23 @@ fn test_int_getters() {
355355
assert_eq!(rs.advance(), Ok(true));
356356

357357
assert_eq!(rs.get_i8(0), Ok(Some(9i8)));
358-
assert_matches!(rs.get_i8(1), Err(CursorError::Conversion(_, _, _)));
359-
assert_matches!(rs.get_i8(2), Err(CursorError::Conversion(_, _, _)));
358+
assert_matches!(rs.get_i8(1), Err(CursorError::Conversion { .. }));
359+
assert_matches!(rs.get_i8(2), Err(CursorError::Conversion { .. }));
360360
assert_eq!(rs.get_i8(3), Ok(None));
361361

362362
assert_eq!(rs.get_u8(0), Ok(Some(9u8)));
363-
assert_matches!(rs.get_u8(1), Err(CursorError::Conversion(_, _, _)));
364-
assert_matches!(rs.get_u8(2), Err(CursorError::Conversion(_, _, _)));
363+
assert_matches!(rs.get_u8(1), Err(CursorError::Conversion { .. }));
364+
assert_matches!(rs.get_u8(2), Err(CursorError::Conversion { .. }));
365365
assert_eq!(rs.get_u8(3), Ok(None));
366366

367367
assert_eq!(rs.get_i16(0), Ok(Some(9i16)));
368-
assert_matches!(rs.get_i16(1), Err(CursorError::Conversion(_, _, _)));
369-
assert_matches!(rs.get_i16(2), Err(CursorError::Conversion(_, _, _)));
368+
assert_matches!(rs.get_i16(1), Err(CursorError::Conversion { .. }));
369+
assert_matches!(rs.get_i16(2), Err(CursorError::Conversion { .. }));
370370
assert_eq!(rs.get_i16(3), Ok(None));
371371

372372
assert_eq!(rs.get_u16(0), Ok(Some(9u16)));
373-
assert_matches!(rs.get_u16(1), Err(CursorError::Conversion(_, _, _)));
374-
assert_matches!(rs.get_u16(2), Err(CursorError::Conversion(_, _, _)));
373+
assert_matches!(rs.get_u16(1), Err(CursorError::Conversion { .. }));
374+
assert_matches!(rs.get_u16(2), Err(CursorError::Conversion { .. }));
375375
assert_eq!(rs.get_u16(3), Ok(None));
376376

377377
assert_eq!(rs.get_i32(0), Ok(Some(9i32)));
@@ -381,7 +381,7 @@ fn test_int_getters() {
381381

382382
assert_eq!(rs.get_u32(0), Ok(Some(9u32)));
383383
assert_eq!(rs.get_u32(1), Ok(Some(87654)));
384-
assert_matches!(rs.get_u32(2), Err(CursorError::Conversion(_, _, _)));
384+
assert_matches!(rs.get_u32(2), Err(CursorError::Conversion { .. }));
385385
assert_eq!(rs.get_u32(3), Ok(None));
386386

387387
assert_eq!(rs.get_i64(0), Ok(Some(9i64)));
@@ -391,7 +391,7 @@ fn test_int_getters() {
391391

392392
assert_eq!(rs.get_u64(0), Ok(Some(9u64)));
393393
assert_eq!(rs.get_u64(1), Ok(Some(87654)));
394-
assert_matches!(rs.get_u64(2), Err(CursorError::Conversion(_, _, _)));
394+
assert_matches!(rs.get_u64(2), Err(CursorError::Conversion { .. }));
395395
assert_eq!(rs.get_u64(3), Ok(None));
396396

397397
assert_eq!(rs.get_i128(0), Ok(Some(9i128)));
@@ -401,7 +401,7 @@ fn test_int_getters() {
401401

402402
assert_eq!(rs.get_u128(0), Ok(Some(9u128)));
403403
assert_eq!(rs.get_u128(1), Ok(Some(87654)));
404-
assert_matches!(rs.get_u128(2), Err(CursorError::Conversion(_, _, _)));
404+
assert_matches!(rs.get_u128(2), Err(CursorError::Conversion { .. }));
405405
assert_eq!(rs.get_u128(3), Ok(None));
406406

407407
assert_eq!(rs.get_isize(0), Ok(Some(9isize)));
@@ -411,7 +411,7 @@ fn test_int_getters() {
411411

412412
assert_eq!(rs.get_usize(0), Ok(Some(9usize)));
413413
assert_eq!(rs.get_usize(1), Ok(Some(87654)));
414-
assert_matches!(rs.get_usize(2), Err(CursorError::Conversion(_, _, _)));
414+
assert_matches!(rs.get_usize(2), Err(CursorError::Conversion { .. }));
415415
assert_eq!(rs.get_usize(3), Ok(None));
416416
}
417417

0 commit comments

Comments
 (0)