mirror of
https://github.com/stalwartlabs/mail-server.git
synced 2025-12-09 04:46:30 +08:00
Clarify breaking changes (closes #2332)
Some checks failed
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
Some checks failed
Scorecard supply-chain security / Scorecard analysis (push) Has been cancelled
This commit is contained in:
parent
de7f0e8e37
commit
7ac984e1c0
4 changed files with 9 additions and 205 deletions
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
All notable changes to this project will be documented in this file. This project adheres to [Semantic Versioning](http://semver.org/).
|
||||
|
||||
## [0.14.1] - 2025-10-27
|
||||
## [0.14.1] - 2025-10-28
|
||||
|
||||
If you are upgrading from v0.13.4 and below, this version includes **breaking changes** to the internal directory, calendar and contacts. Please read the [upgrading documentation](https://stalw.art/docs/install/upgrade) for more information on how to upgrade from previous versions.
|
||||
|
||||
|
|
@ -30,9 +30,15 @@ If you are upgrading from v0.13.4 and below, this version includes **breaking ch
|
|||
- i18n: Swedish language support (contributed by @purung)
|
||||
|
||||
## Changed
|
||||
- **Breaking Database Changes** (migrated automatically on first start):
|
||||
- Internal directory schema changed.
|
||||
- Calendar and Contacts storage schema changed.
|
||||
- Sieve scripts storage schema changed.
|
||||
- Push Subscriptions storage schema changed.
|
||||
- Replaced `sieve.untrusted.limits.max-scripts` and `jmap.push.max-total` with `object-quota.*` settings.
|
||||
- Cluster node roles now allow sharding.
|
||||
|
||||
|
||||
## Fixed
|
||||
- Push Subscription: Clean-up of expired subscriptions and cluster notification of changes (#1248)
|
||||
- CalDAV: Per-user CalDAV properties (#2058)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ use rkyv::{
|
|||
use std::{borrow::Cow, fmt::Debug};
|
||||
use store::{
|
||||
Serialize, SerializeInfallible,
|
||||
write::{Archive, Archiver, BatchBuilder, BlobOp, DirectoryClass, IntoOperations, TagValue},
|
||||
write::{Archive, Archiver, BatchBuilder, BlobOp, DirectoryClass, IntoOperations},
|
||||
};
|
||||
use types::{
|
||||
acl::AclGrant,
|
||||
|
|
@ -34,10 +34,6 @@ pub enum IndexValue<'x> {
|
|||
field: Field,
|
||||
value: Vec<IndexItem<'x>>,
|
||||
},
|
||||
Tag {
|
||||
field: Field,
|
||||
value: Vec<TagValue>,
|
||||
},
|
||||
Blob {
|
||||
value: BlobHash,
|
||||
},
|
||||
|
|
@ -379,15 +375,6 @@ fn build_index(
|
|||
}
|
||||
}
|
||||
}
|
||||
IndexValue::Tag { field, value } => {
|
||||
for item in value {
|
||||
if set {
|
||||
batch.tag(field, item);
|
||||
} else {
|
||||
batch.untag(field, item);
|
||||
}
|
||||
}
|
||||
}
|
||||
IndexValue::Blob { value } => {
|
||||
if set {
|
||||
batch.set(BlobOp::Link { hash: value }, vec![]);
|
||||
|
|
@ -524,27 +511,6 @@ fn merge_index(
|
|||
batch.unindex(field, value.into_owned());
|
||||
}
|
||||
}
|
||||
(
|
||||
IndexValue::Tag {
|
||||
field,
|
||||
value: old_value,
|
||||
},
|
||||
IndexValue::Tag {
|
||||
value: new_value, ..
|
||||
},
|
||||
) => {
|
||||
for old_tag in &old_value {
|
||||
if !new_value.contains(old_tag) {
|
||||
batch.untag(field, old_tag.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for new_tag in new_value {
|
||||
if !old_value.contains(&new_tag) {
|
||||
batch.tag(field, new_tag);
|
||||
}
|
||||
}
|
||||
}
|
||||
(IndexValue::Blob { value: old_hash }, IndexValue::Blob { value: new_hash }) => {
|
||||
batch.clear(BlobOp::Link { hash: old_hash });
|
||||
batch.set(BlobOp::Link { hash: new_hash }, vec![]);
|
||||
|
|
|
|||
|
|
@ -1,151 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2020 Stalwart Labs LLC <hello@stalw.art>
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use std::slice::IterMut;
|
||||
|
||||
use jmap_proto::types::property::Property;
|
||||
use store::{
|
||||
Serialize, SerializedVersion,
|
||||
write::{Archive, Archiver, BatchBuilder, TagValue, ValueClass},
|
||||
};
|
||||
|
||||
pub struct TagManager<
|
||||
T: Into<TagValue>
|
||||
+ PartialEq
|
||||
+ Clone
|
||||
+ Sync
|
||||
+ Send
|
||||
+ rkyv::Archive
|
||||
+ for<'a> rkyv::Serialize<
|
||||
rkyv::api::high::HighSerializer<
|
||||
rkyv::util::AlignedVec,
|
||||
rkyv::ser::allocator::ArenaHandle<'a>,
|
||||
rkyv::rancor::Error,
|
||||
>,
|
||||
>,
|
||||
> {
|
||||
current: Archive<Vec<T>>,
|
||||
added: Vec<T>,
|
||||
removed: Vec<T>,
|
||||
last: LastTag,
|
||||
}
|
||||
|
||||
enum LastTag {
|
||||
Set,
|
||||
Update,
|
||||
None,
|
||||
}
|
||||
|
||||
impl<
|
||||
T: Into<TagValue>
|
||||
+ PartialEq
|
||||
+ Clone
|
||||
+ Sync
|
||||
+ Send
|
||||
+ SerializedVersion
|
||||
+ rkyv::Archive
|
||||
+ for<'a> rkyv::Serialize<
|
||||
rkyv::api::high::HighSerializer<
|
||||
rkyv::util::AlignedVec,
|
||||
rkyv::ser::allocator::ArenaHandle<'a>,
|
||||
rkyv::rancor::Error,
|
||||
>,
|
||||
>,
|
||||
> TagManager<T>
|
||||
{
|
||||
pub fn new(current: Archive<Vec<T>>) -> Self {
|
||||
Self {
|
||||
current,
|
||||
added: Vec::new(),
|
||||
removed: Vec::new(),
|
||||
last: LastTag::None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn set(&mut self, tags: Vec<T>) {
|
||||
if matches!(self.last, LastTag::None) {
|
||||
self.added.clear();
|
||||
self.removed.clear();
|
||||
|
||||
for tag in &tags {
|
||||
if !self.current.inner.contains(tag) {
|
||||
self.added.push(tag.clone());
|
||||
}
|
||||
}
|
||||
|
||||
for tag in &self.current.inner {
|
||||
if !tags.contains(tag) {
|
||||
self.removed.push(tag.clone());
|
||||
}
|
||||
}
|
||||
|
||||
self.current.inner = tags;
|
||||
self.last = LastTag::Set;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn update(&mut self, tag: T, add: bool) {
|
||||
if matches!(self.last, LastTag::None | LastTag::Update) {
|
||||
if add {
|
||||
if !self.current.inner.contains(&tag) {
|
||||
self.added.push(tag.clone());
|
||||
self.current.inner.push(tag);
|
||||
}
|
||||
} else if let Some(index) = self.current.inner.iter().position(|t| t == &tag) {
|
||||
self.current.inner.swap_remove(index);
|
||||
self.removed.push(tag);
|
||||
}
|
||||
self.last = LastTag::Update;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn added(&self) -> &[T] {
|
||||
&self.added
|
||||
}
|
||||
|
||||
pub fn removed(&self) -> &[T] {
|
||||
&self.removed
|
||||
}
|
||||
|
||||
pub fn current(&self) -> &[T] {
|
||||
&self.current.inner
|
||||
}
|
||||
|
||||
pub fn changed_tags(&self) -> impl Iterator<Item = &T> {
|
||||
self.added.iter().chain(self.removed.iter())
|
||||
}
|
||||
|
||||
pub fn inner_tags_mut(&mut self) -> IterMut<'_, T> {
|
||||
self.current.inner.iter_mut()
|
||||
}
|
||||
|
||||
pub fn has_tags(&self) -> bool {
|
||||
!self.current.inner.is_empty()
|
||||
}
|
||||
|
||||
pub fn has_changes(&self) -> bool {
|
||||
!self.added.is_empty() || !self.removed.is_empty()
|
||||
}
|
||||
|
||||
pub fn update_batch(self, batch: &mut BatchBuilder, property: Property) -> trc::Result<()> {
|
||||
let property = u8::from(property);
|
||||
|
||||
batch
|
||||
.assert_value(ValueClass::Property(property), &self.current)
|
||||
.set(
|
||||
ValueClass::Property(property),
|
||||
Archiver::new(self.current.inner).serialize()?,
|
||||
);
|
||||
for added in self.added {
|
||||
batch.tag(property, added);
|
||||
}
|
||||
for removed in self.removed {
|
||||
batch.untag(property, removed);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
|
@ -4,26 +4,9 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-SEL
|
||||
*/
|
||||
|
||||
use super::BitmapHash;
|
||||
use crate::backend::MAX_TOKEN_LENGTH;
|
||||
|
||||
use super::{BitmapClass, BitmapHash};
|
||||
|
||||
impl BitmapClass {
|
||||
pub fn word(token: impl AsRef<[u8]>, field: impl Into<u8>) -> Self {
|
||||
BitmapClass::Text {
|
||||
field: field.into(),
|
||||
token: BitmapHash::new(token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn stemmed(token: impl AsRef<[u8]>, field: impl Into<u8>) -> Self {
|
||||
BitmapClass::Text {
|
||||
field: field.into() | (1 << 7),
|
||||
token: BitmapHash::new(token),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl BitmapHash {
|
||||
pub fn new(item: impl AsRef<[u8]>) -> Self {
|
||||
Self {
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue