mirror of
https://github.com/launchbadge/sqlx
synced 2024-11-10 06:24:16 +00:00
prototyping sql!()
macro
This commit is contained in:
parent
298421b45a
commit
3a76f9d207
14 changed files with 118 additions and 17 deletions
|
@ -33,6 +33,7 @@ url = "2.1.0"
|
|||
[dev-dependencies]
|
||||
matches = "0.1.8"
|
||||
tokio = { version = "0.2.0-alpha.4", default-features = false, features = [ "rt-full" ] }
|
||||
sqlx-macros = { path = "sqlx-macros/" }
|
||||
|
||||
[profile.release]
|
||||
lto = true
|
||||
|
|
19
sqlx-macros/Cargo.toml
Normal file
19
sqlx-macros/Cargo.toml
Normal file
|
@ -0,0 +1,19 @@
|
|||
[package]
|
||||
name = "sqlx-macros"
|
||||
version = "0.1.0"
|
||||
authors = ["Austin Bonander <austin.bonander@gmail.com>"]
|
||||
edition = "2018"
|
||||
|
||||
[lib]
|
||||
proc-macro = true
|
||||
|
||||
[dependencies]
|
||||
futures-preview = "0.3.0-alpha.18"
|
||||
hex = "0.4.0"
|
||||
proc-macro2 = "1.0.6"
|
||||
sqlx = { path = "../", features = ["postgres"] }
|
||||
syn = "1.0"
|
||||
quote = "1.0"
|
||||
sha2 = "0.8.0"
|
||||
tokio = { version = "0.2.0-alpha.4", default-features = false, features = [ "tcp" ] }
|
||||
|
46
sqlx-macros/src/lib.rs
Normal file
46
sqlx-macros/src/lib.rs
Normal file
|
@ -0,0 +1,46 @@
|
|||
extern crate proc_macro;
|
||||
|
||||
use proc_macro::TokenStream;
|
||||
|
||||
use quote::quote;
|
||||
|
||||
use syn::parse_macro_input;
|
||||
|
||||
use sha2::{Sha256, Digest};
|
||||
use sqlx::Postgres;
|
||||
|
||||
use tokio::runtime::Runtime;
|
||||
|
||||
type Error = Box<dyn std::error::Error>;
|
||||
type Result<T> = std::result::Result<T, Error>;
|
||||
|
||||
#[proc_macro]
|
||||
pub fn sql(input: TokenStream) -> TokenStream {
|
||||
let string = parse_macro_input!(input as syn::LitStr).value();
|
||||
|
||||
eprintln!("expanding macro");
|
||||
|
||||
match Runtime::new().map_err(Error::from).and_then(|runtime| runtime.block_on(process_sql(&string))) {
|
||||
Ok(ts) => ts,
|
||||
Err(e) => {
|
||||
let msg = e.to_string();
|
||||
quote! ( compile_error!(#msg) ).into()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
async fn process_sql(sql: &str) -> Result<TokenStream> {
|
||||
let hash = dbg!(hex::encode(&Sha256::digest(sql.as_bytes())));
|
||||
|
||||
let conn = sqlx::Connection::<Postgres>::establish("postgresql://postgres@127.0.0.1/sqlx_test")
|
||||
.await
|
||||
.map_err(|e| format!("failed to connect to database: {}", e))?;
|
||||
|
||||
eprintln!("connection established");
|
||||
|
||||
let prepared = conn.prepare(&hash, sql).await?;
|
||||
|
||||
let msg = format!("{:?}", prepared);
|
||||
|
||||
Ok(quote! { compile_error!(#msg) }.into())
|
||||
}
|
|
@ -75,7 +75,7 @@ pub trait RawConnection: Send {
|
|||
|
||||
async fn prepare(&mut self, name: &str, body: &str) -> crate::Result<PreparedStatement> {
|
||||
// TODO: implement for other backends
|
||||
Err("connection does not support prepare() operation".into())
|
||||
unimplemented!()
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -130,7 +130,7 @@ where
|
|||
/// Prepares a statement.
|
||||
pub async fn prepare(&self, name: &str, body: &str) -> crate::Result<PreparedStatement> {
|
||||
let mut live = self.0.acquire().await;
|
||||
let ret = live.raw.prepare(name, body)?;
|
||||
let ret = live.raw.prepare(name, body).await?;
|
||||
self.0.release(live);
|
||||
Ok(ret)
|
||||
}
|
||||
|
|
|
@ -9,6 +9,8 @@ pub trait Buf {
|
|||
|
||||
fn get_u16<T: ByteOrder>(&mut self) -> io::Result<u16>;
|
||||
|
||||
fn get_i16<T: ByteOrder>(&mut self) -> io::Result<i16>;
|
||||
|
||||
fn get_u24<T: ByteOrder>(&mut self) -> io::Result<u32>;
|
||||
|
||||
fn get_i32<T: ByteOrder>(&mut self) -> io::Result<i32>;
|
||||
|
@ -42,6 +44,13 @@ impl<'a> Buf for &'a [u8] {
|
|||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i16<T: ByteOrder>(&mut self) -> io::Result<i16> {
|
||||
let val = T::read_i16(*self);
|
||||
self.advance(2);
|
||||
|
||||
Ok(val)
|
||||
}
|
||||
|
||||
fn get_i32<T: ByteOrder>(&mut self) -> io::Result<i32> {
|
||||
let val = T::read_i32(*self);
|
||||
self.advance(4);
|
||||
|
|
|
@ -1,8 +1,10 @@
|
|||
use super::{Postgres, PostgresQueryParameters, PostgresRawConnection, PostgresRow};
|
||||
use crate::{connection::RawConnection, postgres::raw::Step, url::Url, Error};
|
||||
use crate::query::QueryParameters;
|
||||
use async_trait::async_trait;
|
||||
use futures_core::stream::BoxStream;
|
||||
use crate::prepared::{PreparedStatement, Field};
|
||||
use crate::postgres::error::ProtocolError;
|
||||
|
||||
#[async_trait]
|
||||
impl RawConnection for PostgresRawConnection {
|
||||
|
@ -94,21 +96,26 @@ impl RawConnection for PostgresRawConnection {
|
|||
Ok(row)
|
||||
}
|
||||
|
||||
fn prepare(&mut self, name: &str, body: &str) -> crate::Result<PreparedStatement> {
|
||||
self.parse(name, body, &[]);
|
||||
async fn prepare(&mut self, name: &str, body: &str) -> crate::Result<PreparedStatement> {
|
||||
self.parse(name, body, &PostgresQueryParameters::new());
|
||||
self.describe(name);
|
||||
self.sync().await?;
|
||||
|
||||
let param_desc= loop {
|
||||
if let Step::ParamDesc(desc) = self.step().await?
|
||||
.ok_or("did not receive ParameterDescription")?
|
||||
let step = self.step().await?
|
||||
.ok_or(ProtocolError("did not receive ParameterDescription"));
|
||||
|
||||
if let Step::ParamDesc(desc) = dbg!(step)?
|
||||
{
|
||||
break desc;
|
||||
}
|
||||
};
|
||||
|
||||
let row_desc = loop {
|
||||
if let Step::RowDesc(desc) = self.step().await?
|
||||
.ok_or("did not receive RowDescription")?
|
||||
let step = self.step().await?
|
||||
.ok_or(ProtocolError("did not receive RowDescription"));
|
||||
|
||||
if let Step::RowDesc(desc) = dbg!(step)?
|
||||
{
|
||||
break desc;
|
||||
}
|
||||
|
|
|
@ -1,11 +1,22 @@
|
|||
use super::protocol::Response;
|
||||
use crate::error::DatabaseError;
|
||||
use std::borrow::Cow;
|
||||
use std::fmt::Debug;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PostgresDatabaseError(pub(super) Box<Response>);
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct ProtocolError<T>(pub(super) T);
|
||||
|
||||
impl DatabaseError for PostgresDatabaseError {
|
||||
fn message(&self) -> &str {
|
||||
self.0.message()
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: AsRef<str> + Debug + Send + Sync> DatabaseError for ProtocolError<T> {
|
||||
fn message(&self) -> &str {
|
||||
self.0.as_ref()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,11 +9,11 @@ pub enum DescribeKind {
|
|||
}
|
||||
|
||||
pub struct Describe<'a> {
|
||||
kind: DescribeKind,
|
||||
pub kind: DescribeKind,
|
||||
|
||||
/// The name of the prepared statement or portal to describe (an empty string selects the
|
||||
/// unnamed prepared statement or portal).
|
||||
name: &'a str,
|
||||
pub name: &'a str,
|
||||
}
|
||||
|
||||
impl Encode for Describe<'_> {
|
||||
|
|
|
@ -65,10 +65,10 @@ fn read_string(buf: &mut &[u8]) -> io::Result<String> {
|
|||
let str_len = memchr::memchr(0u8, buf)
|
||||
.ok_or_else(|| io::Error::new(io::ErrorKind::InvalidData, "unterminated string"))?;
|
||||
|
||||
let string = str::from_utf8(&*buf[..str_len])
|
||||
let string = str::from_utf8(&buf[..str_len])
|
||||
.map_err(|e| io::Error::new(io::ErrorKind::InvalidData, e))?;
|
||||
|
||||
*buf = &*buf[str_len + 1..];
|
||||
*buf = &buf[str_len + 1..];
|
||||
|
||||
Ok(string.to_owned())
|
||||
}
|
||||
|
|
|
@ -26,15 +26,15 @@ impl Decode for RowDescription {
|
|||
let mut fields = Vec::with_capacity(cnt);
|
||||
|
||||
for _ in 0..cnt {
|
||||
fields.push(RowField {
|
||||
fields.push(dbg!(RowField {
|
||||
name: super::read_string(&mut buf)?,
|
||||
table_id: buf.get_u32::<NetworkEndian>()?,
|
||||
attr_num: buf.get_i16::<NetworkEndian>()?,
|
||||
type_id: buf.get_u32::<NetworkEndian>()?,
|
||||
type_size: buf.get_16::<NetworkEndian>()?,
|
||||
type_size: buf.get_i16::<NetworkEndian>()?,
|
||||
type_mod: buf.get_i32::<NetworkEndian>()?,
|
||||
format_code: buf.get_i16::<NetworkEndian>()?,
|
||||
});
|
||||
}));
|
||||
}
|
||||
|
||||
Ok(Self {
|
||||
|
|
|
@ -204,7 +204,9 @@ impl PostgresRawConnection {
|
|||
return Ok(Some(Step::ParamDesc(desc)));
|
||||
},
|
||||
|
||||
Message::
|
||||
Message::RowDescription(desc) => {
|
||||
return Ok(Some(Step::RowDesc(desc)));
|
||||
},
|
||||
|
||||
message => {
|
||||
return Err(io::Error::new(
|
||||
|
@ -296,6 +298,7 @@ impl PostgresRawConnection {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(super) enum Step {
|
||||
Command(u64),
|
||||
Row(PostgresRow),
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
use super::{protocol::DataRow, Postgres};
|
||||
use crate::row::Row;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct PostgresRow(pub(crate) DataRow);
|
||||
|
||||
impl Row for PostgresRow {
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
#[derive(Debug)]
|
||||
pub struct PreparedStatement {
|
||||
pub name: String,
|
||||
pub param_types: Box<[u32]>,
|
||||
pub fields: Vec<Field>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Field {
|
||||
pub name: String,
|
||||
pub table_id: u32,
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
#![feature(proc_macro_hygiene)]
|
||||
|
||||
fn main() {
|
||||
sqlx::sql!("SELECT * from accounts");
|
||||
sqlx_macros::sql!("SELECT * from accounts");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue