From b4f1d768196401f648cdc85620f2b43310f63c74 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Wed, 13 Dec 2023 15:21:55 +0100 Subject: [PATCH 01/10] libsql: attach databases from other namespaces as readonly With this proof-of-concept patch, other namespaces hosted on the same sqld machine can now be attached in readonly mode, so that users can read from other databases when connected to a particular one. --- libsql-server/src/connection/libsql.rs | 22 +++++++++++++++++++++- libsql-server/src/query_analysis.rs | 25 ++++++++++++++++++++++++- 2 files changed, 45 insertions(+), 2 deletions(-) diff --git a/libsql-server/src/connection/libsql.rs b/libsql-server/src/connection/libsql.rs index 7e602edf0e..d26fcfdb62 100644 --- a/libsql-server/src/connection/libsql.rs +++ b/libsql-server/src/connection/libsql.rs @@ -764,12 +764,32 @@ impl Connection { StmtKind::Read | StmtKind::TxnBegin | StmtKind::Other => config.block_reads, StmtKind::Write => config.block_reads || config.block_writes, StmtKind::TxnEnd | StmtKind::Release | StmtKind::Savepoint => false, + StmtKind::Attach | StmtKind::Detach => false, }; if blocked { return Err(Error::Blocked(config.block_reason.clone())); } - let mut stmt = self.conn.prepare(&query.stmt.stmt)?; + let mut stmt = if matches!(query.stmt.kind, StmtKind::Attach) { + let attached_db_name: &str = query.stmt.id.as_deref().unwrap_or(""); + let path = PathBuf::from(self.conn.path().unwrap_or(".")); + let dbs_path = path + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .canonicalize() + .unwrap_or_else(|_| std::path::PathBuf::from("..")); + let query = format!( + "ATTACH DATABASE 'file:{}?mode=ro' AS \"{attached_db_name}\"", + dbs_path.join(attached_db_name).join("data").display() + ); + tracing::trace!("ATTACH rewritten to: {query}"); + self.conn.prepare(&query)? + } else { + self.conn.prepare(&query.stmt.stmt)? + }; + if stmt.readonly() { READ_QUERY_COUNT.increment(1); } else { diff --git a/libsql-server/src/query_analysis.rs b/libsql-server/src/query_analysis.rs index 9423933c8b..3f57f2c5a3 100644 --- a/libsql-server/src/query_analysis.rs +++ b/libsql-server/src/query_analysis.rs @@ -1,6 +1,6 @@ use anyhow::Result; use fallible_iterator::FallibleIterator; -use sqlite3_parser::ast::{Cmd, PragmaBody, QualifiedName, Stmt}; +use sqlite3_parser::ast::{Cmd, Expr, Id, PragmaBody, QualifiedName, Stmt}; use sqlite3_parser::lexer::sql::{Parser, ParserError}; /// A group of statements to be executed together. @@ -11,6 +11,8 @@ pub struct Statement { /// Is the statement an INSERT, UPDATE or DELETE? pub is_iud: bool, pub is_insert: bool, + // Optional id associated with the statement (used for attach/detach) + pub id: Option, } impl Default for Statement { @@ -30,6 +32,8 @@ pub enum StmtKind { Write, Savepoint, Release, + Attach, + Detach, Other, } @@ -113,6 +117,12 @@ impl StmtKind { savepoint_name: Some(_), .. }) => Some(Self::Release), + Cmd::Stmt(Stmt::Attach { + expr: Expr::Id(Id(expr)), + db_name: Expr::Id(Id(name)), + .. + }) if expr == name => Some(Self::Attach), + Cmd::Stmt(Stmt::Detach(_)) => Some(Self::Detach), _ => None, } } @@ -236,6 +246,7 @@ impl Statement { kind: StmtKind::Read, is_iud: false, is_insert: false, + id: None, } } @@ -257,6 +268,7 @@ impl Statement { kind, is_iud: false, is_insert: false, + id: None, }); } } @@ -267,11 +279,22 @@ impl Statement { ); let is_insert = matches!(c, Cmd::Stmt(Stmt::Insert { .. })); + let id = match &c { + Cmd::Stmt(Stmt::Attach { + expr: Expr::Id(Id(expr)), + db_name: Expr::Id(Id(name)), + .. + }) if expr == name => Some(name.clone()), + Cmd::Stmt(Stmt::Detach(Expr::Id(Id(expr)))) => Some(expr.clone()), + _ => None, + }; + Ok(Statement { stmt: c.to_string(), kind, is_iud, is_insert, + id, }) } // The parser needs to be boxed because it's large, and you don't want it on the stack. From 402a5cb6fb43b8aea6b53d536e645fe8826d28ab Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Thu, 14 Dec 2023 14:18:43 +0100 Subject: [PATCH 02/10] connection: add allow_attach to config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Default is false, which means connections are blocked from attaching databases. If allowed, colocated databases can be attached in readonly mode. Example: → attach another as another; select * from another.sqlite_master; TYPE NAME TBL NAME ROOTPAGE SQL table t3 t3 2 CREATE TABLE t3(id) --- libsql-server/src/connection/config.rs | 4 +++ libsql-server/src/connection/libsql.rs | 2 +- libsql-server/src/http/admin/mod.rs | 4 +++ libsql-server/tests/namespaces/meta.rs | 39 ++++++++++++++++++++++++++ 4 files changed, 48 insertions(+), 1 deletion(-) diff --git a/libsql-server/src/connection/config.rs b/libsql-server/src/connection/config.rs index ff420d0695..0e5271ba49 100644 --- a/libsql-server/src/connection/config.rs +++ b/libsql-server/src/connection/config.rs @@ -18,6 +18,7 @@ pub struct DatabaseConfig { pub bottomless_db_id: Option, pub jwt_key: Option, pub txn_timeout: Option, + pub allow_attach: bool, } const fn default_max_size() -> u64 { @@ -35,6 +36,7 @@ impl Default for DatabaseConfig { bottomless_db_id: None, jwt_key: None, txn_timeout: Some(TXN_TIMEOUT), + allow_attach: false, } } } @@ -50,6 +52,7 @@ impl From<&metadata::DatabaseConfig> for DatabaseConfig { bottomless_db_id: value.bottomless_db_id.clone(), jwt_key: value.jwt_key.clone(), txn_timeout: value.txn_timeout_s.map(Duration::from_secs), + allow_attach: value.allow_attach, } } } @@ -65,6 +68,7 @@ impl From<&DatabaseConfig> for metadata::DatabaseConfig { bottomless_db_id: value.bottomless_db_id.clone(), jwt_key: value.jwt_key.clone(), txn_timeout_s: value.txn_timeout.map(|d| d.as_secs()), + allow_attach: value.allow_attach, } } } diff --git a/libsql-server/src/connection/libsql.rs b/libsql-server/src/connection/libsql.rs index d26fcfdb62..0adc0256bc 100644 --- a/libsql-server/src/connection/libsql.rs +++ b/libsql-server/src/connection/libsql.rs @@ -764,7 +764,7 @@ impl Connection { StmtKind::Read | StmtKind::TxnBegin | StmtKind::Other => config.block_reads, StmtKind::Write => config.block_reads || config.block_writes, StmtKind::TxnEnd | StmtKind::Release | StmtKind::Savepoint => false, - StmtKind::Attach | StmtKind::Detach => false, + StmtKind::Attach | StmtKind::Detach => !config.allow_attach, }; if blocked { return Err(Error::Blocked(config.block_reason.clone())); diff --git a/libsql-server/src/http/admin/mod.rs b/libsql-server/src/http/admin/mod.rs index 6738f9eae8..072245e16a 100644 --- a/libsql-server/src/http/admin/mod.rs +++ b/libsql-server/src/http/admin/mod.rs @@ -192,6 +192,7 @@ async fn handle_get_config( max_db_size: Some(max_db_size), heartbeat_url: config.heartbeat_url.clone().map(|u| u.into()), jwt_key: config.jwt_key.clone(), + allow_attach: config.allow_attach, }; Ok(Json(resp)) @@ -236,6 +237,8 @@ struct HttpDatabaseConfig { heartbeat_url: Option, #[serde(default)] jwt_key: Option, + #[serde(default)] + allow_attach: bool, } async fn handle_post_config( @@ -255,6 +258,7 @@ async fn handle_post_config( config.block_reads = req.block_reads; config.block_writes = req.block_writes; config.block_reason = req.block_reason; + config.allow_attach = req.allow_attach; if let Some(size) = req.max_db_size { config.max_db_pages = size.as_u64() / LIBSQL_PAGE_SIZE; } diff --git a/libsql-server/tests/namespaces/meta.rs b/libsql-server/tests/namespaces/meta.rs index aea4dcb8e6..e9c22318ed 100644 --- a/libsql-server/tests/namespaces/meta.rs +++ b/libsql-server/tests/namespaces/meta.rs @@ -137,6 +137,45 @@ fn meta_store() { foo_conn.execute("select 1", ()).await.unwrap(); } + // STEP 4: try attaching a database + { + let foo = Database::open_remote_with_connector( + "http://foo.primary:8080", + "", + TurmoilConnector, + )?; + let foo_conn = foo.connect()?; + + foo_conn.execute("attach foo as foo", ()).await.unwrap_err(); + } + + // STEP 5: update config to allow attaching databases + client + .post( + "http://primary:9090/v1/namespaces/foo/config", + json!({ + "block_reads": false, + "block_writes": false, + "allow_attach": true, + }), + ) + .await?; + + { + let foo = Database::open_remote_with_connector( + "http://foo.primary:8080", + "", + TurmoilConnector, + )?; + let foo_conn = foo.connect()?; + + foo_conn.execute("attach foo as foo", ()).await.unwrap(); + foo_conn + .execute("select * from foo.sqlite_master", ()) + .await + .unwrap(); + } + Ok(()) }); From fbabdb36615161832326d99d44a68a08a0b44e90 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Thu, 14 Dec 2023 14:52:15 +0100 Subject: [PATCH 03/10] libsql,namespaces: add client-side ATTACH support --- libsql-server/tests/namespaces/meta.rs | 3 +-- libsql/src/parser.rs | 10 +++++++++- libsql/src/replication/connection.rs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/libsql-server/tests/namespaces/meta.rs b/libsql-server/tests/namespaces/meta.rs index e9c22318ed..d79f3776f5 100644 --- a/libsql-server/tests/namespaces/meta.rs +++ b/libsql-server/tests/namespaces/meta.rs @@ -169,9 +169,8 @@ fn meta_store() { )?; let foo_conn = foo.connect()?; - foo_conn.execute("attach foo as foo", ()).await.unwrap(); foo_conn - .execute("select * from foo.sqlite_master", ()) + .execute_batch("attach foo as foo; select * from foo.sqlite_master") .await .unwrap(); } diff --git a/libsql/src/parser.rs b/libsql/src/parser.rs index 51c7ba8a78..656fab4c74 100644 --- a/libsql/src/parser.rs +++ b/libsql/src/parser.rs @@ -2,7 +2,7 @@ use crate::{Error, Result}; use fallible_iterator::FallibleIterator; -use sqlite3_parser::ast::{Cmd, PragmaBody, QualifiedName, Stmt, TransactionType}; +use sqlite3_parser::ast::{Cmd, PragmaBody, QualifiedName, Stmt, TransactionType, Expr, Id}; use sqlite3_parser::lexer::sql::{Parser, ParserError}; /// A group of statements to be executed together. @@ -30,6 +30,8 @@ pub enum StmtKind { Write, Savepoint, Release, + Attach, + Detach, Other, } @@ -116,6 +118,12 @@ impl StmtKind { savepoint_name: Some(_), .. }) => Some(Self::Release), + Cmd::Stmt(Stmt::Attach { + expr: Expr::Id(Id(expr)), + db_name: Expr::Id(Id(name)), + .. + }) if expr == name => Some(Self::Attach), + Cmd::Stmt(Stmt::Detach(_)) => Some(Self::Detach), _ => None, } } diff --git a/libsql/src/replication/connection.rs b/libsql/src/replication/connection.rs index 6ea26ccea5..38111b6162 100644 --- a/libsql/src/replication/connection.rs +++ b/libsql/src/replication/connection.rs @@ -66,7 +66,7 @@ impl State { (State::Txn, StmtKind::Release) => State::Txn, (_, StmtKind::Release) => State::Invalid, - (state, StmtKind::Other | StmtKind::Write | StmtKind::Read) => state, + (state, StmtKind::Other | StmtKind::Write | StmtKind::Read | StmtKind::Attach | StmtKind::Detach) => state, (State::Invalid, _) => State::Invalid, (State::Init, StmtKind::TxnBegin) => State::Txn, From 5df5c5d83ad56eda3954fae5cfe27177df7f95d2 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Tue, 2 Jan 2024 11:34:04 +0100 Subject: [PATCH 04/10] attach: support ATTACH x AS y aliasing We're going to need it, because the internal database names in sqld are uuids, and we don't expect users to know or use them. --- libsql-server/src/connection/libsql.rs | 39 ++++++++++++++++---------- libsql-server/src/query_analysis.rs | 21 ++++++-------- 2 files changed, 32 insertions(+), 28 deletions(-) diff --git a/libsql-server/src/connection/libsql.rs b/libsql-server/src/connection/libsql.rs index 0adc0256bc..0989bebe0a 100644 --- a/libsql-server/src/connection/libsql.rs +++ b/libsql-server/src/connection/libsql.rs @@ -771,21 +771,30 @@ impl Connection { } let mut stmt = if matches!(query.stmt.kind, StmtKind::Attach) { - let attached_db_name: &str = query.stmt.id.as_deref().unwrap_or(""); - let path = PathBuf::from(self.conn.path().unwrap_or(".")); - let dbs_path = path - .parent() - .unwrap_or_else(|| std::path::Path::new("..")) - .parent() - .unwrap_or_else(|| std::path::Path::new("..")) - .canonicalize() - .unwrap_or_else(|_| std::path::PathBuf::from("..")); - let query = format!( - "ATTACH DATABASE 'file:{}?mode=ro' AS \"{attached_db_name}\"", - dbs_path.join(attached_db_name).join("data").display() - ); - tracing::trace!("ATTACH rewritten to: {query}"); - self.conn.prepare(&query)? + match &query.stmt.attach_info { + Some((attached, attached_alias)) => { + let path = PathBuf::from(self.conn.path().unwrap_or(".")); + let dbs_path = path + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .canonicalize() + .unwrap_or_else(|_| std::path::PathBuf::from("..")); + let query = format!( + "ATTACH DATABASE 'file:{}?mode=ro' AS \"{attached_alias}\"", + dbs_path.join(attached).join("data").display() + ); + tracing::trace!("ATTACH rewritten to: {query}"); + self.conn.prepare(&query)? + } + None => { + return Err(Error::Internal(format!( + "Failed to ATTACH: {:?}", + query.stmt.attach_info + ))) + } + } } else { self.conn.prepare(&query.stmt.stmt)? }; diff --git a/libsql-server/src/query_analysis.rs b/libsql-server/src/query_analysis.rs index 3f57f2c5a3..c4aa3bd57c 100644 --- a/libsql-server/src/query_analysis.rs +++ b/libsql-server/src/query_analysis.rs @@ -11,8 +11,8 @@ pub struct Statement { /// Is the statement an INSERT, UPDATE or DELETE? pub is_iud: bool, pub is_insert: bool, - // Optional id associated with the statement (used for attach/detach) - pub id: Option, + // Optional id and alias associated with the statement (used for attach/detach) + pub attach_info: Option<(String, String)>, } impl Default for Statement { @@ -117,11 +117,7 @@ impl StmtKind { savepoint_name: Some(_), .. }) => Some(Self::Release), - Cmd::Stmt(Stmt::Attach { - expr: Expr::Id(Id(expr)), - db_name: Expr::Id(Id(name)), - .. - }) if expr == name => Some(Self::Attach), + Cmd::Stmt(Stmt::Attach { .. }) => Some(Self::Attach), Cmd::Stmt(Stmt::Detach(_)) => Some(Self::Detach), _ => None, } @@ -246,7 +242,7 @@ impl Statement { kind: StmtKind::Read, is_iud: false, is_insert: false, - id: None, + attach_info: None, } } @@ -268,7 +264,7 @@ impl Statement { kind, is_iud: false, is_insert: false, - id: None, + attach_info: None, }); } } @@ -279,13 +275,12 @@ impl Statement { ); let is_insert = matches!(c, Cmd::Stmt(Stmt::Insert { .. })); - let id = match &c { + let attach_info = match &c { Cmd::Stmt(Stmt::Attach { expr: Expr::Id(Id(expr)), db_name: Expr::Id(Id(name)), .. - }) if expr == name => Some(name.clone()), - Cmd::Stmt(Stmt::Detach(Expr::Id(Id(expr)))) => Some(expr.clone()), + }) => Some((expr.clone(), name.clone())), _ => None, }; @@ -294,7 +289,7 @@ impl Statement { kind, is_iud, is_insert, - id, + attach_info, }) } // The parser needs to be boxed because it's large, and you don't want it on the stack. From 910f281be03aa67bf307fb9692915292535981ac Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Tue, 2 Jan 2024 13:14:44 +0100 Subject: [PATCH 05/10] attach: fix quoted db names In libsql-server, raw db names are uuids that need to be quoted, so that needs to be supported in the ATTACH layer. As a bonus, "names" that are actually file system paths are refused to prevent abuse. --- libsql-server/src/connection/libsql.rs | 39 +++++++++++++++++--------- libsql-server/src/query_analysis.rs | 2 +- 2 files changed, 27 insertions(+), 14 deletions(-) diff --git a/libsql-server/src/connection/libsql.rs b/libsql-server/src/connection/libsql.rs index 0989bebe0a..49bf59851c 100644 --- a/libsql-server/src/connection/libsql.rs +++ b/libsql-server/src/connection/libsql.rs @@ -748,6 +748,31 @@ impl Connection { Ok(enabled) } + fn prepare_attach_query(&self, attached: &str, attached_alias: &str) -> Result { + let attached = attached.strip_prefix('"').unwrap_or(attached); + let attached = attached.strip_suffix('"').unwrap_or(attached); + if attached.contains('/') { + return Err(Error::Internal(format!( + "Invalid attached database name: {:?}", + attached + ))); + } + let path = PathBuf::from(self.conn.path().unwrap_or(".")); + let dbs_path = path + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .parent() + .unwrap_or_else(|| std::path::Path::new("..")) + .canonicalize() + .unwrap_or_else(|_| std::path::PathBuf::from("..")); + let query = format!( + "ATTACH DATABASE 'file:{}?mode=ro' AS \"{attached_alias}\"", + dbs_path.join(attached).join("data").display() + ); + tracing::trace!("ATTACH rewritten to: {query}"); + Ok(query) + } + fn execute_query( &self, query: &Query, @@ -773,19 +798,7 @@ impl Connection { let mut stmt = if matches!(query.stmt.kind, StmtKind::Attach) { match &query.stmt.attach_info { Some((attached, attached_alias)) => { - let path = PathBuf::from(self.conn.path().unwrap_or(".")); - let dbs_path = path - .parent() - .unwrap_or_else(|| std::path::Path::new("..")) - .parent() - .unwrap_or_else(|| std::path::Path::new("..")) - .canonicalize() - .unwrap_or_else(|_| std::path::PathBuf::from("..")); - let query = format!( - "ATTACH DATABASE 'file:{}?mode=ro' AS \"{attached_alias}\"", - dbs_path.join(attached).join("data").display() - ); - tracing::trace!("ATTACH rewritten to: {query}"); + let query = self.prepare_attach_query(attached, attached_alias)?; self.conn.prepare(&query)? } None => { diff --git a/libsql-server/src/query_analysis.rs b/libsql-server/src/query_analysis.rs index c4aa3bd57c..bda519ec9d 100644 --- a/libsql-server/src/query_analysis.rs +++ b/libsql-server/src/query_analysis.rs @@ -283,7 +283,7 @@ impl Statement { }) => Some((expr.clone(), name.clone())), _ => None, }; - + tracing::info!("attachiu: {:?}", attach_info); Ok(Statement { stmt: c.to_string(), kind, From 56323cb8993cda2a9e1bb249116b8dc31c35f5a4 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Mon, 15 Jan 2024 10:02:37 +0100 Subject: [PATCH 06/10] libsql-server: drop stray serde(default) from allow_attach --- libsql-server/src/query_analysis.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/libsql-server/src/query_analysis.rs b/libsql-server/src/query_analysis.rs index bda519ec9d..a75d1b890d 100644 --- a/libsql-server/src/query_analysis.rs +++ b/libsql-server/src/query_analysis.rs @@ -283,7 +283,6 @@ impl Statement { }) => Some((expr.clone(), name.clone())), _ => None, }; - tracing::info!("attachiu: {:?}", attach_info); Ok(Statement { stmt: c.to_string(), kind, From e4dc15b3f1e9dfccd718f9eaa0d91fd4760174b1 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Mon, 15 Jan 2024 11:12:29 +0100 Subject: [PATCH 07/10] libsql-replication: update proto files --- libsql-replication/proto/metadata.proto | 1 + 1 file changed, 1 insertion(+) diff --git a/libsql-replication/proto/metadata.proto b/libsql-replication/proto/metadata.proto index f695c6e642..f83b590e7b 100644 --- a/libsql-replication/proto/metadata.proto +++ b/libsql-replication/proto/metadata.proto @@ -15,4 +15,5 @@ message DatabaseConfig { optional string bottomless_db_id = 6; optional string jwt_key = 7; optional uint64 txn_timeout_s = 8; + bool allow_attach = 8; } From 64a42b9ace687a8cf1f47ab7e32369ec7f44ed22 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Mon, 15 Jan 2024 11:17:35 +0100 Subject: [PATCH 08/10] libsql-replication: regenerate protobuf --- libsql-replication/src/generated/metadata.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libsql-replication/src/generated/metadata.rs b/libsql-replication/src/generated/metadata.rs index f9e4b28a6b..8bbfe0a7ec 100644 --- a/libsql-replication/src/generated/metadata.rs +++ b/libsql-replication/src/generated/metadata.rs @@ -21,4 +21,6 @@ pub struct DatabaseConfig { pub jwt_key: ::core::option::Option<::prost::alloc::string::String>, #[prost(uint64, optional, tag = "8")] pub txn_timeout_s: ::core::option::Option, + #[prost(bool, tag = "9")] + pub allow_attach: bool, } From 76ed35e8f6b35a5dac7d0feef2974b070ed05a77 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Wed, 17 Jan 2024 13:27:54 +0100 Subject: [PATCH 09/10] tests: move attach to its own test --- libsql-server/tests/namespaces/meta.rs | 40 ++++++++++++++++++++++++-- 1 file changed, 38 insertions(+), 2 deletions(-) diff --git a/libsql-server/tests/namespaces/meta.rs b/libsql-server/tests/namespaces/meta.rs index d79f3776f5..badb5e37a6 100644 --- a/libsql-server/tests/namespaces/meta.rs +++ b/libsql-server/tests/namespaces/meta.rs @@ -137,7 +137,43 @@ fn meta_store() { foo_conn.execute("select 1", ()).await.unwrap(); } - // STEP 4: try attaching a database + Ok(()) + }); + + sim.run().unwrap(); +} + +#[test] +fn meta_attach() { + let mut sim = Builder::new().build(); + let tmp = tempdir().unwrap(); + make_primary(&mut sim, tmp.path().to_path_buf()); + + sim.client("client", async { + let client = Client::new(); + + // STEP 1: create namespace and check that it can be read from + client + .post( + "http://primary:9090/v1/namespaces/foo/create", + json!({ + "max_db_size": "5mb" + }), + ) + .await?; + + { + let foo = Database::open_remote_with_connector( + "http://foo.primary:8080", + "", + TurmoilConnector, + )?; + let foo_conn = foo.connect()?; + + foo_conn.execute("select 1", ()).await.unwrap(); + } + + // STEP 2: try attaching a database { let foo = Database::open_remote_with_connector( "http://foo.primary:8080", @@ -149,7 +185,7 @@ fn meta_store() { foo_conn.execute("attach foo as foo", ()).await.unwrap_err(); } - // STEP 5: update config to allow attaching databases + // STEP 3: update config to allow attaching databases client .post( "http://primary:9090/v1/namespaces/foo/config", From 48ffec9bb518d6d0c330af9f2147ba2245e83c80 Mon Sep 17 00:00:00 2001 From: Piotr Sarna Date: Tue, 30 Jan 2024 14:37:19 +0100 Subject: [PATCH 10/10] libsql-replication: fix proto number after rebase --- libsql-replication/proto/metadata.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libsql-replication/proto/metadata.proto b/libsql-replication/proto/metadata.proto index f83b590e7b..0606342ed0 100644 --- a/libsql-replication/proto/metadata.proto +++ b/libsql-replication/proto/metadata.proto @@ -15,5 +15,5 @@ message DatabaseConfig { optional string bottomless_db_id = 6; optional string jwt_key = 7; optional uint64 txn_timeout_s = 8; - bool allow_attach = 8; + bool allow_attach = 9; }