Detailed changes
@@ -1,7 +1,10 @@
use anyhow::Result;
use db::{
- define_connection, query,
- sqlez::{bindable::Column, statement::Statement},
+ query,
+ sqlez::{
+ bindable::Column, domain::Domain, statement::Statement,
+ thread_safe_connection::ThreadSafeConnection,
+ },
sqlez_macros::sql,
};
use serde::{Deserialize, Serialize};
@@ -50,8 +53,11 @@ impl Column for SerializedCommandInvocation {
}
}
-define_connection!(pub static ref COMMAND_PALETTE_HISTORY: CommandPaletteDB<()> =
- &[sql!(
+pub struct CommandPaletteDB(ThreadSafeConnection);
+
+impl Domain for CommandPaletteDB {
+ const NAME: &str = stringify!(CommandPaletteDB);
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE IF NOT EXISTS command_invocations(
id INTEGER PRIMARY KEY AUTOINCREMENT,
command_name TEXT NOT NULL,
@@ -59,7 +65,9 @@ define_connection!(pub static ref COMMAND_PALETTE_HISTORY: CommandPaletteDB<()>
last_invoked INTEGER DEFAULT (unixepoch()) NOT NULL
) STRICT;
)];
-);
+}
+
+db::static_connection!(COMMAND_PALETTE_HISTORY, CommandPaletteDB, []);
impl CommandPaletteDB {
pub async fn write_command_invocation(
@@ -110,11 +110,14 @@ pub async fn open_test_db<M: Migrator>(db_name: &str) -> ThreadSafeConnection {
}
/// Implements a basic DB wrapper for a given domain
+///
+/// Arguments:
+/// - static variable name for connection
+/// - type of connection wrapper
+/// - dependencies, whose migrations should be run prior to this domain's migrations
#[macro_export]
-macro_rules! define_connection {
- (pub static ref $id:ident: $t:ident<()> = $migrations:expr; $($global:ident)?) => {
- pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection);
-
+macro_rules! static_connection {
+ ($id:ident, $t:ident, [ $($d:ty),* ] $(, $global:ident)?) => {
impl ::std::ops::Deref for $t {
type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection;
@@ -123,16 +126,6 @@ macro_rules! define_connection {
}
}
- impl $crate::sqlez::domain::Domain for $t {
- fn name() -> &'static str {
- stringify!($t)
- }
-
- fn migrations() -> &'static [&'static str] {
- $migrations
- }
- }
-
impl $t {
#[cfg(any(test, feature = "test-support"))]
pub async fn open_test_db(name: &'static str) -> Self {
@@ -142,44 +135,8 @@ macro_rules! define_connection {
#[cfg(any(test, feature = "test-support"))]
pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
- $t($crate::smol::block_on($crate::open_test_db::<$t>(stringify!($id))))
- });
-
- #[cfg(not(any(test, feature = "test-support")))]
- pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
- let db_dir = $crate::database_dir();
- let scope = if false $(|| stringify!($global) == "global")? {
- "global"
- } else {
- $crate::RELEASE_CHANNEL.dev_name()
- };
- $t($crate::smol::block_on($crate::open_db::<$t>(db_dir, scope)))
- });
- };
- (pub static ref $id:ident: $t:ident<$($d:ty),+> = $migrations:expr; $($global:ident)?) => {
- pub struct $t($crate::sqlez::thread_safe_connection::ThreadSafeConnection);
-
- impl ::std::ops::Deref for $t {
- type Target = $crate::sqlez::thread_safe_connection::ThreadSafeConnection;
-
- fn deref(&self) -> &Self::Target {
- &self.0
- }
- }
-
- impl $crate::sqlez::domain::Domain for $t {
- fn name() -> &'static str {
- stringify!($t)
- }
-
- fn migrations() -> &'static [&'static str] {
- $migrations
- }
- }
-
- #[cfg(any(test, feature = "test-support"))]
- pub static $id: std::sync::LazyLock<$t> = std::sync::LazyLock::new(|| {
- $t($crate::smol::block_on($crate::open_test_db::<($($d),+, $t)>(stringify!($id))))
+ #[allow(unused_parens)]
+ $t($crate::smol::block_on($crate::open_test_db::<($($d,)* $t)>(stringify!($id))))
});
#[cfg(not(any(test, feature = "test-support")))]
@@ -190,9 +147,10 @@ macro_rules! define_connection {
} else {
$crate::RELEASE_CHANNEL.dev_name()
};
- $t($crate::smol::block_on($crate::open_db::<($($d),+, $t)>(db_dir, scope)))
+ #[allow(unused_parens)]
+ $t($crate::smol::block_on($crate::open_db::<($($d,)* $t)>(db_dir, scope)))
});
- };
+ }
}
pub fn write_and_log<F>(cx: &App, db_write: impl FnOnce() -> F + Send + 'static)
@@ -219,17 +177,12 @@ mod tests {
enum BadDB {}
impl Domain for BadDB {
- fn name() -> &'static str {
- "db_tests"
- }
-
- fn migrations() -> &'static [&'static str] {
- &[
- sql!(CREATE TABLE test(value);),
- // failure because test already exists
- sql!(CREATE TABLE test(value);),
- ]
- }
+ const NAME: &str = "db_tests";
+ const MIGRATIONS: &[&str] = &[
+ sql!(CREATE TABLE test(value);),
+ // failure because test already exists
+ sql!(CREATE TABLE test(value);),
+ ];
}
let tempdir = tempfile::Builder::new()
@@ -251,25 +204,15 @@ mod tests {
enum CorruptedDB {}
impl Domain for CorruptedDB {
- fn name() -> &'static str {
- "db_tests"
- }
-
- fn migrations() -> &'static [&'static str] {
- &[sql!(CREATE TABLE test(value);)]
- }
+ const NAME: &str = "db_tests";
+ const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test(value);)];
}
enum GoodDB {}
impl Domain for GoodDB {
- fn name() -> &'static str {
- "db_tests" //Notice same name
- }
-
- fn migrations() -> &'static [&'static str] {
- &[sql!(CREATE TABLE test2(value);)] //But different migration
- }
+ const NAME: &str = "db_tests"; //Notice same name
+ const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test2(value);)];
}
let tempdir = tempfile::Builder::new()
@@ -305,25 +248,16 @@ mod tests {
enum CorruptedDB {}
impl Domain for CorruptedDB {
- fn name() -> &'static str {
- "db_tests"
- }
+ const NAME: &str = "db_tests";
- fn migrations() -> &'static [&'static str] {
- &[sql!(CREATE TABLE test(value);)]
- }
+ const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test(value);)];
}
enum GoodDB {}
impl Domain for GoodDB {
- fn name() -> &'static str {
- "db_tests" //Notice same name
- }
-
- fn migrations() -> &'static [&'static str] {
- &[sql!(CREATE TABLE test2(value);)] //But different migration
- }
+ const NAME: &str = "db_tests"; //Notice same name
+ const MIGRATIONS: &[&str] = &[sql!(CREATE TABLE test2(value);)]; // But different migration
}
let tempdir = tempfile::Builder::new()
@@ -2,16 +2,26 @@ use gpui::App;
use sqlez_macros::sql;
use util::ResultExt as _;
-use crate::{define_connection, query, write_and_log};
+use crate::{
+ query,
+ sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
+ write_and_log,
+};
-define_connection!(pub static ref KEY_VALUE_STORE: KeyValueStore<()> =
- &[sql!(
+pub struct KeyValueStore(crate::sqlez::thread_safe_connection::ThreadSafeConnection);
+
+impl Domain for KeyValueStore {
+ const NAME: &str = stringify!(KeyValueStore);
+
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE IF NOT EXISTS kv_store(
key TEXT PRIMARY KEY,
value TEXT NOT NULL
) STRICT;
)];
-);
+}
+
+crate::static_connection!(KEY_VALUE_STORE, KeyValueStore, []);
pub trait Dismissable {
const KEY: &'static str;
@@ -91,15 +101,19 @@ mod tests {
}
}
-define_connection!(pub static ref GLOBAL_KEY_VALUE_STORE: GlobalKeyValueStore<()> =
- &[sql!(
+pub struct GlobalKeyValueStore(ThreadSafeConnection);
+
+impl Domain for GlobalKeyValueStore {
+ const NAME: &str = stringify!(GlobalKeyValueStore);
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE IF NOT EXISTS kv_store(
key TEXT PRIMARY KEY,
value TEXT NOT NULL
) STRICT;
)];
- global
-);
+}
+
+crate::static_connection!(GLOBAL_KEY_VALUE_STORE, GlobalKeyValueStore, [], global);
impl GlobalKeyValueStore {
query! {
@@ -1,13 +1,17 @@
use anyhow::Result;
-use db::sqlez::bindable::{Bind, Column, StaticColumnCount};
-use db::sqlez::statement::Statement;
+use db::{
+ query,
+ sqlez::{
+ bindable::{Bind, Column, StaticColumnCount},
+ domain::Domain,
+ statement::Statement,
+ },
+ sqlez_macros::sql,
+};
use fs::MTime;
use itertools::Itertools as _;
use std::path::PathBuf;
-use db::sqlez_macros::sql;
-use db::{define_connection, query};
-
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
#[derive(Clone, Debug, PartialEq, Default)]
@@ -83,7 +87,11 @@ impl Column for SerializedEditor {
}
}
-define_connection!(
+pub struct EditorDb(db::sqlez::thread_safe_connection::ThreadSafeConnection);
+
+impl Domain for EditorDb {
+ const NAME: &str = stringify!(EditorDb);
+
// Current schema shape using pseudo-rust syntax:
// editors(
// item_id: usize,
@@ -113,7 +121,8 @@ define_connection!(
// start: usize,
// end: usize,
// )
- pub static ref DB: EditorDb<WorkspaceDb> = &[
+
+ const MIGRATIONS: &[&str] = &[
sql! (
CREATE TABLE editors(
item_id INTEGER NOT NULL,
@@ -189,7 +198,9 @@ define_connection!(
) STRICT;
),
];
-);
+}
+
+db::static_connection!(DB, EditorDb, [WorkspaceDb]);
// https://www.sqlite.org/limits.html
// > <..> the maximum value of a host parameter number is SQLITE_MAX_VARIABLE_NUMBER,
@@ -401,12 +401,19 @@ pub fn init(cx: &mut App) {
mod persistence {
use std::path::PathBuf;
- use db::{define_connection, query, sqlez_macros::sql};
+ use db::{
+ query,
+ sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+ };
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
- define_connection! {
- pub static ref IMAGE_VIEWER: ImageViewerDb<WorkspaceDb> =
- &[sql!(
+ pub struct ImageViewerDb(ThreadSafeConnection);
+
+ impl Domain for ImageViewerDb {
+ const NAME: &str = stringify!(ImageViewerDb);
+
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE image_viewers (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -417,9 +424,11 @@ mod persistence {
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;
- )];
+ )];
}
+ db::static_connection!(IMAGE_VIEWER, ImageViewerDb, [WorkspaceDb]);
+
impl ImageViewerDb {
query! {
pub async fn save_image_path(
@@ -850,13 +850,19 @@ impl workspace::SerializableItem for Onboarding {
}
mod persistence {
- use db::{define_connection, query, sqlez_macros::sql};
+ use db::{
+ query,
+ sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+ };
use workspace::WorkspaceDb;
- define_connection! {
- pub static ref ONBOARDING_PAGES: OnboardingPagesDb<WorkspaceDb> =
- &[
- sql!(
+ pub struct OnboardingPagesDb(ThreadSafeConnection);
+
+ impl Domain for OnboardingPagesDb {
+ const NAME: &str = stringify!(OnboardingPagesDb);
+
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE onboarding_pages (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -866,10 +872,11 @@ mod persistence {
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;
- ),
- ];
+ )];
}
+ db::static_connection!(ONBOARDING_PAGES, OnboardingPagesDb, [WorkspaceDb]);
+
impl OnboardingPagesDb {
query! {
pub async fn save_onboarding_page(
@@ -414,13 +414,19 @@ impl workspace::SerializableItem for WelcomePage {
}
mod persistence {
- use db::{define_connection, query, sqlez_macros::sql};
+ use db::{
+ query,
+ sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+ };
use workspace::WorkspaceDb;
- define_connection! {
- pub static ref WELCOME_PAGES: WelcomePagesDb<WorkspaceDb> =
- &[
- sql!(
+ pub struct WelcomePagesDb(ThreadSafeConnection);
+
+ impl Domain for WelcomePagesDb {
+ const NAME: &str = stringify!(WelcomePagesDb);
+
+ const MIGRATIONS: &[&str] = (&[sql!(
CREATE TABLE welcome_pages (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -430,10 +436,11 @@ mod persistence {
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;
- ),
- ];
+ )]);
}
+ db::static_connection!(WELCOME_PAGES, WelcomePagesDb, [WorkspaceDb]);
+
impl WelcomePagesDb {
query! {
pub async fn save_welcome_page(
@@ -3348,12 +3348,15 @@ impl SerializableItem for KeymapEditor {
}
mod persistence {
- use db::{define_connection, query, sqlez_macros::sql};
+ use db::{query, sqlez::domain::Domain, sqlez_macros::sql};
use workspace::WorkspaceDb;
- define_connection! {
- pub static ref KEYBINDING_EDITORS: KeybindingEditorDb<WorkspaceDb> =
- &[sql!(
+ pub struct KeybindingEditorDb(db::sqlez::thread_safe_connection::ThreadSafeConnection);
+
+ impl Domain for KeybindingEditorDb {
+ const NAME: &str = stringify!(KeybindingEditorDb);
+
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE keybinding_editors (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -3362,9 +3365,11 @@ mod persistence {
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;
- )];
+ )];
}
+ db::static_connection!(KEYBINDING_EDITORS, KeybindingEditorDb, [WorkspaceDb]);
+
impl KeybindingEditorDb {
query! {
pub async fn save_keybinding_editor(
@@ -1,8 +1,12 @@
use crate::connection::Connection;
pub trait Domain: 'static {
- fn name() -> &'static str;
- fn migrations() -> &'static [&'static str];
+ const NAME: &str;
+ const MIGRATIONS: &[&str];
+
+ fn should_allow_migration_change(_index: usize, _old: &str, _new: &str) -> bool {
+ false
+ }
}
pub trait Migrator: 'static {
@@ -17,7 +21,11 @@ impl Migrator for () {
impl<D: Domain> Migrator for D {
fn migrate(connection: &Connection) -> anyhow::Result<()> {
- connection.migrate(Self::name(), Self::migrations())
+ connection.migrate(
+ Self::NAME,
+ Self::MIGRATIONS,
+ Self::should_allow_migration_change,
+ )
}
}
@@ -34,7 +34,12 @@ impl Connection {
/// Note: Unlike everything else in SQLez, migrations are run eagerly, without first
/// preparing the SQL statements. This makes it possible to do multi-statement schema
/// updates in a single string without running into prepare errors.
- pub fn migrate(&self, domain: &'static str, migrations: &[&'static str]) -> Result<()> {
+ pub fn migrate(
+ &self,
+ domain: &'static str,
+ migrations: &[&'static str],
+ mut should_allow_migration_change: impl FnMut(usize, &str, &str) -> bool,
+ ) -> Result<()> {
self.with_savepoint("migrating", || {
// Setup the migrations table unconditionally
self.exec(indoc! {"
@@ -65,9 +70,14 @@ impl Connection {
&sqlformat::QueryParams::None,
Default::default(),
);
- if completed_migration == migration {
+ if completed_migration == migration
+ || migration.trim().starts_with("-- ALLOW_MIGRATION_CHANGE")
+ {
// Migration already run. Continue
continue;
+ } else if should_allow_migration_change(index, &completed_migration, &migration)
+ {
+ continue;
} else {
anyhow::bail!(formatdoc! {"
Migration changed for {domain} at step {index}
@@ -108,6 +118,7 @@ mod test {
a TEXT,
b TEXT
)"}],
+ disallow_migration_change,
)
.unwrap();
@@ -136,6 +147,7 @@ mod test {
d TEXT
)"},
],
+ disallow_migration_change,
)
.unwrap();
@@ -214,7 +226,11 @@ mod test {
// Run the migration verifying that the row got dropped
connection
- .migrate("test", &["DELETE FROM test_table"])
+ .migrate(
+ "test",
+ &["DELETE FROM test_table"],
+ disallow_migration_change,
+ )
.unwrap();
assert_eq!(
connection
@@ -232,7 +248,11 @@ mod test {
// Run the same migration again and verify that the table was left unchanged
connection
- .migrate("test", &["DELETE FROM test_table"])
+ .migrate(
+ "test",
+ &["DELETE FROM test_table"],
+ disallow_migration_change,
+ )
.unwrap();
assert_eq!(
connection
@@ -252,27 +272,28 @@ mod test {
.migrate(
"test migration",
&[
- indoc! {"
- CREATE TABLE test (
- col INTEGER
- )"},
- indoc! {"
- INSERT INTO test (col) VALUES (1)"},
+ "CREATE TABLE test (col INTEGER)",
+ "INSERT INTO test (col) VALUES (1)",
],
+ disallow_migration_change,
)
.unwrap();
+ let mut migration_changed = false;
+
// Create another migration with the same domain but different steps
let second_migration_result = connection.migrate(
"test migration",
&[
- indoc! {"
- CREATE TABLE test (
- color INTEGER
- )"},
- indoc! {"
- INSERT INTO test (color) VALUES (1)"},
+ "CREATE TABLE test (color INTEGER )",
+ "INSERT INTO test (color) VALUES (1)",
],
+ |_, old, new| {
+ assert_eq!(old, "CREATE TABLE test (col INTEGER)");
+ assert_eq!(new, "CREATE TABLE test (color INTEGER)");
+ migration_changed = true;
+ false
+ },
);
// Verify new migration returns error when run
@@ -284,7 +305,11 @@ mod test {
let connection = Connection::open_memory(Some("test_create_alter_drop"));
connection
- .migrate("first_migration", &["CREATE TABLE table1(a TEXT) STRICT;"])
+ .migrate(
+ "first_migration",
+ &["CREATE TABLE table1(a TEXT) STRICT;"],
+ disallow_migration_change,
+ )
.unwrap();
connection
@@ -305,6 +330,7 @@ mod test {
ALTER TABLE table2 RENAME TO table1;
"}],
+ disallow_migration_change,
)
.unwrap();
@@ -312,4 +338,8 @@ mod test {
assert_eq!(res, "test text");
}
+
+ fn disallow_migration_change(_: usize, _: &str, _: &str) -> bool {
+ false
+ }
}
@@ -278,12 +278,8 @@ mod test {
enum TestDomain {}
impl Domain for TestDomain {
- fn name() -> &'static str {
- "test"
- }
- fn migrations() -> &'static [&'static str] {
- &["CREATE TABLE test(col1 TEXT, col2 TEXT) STRICT;"]
- }
+ const NAME: &str = "test";
+ const MIGRATIONS: &[&str] = &["CREATE TABLE test(col1 TEXT, col2 TEXT) STRICT;"];
}
for _ in 0..100 {
@@ -312,12 +308,9 @@ mod test {
fn wild_zed_lost_failure() {
enum TestWorkspace {}
impl Domain for TestWorkspace {
- fn name() -> &'static str {
- "workspace"
- }
+ const NAME: &str = "workspace";
- fn migrations() -> &'static [&'static str] {
- &["
+ const MIGRATIONS: &[&str] = &["
CREATE TABLE workspaces(
workspace_id INTEGER PRIMARY KEY,
dock_visible INTEGER, -- Boolean
@@ -336,8 +329,7 @@ mod test {
ON DELETE CASCADE
ON UPDATE CASCADE
) STRICT;
- "]
- }
+ "];
}
let builder =
@@ -9,7 +9,11 @@ use std::path::{Path, PathBuf};
use ui::{App, Context, Pixels, Window};
use util::ResultExt as _;
-use db::{define_connection, query, sqlez::statement::Statement, sqlez_macros::sql};
+use db::{
+ query,
+ sqlez::{domain::Domain, statement::Statement, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+};
use workspace::{
ItemHandle, ItemId, Member, Pane, PaneAxis, PaneGroup, SerializableItem as _, Workspace,
WorkspaceDb, WorkspaceId,
@@ -375,9 +379,13 @@ impl<'de> Deserialize<'de> for SerializedAxis {
}
}
-define_connection! {
- pub static ref TERMINAL_DB: TerminalDb<WorkspaceDb> =
- &[sql!(
+pub struct TerminalDb(ThreadSafeConnection);
+
+impl Domain for TerminalDb {
+ const NAME: &str = stringify!(TerminalDb);
+
+ const MIGRATIONS: &[&str] = &[
+ sql!(
CREATE TABLE terminals (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -414,6 +422,8 @@ define_connection! {
];
}
+db::static_connection!(TERMINAL_DB, TerminalDb, [WorkspaceDb]);
+
impl TerminalDb {
query! {
pub async fn update_workspace_id(
@@ -7,8 +7,10 @@ use crate::{motion::Motion, object::Object};
use anyhow::Result;
use collections::HashMap;
use command_palette_hooks::{CommandPaletteFilter, CommandPaletteInterceptor};
-use db::define_connection;
-use db::sqlez_macros::sql;
+use db::{
+ sqlez::{domain::Domain, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+};
use editor::display_map::{is_invisible, replacement};
use editor::{Anchor, ClipboardSelection, Editor, MultiBuffer, ToPoint as EditorToPoint};
use gpui::{
@@ -1668,8 +1670,12 @@ impl MarksView {
}
}
-define_connection! (
- pub static ref DB: VimDb<WorkspaceDb> = &[
+pub struct VimDb(ThreadSafeConnection);
+
+impl Domain for VimDb {
+ const NAME: &str = stringify!(VimDb);
+
+ const MIGRATIONS: &[&str] = &[
sql! (
CREATE TABLE vim_marks (
workspace_id INTEGER,
@@ -1689,7 +1695,9 @@ define_connection! (
ON vim_global_marks_paths(workspace_id, mark_name);
),
];
-);
+}
+
+db::static_connection!(DB, VimDb, [WorkspaceDb]);
struct SerializedMark {
path: Arc<Path>,
@@ -58,11 +58,7 @@ impl PathList {
let mut paths: Vec<PathBuf> = if serialized.paths.is_empty() {
Vec::new()
} else {
- serde_json::from_str::<Vec<PathBuf>>(&serialized.paths)
- .unwrap_or(Vec::new())
- .into_iter()
- .map(|s| SanitizedPath::from(s).into())
- .collect()
+ serialized.paths.split('\n').map(PathBuf::from).collect()
};
let mut order: Vec<usize> = serialized
@@ -85,7 +81,13 @@ impl PathList {
pub fn serialize(&self) -> SerializedPathList {
use std::fmt::Write as _;
- let paths = serde_json::to_string(&self.paths).unwrap_or_default();
+ let mut paths = String::new();
+ for path in self.paths.iter() {
+ if !paths.is_empty() {
+ paths.push('\n');
+ }
+ paths.push_str(&path.to_string_lossy());
+ }
let mut order = String::new();
for ix in self.order.iter() {
@@ -10,7 +10,11 @@ use std::{
use anyhow::{Context as _, Result, bail};
use collections::HashMap;
-use db::{define_connection, query, sqlez::connection::Connection, sqlez_macros::sql};
+use db::{
+ query,
+ sqlez::{connection::Connection, domain::Domain},
+ sqlez_macros::sql,
+};
use gpui::{Axis, Bounds, Task, WindowBounds, WindowId, point, size};
use project::debugger::breakpoint_store::{BreakpointState, SourceBreakpoint};
@@ -275,186 +279,189 @@ impl sqlez::bindable::Bind for SerializedPixels {
}
}
-define_connection! {
- pub static ref DB: WorkspaceDb<()> =
- &[
- sql!(
- CREATE TABLE workspaces(
- workspace_id INTEGER PRIMARY KEY,
- workspace_location BLOB UNIQUE,
- dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
- dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
- dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
- left_sidebar_open INTEGER, // Boolean
- timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
- FOREIGN KEY(dock_pane) REFERENCES panes(pane_id)
- ) STRICT;
-
- CREATE TABLE pane_groups(
- group_id INTEGER PRIMARY KEY,
- workspace_id INTEGER NOT NULL,
- parent_group_id INTEGER, // NULL indicates that this is a root node
- position INTEGER, // NULL indicates that this is a root node
- axis TEXT NOT NULL, // Enum: 'Vertical' / 'Horizontal'
- FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
- ON DELETE CASCADE
- ON UPDATE CASCADE,
- FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
- ) STRICT;
-
- CREATE TABLE panes(
- pane_id INTEGER PRIMARY KEY,
- workspace_id INTEGER NOT NULL,
- active INTEGER NOT NULL, // Boolean
- FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
- ON DELETE CASCADE
- ON UPDATE CASCADE
- ) STRICT;
-
- CREATE TABLE center_panes(
- pane_id INTEGER PRIMARY KEY,
- parent_group_id INTEGER, // NULL means that this is a root pane
- position INTEGER, // NULL means that this is a root pane
- FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
- ON DELETE CASCADE,
- FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
- ) STRICT;
-
- CREATE TABLE items(
- item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique
- workspace_id INTEGER NOT NULL,
- pane_id INTEGER NOT NULL,
- kind TEXT NOT NULL,
- position INTEGER NOT NULL,
- active INTEGER NOT NULL,
- FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
- ON DELETE CASCADE
- ON UPDATE CASCADE,
- FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
- ON DELETE CASCADE,
- PRIMARY KEY(item_id, workspace_id)
- ) STRICT;
- ),
- sql!(
- ALTER TABLE workspaces ADD COLUMN window_state TEXT;
- ALTER TABLE workspaces ADD COLUMN window_x REAL;
- ALTER TABLE workspaces ADD COLUMN window_y REAL;
- ALTER TABLE workspaces ADD COLUMN window_width REAL;
- ALTER TABLE workspaces ADD COLUMN window_height REAL;
- ALTER TABLE workspaces ADD COLUMN display BLOB;
- ),
- // Drop foreign key constraint from workspaces.dock_pane to panes table.
- sql!(
- CREATE TABLE workspaces_2(
- workspace_id INTEGER PRIMARY KEY,
- workspace_location BLOB UNIQUE,
- dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
- dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
- dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
- left_sidebar_open INTEGER, // Boolean
- timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
- window_state TEXT,
- window_x REAL,
- window_y REAL,
- window_width REAL,
- window_height REAL,
- display BLOB
- ) STRICT;
- INSERT INTO workspaces_2 SELECT * FROM workspaces;
- DROP TABLE workspaces;
- ALTER TABLE workspaces_2 RENAME TO workspaces;
- ),
- // Add panels related information
- sql!(
- ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool
- ALTER TABLE workspaces ADD COLUMN left_dock_active_panel TEXT;
- ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool
- ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;
- ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
- ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;
- ),
- // Add panel zoom persistence
- sql!(
- ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
- ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
- ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
- ),
- // Add pane group flex data
- sql!(
- ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
- ),
- // Add fullscreen field to workspace
- // Deprecated, `WindowBounds` holds the fullscreen state now.
- // Preserving so users can downgrade Zed.
- sql!(
- ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; //bool
- ),
- // Add preview field to items
- sql!(
- ALTER TABLE items ADD COLUMN preview INTEGER; //bool
- ),
- // Add centered_layout field to workspace
- sql!(
- ALTER TABLE workspaces ADD COLUMN centered_layout INTEGER; //bool
- ),
- sql!(
- CREATE TABLE remote_projects (
- remote_project_id INTEGER NOT NULL UNIQUE,
- path TEXT,
- dev_server_name TEXT
- );
- ALTER TABLE workspaces ADD COLUMN remote_project_id INTEGER;
- ALTER TABLE workspaces RENAME COLUMN workspace_location TO local_paths;
- ),
- sql!(
- DROP TABLE remote_projects;
- CREATE TABLE dev_server_projects (
- id INTEGER NOT NULL UNIQUE,
- path TEXT,
- dev_server_name TEXT
- );
- ALTER TABLE workspaces DROP COLUMN remote_project_id;
- ALTER TABLE workspaces ADD COLUMN dev_server_project_id INTEGER;
- ),
- sql!(
- ALTER TABLE workspaces ADD COLUMN local_paths_order BLOB;
- ),
- sql!(
- ALTER TABLE workspaces ADD COLUMN session_id TEXT DEFAULT NULL;
- ),
- sql!(
- ALTER TABLE workspaces ADD COLUMN window_id INTEGER DEFAULT NULL;
- ),
- sql!(
- ALTER TABLE panes ADD COLUMN pinned_count INTEGER DEFAULT 0;
- ),
- sql!(
- CREATE TABLE ssh_projects (
- id INTEGER PRIMARY KEY,
- host TEXT NOT NULL,
- port INTEGER,
- path TEXT NOT NULL,
- user TEXT
- );
- ALTER TABLE workspaces ADD COLUMN ssh_project_id INTEGER REFERENCES ssh_projects(id) ON DELETE CASCADE;
- ),
- sql!(
- ALTER TABLE ssh_projects RENAME COLUMN path TO paths;
- ),
- sql!(
- CREATE TABLE toolchains (
- workspace_id INTEGER,
- worktree_id INTEGER,
- language_name TEXT NOT NULL,
- name TEXT NOT NULL,
- path TEXT NOT NULL,
- PRIMARY KEY (workspace_id, worktree_id, language_name)
- );
- ),
- sql!(
- ALTER TABLE toolchains ADD COLUMN raw_json TEXT DEFAULT "{}";
- ),
- sql!(
+pub struct WorkspaceDb(ThreadSafeConnection);
+
+impl Domain for WorkspaceDb {
+ const NAME: &str = stringify!(WorkspaceDb);
+
+ const MIGRATIONS: &[&str] = &[
+ sql!(
+ CREATE TABLE workspaces(
+ workspace_id INTEGER PRIMARY KEY,
+ workspace_location BLOB UNIQUE,
+ dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+ dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
+ dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+ left_sidebar_open INTEGER, // Boolean
+ timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ FOREIGN KEY(dock_pane) REFERENCES panes(pane_id)
+ ) STRICT;
+
+ CREATE TABLE pane_groups(
+ group_id INTEGER PRIMARY KEY,
+ workspace_id INTEGER NOT NULL,
+ parent_group_id INTEGER, // NULL indicates that this is a root node
+ position INTEGER, // NULL indicates that this is a root node
+ axis TEXT NOT NULL, // Enum: 'Vertical' / 'Horizontal'
+ FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+ ) STRICT;
+
+ CREATE TABLE panes(
+ pane_id INTEGER PRIMARY KEY,
+ workspace_id INTEGER NOT NULL,
+ active INTEGER NOT NULL, // Boolean
+ FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE
+ ) STRICT;
+
+ CREATE TABLE center_panes(
+ pane_id INTEGER PRIMARY KEY,
+ parent_group_id INTEGER, // NULL means that this is a root pane
+ position INTEGER, // NULL means that this is a root pane
+ FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
+ ON DELETE CASCADE,
+ FOREIGN KEY(parent_group_id) REFERENCES pane_groups(group_id) ON DELETE CASCADE
+ ) STRICT;
+
+ CREATE TABLE items(
+ item_id INTEGER NOT NULL, // This is the item's view id, so this is not unique
+ workspace_id INTEGER NOT NULL,
+ pane_id INTEGER NOT NULL,
+ kind TEXT NOT NULL,
+ position INTEGER NOT NULL,
+ active INTEGER NOT NULL,
+ FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
+ ON DELETE CASCADE
+ ON UPDATE CASCADE,
+ FOREIGN KEY(pane_id) REFERENCES panes(pane_id)
+ ON DELETE CASCADE,
+ PRIMARY KEY(item_id, workspace_id)
+ ) STRICT;
+ ),
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN window_state TEXT;
+ ALTER TABLE workspaces ADD COLUMN window_x REAL;
+ ALTER TABLE workspaces ADD COLUMN window_y REAL;
+ ALTER TABLE workspaces ADD COLUMN window_width REAL;
+ ALTER TABLE workspaces ADD COLUMN window_height REAL;
+ ALTER TABLE workspaces ADD COLUMN display BLOB;
+ ),
+ // Drop foreign key constraint from workspaces.dock_pane to panes table.
+ sql!(
+ CREATE TABLE workspaces_2(
+ workspace_id INTEGER PRIMARY KEY,
+ workspace_location BLOB UNIQUE,
+ dock_visible INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+ dock_anchor TEXT, // Deprecated. Preserving so users can downgrade Zed.
+ dock_pane INTEGER, // Deprecated. Preserving so users can downgrade Zed.
+ left_sidebar_open INTEGER, // Boolean
+ timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ window_state TEXT,
+ window_x REAL,
+ window_y REAL,
+ window_width REAL,
+ window_height REAL,
+ display BLOB
+ ) STRICT;
+ INSERT INTO workspaces_2 SELECT * FROM workspaces;
+ DROP TABLE workspaces;
+ ALTER TABLE workspaces_2 RENAME TO workspaces;
+ ),
+ // Add panels related information
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN left_dock_visible INTEGER; //bool
+ ALTER TABLE workspaces ADD COLUMN left_dock_active_panel TEXT;
+ ALTER TABLE workspaces ADD COLUMN right_dock_visible INTEGER; //bool
+ ALTER TABLE workspaces ADD COLUMN right_dock_active_panel TEXT;
+ ALTER TABLE workspaces ADD COLUMN bottom_dock_visible INTEGER; //bool
+ ALTER TABLE workspaces ADD COLUMN bottom_dock_active_panel TEXT;
+ ),
+ // Add panel zoom persistence
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN left_dock_zoom INTEGER; //bool
+ ALTER TABLE workspaces ADD COLUMN right_dock_zoom INTEGER; //bool
+ ALTER TABLE workspaces ADD COLUMN bottom_dock_zoom INTEGER; //bool
+ ),
+ // Add pane group flex data
+ sql!(
+ ALTER TABLE pane_groups ADD COLUMN flexes TEXT;
+ ),
+ // Add fullscreen field to workspace
+ // Deprecated, `WindowBounds` holds the fullscreen state now.
+ // Preserving so users can downgrade Zed.
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN fullscreen INTEGER; //bool
+ ),
+ // Add preview field to items
+ sql!(
+ ALTER TABLE items ADD COLUMN preview INTEGER; //bool
+ ),
+ // Add centered_layout field to workspace
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN centered_layout INTEGER; //bool
+ ),
+ sql!(
+ CREATE TABLE remote_projects (
+ remote_project_id INTEGER NOT NULL UNIQUE,
+ path TEXT,
+ dev_server_name TEXT
+ );
+ ALTER TABLE workspaces ADD COLUMN remote_project_id INTEGER;
+ ALTER TABLE workspaces RENAME COLUMN workspace_location TO local_paths;
+ ),
+ sql!(
+ DROP TABLE remote_projects;
+ CREATE TABLE dev_server_projects (
+ id INTEGER NOT NULL UNIQUE,
+ path TEXT,
+ dev_server_name TEXT
+ );
+ ALTER TABLE workspaces DROP COLUMN remote_project_id;
+ ALTER TABLE workspaces ADD COLUMN dev_server_project_id INTEGER;
+ ),
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN local_paths_order BLOB;
+ ),
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN session_id TEXT DEFAULT NULL;
+ ),
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN window_id INTEGER DEFAULT NULL;
+ ),
+ sql!(
+ ALTER TABLE panes ADD COLUMN pinned_count INTEGER DEFAULT 0;
+ ),
+ sql!(
+ CREATE TABLE ssh_projects (
+ id INTEGER PRIMARY KEY,
+ host TEXT NOT NULL,
+ port INTEGER,
+ path TEXT NOT NULL,
+ user TEXT
+ );
+ ALTER TABLE workspaces ADD COLUMN ssh_project_id INTEGER REFERENCES ssh_projects(id) ON DELETE CASCADE;
+ ),
+ sql!(
+ ALTER TABLE ssh_projects RENAME COLUMN path TO paths;
+ ),
+ sql!(
+ CREATE TABLE toolchains (
+ workspace_id INTEGER,
+ worktree_id INTEGER,
+ language_name TEXT NOT NULL,
+ name TEXT NOT NULL,
+ path TEXT NOT NULL,
+ PRIMARY KEY (workspace_id, worktree_id, language_name)
+ );
+ ),
+ sql!(
+ ALTER TABLE toolchains ADD COLUMN raw_json TEXT DEFAULT "{}";
+ ),
+ sql!(
CREATE TABLE breakpoints (
workspace_id INTEGER NOT NULL,
path TEXT NOT NULL,
@@ -466,141 +473,165 @@ define_connection! {
ON UPDATE CASCADE
);
),
- sql!(
- ALTER TABLE workspaces ADD COLUMN local_paths_array TEXT;
- CREATE UNIQUE INDEX local_paths_array_uq ON workspaces(local_paths_array);
- ALTER TABLE workspaces ADD COLUMN local_paths_order_array TEXT;
- ),
- sql!(
- ALTER TABLE breakpoints ADD COLUMN state INTEGER DEFAULT(0) NOT NULL
- ),
- sql!(
- ALTER TABLE breakpoints DROP COLUMN kind
- ),
- sql!(ALTER TABLE toolchains ADD COLUMN relative_worktree_path TEXT DEFAULT "" NOT NULL),
- sql!(
- ALTER TABLE breakpoints ADD COLUMN condition TEXT;
- ALTER TABLE breakpoints ADD COLUMN hit_condition TEXT;
- ),
- sql!(CREATE TABLE toolchains2 (
- workspace_id INTEGER,
- worktree_id INTEGER,
- language_name TEXT NOT NULL,
- name TEXT NOT NULL,
- path TEXT NOT NULL,
- raw_json TEXT NOT NULL,
- relative_worktree_path TEXT NOT NULL,
- PRIMARY KEY (workspace_id, worktree_id, language_name, relative_worktree_path)) STRICT;
- INSERT INTO toolchains2
- SELECT * FROM toolchains;
- DROP TABLE toolchains;
- ALTER TABLE toolchains2 RENAME TO toolchains;
- ),
- sql!(
- CREATE TABLE ssh_connections (
- id INTEGER PRIMARY KEY,
- host TEXT NOT NULL,
- port INTEGER,
- user TEXT
- );
+ sql!(
+ ALTER TABLE workspaces ADD COLUMN local_paths_array TEXT;
+ CREATE UNIQUE INDEX local_paths_array_uq ON workspaces(local_paths_array);
+ ALTER TABLE workspaces ADD COLUMN local_paths_order_array TEXT;
+ ),
+ sql!(
+ ALTER TABLE breakpoints ADD COLUMN state INTEGER DEFAULT(0) NOT NULL
+ ),
+ sql!(
+ ALTER TABLE breakpoints DROP COLUMN kind
+ ),
+ sql!(ALTER TABLE toolchains ADD COLUMN relative_worktree_path TEXT DEFAULT "" NOT NULL),
+ sql!(
+ ALTER TABLE breakpoints ADD COLUMN condition TEXT;
+ ALTER TABLE breakpoints ADD COLUMN hit_condition TEXT;
+ ),
+ sql!(CREATE TABLE toolchains2 (
+ workspace_id INTEGER,
+ worktree_id INTEGER,
+ language_name TEXT NOT NULL,
+ name TEXT NOT NULL,
+ path TEXT NOT NULL,
+ raw_json TEXT NOT NULL,
+ relative_worktree_path TEXT NOT NULL,
+ PRIMARY KEY (workspace_id, worktree_id, language_name, relative_worktree_path)) STRICT;
+ INSERT INTO toolchains2
+ SELECT * FROM toolchains;
+ DROP TABLE toolchains;
+ ALTER TABLE toolchains2 RENAME TO toolchains;
+ ),
+ sql!(
+ CREATE TABLE ssh_connections (
+ id INTEGER PRIMARY KEY,
+ host TEXT NOT NULL,
+ port INTEGER,
+ user TEXT
+ );
- INSERT INTO ssh_connections (host, port, user)
- SELECT DISTINCT host, port, user
- FROM ssh_projects;
-
- CREATE TABLE workspaces_2(
- workspace_id INTEGER PRIMARY KEY,
- paths TEXT,
- paths_order TEXT,
- ssh_connection_id INTEGER REFERENCES ssh_connections(id),
- timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
- window_state TEXT,
- window_x REAL,
- window_y REAL,
- window_width REAL,
- window_height REAL,
- display BLOB,
- left_dock_visible INTEGER,
- left_dock_active_panel TEXT,
- right_dock_visible INTEGER,
- right_dock_active_panel TEXT,
- bottom_dock_visible INTEGER,
- bottom_dock_active_panel TEXT,
- left_dock_zoom INTEGER,
- right_dock_zoom INTEGER,
- bottom_dock_zoom INTEGER,
- fullscreen INTEGER,
- centered_layout INTEGER,
- session_id TEXT,
- window_id INTEGER
- ) STRICT;
-
- INSERT
- INTO workspaces_2
- SELECT
- workspaces.workspace_id,
- CASE
- WHEN ssh_projects.id IS NOT NULL THEN ssh_projects.paths
+ INSERT INTO ssh_connections (host, port, user)
+ SELECT DISTINCT host, port, user
+ FROM ssh_projects;
+
+ CREATE TABLE workspaces_2(
+ workspace_id INTEGER PRIMARY KEY,
+ paths TEXT,
+ paths_order TEXT,
+ ssh_connection_id INTEGER REFERENCES ssh_connections(id),
+ timestamp TEXT DEFAULT CURRENT_TIMESTAMP NOT NULL,
+ window_state TEXT,
+ window_x REAL,
+ window_y REAL,
+ window_width REAL,
+ window_height REAL,
+ display BLOB,
+ left_dock_visible INTEGER,
+ left_dock_active_panel TEXT,
+ right_dock_visible INTEGER,
+ right_dock_active_panel TEXT,
+ bottom_dock_visible INTEGER,
+ bottom_dock_active_panel TEXT,
+ left_dock_zoom INTEGER,
+ right_dock_zoom INTEGER,
+ bottom_dock_zoom INTEGER,
+ fullscreen INTEGER,
+ centered_layout INTEGER,
+ session_id TEXT,
+ window_id INTEGER
+ ) STRICT;
+
+ INSERT
+ INTO workspaces_2
+ SELECT
+ workspaces.workspace_id,
+ CASE
+ WHEN ssh_projects.id IS NOT NULL THEN ssh_projects.paths
+ ELSE
+ CASE
+ WHEN workspaces.local_paths_array IS NULL OR workspaces.local_paths_array = "" THEN
+ NULL
+ ELSE
+ replace(workspaces.local_paths_array, ',', "\n")
+ END
+ END as paths,
+
+ CASE
+ WHEN ssh_projects.id IS NOT NULL THEN ""
+ ELSE workspaces.local_paths_order_array
+ END as paths_order,
+
+ CASE
+ WHEN ssh_projects.id IS NOT NULL THEN (
+ SELECT ssh_connections.id
+ FROM ssh_connections
+ WHERE
+ ssh_connections.host IS ssh_projects.host AND
+ ssh_connections.port IS ssh_projects.port AND
+ ssh_connections.user IS ssh_projects.user
+ )
+ ELSE NULL
+ END as ssh_connection_id,
+
+ workspaces.timestamp,
+ workspaces.window_state,
+ workspaces.window_x,
+ workspaces.window_y,
+ workspaces.window_width,
+ workspaces.window_height,
+ workspaces.display,
+ workspaces.left_dock_visible,
+ workspaces.left_dock_active_panel,
+ workspaces.right_dock_visible,
+ workspaces.right_dock_active_panel,
+ workspaces.bottom_dock_visible,
+ workspaces.bottom_dock_active_panel,
+ workspaces.left_dock_zoom,
+ workspaces.right_dock_zoom,
+ workspaces.bottom_dock_zoom,
+ workspaces.fullscreen,
+ workspaces.centered_layout,
+ workspaces.session_id,
+ workspaces.window_id
+ FROM
+ workspaces LEFT JOIN
+ ssh_projects ON
+ workspaces.ssh_project_id = ssh_projects.id;
+
+ DROP TABLE ssh_projects;
+ DROP TABLE workspaces;
+ ALTER TABLE workspaces_2 RENAME TO workspaces;
+
+ CREATE UNIQUE INDEX ix_workspaces_location ON workspaces(ssh_connection_id, paths);
+ ),
+ // Fix any data from when workspaces.paths were briefly encoded as JSON arrays
+ sql!(
+ UPDATE workspaces
+ SET paths = CASE
+ WHEN substr(paths, 1, 2) = '[' || '"' AND substr(paths, -2, 2) = '"' || ']' THEN
+ replace(
+ substr(paths, 3, length(paths) - 4),
+ '"' || ',' || '"',
+ CHAR(10)
+ )
ELSE
- CASE
- WHEN workspaces.local_paths_array IS NULL OR workspaces.local_paths_array = "" THEN
- NULL
- ELSE
- json('[' || '"' || replace(workspaces.local_paths_array, ',', '"' || "," || '"') || '"' || ']')
- END
- END as paths,
-
- CASE
- WHEN ssh_projects.id IS NOT NULL THEN ""
- ELSE workspaces.local_paths_order_array
- END as paths_order,
-
- CASE
- WHEN ssh_projects.id IS NOT NULL THEN (
- SELECT ssh_connections.id
- FROM ssh_connections
- WHERE
- ssh_connections.host IS ssh_projects.host AND
- ssh_connections.port IS ssh_projects.port AND
- ssh_connections.user IS ssh_projects.user
- )
- ELSE NULL
- END as ssh_connection_id,
-
- workspaces.timestamp,
- workspaces.window_state,
- workspaces.window_x,
- workspaces.window_y,
- workspaces.window_width,
- workspaces.window_height,
- workspaces.display,
- workspaces.left_dock_visible,
- workspaces.left_dock_active_panel,
- workspaces.right_dock_visible,
- workspaces.right_dock_active_panel,
- workspaces.bottom_dock_visible,
- workspaces.bottom_dock_active_panel,
- workspaces.left_dock_zoom,
- workspaces.right_dock_zoom,
- workspaces.bottom_dock_zoom,
- workspaces.fullscreen,
- workspaces.centered_layout,
- workspaces.session_id,
- workspaces.window_id
- FROM
- workspaces LEFT JOIN
- ssh_projects ON
- workspaces.ssh_project_id = ssh_projects.id;
-
- DROP TABLE ssh_projects;
- DROP TABLE workspaces;
- ALTER TABLE workspaces_2 RENAME TO workspaces;
-
- CREATE UNIQUE INDEX ix_workspaces_location ON workspaces(ssh_connection_id, paths);
- ),
+ replace(paths, ',', CHAR(10))
+ END
+ WHERE paths IS NOT NULL
+ ),
];
+
+ // Allow recovering from bad migration that was initially shipped to nightly
+ // when introducing the ssh_connections table.
+ fn should_allow_migration_change(_index: usize, old: &str, new: &str) -> bool {
+ old.starts_with("CREATE TABLE ssh_connections")
+ && new.starts_with("CREATE TABLE ssh_connections")
+ }
}
+db::static_connection!(DB, WorkspaceDb, []);
+
impl WorkspaceDb {
/// Returns a serialized workspace for the given worktree_roots. If the passed array
/// is empty, the most recent workspace is returned instead. If no workspace for the
@@ -1803,6 +1834,7 @@ mod tests {
ON DELETE CASCADE
) STRICT;
)],
+ |_, _, _| false,
)
.unwrap();
})
@@ -1851,6 +1883,7 @@ mod tests {
REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;)],
+ |_, _, _| false,
)
})
.await
@@ -1,10 +1,17 @@
use anyhow::Result;
-use db::{define_connection, query, sqlez::statement::Statement, sqlez_macros::sql};
+use db::{
+ query,
+ sqlez::{domain::Domain, statement::Statement, thread_safe_connection::ThreadSafeConnection},
+ sqlez_macros::sql,
+};
use workspace::{ItemId, WorkspaceDb, WorkspaceId};
-define_connection! {
- pub static ref COMPONENT_PREVIEW_DB: ComponentPreviewDb<WorkspaceDb> =
- &[sql!(
+pub struct ComponentPreviewDb(ThreadSafeConnection);
+
+impl Domain for ComponentPreviewDb {
+ const NAME: &str = stringify!(ComponentPreviewDb);
+
+ const MIGRATIONS: &[&str] = &[sql!(
CREATE TABLE component_previews (
workspace_id INTEGER,
item_id INTEGER UNIQUE,
@@ -13,9 +20,11 @@ define_connection! {
FOREIGN KEY(workspace_id) REFERENCES workspaces(workspace_id)
ON DELETE CASCADE
) STRICT;
- )];
+ )];
}
+db::static_connection!(COMPONENT_PREVIEW_DB, ComponentPreviewDb, [WorkspaceDb]);
+
impl ComponentPreviewDb {
pub async fn save_active_page(
&self,