Get rid of postgres-shared
This commit is contained in:
parent
e0c37c3681
commit
4d16fbb906
@ -2,7 +2,6 @@
|
|||||||
members = [
|
members = [
|
||||||
"codegen",
|
"codegen",
|
||||||
"postgres-protocol",
|
"postgres-protocol",
|
||||||
"postgres-shared",
|
|
||||||
"tokio-postgres",
|
"tokio-postgres",
|
||||||
"tokio-postgres-native-tls",
|
"tokio-postgres-native-tls",
|
||||||
"tokio-postgres-openssl",
|
"tokio-postgres-openssl",
|
||||||
|
@ -9,7 +9,7 @@ mod sqlstate;
|
|||||||
mod type_gen;
|
mod type_gen;
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let path = Path::new("../postgres-shared/src");
|
let path = Path::new("../tokio-postgres/src");
|
||||||
sqlstate::build(path);
|
sqlstate::build(path);
|
||||||
type_gen::build(path);
|
type_gen::build(path);
|
||||||
}
|
}
|
||||||
|
@ -1,29 +0,0 @@
|
|||||||
[package]
|
|
||||||
name = "postgres-shared"
|
|
||||||
version = "0.4.1"
|
|
||||||
authors = ["Steven Fackler <sfackler@gmail.com>"]
|
|
||||||
license = "MIT"
|
|
||||||
description = "Internal crate used by postgres and postgres-tokio"
|
|
||||||
repository = "https://github.com/sfackler/rust-postgres"
|
|
||||||
|
|
||||||
[features]
|
|
||||||
"with-bit-vec-0.5" = ["bit-vec"]
|
|
||||||
"with-chrono-0.4" = ["chrono"]
|
|
||||||
"with-eui48-0.3" = ["eui48"]
|
|
||||||
"with-geo-0.10" = ["geo"]
|
|
||||||
with-serde_json-1 = ["serde", "serde_json"]
|
|
||||||
"with-uuid-0.6" = ["uuid"]
|
|
||||||
|
|
||||||
[dependencies]
|
|
||||||
hex = "0.3"
|
|
||||||
fallible-iterator = "0.1.3"
|
|
||||||
phf = "0.7.22"
|
|
||||||
postgres-protocol = { version = "0.3", path = "../postgres-protocol" }
|
|
||||||
|
|
||||||
bit-vec = { version = "0.5", optional = true }
|
|
||||||
chrono = { version = "0.4", optional = true }
|
|
||||||
eui48 = { version = "0.3", optional = true }
|
|
||||||
geo = { version = "0.10", optional = true }
|
|
||||||
serde = { version = "1.0", optional = true }
|
|
||||||
serde_json = { version = "1.0", optional = true }
|
|
||||||
uuid = { version = "0.6", optional = true }
|
|
@ -1,448 +0,0 @@
|
|||||||
//! Errors.
|
|
||||||
|
|
||||||
use fallible_iterator::FallibleIterator;
|
|
||||||
use postgres_protocol::message::backend::{ErrorFields, ErrorResponseBody};
|
|
||||||
use std::convert::From;
|
|
||||||
use std::error;
|
|
||||||
use std::fmt;
|
|
||||||
use std::io;
|
|
||||||
|
|
||||||
pub use self::sqlstate::*;
|
|
||||||
|
|
||||||
mod sqlstate;
|
|
||||||
|
|
||||||
/// The severity of a Postgres error or notice.
|
|
||||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
|
||||||
pub enum Severity {
|
|
||||||
/// PANIC
|
|
||||||
Panic,
|
|
||||||
/// FATAL
|
|
||||||
Fatal,
|
|
||||||
/// ERROR
|
|
||||||
Error,
|
|
||||||
/// WARNING
|
|
||||||
Warning,
|
|
||||||
/// NOTICE
|
|
||||||
Notice,
|
|
||||||
/// DEBUG
|
|
||||||
Debug,
|
|
||||||
/// INFO
|
|
||||||
Info,
|
|
||||||
/// LOG
|
|
||||||
Log,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for Severity {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
let s = match *self {
|
|
||||||
Severity::Panic => "PANIC",
|
|
||||||
Severity::Fatal => "FATAL",
|
|
||||||
Severity::Error => "ERROR",
|
|
||||||
Severity::Warning => "WARNING",
|
|
||||||
Severity::Notice => "NOTICE",
|
|
||||||
Severity::Debug => "DEBUG",
|
|
||||||
Severity::Info => "INFO",
|
|
||||||
Severity::Log => "LOG",
|
|
||||||
};
|
|
||||||
fmt.write_str(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Severity {
|
|
||||||
fn from_str(s: &str) -> Option<Severity> {
|
|
||||||
match s {
|
|
||||||
"PANIC" => Some(Severity::Panic),
|
|
||||||
"FATAL" => Some(Severity::Fatal),
|
|
||||||
"ERROR" => Some(Severity::Error),
|
|
||||||
"WARNING" => Some(Severity::Warning),
|
|
||||||
"NOTICE" => Some(Severity::Notice),
|
|
||||||
"DEBUG" => Some(Severity::Debug),
|
|
||||||
"INFO" => Some(Severity::Info),
|
|
||||||
"LOG" => Some(Severity::Log),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A Postgres error or notice.
|
|
||||||
#[derive(Clone, PartialEq, Eq)]
|
|
||||||
pub struct DbError {
|
|
||||||
/// The field contents are ERROR, FATAL, or PANIC (in an error message),
|
|
||||||
/// or WARNING, NOTICE, DEBUG, INFO, or LOG (in a notice message), or a
|
|
||||||
/// localized translation of one of these.
|
|
||||||
pub severity: String,
|
|
||||||
|
|
||||||
/// A parsed, nonlocalized version of `severity`. (PostgreSQL 9.6+)
|
|
||||||
pub parsed_severity: Option<Severity>,
|
|
||||||
|
|
||||||
/// The SQLSTATE code for the error.
|
|
||||||
pub code: SqlState,
|
|
||||||
|
|
||||||
/// The primary human-readable error message. This should be accurate but
|
|
||||||
/// terse (typically one line).
|
|
||||||
pub message: String,
|
|
||||||
|
|
||||||
/// An optional secondary error message carrying more detail about the
|
|
||||||
/// problem. Might run to multiple lines.
|
|
||||||
pub detail: Option<String>,
|
|
||||||
|
|
||||||
/// An optional suggestion what to do about the problem. This is intended
|
|
||||||
/// to differ from Detail in that it offers advice (potentially
|
|
||||||
/// inappropriate) rather than hard facts. Might run to multiple lines.
|
|
||||||
pub hint: Option<String>,
|
|
||||||
|
|
||||||
/// An optional error cursor position into either the original query string
|
|
||||||
/// or an internally generated query.
|
|
||||||
pub position: Option<ErrorPosition>,
|
|
||||||
|
|
||||||
/// An indication of the context in which the error occurred. Presently
|
|
||||||
/// this includes a call stack traceback of active procedural language
|
|
||||||
/// functions and internally-generated queries. The trace is one entry per
|
|
||||||
/// line, most recent first.
|
|
||||||
pub where_: Option<String>,
|
|
||||||
|
|
||||||
/// If the error was associated with a specific database object, the name
|
|
||||||
/// of the schema containing that object, if any. (PostgreSQL 9.3+)
|
|
||||||
pub schema: Option<String>,
|
|
||||||
|
|
||||||
/// If the error was associated with a specific table, the name of the
|
|
||||||
/// table. (Refer to the schema name field for the name of the table's
|
|
||||||
/// schema.) (PostgreSQL 9.3+)
|
|
||||||
pub table: Option<String>,
|
|
||||||
|
|
||||||
/// If the error was associated with a specific table column, the name of
|
|
||||||
/// the column. (Refer to the schema and table name fields to identify the
|
|
||||||
/// table.) (PostgreSQL 9.3+)
|
|
||||||
pub column: Option<String>,
|
|
||||||
|
|
||||||
/// If the error was associated with a specific data type, the name of the
|
|
||||||
/// data type. (Refer to the schema name field for the name of the data
|
|
||||||
/// type's schema.) (PostgreSQL 9.3+)
|
|
||||||
pub datatype: Option<String>,
|
|
||||||
|
|
||||||
/// If the error was associated with a specific constraint, the name of the
|
|
||||||
/// constraint. Refer to fields listed above for the associated table or
|
|
||||||
/// domain. (For this purpose, indexes are treated as constraints, even if
|
|
||||||
/// they weren't created with constraint syntax.) (PostgreSQL 9.3+)
|
|
||||||
pub constraint: Option<String>,
|
|
||||||
|
|
||||||
/// The file name of the source-code location where the error was reported.
|
|
||||||
pub file: Option<String>,
|
|
||||||
|
|
||||||
/// The line number of the source-code location where the error was
|
|
||||||
/// reported.
|
|
||||||
pub line: Option<u32>,
|
|
||||||
|
|
||||||
/// The name of the source-code routine reporting the error.
|
|
||||||
pub routine: Option<String>,
|
|
||||||
|
|
||||||
_p: (),
|
|
||||||
}
|
|
||||||
|
|
||||||
impl DbError {
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn new(fields: &mut ErrorFields) -> io::Result<DbError> {
|
|
||||||
let mut severity = None;
|
|
||||||
let mut parsed_severity = None;
|
|
||||||
let mut code = None;
|
|
||||||
let mut message = None;
|
|
||||||
let mut detail = None;
|
|
||||||
let mut hint = None;
|
|
||||||
let mut normal_position = None;
|
|
||||||
let mut internal_position = None;
|
|
||||||
let mut internal_query = None;
|
|
||||||
let mut where_ = None;
|
|
||||||
let mut schema = None;
|
|
||||||
let mut table = None;
|
|
||||||
let mut column = None;
|
|
||||||
let mut datatype = None;
|
|
||||||
let mut constraint = None;
|
|
||||||
let mut file = None;
|
|
||||||
let mut line = None;
|
|
||||||
let mut routine = None;
|
|
||||||
|
|
||||||
while let Some(field) = fields.next()? {
|
|
||||||
match field.type_() {
|
|
||||||
b'S' => severity = Some(field.value().to_owned()),
|
|
||||||
b'C' => code = Some(SqlState::from_code(field.value())),
|
|
||||||
b'M' => message = Some(field.value().to_owned()),
|
|
||||||
b'D' => detail = Some(field.value().to_owned()),
|
|
||||||
b'H' => hint = Some(field.value().to_owned()),
|
|
||||||
b'P' => {
|
|
||||||
normal_position = Some(field.value().parse::<u32>().map_err(|_| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"`P` field did not contain an integer",
|
|
||||||
)
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
b'p' => {
|
|
||||||
internal_position = Some(field.value().parse::<u32>().map_err(|_| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"`p` field did not contain an integer",
|
|
||||||
)
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
b'q' => internal_query = Some(field.value().to_owned()),
|
|
||||||
b'W' => where_ = Some(field.value().to_owned()),
|
|
||||||
b's' => schema = Some(field.value().to_owned()),
|
|
||||||
b't' => table = Some(field.value().to_owned()),
|
|
||||||
b'c' => column = Some(field.value().to_owned()),
|
|
||||||
b'd' => datatype = Some(field.value().to_owned()),
|
|
||||||
b'n' => constraint = Some(field.value().to_owned()),
|
|
||||||
b'F' => file = Some(field.value().to_owned()),
|
|
||||||
b'L' => {
|
|
||||||
line = Some(field.value().parse::<u32>().map_err(|_| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"`L` field did not contain an integer",
|
|
||||||
)
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
b'R' => routine = Some(field.value().to_owned()),
|
|
||||||
b'V' => {
|
|
||||||
parsed_severity = Some(Severity::from_str(field.value()).ok_or_else(|| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"`V` field contained an invalid value",
|
|
||||||
)
|
|
||||||
})?);
|
|
||||||
}
|
|
||||||
_ => {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(DbError {
|
|
||||||
severity: severity
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`S` field missing"))?,
|
|
||||||
parsed_severity: parsed_severity,
|
|
||||||
code: code
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`C` field missing"))?,
|
|
||||||
message: message
|
|
||||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidInput, "`M` field missing"))?,
|
|
||||||
detail: detail,
|
|
||||||
hint: hint,
|
|
||||||
position: match normal_position {
|
|
||||||
Some(position) => Some(ErrorPosition::Normal(position)),
|
|
||||||
None => match internal_position {
|
|
||||||
Some(position) => Some(ErrorPosition::Internal {
|
|
||||||
position: position,
|
|
||||||
query: internal_query.ok_or_else(|| {
|
|
||||||
io::Error::new(
|
|
||||||
io::ErrorKind::InvalidInput,
|
|
||||||
"`q` field missing but `p` field present",
|
|
||||||
)
|
|
||||||
})?,
|
|
||||||
}),
|
|
||||||
None => None,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
where_: where_,
|
|
||||||
schema: schema,
|
|
||||||
table: table,
|
|
||||||
column: column,
|
|
||||||
datatype: datatype,
|
|
||||||
constraint: constraint,
|
|
||||||
file: file,
|
|
||||||
line: line,
|
|
||||||
routine: routine,
|
|
||||||
_p: (),
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// manual impl to leave out _p
|
|
||||||
impl fmt::Debug for DbError {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt.debug_struct("DbError")
|
|
||||||
.field("severity", &self.severity)
|
|
||||||
.field("parsed_severity", &self.parsed_severity)
|
|
||||||
.field("code", &self.code)
|
|
||||||
.field("message", &self.message)
|
|
||||||
.field("detail", &self.detail)
|
|
||||||
.field("hint", &self.hint)
|
|
||||||
.field("position", &self.position)
|
|
||||||
.field("where_", &self.where_)
|
|
||||||
.field("schema", &self.schema)
|
|
||||||
.field("table", &self.table)
|
|
||||||
.field("column", &self.column)
|
|
||||||
.field("datatype", &self.datatype)
|
|
||||||
.field("constraint", &self.constraint)
|
|
||||||
.field("file", &self.file)
|
|
||||||
.field("line", &self.line)
|
|
||||||
.field("routine", &self.routine)
|
|
||||||
.finish()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl fmt::Display for DbError {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
write!(fmt, "{}: {}", self.severity, self.message)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for DbError {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
&self.message
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Represents the position of an error in a query.
|
|
||||||
#[derive(Clone, PartialEq, Eq, Debug)]
|
|
||||||
pub enum ErrorPosition {
|
|
||||||
/// A position in the original query.
|
|
||||||
Normal(u32),
|
|
||||||
/// A position in an internally generated query.
|
|
||||||
Internal {
|
|
||||||
/// The byte position.
|
|
||||||
position: u32,
|
|
||||||
/// A query generated by the Postgres server.
|
|
||||||
query: String,
|
|
||||||
},
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn connect(e: Box<error::Error + Sync + Send>) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::ConnectParams(e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn tls(e: Box<error::Error + Sync + Send>) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::Tls(e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn db(e: DbError) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::Db(e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn __db(e: ErrorResponseBody) -> Error {
|
|
||||||
match DbError::new(&mut e.fields()) {
|
|
||||||
Ok(e) => Error(Box::new(ErrorKind::Db(e))),
|
|
||||||
Err(e) => Error(Box::new(ErrorKind::Io(e))),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn __user<T>(e: T) -> Error
|
|
||||||
where
|
|
||||||
T: Into<Box<error::Error + Sync + Send>>,
|
|
||||||
{
|
|
||||||
Error(Box::new(ErrorKind::Conversion(e.into())))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn io(e: io::Error) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::Io(e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[doc(hidden)]
|
|
||||||
pub fn conversion(e: Box<error::Error + Sync + Send>) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::Conversion(e)))
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Debug)]
|
|
||||||
enum ErrorKind {
|
|
||||||
ConnectParams(Box<error::Error + Sync + Send>),
|
|
||||||
Tls(Box<error::Error + Sync + Send>),
|
|
||||||
Db(DbError),
|
|
||||||
Io(io::Error),
|
|
||||||
Conversion(Box<error::Error + Sync + Send>),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An error communicating with the Postgres server.
|
|
||||||
#[derive(Debug)]
|
|
||||||
pub struct Error(Box<ErrorKind>);
|
|
||||||
|
|
||||||
impl fmt::Display for Error {
|
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
|
|
||||||
fmt.write_str(error::Error::description(self))?;
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::ConnectParams(ref err) => write!(fmt, ": {}", err),
|
|
||||||
ErrorKind::Tls(ref err) => write!(fmt, ": {}", err),
|
|
||||||
ErrorKind::Db(ref err) => write!(fmt, ": {}", err),
|
|
||||||
ErrorKind::Io(ref err) => write!(fmt, ": {}", err),
|
|
||||||
ErrorKind::Conversion(ref err) => write!(fmt, ": {}", err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl error::Error for Error {
|
|
||||||
fn description(&self) -> &str {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::ConnectParams(_) => "invalid connection parameters",
|
|
||||||
ErrorKind::Tls(_) => "TLS handshake error",
|
|
||||||
ErrorKind::Db(_) => "database error",
|
|
||||||
ErrorKind::Io(_) => "IO error",
|
|
||||||
ErrorKind::Conversion(_) => "type conversion error",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn cause(&self) -> Option<&error::Error> {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::ConnectParams(ref err) => Some(&**err),
|
|
||||||
ErrorKind::Tls(ref err) => Some(&**err),
|
|
||||||
ErrorKind::Db(ref err) => Some(err),
|
|
||||||
ErrorKind::Io(ref err) => Some(err),
|
|
||||||
ErrorKind::Conversion(ref err) => Some(&**err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Error {
|
|
||||||
/// Returns the SQLSTATE error code associated with this error if it is a DB
|
|
||||||
/// error.
|
|
||||||
pub fn code(&self) -> Option<&SqlState> {
|
|
||||||
self.as_db().map(|e| &e.code)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner error if this is a connection parameter error.
|
|
||||||
pub fn as_connection(&self) -> Option<&(error::Error + 'static + Sync + Send)> {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::ConnectParams(ref err) => Some(&**err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the `DbError` associated with this error if it is a DB error.
|
|
||||||
pub fn as_db(&self) -> Option<&DbError> {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::Db(ref err) => Some(err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner error if this is a conversion error.
|
|
||||||
pub fn as_conversion(&self) -> Option<&(error::Error + 'static + Sync + Send)> {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::Conversion(ref err) => Some(&**err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Returns the inner `io::Error` associated with this error if it is an IO
|
|
||||||
/// error.
|
|
||||||
pub fn as_io(&self) -> Option<&io::Error> {
|
|
||||||
match *self.0 {
|
|
||||||
ErrorKind::Io(ref err) => Some(err),
|
|
||||||
_ => None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<io::Error> for Error {
|
|
||||||
fn from(err: io::Error) -> Error {
|
|
||||||
Error(Box::new(ErrorKind::Io(err)))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl From<Error> for io::Error {
|
|
||||||
fn from(err: Error) -> io::Error {
|
|
||||||
match *err.0 {
|
|
||||||
ErrorKind::Io(e) => e,
|
|
||||||
_ => io::Error::new(io::ErrorKind::Other, err),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,32 +0,0 @@
|
|||||||
#![allow(unknown_lints)] // for clippy
|
|
||||||
|
|
||||||
extern crate fallible_iterator;
|
|
||||||
extern crate hex;
|
|
||||||
extern crate phf;
|
|
||||||
extern crate postgres_protocol;
|
|
||||||
|
|
||||||
pub mod error;
|
|
||||||
pub mod params;
|
|
||||||
pub mod rows;
|
|
||||||
pub mod stmt;
|
|
||||||
pub mod types;
|
|
||||||
|
|
||||||
/// Contains information necessary to cancel queries for a session.
|
|
||||||
#[derive(Copy, Clone, Debug)]
|
|
||||||
pub struct CancelData {
|
|
||||||
/// The process ID of the session.
|
|
||||||
pub process_id: i32,
|
|
||||||
/// The secret key for the session.
|
|
||||||
pub secret_key: i32,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An asynchronous notification.
|
|
||||||
#[derive(Clone, Debug)]
|
|
||||||
pub struct Notification {
|
|
||||||
/// The process ID of the notifying backend process.
|
|
||||||
pub process_id: i32,
|
|
||||||
/// The name of the channel that the notify has been raised on.
|
|
||||||
pub channel: String,
|
|
||||||
/// The "payload" string passed from the notifying process.
|
|
||||||
pub payload: String,
|
|
||||||
}
|
|
@ -1,295 +0,0 @@
|
|||||||
//! Connection parameters
|
|
||||||
use std::error::Error;
|
|
||||||
use std::mem;
|
|
||||||
use std::path::PathBuf;
|
|
||||||
use std::str::FromStr;
|
|
||||||
use std::time::Duration;
|
|
||||||
|
|
||||||
use error;
|
|
||||||
use params::url::Url;
|
|
||||||
|
|
||||||
mod url;
|
|
||||||
|
|
||||||
/// The host.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub enum Host {
|
|
||||||
/// A TCP hostname.
|
|
||||||
Tcp(String),
|
|
||||||
/// The path to a directory containing the server's Unix socket.
|
|
||||||
Unix(PathBuf),
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Authentication information.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct User {
|
|
||||||
name: String,
|
|
||||||
password: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl User {
|
|
||||||
/// The username.
|
|
||||||
pub fn name(&self) -> &str {
|
|
||||||
&self.name
|
|
||||||
}
|
|
||||||
|
|
||||||
/// An optional password.
|
|
||||||
pub fn password(&self) -> Option<&str> {
|
|
||||||
self.password.as_ref().map(|p| &**p)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Information necessary to open a new connection to a Postgres server.
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
|
|
||||||
pub struct ConnectParams {
|
|
||||||
host: Host,
|
|
||||||
port: u16,
|
|
||||||
user: Option<User>,
|
|
||||||
database: Option<String>,
|
|
||||||
options: Vec<(String, String)>,
|
|
||||||
connect_timeout: Option<Duration>,
|
|
||||||
keepalive: Option<Duration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl ConnectParams {
|
|
||||||
/// Returns a new builder.
|
|
||||||
pub fn builder() -> Builder {
|
|
||||||
Builder::new()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The target host.
|
|
||||||
pub fn host(&self) -> &Host {
|
|
||||||
&self.host
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The target port.
|
|
||||||
///
|
|
||||||
/// Defaults to 5432.
|
|
||||||
pub fn port(&self) -> u16 {
|
|
||||||
self.port
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The user to log in as.
|
|
||||||
///
|
|
||||||
/// A user is required to open a new connection but not to cancel a query.
|
|
||||||
pub fn user(&self) -> Option<&User> {
|
|
||||||
self.user.as_ref()
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The database to connect to.
|
|
||||||
pub fn database(&self) -> Option<&str> {
|
|
||||||
self.database.as_ref().map(|d| &**d)
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Runtime parameters to be passed to the Postgres backend.
|
|
||||||
pub fn options(&self) -> &[(String, String)] {
|
|
||||||
&self.options
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A timeout to apply to each socket-level connection attempt.
|
|
||||||
pub fn connect_timeout(&self) -> Option<Duration> {
|
|
||||||
self.connect_timeout
|
|
||||||
}
|
|
||||||
|
|
||||||
/// The interval at which TCP keepalive messages are sent on the socket.
|
|
||||||
///
|
|
||||||
/// This is ignored for Unix sockets.
|
|
||||||
pub fn keepalive(&self) -> Option<Duration> {
|
|
||||||
self.keepalive
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for ConnectParams {
|
|
||||||
type Err = error::Error;
|
|
||||||
|
|
||||||
fn from_str(s: &str) -> Result<ConnectParams, error::Error> {
|
|
||||||
s.into_connect_params().map_err(error::connect)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A builder for `ConnectParams`.
|
|
||||||
pub struct Builder {
|
|
||||||
port: u16,
|
|
||||||
user: Option<User>,
|
|
||||||
database: Option<String>,
|
|
||||||
options: Vec<(String, String)>,
|
|
||||||
connect_timeout: Option<Duration>,
|
|
||||||
keepalive: Option<Duration>,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Builder {
|
|
||||||
/// Creates a new builder.
|
|
||||||
pub fn new() -> Builder {
|
|
||||||
Builder {
|
|
||||||
port: 5432,
|
|
||||||
user: None,
|
|
||||||
database: None,
|
|
||||||
options: vec![],
|
|
||||||
connect_timeout: None,
|
|
||||||
keepalive: None,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the port.
|
|
||||||
pub fn port(&mut self, port: u16) -> &mut Builder {
|
|
||||||
self.port = port;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the user.
|
|
||||||
pub fn user(&mut self, name: &str, password: Option<&str>) -> &mut Builder {
|
|
||||||
self.user = Some(User {
|
|
||||||
name: name.to_string(),
|
|
||||||
password: password.map(ToString::to_string),
|
|
||||||
});
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the database.
|
|
||||||
pub fn database(&mut self, database: &str) -> &mut Builder {
|
|
||||||
self.database = Some(database.to_string());
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Adds a runtime parameter.
|
|
||||||
pub fn option(&mut self, name: &str, value: &str) -> &mut Builder {
|
|
||||||
self.options.push((name.to_string(), value.to_string()));
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the connection timeout.
|
|
||||||
pub fn connect_timeout(&mut self, connect_timeout: Option<Duration>) -> &mut Builder {
|
|
||||||
self.connect_timeout = connect_timeout;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Sets the keepalive interval.
|
|
||||||
pub fn keepalive(&mut self, keepalive: Option<Duration>) -> &mut Builder {
|
|
||||||
self.keepalive = keepalive;
|
|
||||||
self
|
|
||||||
}
|
|
||||||
|
|
||||||
/// Constructs a `ConnectParams` from the builder.
|
|
||||||
pub fn build(&mut self, host: Host) -> ConnectParams {
|
|
||||||
ConnectParams {
|
|
||||||
host: host,
|
|
||||||
port: self.port,
|
|
||||||
user: self.user.take(),
|
|
||||||
database: self.database.take(),
|
|
||||||
options: mem::replace(&mut self.options, vec![]),
|
|
||||||
connect_timeout: self.connect_timeout,
|
|
||||||
keepalive: self.keepalive,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// A trait implemented by types that can be converted into a `ConnectParams`.
|
|
||||||
pub trait IntoConnectParams {
|
|
||||||
/// Converts the value of `self` into a `ConnectParams`.
|
|
||||||
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>>;
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoConnectParams for ConnectParams {
|
|
||||||
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> {
|
|
||||||
Ok(self)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl<'a> IntoConnectParams for &'a str {
|
|
||||||
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> {
|
|
||||||
match Url::parse(self) {
|
|
||||||
Ok(url) => url.into_connect_params(),
|
|
||||||
Err(err) => Err(err.into()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoConnectParams for String {
|
|
||||||
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> {
|
|
||||||
self.as_str().into_connect_params()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl IntoConnectParams for Url {
|
|
||||||
fn into_connect_params(self) -> Result<ConnectParams, Box<Error + Sync + Send>> {
|
|
||||||
let Url {
|
|
||||||
host,
|
|
||||||
port,
|
|
||||||
user,
|
|
||||||
path:
|
|
||||||
url::Path {
|
|
||||||
path,
|
|
||||||
query: options,
|
|
||||||
..
|
|
||||||
},
|
|
||||||
..
|
|
||||||
} = self;
|
|
||||||
|
|
||||||
let mut builder = ConnectParams::builder();
|
|
||||||
|
|
||||||
if let Some(port) = port {
|
|
||||||
builder.port(port);
|
|
||||||
}
|
|
||||||
|
|
||||||
if let Some(info) = user {
|
|
||||||
builder.user(&info.user, info.pass.as_ref().map(|p| &**p));
|
|
||||||
}
|
|
||||||
|
|
||||||
if !path.is_empty() {
|
|
||||||
// path contains the leading /
|
|
||||||
builder.database(&path[1..]);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (name, value) in options {
|
|
||||||
match &*name {
|
|
||||||
"connect_timeout" => {
|
|
||||||
let timeout = value.parse().map_err(|_| "invalid connect_timeout")?;
|
|
||||||
let timeout = Duration::from_secs(timeout);
|
|
||||||
builder.connect_timeout(Some(timeout));
|
|
||||||
}
|
|
||||||
"keepalive" => {
|
|
||||||
let keepalive = value.parse().map_err(|_| "invalid keepalive")?;
|
|
||||||
let keepalive = Duration::from_secs(keepalive);
|
|
||||||
builder.keepalive(Some(keepalive));
|
|
||||||
}
|
|
||||||
_ => {
|
|
||||||
builder.option(&name, &value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let maybe_path = url::decode_component(&host)?;
|
|
||||||
let host = if maybe_path.starts_with('/') {
|
|
||||||
Host::Unix(maybe_path.into())
|
|
||||||
} else {
|
|
||||||
Host::Tcp(maybe_path)
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok(builder.build(host))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(test)]
|
|
||||||
mod test {
|
|
||||||
use super::*;
|
|
||||||
|
|
||||||
#[test]
|
|
||||||
fn parse_url() {
|
|
||||||
let params = "postgres://user@host:44/dbname?connect_timeout=10&application_name=foo";
|
|
||||||
let params = params.into_connect_params().unwrap();
|
|
||||||
assert_eq!(
|
|
||||||
params.user(),
|
|
||||||
Some(&User {
|
|
||||||
name: "user".to_string(),
|
|
||||||
password: None,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
assert_eq!(params.host(), &Host::Tcp("host".to_string()));
|
|
||||||
assert_eq!(params.port(), 44);
|
|
||||||
assert_eq!(params.database(), Some("dbname"));
|
|
||||||
assert_eq!(
|
|
||||||
params.options(),
|
|
||||||
&[("application_name".to_string(), "foo".to_string())][..]
|
|
||||||
);
|
|
||||||
assert_eq!(params.connect_timeout(), Some(Duration::from_secs(10)));
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,465 +0,0 @@
|
|||||||
// Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
|
|
||||||
// file at the top-level directory of this distribution and at
|
|
||||||
// http://rust-lang.org/COPYRIGHT.
|
|
||||||
//
|
|
||||||
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
|
|
||||||
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
||||||
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
||||||
// option. This file may not be copied, modified, or distributed
|
|
||||||
// except according to those terms.
|
|
||||||
use hex::FromHex;
|
|
||||||
use std::str::FromStr;
|
|
||||||
|
|
||||||
pub struct Url {
|
|
||||||
pub scheme: String,
|
|
||||||
pub user: Option<UserInfo>,
|
|
||||||
pub host: String,
|
|
||||||
pub port: Option<u16>,
|
|
||||||
pub path: Path,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct Path {
|
|
||||||
pub path: String,
|
|
||||||
pub query: Query,
|
|
||||||
pub fragment: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub struct UserInfo {
|
|
||||||
pub user: String,
|
|
||||||
pub pass: Option<String>,
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type Query = Vec<(String, String)>;
|
|
||||||
|
|
||||||
impl Url {
|
|
||||||
pub fn new(
|
|
||||||
scheme: String,
|
|
||||||
user: Option<UserInfo>,
|
|
||||||
host: String,
|
|
||||||
port: Option<u16>,
|
|
||||||
path: String,
|
|
||||||
query: Query,
|
|
||||||
fragment: Option<String>,
|
|
||||||
) -> Url {
|
|
||||||
Url {
|
|
||||||
scheme: scheme,
|
|
||||||
user: user,
|
|
||||||
host: host,
|
|
||||||
port: port,
|
|
||||||
path: Path::new(path, query, fragment),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(rawurl: &str) -> DecodeResult<Url> {
|
|
||||||
// scheme
|
|
||||||
let (scheme, rest) = get_scheme(rawurl)?;
|
|
||||||
|
|
||||||
// authority
|
|
||||||
let (userinfo, host, port, rest) = get_authority(rest)?;
|
|
||||||
|
|
||||||
// path
|
|
||||||
let has_authority = !host.is_empty();
|
|
||||||
let (path, rest) = get_path(rest, has_authority)?;
|
|
||||||
|
|
||||||
// query and fragment
|
|
||||||
let (query, fragment) = get_query_fragment(rest)?;
|
|
||||||
|
|
||||||
let url = Url::new(
|
|
||||||
scheme.to_owned(),
|
|
||||||
userinfo,
|
|
||||||
host.to_owned(),
|
|
||||||
port,
|
|
||||||
path,
|
|
||||||
query,
|
|
||||||
fragment,
|
|
||||||
);
|
|
||||||
Ok(url)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Path {
|
|
||||||
pub fn new(path: String, query: Query, fragment: Option<String>) -> Path {
|
|
||||||
Path {
|
|
||||||
path: path,
|
|
||||||
query: query,
|
|
||||||
fragment: fragment,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn parse(rawpath: &str) -> DecodeResult<Path> {
|
|
||||||
let (path, rest) = get_path(rawpath, false)?;
|
|
||||||
|
|
||||||
// query and fragment
|
|
||||||
let (query, fragment) = get_query_fragment(&rest)?;
|
|
||||||
|
|
||||||
Ok(Path {
|
|
||||||
path: path,
|
|
||||||
query: query,
|
|
||||||
fragment: fragment,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl UserInfo {
|
|
||||||
#[inline]
|
|
||||||
pub fn new(user: String, pass: Option<String>) -> UserInfo {
|
|
||||||
UserInfo {
|
|
||||||
user: user,
|
|
||||||
pass: pass,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type DecodeResult<T> = Result<T, String>;
|
|
||||||
|
|
||||||
pub fn decode_component(container: &str) -> DecodeResult<String> {
|
|
||||||
decode_inner(container, false)
|
|
||||||
}
|
|
||||||
|
|
||||||
fn decode_inner(c: &str, full_url: bool) -> DecodeResult<String> {
|
|
||||||
let mut out = String::new();
|
|
||||||
let mut iter = c.as_bytes().iter().cloned();
|
|
||||||
|
|
||||||
loop {
|
|
||||||
match iter.next() {
|
|
||||||
Some(b) => {
|
|
||||||
match b as char {
|
|
||||||
'%' => {
|
|
||||||
let bytes = match (iter.next(), iter.next()) {
|
|
||||||
(Some(one), Some(two)) => [one, two],
|
|
||||||
_ => {
|
|
||||||
return Err("Malformed input: found '%' without two \
|
|
||||||
trailing bytes"
|
|
||||||
.to_owned())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
let bytes_from_hex = match Vec::<u8>::from_hex(&bytes) {
|
|
||||||
Ok(b) => b,
|
|
||||||
_ => {
|
|
||||||
return Err("Malformed input: found '%' followed by \
|
|
||||||
invalid hex values. Character '%' must \
|
|
||||||
escaped."
|
|
||||||
.to_owned())
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Only decode some characters if full_url:
|
|
||||||
match bytes_from_hex[0] as char {
|
|
||||||
// gen-delims:
|
|
||||||
':' | '/' | '?' | '#' | '[' | ']' | '@' | '!' | '$' | '&' | '"'
|
|
||||||
| '(' | ')' | '*' | '+' | ',' | ';' | '='
|
|
||||||
if full_url =>
|
|
||||||
{
|
|
||||||
out.push('%');
|
|
||||||
out.push(bytes[0] as char);
|
|
||||||
out.push(bytes[1] as char);
|
|
||||||
}
|
|
||||||
|
|
||||||
ch => out.push(ch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ch => out.push(ch),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => return Ok(out),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn split_char_first(s: &str, c: char) -> (&str, &str) {
|
|
||||||
let mut iter = s.splitn(2, c);
|
|
||||||
|
|
||||||
match (iter.next(), iter.next()) {
|
|
||||||
(Some(a), Some(b)) => (a, b),
|
|
||||||
(Some(a), None) => (a, ""),
|
|
||||||
(None, _) => unreachable!(),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fn query_from_str(rawquery: &str) -> DecodeResult<Query> {
|
|
||||||
let mut query: Query = vec![];
|
|
||||||
if !rawquery.is_empty() {
|
|
||||||
for p in rawquery.split('&') {
|
|
||||||
let (k, v) = split_char_first(p, '=');
|
|
||||||
query.push((decode_component(k)?, decode_component(v)?));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Ok(query)
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn get_scheme(rawurl: &str) -> DecodeResult<(&str, &str)> {
|
|
||||||
for (i, c) in rawurl.chars().enumerate() {
|
|
||||||
let result = match c {
|
|
||||||
'A'...'Z' | 'a'...'z' => continue,
|
|
||||||
'0'...'9' | '+' | '-' | '.' => {
|
|
||||||
if i != 0 {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
Err("url: Scheme must begin with a letter.".to_owned())
|
|
||||||
}
|
|
||||||
':' => {
|
|
||||||
if i == 0 {
|
|
||||||
Err("url: Scheme cannot be empty.".to_owned())
|
|
||||||
} else {
|
|
||||||
Ok((&rawurl[0..i], &rawurl[i + 1..rawurl.len()]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => Err("url: Invalid character in scheme.".to_owned()),
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
Err("url: Scheme must be terminated with a colon.".to_owned())
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns userinfo, host, port, and unparsed part, or an error
|
|
||||||
fn get_authority(rawurl: &str) -> DecodeResult<(Option<UserInfo>, &str, Option<u16>, &str)> {
|
|
||||||
enum State {
|
|
||||||
Start, // starting state
|
|
||||||
PassHostPort, // could be in user or port
|
|
||||||
Ip6Port, // either in ipv6 host or port
|
|
||||||
Ip6Host, // are in an ipv6 host
|
|
||||||
InHost, // are in a host - may be ipv6, but don't know yet
|
|
||||||
InPort, // are in port
|
|
||||||
}
|
|
||||||
|
|
||||||
#[derive(Clone, PartialEq)]
|
|
||||||
enum Input {
|
|
||||||
Digit, // all digits
|
|
||||||
Hex, // digits and letters a-f
|
|
||||||
Unreserved, // all other legal characters
|
|
||||||
}
|
|
||||||
|
|
||||||
if !rawurl.starts_with("//") {
|
|
||||||
// there is no authority.
|
|
||||||
return Ok((None, "", None, rawurl));
|
|
||||||
}
|
|
||||||
|
|
||||||
let len = rawurl.len();
|
|
||||||
let mut st = State::Start;
|
|
||||||
let mut input = Input::Digit; // most restricted, start here.
|
|
||||||
|
|
||||||
let mut userinfo = None;
|
|
||||||
let mut host = "";
|
|
||||||
let mut port = None;
|
|
||||||
|
|
||||||
let mut colon_count = 0usize;
|
|
||||||
let mut pos = 0;
|
|
||||||
let mut begin = 2;
|
|
||||||
let mut end = len;
|
|
||||||
|
|
||||||
for (i, c) in rawurl.chars().enumerate().skip(2) {
|
|
||||||
// deal with input class first
|
|
||||||
match c {
|
|
||||||
'0'...'9' => (),
|
|
||||||
'A'...'F' | 'a'...'f' => {
|
|
||||||
if input == Input::Digit {
|
|
||||||
input = Input::Hex;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
'G'...'Z'
|
|
||||||
| 'g'...'z'
|
|
||||||
| '-'
|
|
||||||
| '.'
|
|
||||||
| '_'
|
|
||||||
| '~'
|
|
||||||
| '%'
|
|
||||||
| '&'
|
|
||||||
| '\''
|
|
||||||
| '('
|
|
||||||
| ')'
|
|
||||||
| '+'
|
|
||||||
| '!'
|
|
||||||
| '*'
|
|
||||||
| ','
|
|
||||||
| ';'
|
|
||||||
| '=' => input = Input::Unreserved,
|
|
||||||
':' | '@' | '?' | '#' | '/' => {
|
|
||||||
// separators, don't change anything
|
|
||||||
}
|
|
||||||
_ => return Err("Illegal character in authority".to_owned()),
|
|
||||||
}
|
|
||||||
|
|
||||||
// now process states
|
|
||||||
match c {
|
|
||||||
':' => {
|
|
||||||
colon_count += 1;
|
|
||||||
match st {
|
|
||||||
State::Start => {
|
|
||||||
pos = i;
|
|
||||||
st = State::PassHostPort;
|
|
||||||
}
|
|
||||||
State::PassHostPort => {
|
|
||||||
// multiple colons means ipv6 address.
|
|
||||||
if input == Input::Unreserved {
|
|
||||||
return Err("Illegal characters in IPv6 address.".to_owned());
|
|
||||||
}
|
|
||||||
st = State::Ip6Host;
|
|
||||||
}
|
|
||||||
State::InHost => {
|
|
||||||
pos = i;
|
|
||||||
if input == Input::Unreserved {
|
|
||||||
// must be port
|
|
||||||
host = &rawurl[begin..i];
|
|
||||||
st = State::InPort;
|
|
||||||
} else {
|
|
||||||
// can't be sure whether this is an ipv6 address or a port
|
|
||||||
st = State::Ip6Port;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
State::Ip6Port => {
|
|
||||||
if input == Input::Unreserved {
|
|
||||||
return Err("Illegal characters in authority.".to_owned());
|
|
||||||
}
|
|
||||||
st = State::Ip6Host;
|
|
||||||
}
|
|
||||||
State::Ip6Host => {
|
|
||||||
if colon_count > 7 {
|
|
||||||
host = &rawurl[begin..i];
|
|
||||||
pos = i;
|
|
||||||
st = State::InPort;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_ => return Err("Invalid ':' in authority.".to_owned()),
|
|
||||||
}
|
|
||||||
input = Input::Digit; // reset input class
|
|
||||||
}
|
|
||||||
|
|
||||||
'@' => {
|
|
||||||
input = Input::Digit; // reset input class
|
|
||||||
colon_count = 0; // reset count
|
|
||||||
match st {
|
|
||||||
State::Start => {
|
|
||||||
let user = decode_component(&rawurl[begin..i])?;
|
|
||||||
userinfo = Some(UserInfo::new(user, None));
|
|
||||||
st = State::InHost;
|
|
||||||
}
|
|
||||||
State::PassHostPort => {
|
|
||||||
let user = decode_component(&rawurl[begin..pos])?;
|
|
||||||
let pass = decode_component(&rawurl[pos + 1..i])?;
|
|
||||||
userinfo = Some(UserInfo::new(user, Some(pass)));
|
|
||||||
st = State::InHost;
|
|
||||||
}
|
|
||||||
_ => return Err("Invalid '@' in authority.".to_owned()),
|
|
||||||
}
|
|
||||||
begin = i + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
'?' | '#' | '/' => {
|
|
||||||
end = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => (),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// finish up
|
|
||||||
match st {
|
|
||||||
State::PassHostPort | State::Ip6Port => {
|
|
||||||
if input != Input::Digit {
|
|
||||||
return Err("Non-digit characters in port.".to_owned());
|
|
||||||
}
|
|
||||||
host = &rawurl[begin..pos];
|
|
||||||
port = Some(&rawurl[pos + 1..end]);
|
|
||||||
}
|
|
||||||
State::Ip6Host | State::InHost | State::Start => host = &rawurl[begin..end],
|
|
||||||
State::InPort => {
|
|
||||||
if input != Input::Digit {
|
|
||||||
return Err("Non-digit characters in port.".to_owned());
|
|
||||||
}
|
|
||||||
port = Some(&rawurl[pos + 1..end]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let rest = &rawurl[end..len];
|
|
||||||
// If we have a port string, ensure it parses to u16.
|
|
||||||
let port = match port {
|
|
||||||
None => None,
|
|
||||||
opt => match opt.and_then(|p| FromStr::from_str(p).ok()) {
|
|
||||||
None => return Err(format!("Failed to parse port: {:?}", port)),
|
|
||||||
opt => opt,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
Ok((userinfo, host, port, rest))
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the path and unparsed part of url, or an error
|
|
||||||
fn get_path(rawurl: &str, is_authority: bool) -> DecodeResult<(String, &str)> {
|
|
||||||
let len = rawurl.len();
|
|
||||||
let mut end = len;
|
|
||||||
for (i, c) in rawurl.chars().enumerate() {
|
|
||||||
match c {
|
|
||||||
'A'...'Z'
|
|
||||||
| 'a'...'z'
|
|
||||||
| '0'...'9'
|
|
||||||
| '&'
|
|
||||||
| '\''
|
|
||||||
| '('
|
|
||||||
| ')'
|
|
||||||
| '.'
|
|
||||||
| '@'
|
|
||||||
| ':'
|
|
||||||
| '%'
|
|
||||||
| '/'
|
|
||||||
| '+'
|
|
||||||
| '!'
|
|
||||||
| '*'
|
|
||||||
| ','
|
|
||||||
| ';'
|
|
||||||
| '='
|
|
||||||
| '_'
|
|
||||||
| '-'
|
|
||||||
| '~' => continue,
|
|
||||||
'?' | '#' => {
|
|
||||||
end = i;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
_ => return Err("Invalid character in path.".to_owned()),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if is_authority && end != 0 && !rawurl.starts_with('/') {
|
|
||||||
Err("Non-empty path must begin with '/' in presence of authority.".to_owned())
|
|
||||||
} else {
|
|
||||||
Ok((decode_component(&rawurl[0..end])?, &rawurl[end..len]))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// returns the parsed query and the fragment, if present
|
|
||||||
fn get_query_fragment(rawurl: &str) -> DecodeResult<(Query, Option<String>)> {
|
|
||||||
let (before_fragment, raw_fragment) = split_char_first(rawurl, '#');
|
|
||||||
|
|
||||||
// Parse the fragment if available
|
|
||||||
let fragment = match raw_fragment {
|
|
||||||
"" => None,
|
|
||||||
raw => Some(decode_component(raw)?),
|
|
||||||
};
|
|
||||||
|
|
||||||
match before_fragment.chars().next() {
|
|
||||||
Some('?') => Ok((query_from_str(&before_fragment[1..])?, fragment)),
|
|
||||||
None => Ok((vec![], fragment)),
|
|
||||||
_ => Err(format!(
|
|
||||||
"Query didn't start with '?': '{}..'",
|
|
||||||
before_fragment
|
|
||||||
)),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Url {
|
|
||||||
type Err = String;
|
|
||||||
fn from_str(s: &str) -> Result<Url, String> {
|
|
||||||
Url::parse(s)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
impl FromStr for Path {
|
|
||||||
type Err = String;
|
|
||||||
fn from_str(s: &str) -> Result<Path, String> {
|
|
||||||
Path::parse(s)
|
|
||||||
}
|
|
||||||
}
|
|
@ -23,12 +23,12 @@ features = [
|
|||||||
circle-ci = { repository = "sfackler/rust-postgres" }
|
circle-ci = { repository = "sfackler/rust-postgres" }
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
"with-bit-vec-0.5" = ["postgres-shared/with-bit-vec-0.5"]
|
"with-bit-vec-0.5" = ["bit-vec"]
|
||||||
"with-chrono-0.4" = ["postgres-shared/with-chrono-0.4"]
|
"with-chrono-0.4" = ["chrono"]
|
||||||
"with-eui48-0.3" = ["postgres-shared/with-eui48-0.3"]
|
"with-eui48-0.3" = ["eui48"]
|
||||||
"with-geo-0.10" = ["postgres-shared/with-geo-0.10"]
|
"with-geo-0.10" = ["geo"]
|
||||||
"with-serde_json-1" = ["postgres-shared/with-serde_json-1"]
|
with-serde_json-1 = ["serde", "serde_json"]
|
||||||
"with-uuid-0.6" = ["postgres-shared/with-uuid-0.6"]
|
"with-uuid-0.6" = ["uuid"]
|
||||||
|
|
||||||
[dependencies]
|
[dependencies]
|
||||||
antidote = "1.0"
|
antidote = "1.0"
|
||||||
@ -37,14 +37,21 @@ fallible-iterator = "0.1.3"
|
|||||||
futures = "0.1.7"
|
futures = "0.1.7"
|
||||||
futures-cpupool = "0.1"
|
futures-cpupool = "0.1"
|
||||||
log = "0.4"
|
log = "0.4"
|
||||||
phf = "=0.7.22"
|
phf = "0.7.23"
|
||||||
postgres-protocol = { version = "0.3.0", path = "../postgres-protocol" }
|
postgres-protocol = { version = "0.3.0", path = "../postgres-protocol" }
|
||||||
postgres-shared = { version = "0.4.0", path = "../postgres-shared" }
|
|
||||||
state_machine_future = "0.1.7"
|
state_machine_future = "0.1.7"
|
||||||
tokio-codec = "0.1"
|
tokio-codec = "0.1"
|
||||||
tokio-io = "0.1"
|
tokio-io = "0.1"
|
||||||
void = "1.0"
|
void = "1.0"
|
||||||
|
|
||||||
|
bit-vec = { version = "0.5", optional = true }
|
||||||
|
chrono = { version = "0.4", optional = true }
|
||||||
|
eui48 = { version = "0.3", optional = true }
|
||||||
|
geo = { version = "0.10", optional = true }
|
||||||
|
serde = { version = "1.0", optional = true }
|
||||||
|
serde_json = { version = "1.0", optional = true }
|
||||||
|
uuid = { version = "0.6", optional = true }
|
||||||
|
|
||||||
[dev-dependencies]
|
[dev-dependencies]
|
||||||
tokio = "0.1.7"
|
tokio = "0.1.7"
|
||||||
env_logger = "0.5"
|
env_logger = "0.5"
|
||||||
|
@ -4,7 +4,6 @@ extern crate fallible_iterator;
|
|||||||
extern crate futures_cpupool;
|
extern crate futures_cpupool;
|
||||||
extern crate phf;
|
extern crate phf;
|
||||||
extern crate postgres_protocol;
|
extern crate postgres_protocol;
|
||||||
extern crate postgres_shared;
|
|
||||||
extern crate tokio_codec;
|
extern crate tokio_codec;
|
||||||
extern crate tokio_io;
|
extern crate tokio_io;
|
||||||
extern crate void;
|
extern crate void;
|
||||||
@ -18,29 +17,26 @@ extern crate state_machine_future;
|
|||||||
|
|
||||||
use bytes::{Bytes, IntoBuf};
|
use bytes::{Bytes, IntoBuf};
|
||||||
use futures::{Async, Future, Poll, Stream};
|
use futures::{Async, Future, Poll, Stream};
|
||||||
use postgres_shared::rows::RowIndex;
|
|
||||||
use std::error::Error as StdError;
|
use std::error::Error as StdError;
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
use std::sync::atomic::{AtomicUsize, Ordering};
|
use std::sync::atomic::{AtomicUsize, Ordering};
|
||||||
use tokio_io::{AsyncRead, AsyncWrite};
|
use tokio_io::{AsyncRead, AsyncWrite};
|
||||||
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use postgres_shared::stmt::Column;
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use postgres_shared::{params, types};
|
|
||||||
#[doc(inline)]
|
|
||||||
pub use postgres_shared::{CancelData, Notification};
|
|
||||||
|
|
||||||
pub use builder::*;
|
pub use builder::*;
|
||||||
pub use error::*;
|
pub use error::*;
|
||||||
use proto::CancelFuture;
|
use proto::CancelFuture;
|
||||||
|
use rows::RowIndex;
|
||||||
|
pub use stmt::Column;
|
||||||
pub use tls::*;
|
pub use tls::*;
|
||||||
use types::{FromSql, ToSql, Type};
|
use types::{FromSql, ToSql, Type};
|
||||||
|
|
||||||
mod builder;
|
mod builder;
|
||||||
pub mod error;
|
pub mod error;
|
||||||
mod proto;
|
mod proto;
|
||||||
|
pub mod rows;
|
||||||
|
mod stmt;
|
||||||
mod tls;
|
mod tls;
|
||||||
|
pub mod types;
|
||||||
|
|
||||||
fn next_statement() -> String {
|
fn next_statement() -> String {
|
||||||
static ID: AtomicUsize = AtomicUsize::new(0);
|
static ID: AtomicUsize = AtomicUsize::new(0);
|
||||||
@ -386,3 +382,23 @@ impl Future for BatchExecute {
|
|||||||
self.0.poll()
|
self.0.poll()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Contains information necessary to cancel queries for a session.
|
||||||
|
#[derive(Copy, Clone, Debug)]
|
||||||
|
pub struct CancelData {
|
||||||
|
/// The process ID of the session.
|
||||||
|
pub process_id: i32,
|
||||||
|
/// The secret key for the session.
|
||||||
|
pub secret_key: i32,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An asynchronous notification.
|
||||||
|
#[derive(Clone, Debug)]
|
||||||
|
pub struct Notification {
|
||||||
|
/// The process ID of the notifying backend process.
|
||||||
|
pub process_id: i32,
|
||||||
|
/// The name of the channel that the notify has been raised on.
|
||||||
|
pub channel: String,
|
||||||
|
/// The "payload" string passed from the notifying process.
|
||||||
|
pub payload: String,
|
||||||
|
}
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use postgres_protocol::message::backend::DataRowBody;
|
use postgres_protocol::message::backend::DataRowBody;
|
||||||
use postgres_shared::rows::{RowData, RowIndex};
|
|
||||||
use std::fmt;
|
use std::fmt;
|
||||||
|
|
||||||
use proto::statement::Statement;
|
use proto::statement::Statement;
|
||||||
|
use rows::{RowData, RowIndex};
|
||||||
use types::{FromSql, WrongType};
|
use types::{FromSql, WrongType};
|
||||||
use {Column, Error};
|
use {Column, Error};
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
use postgres_shared::stmt::Column;
|
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
|
|
||||||
use proto::client::WeakClient;
|
use proto::client::WeakClient;
|
||||||
use types::Type;
|
use types::Type;
|
||||||
|
use Column;
|
||||||
|
|
||||||
pub struct StatementInner {
|
pub struct StatementInner {
|
||||||
client: WeakClient,
|
client: WeakClient,
|
||||||
|
Loading…
Reference in New Issue
Block a user