@@ -23,6 +23,7 @@ pub struct Database {
2323 pub id : String ,
2424 pub description : Option < String > ,
2525 pub default_connection_id : String ,
26+ #[ serde( default ) ]
2627 attachments : Vec < DatabaseAttachment > ,
2728}
2829
@@ -82,7 +83,10 @@ fn fetch_database(api: &ApiClient, id: &str) -> Database {
8283
8384pub fn try_resolve_database ( api : & ApiClient , id_or_description : & str ) -> Result < Database , String > {
8485 // Try a direct id lookup first — avoids the list round-trip for the common case.
85- if let Some ( db) = api. get_none_if_not_found ( & format ! ( "/databases/{id_or_description}" ) ) {
86+ // Percent-encode the segment so descriptions containing spaces or other URL-unsafe
87+ // characters don't cause a URL parse error before the list fallback can run.
88+ let encoded = urlencoding:: encode ( id_or_description) ;
89+ if let Some ( db) = api. get_none_if_not_found ( & format ! ( "/databases/{encoded}" ) ) {
8690 return Ok ( db) ;
8791 }
8892
@@ -436,6 +440,11 @@ pub fn create(
436440 }
437441 } ;
438442
443+ if let Err ( e) = crate :: config:: save_current_database ( "default" , workspace_id, & result. id ) {
444+ use crossterm:: style:: Stylize ;
445+ eprintln ! ( "{}" , format!( "warning: database created but could not set as current: {e}" ) . yellow( ) ) ;
446+ }
447+
439448 match format {
440449 "json" => println ! ( "{}" , serde_json:: to_string_pretty( & result) . unwrap( ) ) ,
441450 "yaml" => print ! ( "{}" , serde_yaml:: to_string( & result) . unwrap( ) ) ,
@@ -450,6 +459,34 @@ pub fn create(
450459 }
451460}
452461
462+ pub fn set ( workspace_id : & str , id_or_description : & str ) {
463+ use crossterm:: style:: Stylize ;
464+ let api = ApiClient :: new ( Some ( workspace_id) ) ;
465+ let db = resolve_database ( & api, id_or_description) ;
466+ if let Err ( e) = crate :: config:: save_current_database ( "default" , workspace_id, & db. id ) {
467+ eprintln ! ( "{}" , format!( "error saving current database: {e}" ) . red( ) ) ;
468+ std:: process:: exit ( 1 ) ;
469+ }
470+ println ! ( "{}" , format!( "Current database set to {}" , db. id) . green( ) ) ;
471+ }
472+
473+ fn resolve_current_database ( provided : Option < & str > , workspace_id : & str ) -> String {
474+ if let Some ( id) = provided {
475+ return id. to_string ( ) ;
476+ }
477+ match crate :: config:: load_current_database ( "default" , workspace_id) {
478+ Some ( id) => id,
479+ None => {
480+ use crossterm:: style:: Stylize ;
481+ eprintln ! (
482+ "{}" ,
483+ "error: no current database set. Use 'hotdata databases set <id>' or pass a database id." . red( )
484+ ) ;
485+ std:: process:: exit ( 1 ) ;
486+ }
487+ }
488+ }
489+
453490pub fn delete ( workspace_id : & str , id_or_description : & str ) {
454491 use crossterm:: style:: Stylize ;
455492
@@ -462,12 +499,19 @@ pub fn delete(workspace_id: &str, id_or_description: &str) {
462499 std:: process:: exit ( 1 ) ;
463500 }
464501
502+ // If the deleted database was the current one, clear it so subsequent
503+ // commands don't silently send a stale X-Database-Id header.
504+ if crate :: config:: load_current_database ( "default" , workspace_id) . as_deref ( ) == Some ( & db. id ) {
505+ let _ = crate :: config:: clear_current_database ( "default" , workspace_id) ;
506+ }
507+
465508 println ! ( "{}" , "Database deleted." . green( ) ) ;
466509}
467510
468- pub fn tables_list ( workspace_id : & str , database : & str , schema : Option < & str > , format : & str ) {
511+ pub fn tables_list ( workspace_id : & str , database : Option < & str > , schema : Option < & str > , format : & str ) {
512+ let database = resolve_current_database ( database, workspace_id) ;
469513 let api = ApiClient :: new ( Some ( workspace_id) ) ;
470- let db = resolve_database ( & api, database) ;
514+ let db = resolve_database ( & api, & database) ;
471515 let tables = collect_tables ( & api, & db. default_connection_id , schema) ;
472516
473517 let rows = table_rows ( tables) ;
@@ -502,7 +546,7 @@ pub fn tables_list(workspace_id: &str, database: &str, schema: Option<&str>, for
502546
503547pub fn tables_load (
504548 workspace_id : & str ,
505- database : & str ,
549+ database : Option < & str > ,
506550 table : & str ,
507551 schema : Option < & str > ,
508552 file : Option < & str > ,
@@ -511,8 +555,9 @@ pub fn tables_load(
511555) {
512556 use crossterm:: style:: Stylize ;
513557
558+ let database = resolve_current_database ( database, workspace_id) ;
514559 let api = ApiClient :: new ( Some ( workspace_id) ) ;
515- let db = resolve_database ( & api, database) ;
560+ let db = resolve_database ( & api, & database) ;
516561 let schema = schema_name ( schema) ;
517562
518563 // clap enforces mutual exclusion; only one of these is ever Some.
@@ -564,11 +609,12 @@ pub fn tables_load(
564609 println ! ( "rows: {}" , result. row_count) ;
565610}
566611
567- pub fn tables_delete ( workspace_id : & str , database : & str , table : & str , schema : Option < & str > ) {
612+ pub fn tables_delete ( workspace_id : & str , database : Option < & str > , table : & str , schema : Option < & str > ) {
568613 use crossterm:: style:: Stylize ;
569614
615+ let database = resolve_current_database ( database, workspace_id) ;
570616 let api = ApiClient :: new ( Some ( workspace_id) ) ;
571- let db = resolve_database ( & api, database) ;
617+ let db = resolve_database ( & api, & database) ;
572618 let schema = schema_name ( schema) ;
573619
574620 let path = managed_table_delete_path ( & db. default_connection_id , schema, table) ;
0 commit comments