There is such a module code (for working with a database):
use tokio_postgres::{NoTls, Error};
pub async fn hello() -> Result<(), Error> {
// Connect to the database.
let (client, connection) =
tokio_postgres::connect("host=localhost user=postgres", NoTls).await?;
// The connection object performs the actual communication with the database,
// so spawn it off to run on its own.
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
// Now we can execute a simple statement that just returns its parameter.
let rows = client
.query("SELECT $1::TEXT", &[&"hello world"])
.await?;
// And then check that we got back the same string we sent over.
let value: &str = rows[0].get(0);
assert_eq!(value, "hello world");
Ok(())
}
Question:
How, in this case, the access to the database should be written?
(the guide doesn't say anything about it - or I didn't fully understand it.)
https://docs.rs/tokio-postgres/0.5.5/tokio_postgres/
What mechanisms in this case will protect access to the database from sql injections?
The simplest general use case is needed.
client.query(statement, params) will convert the first argument statement to a prepared statement and execute it with the params.
To be safe from sql injection, make sure that all user data is passed in the second params argument.
DO NOT DO THIS:
let id = "SOME DATA FROM THE USER";
let rows = client
.query(format!("SELECT * FROM SomeTable WHERE id = {}", id), &[])
.await?;
DO THIS:
let id = "SOME DATA FROM THE USER";
let rows = client
.query("SELECT * FROM SomeTable WHERE id = $1", &[&id])
.await?;
Explanation:
In tokio-postgres most client methods (query* or execute*) can accept either a &str or Statement for the sql statement. If passed a &str it will create a prepared statement (Statement object) for you.
Related
This is my async function that uses rust to connect to an existing mongoDB database. Is there a way to return / export the client variable / object and make it usable in other Files / Functions?
async fn connect_to_db() -> Result<(), Box<dyn Error>> {
// Load the MongoDB connection string from an environment variable (or string):
let client_uri = "mongodb://localhost:27017";
let options =
ClientOptions::parse_with_resolver_config(&client_uri, ResolverConfig::cloudflare())
.await?;
let client = Client::with_options(options)?;
let db = client.database("fiesta");
// Select Collection(s)
let user_col: Collection<User> = db.collection("users");
let skills_col: Collection<Skill> = db.collection("skills");
//ok.
Ok(())
}
Help would be appreciated.
Dependencies:
rocketrs
mongodb
tokio & serde
I have tried multiple things such as changing the lifetimes and changing the return type of the function, but to no avail.
I need to pass several parameters into the tokio_postgres::query Statement. How can I properly do this?
The way I do it below doesn't work, what gets passed into the SQL database is the unconverted $2, instead of the date 2021-04-06.
use tokio_postgres::{Error, NoTls};
#[tokio::main]
async fn main() -> Result<(), Error> {
let (client, connection) = tokio_postgres::connect(
"dbname=database user=admin password=postgres host=db port=5432",
NoTls,
)
.await?;
// Spawn the connector in a separate async task
tokio::spawn(async move {
if let Err(e) = connection.await {
eprintln!("connection error: {}", e);
}
});
// How do I pass several parameters correctly here?
client
.query(
"INSERT INTO table_name (url, date_added) \
VALUES ('$1', '$2');",
&[&url, &"2021-04-06"],
)
.await?;
}
I'm trying to create an API using Actix-web, async-grahpql and sqlx with postgresql
In the QueryRoot of the async-graphql I am trying to capture the reference of the DB and make the query the database with sqlx, but it gives me an error
let items = Todo::list(&pool).await?;
| ^^^^^ expected struct `sqlx::Pool`, found enum `std::result::Result`
Here I want to capture the reference
use crate::todo::*;
use async_graphql::{Context, FieldResult};
use sqlx::postgres::PgPool;
pub struct QueryRoot;
#[async_graphql::Object]
impl QueryRoot {
async fn todos(&self, ctx: &Context<'_>) -> FieldResult<Vec<Todo>> {
let pool = ctx.data::<PgPool>();
let items = Todo::list(&pool).await?; //<-- This line generates an error
Ok(items)
}
}
Here I define the references
pub fn run(listener: TcpListener, db_pool: PgPool) -> Result<Server, std::io::Error> {
let data_db_pool = Data::new(db_pool);
//GraphQL
let schema = Schema::build(QueryRoot, MutationRoot, EmptySubscription)
.data(data_db_pool.clone()) //<- DB reference
.finish();
let server = HttpServer::new(move || {
App::new()
.app_data(db_pool.clone()) //<- DB reference
.data(schema.clone())
.route("/graphql", web::post().to(graphql))
.route("/graphql", web::get().to(graphql_playground))
})
.listen(listener)?
.run();
Ok(server)
}
What am I doing wrong? The complete code can be found here.
ctx.data::<T>() returns a Result wrapping a reference to T. You probably want.
let pool = ctx.data::<PgPool>()?;
// ^ return on Err, otherwise yield the &PgPool
let items = Todo::list(pool).await?;
// ^^^^ shouldn't need & here
I've got an Actix-web server that connects to a Postgres DB.
I've noticed that after a 1000 requests my Postgres DB's RAM usage has spiked.
When I stop actix-web, the RAM held by the db is cleared. This leads me to believe that my code is not releasing the connection.
I cannot find an example of connections actually being released. It looks like it's inferred in everyone else's code.
Here's mine:
async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
let client: Client = pool.get().await?;
let sql = format!("select \"json\" from public.table_a WHERE a={} and b={}", a, b);
let stmt = client.prepare(&sql).await?;
let row = client.query_one(&stmt, &[]).await?;
let result : Value = row.get(0);
Ok(result)
}
#[derive(Deserialize)]
pub struct MyRequest {
a: f32,
b: f32
}
#[get("/hello")]
async fn sv_hellow_world(info: web::Query<MyRequest>, db_pool: web::Data<Pool>) -> Result<HttpResponse, Error> {
let response : Value = hellow_world(info.a, info.b, &db_pool).await?;
Ok(HttpResponse::Ok().json(response))
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
dotenv().ok();
let config = Config::from_env().unwrap();
let pool = config.pg.create_pool(tokio_postgres::NoTls).unwrap();
env_logger::from_env(Env::default().default_filter_or("info")).init();
let server = HttpServer::new(move || App::new().wrap(Logger::default()).wrap(Logger::new("%a %{User-Agent}i")).data(pool.clone()).service(sv_hellow_world))
.bind("0.0.0.0:3000")?
.run();
server.await
}
Based on further testing, #Werner determined that the code was piling up server-side prepared statements.
It is not clear whether these statements can be closed using this library.
Either of two approaches can be used to avoid this problem:
Use a single, shared prepared statement
Use the direct query form instead of the prepared statement
I recommend the first approach on principle as it is more efficient and protects against SQL Injection. It should look something like this:
async fn hellow_world(a : f32, b : f32, pool: &Pool) -> Result<Value, PoolError> {
let client: Client = pool.get().await?;
let stmt = client.prepare("select \"json\" from public.table_a WHERE a=$1::numeric and b=$2::numeric").await?;
let row = client.query_one(&stmt, &[&a, &b]).await?;
let result : Value = row.get(0);
Ok(result)
}
Using this code, only one prepared statement should be created on each of the pool's connections.
Trying to make server with actix-web & mongodb in rust. Getting error
the trait std::convert::From<mongodb::error::Error> is not implemented for std::io::Error
here is my code
use actix_web::{web, App, HttpRequest, HttpServer, Responder};
use mongodb::{options::ClientOptions, Client};
async fn greet(req: HttpRequest) -> impl Responder {
let name = req.match_info().get("name").unwrap_or("World");
format!("Hello {}!", &name)
}
#[actix_rt::main]
async fn main() -> std::io::Result<()> {
// Parse a connection string into an options struct.
let mut client_options = ClientOptions::parse("mongodb://localhost:27017")?;
// Manually set an option.
client_options.app_name = Some("My App".to_string());
// Get a handle to the deployment.
let client = Client::with_options(client_options)?;
// List the names of the databases in that deployment.
for db_name in client.list_database_names(None)? {
println!("{}", db_name);
}
HttpServer::new(|| {
App::new()
.route("/", web::get().to(greet))
.route("/{name}", web::get().to(greet))
})
.bind("127.0.0.1:8000")?
.run()
.await
}
Did I missed anything?
It means that one of the functions you are calling with a ? at the end can return a mongodb::error::Error. But the signature of the main is a std::io::Result<()>, wich is an implied Result<(), std::io::Error>. The only error type it can accept is a io::Error, not a mongodb::Error.
It looks like all the functions you are escaping might return this mongodb::error::Error, so you can try to change the main signature to such a result: Result<(). mongodb::error::Error>.
But I would recommend you do proper error handling on those potential errors, as this is your main(). Change those ? to .expect("Some error message"); at least. The program will still crash, but it will crash in a way that is meaningful to you.