Skip to main content

hyperdb_api_core/client/
mod.rs

1// Copyright (c) 2026, Salesforce, Inc. All rights reserved.
2// SPDX-License-Identifier: Apache-2.0 OR MIT
3
4#![allow(
5    missing_docs,
6    reason = "internal crate; not published to crates.io. Docs are contributor-facing, covered in DEVELOPMENT.md."
7)]
8
9//! Client library for Hyper database.
10//!
11//! This crate provides both synchronous and asynchronous PostgreSQL-wire-protocol clients
12//! specifically for Hyper database servers, with support for `HyperBinary`
13//! data format, gRPC transport, and Salesforce Data Cloud authentication.
14//!
15//! # Features
16//!
17//! ## Core Client Features
18//! - **Dual architecture**: `AsyncClient` for async applications, `Client` for sync
19//! - Thread-safe clients (can be shared between threads/tasks)
20//! - Multiple authentication methods: cleartext, MD5, SCRAM-SHA-256
21//! - Simple query protocol for ad-hoc queries
22//! - Extended query protocol for prepared statements
23//! - COPY protocol for high-performance bulk insertion
24//! - Optional TLS support (rustls)
25//!
26//! ## Advanced Transport Features
27//! - **gRPC transport**: Query-only access with Arrow IPC format
28//! - **Salesforce authentication**: OAuth 2.0 and JWT Bearer Token flows
29//! - **Connection pooling**: Async connection pooling via deadpool
30//!
31//! # Quick Start
32//!
33//! ## Synchronous Client
34//!
35//! ```no_run
36//! use hyperdb_api_core::client::{Client, Config};
37//!
38//! fn main() -> hyperdb_api_core::client::Result<()> {
39//!     let config = Config::new()
40//!         .with_host("localhost")
41//!         .with_port(7483)
42//!         .with_database("test.hyper");
43//!
44//!     let client = Client::connect(&config)?;
45//!
46//!     let rows = client.query("SELECT 1 as value")?;
47//!     for row in rows {
48//!         println!("value: {:?}", row.get_i32(0));
49//!     }
50//!
51//!     client.close()?;
52//!     Ok(())
53//! }
54//! ```
55//!
56//! ## Asynchronous Client
57//!
58//! ```no_run
59//! use hyperdb_api_core::client::{AsyncClient, Config};
60//!
61//! #[tokio::main]
62//! async fn main() -> hyperdb_api_core::client::Result<()> {
63//!     let config = Config::new()
64//!         .with_host("localhost")
65//!         .with_port(7483)
66//!         .with_database("test.hyper");
67//!
68//!     let client = AsyncClient::connect(&config).await?;
69//!     let rows = client.query("SELECT 1").await?;
70//!     client.close().await?;
71//!     Ok(())
72//! }
73//! ```
74//!
75//! ## gRPC Client
76//!
77//! ```no_run
78//! use hyperdb_api_core::client::grpc::{GrpcClient, GrpcConfig};
79//!
80//! #[tokio::main]
81//! async fn main() -> hyperdb_api_core::client::Result<()> {
82//!     let config = GrpcConfig::new("http://localhost:7484");
83//!     let mut client = GrpcClient::connect(config).await?;
84//!
85//!     let result = client.execute_query("SELECT 1").await?;
86//!     println!("Query complete: {}", result.is_complete());
87//!     Ok(())
88//! }
89//! ```
90//!
91//! ## Salesforce Authentication
92//!
93//! ```ignore
94//! use hyperdb_api_salesforce::{SalesforceAuthConfig, AuthMode, DataCloudTokenProvider};
95//! use hyperdb_api_core::client::grpc::{GrpcClient, GrpcConfig};
96//!
97//! #[tokio::main]
98//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
99//!     let auth_config = SalesforceAuthConfig::new(
100//!         "https://login.salesforce.com",
101//!         "your-client-id",
102//!     )?.auth_mode(AuthMode::password("user@example.com", "password"));
103//!
104//!     let mut token_provider = DataCloudTokenProvider::new(auth_config)?;
105//!     let token = token_provider.get_token().await?;
106//!
107//!     let grpc_config = GrpcConfig::new("https://hyper.data.salesforce.com")
108//!         .header("Authorization", token.bearer_token())
109//!         .header("audience", token.tenant_url_str());
110//!
111//!     let mut client = GrpcClient::connect(grpc_config).await?;
112//!     let result = client.execute_query("SELECT 1").await?;
113//!     Ok(())
114//! }
115//! ```
116//!
117//! # Authentication Methods
118//!
119//! ## Basic Authentication
120//!
121//! ```no_run
122//! use hyperdb_api_core::client::Config;
123//!
124//! let config = Config::new()
125//!     .with_host("localhost")
126//!     .with_port(7483)
127//!     .with_user("myuser")
128//!     .with_password("mypassword")
129//!     .with_database("test.hyper");
130//! ```
131//!
132//! Supported methods:
133//! - Trust (no password required)
134//! - Cleartext password
135//! - MD5 password hash
136//! - SCRAM-SHA-256 (most secure)
137//!
138//! ## Salesforce Data Cloud Authentication
139//!
140//! Three authentication modes are supported:
141//!
142//! - **Password**: Username + password + client secret (OAuth password grant)
143//! - **`PrivateKey`**: JWT Bearer Token Flow using RSA private key (recommended for server-to-server)
144//! - **`RefreshToken`**: OAuth refresh token + client secret
145//!
146//! See the Salesforce authentication section above for a complete example.
147//!
148//! # Bulk Insertion with COPY
149//!
150//! ## Synchronous COPY
151//!
152//! ```no_run
153//! use hyperdb_api_core::client::{Client, Config};
154//! use hyperdb_api_core::protocol::copy;
155//!
156//! # fn example() -> hyperdb_api_core::client::Result<()> {
157//! let config = Config::new().with_host("localhost").with_port(7483);
158//! let client = Client::connect(&config)?;
159//!
160//! let mut writer = client.copy_in("\"my_table\"", &["col1", "col2"])?;
161//!
162//! // Build binary data
163//! let mut buf = bytes::BytesMut::new();
164//! copy::write_header(&mut buf);
165//! copy::write_i32(&mut buf, 42);
166//! copy::write_varbinary(&mut buf, b"hello");
167//!
168//! writer.send(&buf)?;
169//! let rows = writer.finish()?;
170//! # Ok(())
171//! # }
172//! ```
173//!
174//! ## Asynchronous COPY
175//!
176//! ```no_run
177//! use hyperdb_api_core::client::{AsyncClient, Config};
178//! use hyperdb_api_core::protocol::copy;
179//!
180//! #[tokio::main]
181//! async fn example() -> hyperdb_api_core::client::Result<()> {
182//!     let config = Config::new().with_host("localhost").with_port(7483);
183//!     let client = AsyncClient::connect(&config).await?;
184//!
185//!     let mut writer = client.copy_in("\"my_table\"", &["col1", "col2"]).await?;
186//!
187//!     // Build binary data
188//!     let mut buf = bytes::BytesMut::new();
189//!     copy::write_header(&mut buf);
190//!     copy::write_i32(&mut buf, 42);
191//!     copy::write_varbinary(&mut buf, b"hello");
192//!
193//!     writer.send(&buf).await?;
194//!     let rows = writer.finish().await?;
195//!     Ok(())
196//! }
197//! ```
198//!
199//! # gRPC Transport Details
200//!
201//! The gRPC transport provides read-only access to Hyper databases with the following benefits:
202//!
203//! - Better support for load balancing
204//! - Built-in streaming for large result sets  
205//! - HTTP/2 multiplexing
206//! - Easier integration with service meshes
207//! - Arrow IPC format for efficient data transfer
208//!
209//! ## gRPC Limitations
210//!
211//! The gRPC interface is **read-only**:
212//! - Only SELECT queries are supported
213//! - No INSERT, UPDATE, DELETE, or DDL operations
214//! - No COPY protocol for bulk data insertion
215//!
216//! For write operations, use the standard TCP connection.
217//!
218//! ## gRPC Parameterized Queries
219//!
220//! ```no_run
221//! use hyperdb_api_core::client::grpc::{GrpcClient, GrpcConfig, QueryParameters, ParameterStyle};
222//!
223//! #[tokio::main]
224//! async fn main() -> Result<(), Box<dyn std::error::Error>> {
225//!     let config = GrpcConfig::new("http://localhost:7484");
226//!     let mut client = GrpcClient::connect(config).await?;
227//!
228//!     // Dollar-numbered parameters ($1, $2, ...) - use serde_json::json! for mixed types
229//!     let params = QueryParameters::from_json_value(&serde_json::json!([42, "Alice"]))?;
230//!     let result = client.execute_query_with_params(
231//!         "SELECT * FROM users WHERE id = $1 AND name = $2",
232//!         params,
233//!         ParameterStyle::DollarNumbered,
234//!     ).await?;
235//!
236//!     // Named parameters using builder pattern
237//!     let params = QueryParameters::json_named()
238//!         .add("id", &42i64)?
239//!         .add("name", &"Alice")?
240//!         .build();
241//!     let result = client.execute_query_with_params(
242//!         "SELECT * FROM users WHERE id = :id AND name = :name",
243//!         params,
244//!         ParameterStyle::Named,
245//!     ).await?;
246//!
247//!     Ok(())
248//! }
249//! ```
250//!
251//! # Feature Flags
252//!
253//! - **`salesforce-auth`**: Salesforce Data Cloud OAuth authentication (via `hyperdb-api-salesforce` crate)
254//!
255//! **Always Available (no feature flags required):**
256//! - TLS support (rustls)
257//! - gRPC transport with Arrow IPC format
258//! - Async client (`AsyncClient`)
259//!
260//! # Attribution
261//!
262//! The `hyper-client` crate code was inspired by the design patterns and API
263//! structure of the [`libpq`](https://crates.io/crates/libpq) Rust crate (MIT License).
264//! While `hyper-client` does not depend on the `libpq` crate, its connection
265//! management patterns served as valuable inspiration during development.
266//!
267//! **libpq crate:**
268//! - Repository: <https://crates.io/crates/libpq>
269//! - License: MIT License
270//! - Note: The `libpq` crate is not a dependency of this project.
271
272#![warn(missing_docs, rust_2018_idioms, clippy::all)]
273
274pub mod auth;
275pub mod cancel;
276#[allow(
277    clippy::module_inception,
278    reason = "preserved submodule name after collapsing the old hyper-client crate into hyperdb-api-core/src/client/"
279)]
280pub mod client;
281pub mod config;
282pub mod connection;
283pub mod endpoint;
284pub mod error;
285pub mod notice;
286pub mod prepare;
287pub mod prepared_stream;
288pub mod row;
289pub mod statement;
290pub mod sync_stream;
291
292// Async modules
293pub mod async_client;
294pub mod async_connection;
295pub mod async_prepared_stream;
296pub mod async_stream;
297pub mod async_stream_query;
298
299pub mod tls;
300
301pub mod grpc;
302
303// Re-exports - Sync client
304pub use async_client::AsyncPreparedStatement;
305pub use cancel::Cancellable;
306pub use client::{Client, CopyInWriter, QueryStream};
307pub use config::Config;
308pub use endpoint::ConnectionEndpoint;
309pub use error::{Error, ErrorKind, Result};
310pub use notice::{Notice, NoticeReceiver};
311pub use prepare::{OwnedPreparedStatement, PreparedStatement, SqlParam};
312pub use row::{BatchRow, FromBinaryValue, Row, StreamRow};
313pub use statement::{Column, ColumnFormat};
314
315// Re-exports - Async client
316pub use async_client::{AsyncClient, AsyncCopyInWriter, AsyncCopyInWriterOwned};
317pub use async_connection::AsyncRawConnection;
318pub use async_prepared_stream::AsyncPreparedQueryStream;
319pub use async_stream::AsyncStream;
320pub use async_stream_query::AsyncQueryStream;
321pub use prepared_stream::PreparedQueryStream;
322pub use sync_stream::SyncStream;
323
324// gRPC types (always available)
325pub use grpc::{GrpcClient, GrpcConfig, GrpcError, GrpcQueryResult, GrpcResultChunk};
326
327// Re-exports of the sibling submodules, so existing `use crate::client::{protocol, types}`
328// paths (from when these were separate crates) still resolve.
329pub use crate::{protocol, types};