Skip to content

Commit

Permalink
feat: supports if_not_exists when create database
Browse files Browse the repository at this point in the history
  • Loading branch information
killme2008 committed Dec 26, 2022
1 parent 0c37df2 commit 5406731
Show file tree
Hide file tree
Showing 14 changed files with 195 additions and 5 deletions.
1 change: 1 addition & 0 deletions src/api/greptime/v1/admin.proto
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ message DropTableExpr {
message CreateDatabaseExpr {
//TODO(hl): maybe rename to schema_name?
string database_name = 1;
bool create_if_not_exists = 2;
}

message AddColumns {
Expand Down
4 changes: 4 additions & 0 deletions src/datanode/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,9 @@ pub enum Error {
source: catalog::error::Error,
},

#[snafu(display("Schema already exists, name: {}", name))]
SchemaExists { name: String, backtrace: Backtrace },

#[snafu(display("Failed to decode as physical plan, source: {}", source))]
IntoPhysicalPlan {
#[snafu(backtrace)]
Expand Down Expand Up @@ -341,6 +344,7 @@ impl ErrorExt for Error {
| Error::CatalogNotFound { .. }
| Error::SchemaNotFound { .. }
| Error::ConstraintNotSupported { .. }
| Error::SchemaExists { .. }
| Error::ParseTimestamp { .. } => StatusCode::InvalidArguments,

// TODO(yingwen): Further categorize http error.
Expand Down
1 change: 1 addition & 0 deletions src/datanode/src/instance/grpc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ impl Instance {
) -> AdminResult {
let req = CreateDatabaseRequest {
db_name: create_database_expr.database_name,
create_if_not_exists: create_database_expr.create_if_not_exists,
};
let result = self.sql_handler.create_database(req).await;
match result {
Expand Down
1 change: 1 addition & 0 deletions src/datanode/src/instance/sql.rs
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ impl Instance {
Statement::CreateDatabase(c) => {
let request = CreateDatabaseRequest {
db_name: c.name.to_string(),
create_if_not_exists: c.if_not_exists,
};

info!("Creating a new database: {}", request.db_name);
Expand Down
16 changes: 12 additions & 4 deletions src/datanode/src/sql/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,22 +33,30 @@ use table::requests::*;
use crate::error::{
self, CatalogNotFoundSnafu, CatalogSnafu, ConstraintNotSupportedSnafu, CreateSchemaSnafu,
CreateTableSnafu, InsertSystemCatalogSnafu, InvalidPrimaryKeySnafu, KeyColumnNotFoundSnafu,
RegisterSchemaSnafu, Result, SchemaNotFoundSnafu,
RegisterSchemaSnafu, Result, SchemaExistsSnafu, SchemaNotFoundSnafu,
};
use crate::sql::SqlHandler;

impl SqlHandler {
pub(crate) async fn create_database(&self, req: CreateDatabaseRequest) -> Result<Output> {
let schema = req.db_name;
let req = RegisterSchemaRequest {
let reg_req = RegisterSchemaRequest {
catalog: DEFAULT_CATALOG_NAME.to_string(),
schema: schema.clone(),
};
self.catalog_manager
.register_schema(req)
let success = self
.catalog_manager
.register_schema(reg_req)
.await
.context(RegisterSchemaSnafu)?;

// FIXME(dennis): looks like register_schema always returns true even
// even when the schema already exists.
ensure!(
success || req.create_if_not_exists,
SchemaExistsSnafu { name: schema }
);

info!("Successfully created database: {:?}", schema);
Ok(Output::AffectedRows(1))
}
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/instance/distributed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ impl DistInstance {
Statement::CreateDatabase(stmt) => {
let expr = CreateDatabaseExpr {
database_name: stmt.name.to_string(),
create_if_not_exists: stmt.if_not_exists,
};
self.handle_create_database(expr).await?;
Ok(Output::AffectedRows(1))
Expand Down
1 change: 1 addition & 0 deletions src/frontend/src/instance/prometheus.rs
Original file line number Diff line number Diff line change
Expand Up @@ -210,6 +210,7 @@ mod tests {
instance
.handle_create_database(CreateDatabaseExpr {
database_name: db.to_string(),
create_if_not_exists: true,
})
.await
.unwrap();
Expand Down
20 changes: 19 additions & 1 deletion src/sql/src/parsers/create_parser.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ impl<'a> ParserContext<'a> {
Token::Word(w) => match w.keyword {
Keyword::TABLE => self.parse_create_table(),

Keyword::DATABASE => self.parse_create_database(),
Keyword::SCHEMA | Keyword::DATABASE => self.parse_create_database(),

_ => self.unsupported(w.to_string()),
},
Expand All @@ -57,6 +57,10 @@ impl<'a> ParserContext<'a> {
fn parse_create_database(&mut self) -> Result<Statement> {
self.parser.next_token();

let if_not_exists =
self.parser
.parse_keywords(&[Keyword::IF, Keyword::NOT, Keyword::EXISTS]);

let database_name = self
.parser
.parse_object_name()
Expand All @@ -68,6 +72,7 @@ impl<'a> ParserContext<'a> {

Ok(Statement::CreateDatabase(CreateDatabase {
name: database_name,
if_not_exists,
}))
}

Expand Down Expand Up @@ -583,6 +588,19 @@ mod tests {
match &stmts[0] {
Statement::CreateDatabase(c) => {
assert_eq!(c.name.to_string(), "prometheus");
assert!(!c.if_not_exists);
}
_ => unreachable!(),
}

let sql = "create database if not exists prometheus";
let stmts = ParserContext::create_with_dialect(sql, &GenericDialect {}).unwrap();

assert_eq!(1, stmts.len());
match &stmts[0] {
Statement::CreateDatabase(c) => {
assert_eq!(c.name.to_string(), "prometheus");
assert!(c.if_not_exists);
}
_ => unreachable!(),
}
Expand Down
2 changes: 2 additions & 0 deletions src/sql/src/statements/create.rs
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,6 @@ pub struct PartitionEntry {
#[derive(Debug, PartialEq, Eq, Clone)]
pub struct CreateDatabase {
pub name: ObjectName,
/// Create if not exists
pub if_not_exists: bool,
}
1 change: 1 addition & 0 deletions src/table/src/requests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ pub struct InsertRequest {
#[derive(Debug, Clone)]
pub struct CreateDatabaseRequest {
pub db_name: String,
pub create_if_not_exists: bool,
}

/// Create table request
Expand Down
50 changes: 50 additions & 0 deletions tests/cases/standalone/catalog/schema.output
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
CREATE SCHEMA test;

MutateResult { success: 1, failure: 0 }

CREATE TABLE test.hello(i BIGINT TIME INDEX);

MutateResult { success: 1, failure: 0 }

DROP TABLE test.hello;

MutateResult { success: 1, failure: 0 }

DROP SCHEMA test;

Failed to execute, error: Datanode { code: 1001, msg: "Failed to execute sql, source: Cannot parse SQL, source: SQL statement is not supported: DROP SCHEMA test;, keyword: SCHEMA" }

CREATE SCHEMA test;

MutateResult { success: 1, failure: 0 }

CREATE TABLE test.hello(i BIGINT TIME INDEX);

MutateResult { success: 1, failure: 0 }

INSERT INTO test.hello VALUES (2), (3), (4);

MutateResult { success: 3, failure: 0 }

SELECT * FROM test.hello;

+-----------------------+
| i, #Timestamp, #Int64 |
+-----------------------+
| 2 |
| 3 |
| 4 |
+-----------------------+

DROP TABLE test.hello;

MutateResult { success: 1, failure: 0 }

DROP SCHEMA test;

Failed to execute, error: Datanode { code: 1001, msg: "Failed to execute sql, source: Cannot parse SQL, source: SQL statement is not supported: DROP SCHEMA test;, keyword: SCHEMA" }

SELECT * FROM test.hello;

Failed to execute, error: Datanode { code: 3000, msg: "Failed to execute sql, source: Cannot plan SQL: SELECT * FROM test.hello, source: Error during planning: table 'greptime.test.hello' not found" }

21 changes: 21 additions & 0 deletions tests/cases/standalone/catalog/schema.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
CREATE SCHEMA test;

CREATE TABLE test.hello(i BIGINT TIME INDEX);

DROP TABLE test.hello;

DROP SCHEMA test;

CREATE SCHEMA test;

CREATE TABLE test.hello(i BIGINT TIME INDEX);

INSERT INTO test.hello VALUES (2), (3), (4);

SELECT * FROM test.hello;

DROP TABLE test.hello;

DROP SCHEMA test;

SELECT * FROM test.hello;
58 changes: 58 additions & 0 deletions tests/cases/standalone/create/create.result
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
CREATE TABLE integers (i BIGINT TIME INDEX);

MutateResult { success: 1, failure: 0 }

CREATE TABLE IF NOT EXISTS integers (i BIGINT TIME INDEX);

MutateResult { success: 1, failure: 0 }

CREATE TABLE test1 (i INTEGER, j INTEGER);

Failed to execute, error: Datanode { code: 1004, msg: "Missing timestamp column in request" }

CREATE TABLE test1 (i INTEGER, j BIGINT TIME INDEX NOT NULL);

MutateResult { success: 1, failure: 0 }

CREATE TABLE test2 (i INTEGER, j BIGINT TIME INDEX NULL);

Failed to execute, error: Datanode { code: 2000, msg: "Failed to execute sql, source: Cannot parse SQL, source: Unexpected token while parsing SQL statement: CREATE TABLE test2 (i INTEGER, j BIGINT TIME INDEX NULL);, expected: 'NOT NULL', found: NULL, source: sql parser error: Expected NOT, found: NULL" }

CREATE TABLE test2 (i INTEGER, j BIGINT TIME INDEX);

MutateResult { success: 1, failure: 0 }

DESC TABLE INTEGERS;

Failed to execute, error: Datanode { code: 1004, msg: "Failed to execute sql, source: Table not found: INTEGERS" }

DESC TABLE test1;

+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+
| Field, #Field, #String | Type, #Field, #String | Null, #Field, #String | Default, #Field, #String | Semantic Type, #Field, #String |
+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+
| i | Int32 | YES | | VALUE |
| j | Int64 | NO | | TIME INDEX |
+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+

DESC TABLE test2;

+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+
| Field, #Field, #String | Type, #Field, #String | Null, #Field, #String | Default, #Field, #String | Semantic Type, #Field, #String |
+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+
| i | Int32 | YES | | VALUE |
| j | Int64 | NO | | TIME INDEX |
+------------------------+-----------------------+-----------------------+--------------------------+--------------------------------+

DROP TABLE integers;

MutateResult { success: 1, failure: 0 }

DROP TABLE test1;

MutateResult { success: 1, failure: 0 }

DROP TABLE test2;

MutateResult { success: 1, failure: 0 }

23 changes: 23 additions & 0 deletions tests/cases/standalone/create/create.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
CREATE TABLE integers (i BIGINT TIME INDEX);

CREATE TABLE IF NOT EXISTS integers (i BIGINT TIME INDEX);

CREATE TABLE test1 (i INTEGER, j INTEGER);

CREATE TABLE test1 (i INTEGER, j BIGINT TIME INDEX NOT NULL);

CREATE TABLE test2 (i INTEGER, j BIGINT TIME INDEX NULL);

CREATE TABLE test2 (i INTEGER, j BIGINT TIME INDEX);

DESC TABLE INTEGERS;

DESC TABLE test1;

DESC TABLE test2;

DROP TABLE integers;

DROP TABLE test1;

DROP TABLE test2;

0 comments on commit 5406731

Please sign in to comment.