snapshot of https://github.com/toeverything/AFFiNE.git (canary, 2026-09-16; blobs > 131072B omitted)
Some checks failed
Build & Test / Analyze (javascript, affine) (push) Failing after 4s
Build & Test / Analyze (typescript, affine) (push) Failing after 3s
Build & Test / Rust test filter (push) Failing after 4s
Build & Test / Check yarn binary (push) Failing after 3s
Build & Test / E2E BlockSuite Test (3) (push) Failing after 3s
Build & Test / Analyze (javascript, blocksuite) (push) Failing after 3s
Build & Test / Analyze (typescript, blocksuite) (push) Failing after 3s
Build & Test / Mobile native build filter (push) Failing after 3s
Build & Test / E2E BlockSuite Test (1) (push) Failing after 4s
Build & Test / E2E BlockSuite Test (2) (push) Failing after 4s
Build & Test / E2E Mobile Test (1) (push) Failing after 3s
Build & Test / Unit Test (1) (push) Has been skipped
Build & Test / Unit Test (2) (push) Has been skipped
Build & Test / Unit Test (3) (push) Has been skipped
Build & Test / Unit Test (4) (push) Has been skipped
Build & Test / Unit Test (5) (push) Has been skipped
Build & Test / Native Unit Test (push) Has been skipped
Build & Test / E2E BlockSuite Test (4) (push) Failing after 3s
Build & Test / E2E BlockSuite Test (5) (push) Failing after 3s
Build & Test / E2E BlockSuite Cross Browser Test (push) Failing after 3s
Build & Test / E2E Mobile Test (2) (push) Failing after 3s
Build & Test / Build AFFiNE native (x86_64-unknown-linux-gnu) (push) Failing after 3s
Build & Test / Build Server native (push) Failing after 3s
Build & Test / Check Git Status (push) Has been skipped
Build & Test / Server Test (0, 4) (push) Has been skipped
Build & Test / Server Test (1, 4) (push) Has been skipped
Build & Test / Server Test (2, 4) (push) Has been skipped
Build & Test / Server Test (3, 4) (push) Has been skipped
Build & Test / Server Test with Elasticsearch (push) Has been skipped
Build & Test / Build @affine/electron renderer (push) Failing after 3s
Build & Test / Copilot test filter (push) Failing after 4s
Build & Test / Lint Rust (push) Has been skipped
Build & Test / Server Test with Embedded Search (push) Has been skipped
Build & Test / Server Test with Manticore Search (push) Has been skipped
Build & Test / Server E2E Test (push) Has been skipped
Build & Test / Cloud Desktop E2E Test (push) Has been skipped
Build & Test / Cloud E2E Test 1/2 (push) Has been skipped
Build & Test / Cloud E2E Test 2/2 (push) Has been skipped
Build & Test / Build Android app (push) Has been skipped
Build & Test / Test Android app (push) Has been skipped
Build & Test / Run native tests (push) Has been skipped
Build & Test / Server Copilot Api Test (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (1, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (2, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (3, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (4, 5) (push) Has been skipped
Build & Test / Frontend Copilot E2E Test (5, 5) (push) Has been skipped
Release / Canary Gate (push) Has been skipped
Build & Test / Build AFFiNE native (aarch64-apple-darwin) (push) Has been cancelled
Build & Test / Lint (push) Has been cancelled
Build & Test / Typecheck (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-apple-darwin) (push) Has been cancelled
Build & Test / Desktop Test (ubuntu-latest, linux, x64, x86_64-unknown-linux-gnu, true) (push) Has been cancelled
Build & Test / Desktop Test (windows-latest, windows, x64, x86_64-pc-windows-msvc, true) (push) Has been cancelled
Build & Test / E2E Test (1) (push) Has been cancelled
Build & Test / E2E Test (2) (push) Has been cancelled
Build & Test / E2E Test (3) (push) Has been cancelled
Build & Test / E2E Test (4) (push) Has been cancelled
Build & Test / E2E Test (5) (push) Has been cancelled
Build & Test / Build AFFiNE native (aarch64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Build AFFiNE native (x86_64-pc-windows-msvc) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, arm64, aarch64-apple-darwin, true) (push) Has been cancelled
Build & Test / Desktop Test (macos-latest, macos, x64, x86_64-apple-darwin, false) (push) Has been cancelled
Build & Test / 3, 2, 1 Launch (push) Has been cancelled
Build & Test / Build iOS app (push) Has been cancelled
Copilot Test Automatically / Setup Test (push) Failing after 1s
Release / Prepare (push) Failing after 2s
Release / Release Cloud (push) Has been skipped
Release / Release Desktop (push) Has been skipped
Release / Release Docker Image (push) Has been skipped
Release / Release Mobile (push) Has been skipped

This commit is contained in:
upstream-mirror
2026-09-16 04:01:20 +05:00
commit 702cc698a7
9990 changed files with 1073618 additions and 0 deletions

View File

@@ -0,0 +1,8 @@
[package]
edition = "2024"
name = "affine_schema"
publish = false
version = "0.0.0"
[dependencies]
sqlx = { workspace = true, default-features = false, features = ["migrate"] }

View File

@@ -0,0 +1 @@
A temporary crate to share the schema between AFFiNE native and `build.rs` in the AFFiNE native.

View File

@@ -0,0 +1,261 @@
use std::collections::BTreeSet;
use sqlx::{Pool, Row, sqlite::Sqlite};
pub struct ImportSchemaRules {
pub tables: &'static [ImportTableRule],
pub indexes: &'static [ImportIndexRule],
}
pub struct ImportTableRule {
pub name: &'static str,
pub columns: &'static [&'static str],
pub enforce_columns: bool,
pub required: bool,
}
pub struct ImportIndexRule {
pub name: &'static str,
pub table: &'static str,
pub columns: &'static [&'static str],
pub required: bool,
}
pub const V2_IMPORT_SCHEMA_RULES: ImportSchemaRules = ImportSchemaRules {
tables: &[
ImportTableRule {
name: "meta",
columns: &["space_id"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "snapshots",
columns: &["doc_id", "data", "created_at", "updated_at"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "updates",
columns: &["doc_id", "created_at", "data"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "clocks",
columns: &["doc_id", "timestamp"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "blobs",
columns: &["key", "data", "mime", "size", "created_at", "deleted_at"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "peer_clocks",
columns: &["peer", "doc_id", "remote_clock", "pulled_remote_clock", "pushed_clock"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "peer_blob_sync",
columns: &["peer", "blob_id", "uploaded_at"],
enforce_columns: true,
required: false,
},
ImportTableRule {
name: "idx_snapshots",
columns: &["index_name", "data", "created_at"],
enforce_columns: true,
required: false,
},
ImportTableRule {
name: "indexer_sync",
columns: &["doc_id", "indexed_clock", "indexer_version"],
enforce_columns: true,
required: false,
},
ImportTableRule {
name: "_sqlx_migrations",
columns: &[],
enforce_columns: false,
required: false,
},
],
indexes: &[
ImportIndexRule {
name: "peer_clocks_doc_id",
table: "peer_clocks",
columns: &["doc_id"],
required: true,
},
ImportIndexRule {
name: "peer_blob_sync_peer",
table: "peer_blob_sync",
columns: &["peer"],
required: false,
},
],
};
pub const V1_IMPORT_SCHEMA_RULES: ImportSchemaRules = ImportSchemaRules {
tables: &[
ImportTableRule {
name: "updates",
columns: &["id", "timestamp", "data", "doc_id"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "blobs",
columns: &["key", "data", "timestamp"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "version_info",
columns: &["version", "timestamp"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "server_clock",
columns: &["key", "data", "timestamp"],
enforce_columns: true,
required: true,
},
ImportTableRule {
name: "sync_metadata",
columns: &["key", "data", "timestamp"],
enforce_columns: true,
required: true,
},
],
indexes: &[ImportIndexRule {
name: "idx_doc_id",
table: "updates",
columns: &["doc_id"],
required: false,
}],
};
pub async fn validate_import_schema(pool: &Pool<Sqlite>, rules: &ImportSchemaRules) -> sqlx::Result<bool> {
validate_schema(pool, rules, ValidationMode::Strict).await
}
pub async fn validate_required_schema(pool: &Pool<Sqlite>, rules: &ImportSchemaRules) -> sqlx::Result<bool> {
validate_schema(pool, rules, ValidationMode::RequiredOnly).await
}
#[derive(Clone, Copy)]
enum ValidationMode {
Strict,
RequiredOnly,
}
async fn validate_schema(pool: &Pool<Sqlite>, rules: &ImportSchemaRules, mode: ValidationMode) -> sqlx::Result<bool> {
let query = match mode {
ValidationMode::Strict => {
"SELECT type, name, tbl_name FROM sqlite_schema WHERE type IN ('table', 'index', 'view', 'trigger')"
}
ValidationMode::RequiredOnly => "SELECT type, name, tbl_name FROM sqlite_schema WHERE type IN ('table', 'index')",
};
let rows = sqlx::query(query).fetch_all(pool).await?;
let mut seen_tables = BTreeSet::new();
let mut seen_indexes = BTreeSet::new();
for row in rows {
let object_type: String = row.try_get("type")?;
let name: String = row.try_get("name")?;
let table_name: String = row.try_get("tbl_name")?;
if name.starts_with("sqlite_") {
continue;
}
match object_type.as_str() {
"table" => {
let Some(rule) = rules.tables.iter().find(|rule| rule.name == name) else {
if matches!(mode, ValidationMode::Strict) {
return Ok(false);
}
continue;
};
if rule.enforce_columns && !table_columns_match(pool, rule).await? {
return Ok(false);
}
seen_tables.insert(name);
}
"index" => {
let Some(rule) = rules
.indexes
.iter()
.find(|rule| rule.name == name && rule.table == table_name)
else {
if matches!(mode, ValidationMode::Strict) {
return Ok(false);
}
continue;
};
if !index_columns_match(pool, rule).await? {
return Ok(false);
}
seen_indexes.insert(name);
}
"view" | "trigger" => return Ok(false),
_ => return Ok(false),
}
}
if rules
.tables
.iter()
.filter(|rule| rule.required)
.any(|rule| !seen_tables.contains(rule.name))
{
return Ok(false);
}
if rules
.indexes
.iter()
.filter(|rule| rule.required)
.any(|rule| !seen_indexes.contains(rule.name))
{
return Ok(false);
}
Ok(true)
}
async fn table_columns_match(pool: &Pool<Sqlite>, rule: &ImportTableRule) -> sqlx::Result<bool> {
let pragma = format!("PRAGMA table_info(\"{}\")", rule.name);
let rows = sqlx::query(&pragma).fetch_all(pool).await?;
let columns = rows
.into_iter()
.map(|row| row.try_get::<String, _>("name"))
.collect::<std::result::Result<BTreeSet<_>, _>>()?;
Ok(columns == rule.columns.iter().map(|column| (*column).to_string()).collect())
}
async fn index_columns_match(pool: &Pool<Sqlite>, rule: &ImportIndexRule) -> sqlx::Result<bool> {
let pragma = format!("PRAGMA index_info(\"{}\")", rule.name);
let rows = sqlx::query(&pragma).fetch_all(pool).await?;
let columns = rows
.into_iter()
.map(|row| row.try_get::<String, _>("name"))
.collect::<std::result::Result<Vec<_>, _>>()?;
Ok(
columns
== rule
.columns
.iter()
.map(|column| (*column).to_string())
.collect::<Vec<_>>(),
)
}

View File

@@ -0,0 +1,135 @@
use std::borrow::Cow;
use sqlx::migrate::{Migration, MigrationType, Migrator};
pub mod import_validation;
pub mod v1;
type SimpleMigration = (
/* name */ &'static str,
/* up */ &'static str,
/* down */ Option<&'static str>,
);
// ORDER MATTERS
const MIGRATIONS: &[SimpleMigration] = &[
// v2 db init
(
"init_v2",
r#"
CREATE TABLE "meta" (
space_id VARCHAR PRIMARY KEY NOT NULL
);
CREATE TABLE "snapshots" (
doc_id VARCHAR PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
updated_at TIMESTAMP NOT NULL
);
CREATE TABLE "updates" (
doc_id VARCHAR NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
data BLOB NOT NULL,
PRIMARY KEY (doc_id, created_at)
);
CREATE TABLE "clocks" (
doc_id VARCHAR PRIMARY KEY NOT NULL,
timestamp TIMESTAMP NOT NULL
);
CREATE TABLE "blobs" (
key VARCHAR PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
mime VARCHAR NOT NULL,
size INTEGER NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
deleted_at TIMESTAMP
);
CREATE TABLE "peer_clocks" (
peer VARCHAR NOT NULL,
doc_id VARCHAR NOT NULL,
remote_clock TIMESTAMP NOT NULL DEFAULT 0,
pulled_remote_clock TIMESTAMP NOT NULL DEFAULT 0,
pushed_clock TIMESTAMP NOT NULL DEFAULT 0,
PRIMARY KEY (peer, doc_id)
);
CREATE INDEX peer_clocks_doc_id ON peer_clocks (doc_id);
"#,
None,
),
// add blob_sync table
(
"add_blob_sync",
r#"
CREATE TABLE "peer_blob_sync" (
peer VARCHAR NOT NULL,
blob_id VARCHAR NOT NULL,
uploaded_at TIMESTAMP,
PRIMARY KEY (peer, blob_id)
);
CREATE INDEX peer_blob_sync_peer ON peer_blob_sync (peer);
"#,
None,
),
// add idx snapshots
(
"add_idx_snapshots",
r#"
CREATE TABLE idx_snapshots (
index_name TEXT PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
"#,
None,
),
// add indexer sync table
(
"add_indexer_sync",
r#"
CREATE TABLE "indexer_sync" (
doc_id VARCHAR PRIMARY KEY NOT NULL,
indexed_clock TIMESTAMP NOT NULL DEFAULT 0,
indexer_version INTEGER NOT NULL DEFAULT 0
);
"#,
None,
),
];
pub fn get_migrator() -> Migrator {
let mut migrations = vec![];
MIGRATIONS.iter().for_each(|&(name, up, down)| {
migrations.push(Migration::new(
migrations.len() as i64 + 1,
Cow::from(name),
if down.is_some() {
MigrationType::ReversibleUp
} else {
MigrationType::Simple
},
Cow::from(up),
false,
));
if let Some(down) = down {
migrations.push(Migration::new(
migrations.len() as i64 + 1,
Cow::from(name),
MigrationType::ReversibleDown,
Cow::from(down),
false,
));
}
});
Migrator {
migrations: Cow::Owned(migrations),
..Migrator::DEFAULT
}
}

View File

@@ -0,0 +1,26 @@
pub const SCHEMA: &str = r#"CREATE TABLE IF NOT EXISTS "updates" (
id INTEGER PRIMARY KEY AUTOINCREMENT,
data BLOB NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL,
doc_id TEXT
);
CREATE TABLE IF NOT EXISTS "blobs" (
key TEXT PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS "version_info" (
version NUMBER NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS "server_clock" (
key TEXT PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
);
CREATE TABLE IF NOT EXISTS "sync_metadata" (
key TEXT PRIMARY KEY NOT NULL,
data BLOB NOT NULL,
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL
)
"#;