Skip to content

Commit

Permalink
Copy en files to zh
Browse files Browse the repository at this point in the history
Signed-off-by: Michael Yuan <[email protected]>
  • Loading branch information
juntao committed Jul 18, 2024
1 parent 1b22873 commit f7f319b
Show file tree
Hide file tree
Showing 7 changed files with 650 additions and 145 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ The database connection is necessary for today's enterprise development. WasmEdg

<!-- prettier-ignore -->
:::note
Before we start, ensure [you have Rust and WasmEdge installed](../setup.md). If you are connecting to a remote MySQL database using TLS, you will need to [install the TLS plugin](../../../start/install.md#tls-plug-in) for WasmEdge as well.
Before we start, [you need to have Rust and WasmEdge installed](../setup.md).
Make sure that you read the [special notes on networking apps](../setup#special-notes) especially if you are compiling Rust programs on a Mac.
:::

## Run the example
Expand All @@ -20,28 +21,201 @@ git clone https://github.com/WasmEdge/wasmedge-db-examples
cd wasmedge-db-examples/mysql_async

# Compile the rust code into WASM
cargo build --target wasm32-wasi --release
RUSTFLAGS="--cfg wasmedge --cfg tokio_unstable" cargo build --target wasm32-wasi --release

# Execute MySQL statements against a MySQL database at mysql://user:[email protected]:3306
wasmedge --env "DATABASE_URL=mysql://user:[email protected]:3306/mysql" target/wasm32-wasi/release/crud.wasm
```

To use TLS, you will need to turn on the `default-rustls` feature in `Cargo.toml`.

```toml
mysql_async_wasi = { version = "0.31", features = [ "default-rustls" ] }
```
<!-- prettier-ignore -->
:::note
Since we have TLS enabled by default in this example, you will need the [wasi-sdk version of clang](../setup#tls-on-macos) for compiling it on MacOS.
:::

To use TLS, you will need to turn on the `default-rustls` feature on the `mysql_async` crate in `Cargo.toml`.
Then, run the application as follows.

```toml
# Execute MySQL statements against an AWS RDS database that requires TLS
wasmedge --env "DATABASE_SSL=1" --env "DATABASE_URL=mysql://user:[email protected]:3306/mysql" crud.wasm
```

## Code explanation
## Configuration

In order to compile the `mysql_async` and `tokio` crates, we will need to apply two patches to add
WasmEdge-specific socket APIs to those crates. The following example shows that the TLS connection is enabled.

```
[patch.crates-io]
tokio = { git = "https://github.com/second-state/wasi_tokio.git", branch = "v1.36.x" }
socket2 = { git = "https://github.com/second-state/socket2.git", branch = "v0.5.x" }
[dependencies]
mysql_async = { version = "0.34", default-features=false, features = [ "default-rustls" ], git="https://github.com/blackbeam/mysql_async.git" }
zstd-sys = "=2.0.9"
tokio = { version = "1", features = [ "io-util", "fs", "net", "time", "rt", "macros"] }
```

## Code example

The following code shows how to connect to a MySQL database server, and then insert, update, and delete records using SQL
statements.

Connect to a MySQL database.

```
// Below we create a customized connection pool
let opts = Opts::from_url(&*get_url()).unwrap();
let mut builder = OptsBuilder::from_opts(opts);
if std::env::var("DATABASE_SSL").is_ok() {
builder = builder.ssl_opts(SslOpts::default());
}
// The connection pool will have a min of 5 and max of 10 connections.
let constraints = PoolConstraints::new(5, 10).unwrap();
let pool_opts = PoolOpts::default().with_constraints(constraints);
let pool = Pool::new(builder.pool_opts(pool_opts));
let mut conn = pool.get_conn().await.unwrap();
```

Create a table on the connected database.

```
// create table if no tables exist
let result = r"SHOW TABLES LIKE 'orders';"
.with(())
.map(&mut conn, |s: String| String::from(s))
.await?;
if result.len() == 0 {
// table doesn't exist, create a new one
r"CREATE TABLE orders (order_id INT, production_id INT, quantity INT, amount FLOAT, shipping FLOAT, tax FLOAT, shipping_address VARCHAR(20));".ignore(&mut conn).await?;
println!("create new table");
} else {
// delete all data from the table.
println!("delete all from orders");
r"DELETE FROM orders;".ignore(&mut conn).await?;
}
```

Insert some records into the MySQL database using SQL.

```
let orders = vec![
Order::new(1, 12, 2, 56.0, 15.0, 2.0, String::from("Mataderos 2312")),
Order::new(2, 15, 3, 256.0, 30.0, 16.0, String::from("1234 NW Bobcat")),
Order::new(3, 11, 5, 536.0, 50.0, 24.0, String::from("20 Havelock")),
Order::new(4, 8, 8, 126.0, 20.0, 12.0, String::from("224 Pandan Loop")),
Order::new(5, 24, 1, 46.0, 10.0, 2.0, String::from("No.10 Jalan Besar")),
];
r"INSERT INTO orders (order_id, production_id, quantity, amount, shipping, tax, shipping_address)
VALUES (:order_id, :production_id, :quantity, :amount, :shipping, :tax, :shipping_address)"
.with(orders.iter().map(|order| {
params! {
"order_id" => order.order_id,
"production_id" => order.production_id,
"quantity" => order.quantity,
"amount" => order.amount,
"shipping" => order.shipping,
"tax" => order.tax,
"shipping_address" => &order.shipping_address,
}
}))
.batch(&mut conn)
.await?;
```

Query the database.

```
// query data
let loaded_orders = "SELECT * FROM orders"
.with(())
.map(
&mut conn,
|(order_id, production_id, quantity, amount, shipping, tax, shipping_address)| {
Order::new(
order_id,
production_id,
quantity,
amount,
shipping,
tax,
shipping_address,
)
},
)
.await?;
dbg!(loaded_orders.len());
dbg!(loaded_orders);
```

Delete some records from the database.

```
// // delete some data
r"DELETE FROM orders WHERE order_id=4;"
.ignore(&mut conn)
.await?;
// query data
let loaded_orders = "SELECT * FROM orders"
.with(())
.map(
&mut conn,
|(order_id, production_id, quantity, amount, shipping, tax, shipping_address)| {
Order::new(
order_id,
production_id,
quantity,
amount,
shipping,
tax,
shipping_address,
)
},
)
.await?;
dbg!(loaded_orders.len());
dbg!(loaded_orders);
```

Update records in the MySQL database.

```
// // update some data
r"UPDATE orders
SET shipping_address = '8366 Elizabeth St.'
WHERE order_id = 2;"
.ignore(&mut conn)
.await?;
// query data
let loaded_orders = "SELECT * FROM orders"
.with(())
.map(
&mut conn,
|(order_id, production_id, quantity, amount, shipping, tax, shipping_address)| {
Order::new(
order_id,
production_id,
quantity,
amount,
shipping,
tax,
shipping_address,
)
},
)
.await?;
dbg!(loaded_orders.len());
dbg!(loaded_orders);
```

Close the database connection.

```
drop(conn);
pool.disconnect().await.unwrap();
```

<!-- prettier-ignore -->
:::info
Work in Progress
:::
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ A database connection is necessary for today's enterprise development. WasmEdge

<!-- prettier-ignore -->
:::note
Before we start, make sure [you have Rust and WasmEdge installed](../setup.md).
Before we start, [you need to have Rust and WasmEdge installed](../setup.md).
Make sure that you read the [special notes on networking apps](../setup#special-notes) especially if you are compiling Rust programs on a Mac.
:::

## Run the example
Expand All @@ -20,15 +21,134 @@ git clone https://github.com/WasmEdge/wasmedge-db-examples
cd wasmedge-db-examples/postgres

# Compile the rust code into WASM
cargo build --target wasm32-wasi --release
RUSTFLAGS="--cfg wasmedge --cfg tokio_unstable" cargo build --target wasm32-wasi --release

# Execute SQL statements against a PostgreSQL database at postgres://user:passwd@localhost/testdb
wasmedge --env "DATABASE_URL=postgres://user:passwd@localhost/testdb" target/wasm32-wasi/release/crud.wasm
```

## Configuration

In order to compile the `tokio-postgres` and `tokio` crates, we will need to apply patches to add WasmEdge-specific socket APIs to those crates in `Cargo.toml`.

```
[patch.crates-io]
tokio = { git = "https://github.com/second-state/wasi_tokio.git", branch = "v1.36.x" }
socket2 = { git = "https://github.com/second-state/socket2.git", branch = "v0.5.x" }
tokio-postgres = { git = "https://github.com/second-state/rust-postgres.git" }
[dependencies]
tokio-postgres = "0.7"
tokio = { version = "1", features = [
"io-util",
"fs",
"net",
"time",
"rt",
"macros",
] }
```

## Code explanation

<!-- prettier-ignore -->
:::info
Work in Progress
:::
We first use a Rust struct to represent the database table.

```rust
#[derive(Debug)]
struct Order {
order_id: i32,
production_id: i32,
quantity: i32,
amount: f32,
shipping: f32,
tax: f32,
shipping_address: String,
}

impl Order {
fn new(
order_id: i32,
production_id: i32,
quantity: i32,
amount: f32,
shipping: f32,
tax: f32,
shipping_address: String,
) -> Self {
Self {
order_id,
production_id,
quantity,
amount,
shipping,
tax,
shipping_address,
}
}
}
```

Then, you can use the `tokio-postgres` API to access the database through its connection URL.
The code below shows how to perform basic CRUD operations using SQL commands.

```rust
#[tokio::main(flavor = "current_thread")]
async fn main() -> Result<(), Error> {
// Connect to the database.
let (client, connection) = tokio_postgres::connect(&*get_url(), 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);
}
});

client.execute("CREATE TABLE IF NOT EXISTS orders (order_id INT, production_id INT, quantity INT, amount REAL, shipping REAL, tax REAL, shipping_address VARCHAR(256));", &[]).await?;

let orders = vec![
Order::new(1, 12, 2, 56.0, 15.0, 2.0, String::from("Mataderos 2312")),
Order::new(2, 15, 3, 256.0, 30.0, 16.0, String::from("1234 NW Bobcat")),
Order::new(3, 11, 5, 536.0, 50.0, 24.0, String::from("20 Havelock")),
Order::new(4, 8, 8, 126.0, 20.0, 12.0, String::from("224 Pandan Loop")),
Order::new(5, 24, 1, 46.0, 10.0, 2.0, String::from("No.10 Jalan Besar")),
];

for order in orders.iter() {
client.execute(
"INSERT INTO orders (order_id, production_id, quantity, amount, shipping, tax, shipping_address) VALUES ($1, $2, $3, $4, $5, $6, $7)",
&[&order.order_id, &order.production_id, &order.quantity, &order.amount, &order.shipping, &order.tax, &order.shipping_address]
).await?;
}

let rows = client.query("SELECT * FROM orders;", &[]).await?;
for row in rows.iter() {
let order_id : i32 = row.get(0);
println!("order_id {}", order_id);

let production_id : i32 = row.get(1);
println!("production_id {}", production_id);

let quantity : i32 = row.get(2);
println!("quantity {}", quantity);

let amount : f32 = row.get(3);
println!("amount {}", amount);

let shipping : f32 = row.get(4);
println!("shipping {}", shipping);

let tax : f32 = row.get(5);
println!("tax {}", tax);

let shipping_address : &str = row.get(6);
println!("shipping_address {}", shipping_address);
}

client.execute("DELETE FROM orders;", &[]).await?;

Ok(())
}
```

Loading

0 comments on commit f7f319b

Please sign in to comment.