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