feat: create table through GRPC interface (#224)

* feat: create table through GRPC interface

* move `CreateExpr` `oneof` expr of `AdminExpr` in `admin.proto`, and implement the admin GRPC interface

* add `table_options` and `partition_options` to `CreateExpr`

* resolve code review comments

Co-authored-by: luofucong <luofucong@greptime.com>
This commit is contained in:
LFC
2022-09-06 12:51:07 +08:00
committed by GitHub
parent 3f9144a2e3
commit 119ff2fc2e
26 changed files with 605 additions and 77 deletions

View File

@@ -1,14 +1,56 @@
use api::v1::*;
use snafu::prelude::*;
use crate::database::PROTOCOL_VERSION;
use crate::error;
use crate::Client;
use crate::Result;
#[derive(Clone, Debug)]
pub struct Admin {
name: String,
client: Client,
}
impl Admin {
pub fn new(client: Client) -> Self {
Self { client }
pub fn new(name: impl Into<String>, client: Client) -> Self {
Self {
name: name.into(),
client,
}
}
// TODO(jiachun): admin api
pub async fn create(&self, expr: CreateExpr) -> Result<AdminResult> {
let header = ExprHeader {
version: PROTOCOL_VERSION,
};
let expr = AdminExpr {
header: Some(header),
expr: Some(admin_expr::Expr::Create(expr)),
};
// `remove(0)` is safe because of `do_request`'s invariants.
Ok(self.do_request(vec![expr]).await?.remove(0))
}
/// Invariants: the lengths of input vec (`Vec<AdminExpr>`) and output vec (`Vec<AdminResult>`) are equal.
async fn do_request(&self, exprs: Vec<AdminExpr>) -> Result<Vec<AdminResult>> {
let expr_count = exprs.len();
let req = AdminRequest {
name: self.name.clone(),
exprs,
};
let resp = self.client.admin(req).await?;
let results = resp.results;
ensure!(
results.len() == expr_count,
error::MissingResultSnafu {
name: "admin_results",
expected: expr_count,
actual: results.len(),
}
);
Ok(results)
}
}

View File

@@ -1,4 +1,3 @@
use std::ops::Deref;
use std::sync::Arc;
use api::v1::codec::SelectResult as GrpcSelectResult;
@@ -13,10 +12,9 @@ use common_grpc::DefaultAsPlanImpl;
use datafusion::physical_plan::ExecutionPlan;
use snafu::{ensure, OptionExt, ResultExt};
use crate::error::{self, MissingResultSnafu};
use crate::error;
use crate::{
error::DatanodeSnafu, error::DecodeSelectSnafu, error::EncodePhysicalSnafu,
error::MissingHeaderSnafu, Client, Result,
error::DatanodeSnafu, error::DecodeSelectSnafu, error::EncodePhysicalSnafu, Client, Result,
};
pub const PROTOCOL_VERSION: u32 = 1;
@@ -97,36 +95,7 @@ impl Database {
};
let obj_result = self.object(expr).await?;
let header = obj_result.header.context(MissingHeaderSnafu)?;
if !StatusCode::is_success(header.code) {
return DatanodeSnafu {
code: header.code,
msg: header.err_msg,
}
.fail();
}
let obj_result = obj_result.result.context(MissingResultSnafu {
name: "select_result".to_string(),
expected: 1_usize,
actual: 0_usize,
})?;
let result = match obj_result {
object_result::Result::Select(select) => {
let result = select
.raw_data
.deref()
.try_into()
.context(DecodeSelectSnafu)?;
ObjectResult::Select(result)
}
object_result::Result::Mutate(mutate) => ObjectResult::Mutate(mutate),
};
Ok(result)
obj_result.try_into()
}
// TODO(jiachun) update/delete
@@ -165,6 +134,34 @@ pub enum ObjectResult {
Mutate(GrpcMutateResult),
}
impl TryFrom<api::v1::ObjectResult> for ObjectResult {
type Error = error::Error;
fn try_from(object_result: api::v1::ObjectResult) -> std::result::Result<Self, Self::Error> {
let header = object_result.header.context(error::MissingHeaderSnafu)?;
if !StatusCode::is_success(header.code) {
return DatanodeSnafu {
code: header.code,
msg: header.err_msg,
}
.fail();
}
let obj_result = object_result.result.context(error::MissingResultSnafu {
name: "result".to_string(),
expected: 1_usize,
actual: 0_usize,
})?;
Ok(match obj_result {
object_result::Result::Select(select) => {
let result = (*select.raw_data).try_into().context(DecodeSelectSnafu)?;
ObjectResult::Select(result)
}
object_result::Result::Mutate(mutate) => ObjectResult::Mutate(mutate),
})
}
}
pub enum Select {
Sql(String),
}

View File

@@ -1,3 +1,4 @@
pub mod admin;
mod client;
mod database;
mod error;