1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
//! Error types for the DuckLake DataFusion extension
use std::fmt;
use thiserror::Error;
/// The data-write mode that attempted an unsupported column type change.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeChangeWriteMode {
/// Drop existing data and replace with new data.
Replace,
/// Keep existing data and append new records.
Append,
}
impl fmt::Display for TypeChangeWriteMode {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeChangeWriteMode::Replace => write!(f, "Replace"),
TypeChangeWriteMode::Append => write!(f, "Append"),
}
}
}
/// The operation that attempted an unsupported column type change.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TypeChangeOperation {
/// Explicit metadata-only schema evolution through `promote_column_type`.
PromoteColumnType,
/// A data write tried to change the type of an existing same-name column.
DataWrite {
mode: TypeChangeWriteMode,
},
}
impl fmt::Display for TypeChangeOperation {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
TypeChangeOperation::PromoteColumnType => write!(f, "promote_column_type"),
TypeChangeOperation::DataWrite {
mode,
} => write!(f, "{mode} data write"),
}
}
}
/// Error type for DuckLake operations
#[derive(Error, Debug)]
pub enum DuckLakeError {
/// Error from DataFusion
#[error("DataFusion error: {0}")]
DataFusion(#[from] datafusion::error::DataFusionError),
/// Error from Arrow
#[error("Arrow error: {0}")]
Arrow(#[from] arrow::error::ArrowError),
/// DuckDB error
#[cfg(feature = "metadata-duckdb")]
#[error("DuckDB error: {0}")]
DuckDb(#[from] duckdb::Error),
/// sqlx database error (for PostgreSQL/MySQL/SQLite metadata providers)
#[cfg(any(
feature = "metadata-postgres",
feature = "metadata-mysql",
feature = "metadata-sqlite"
))]
#[error("Database error: {0}")]
Sqlx(#[from] sqlx::Error),
/// Catalog not found
#[error("Catalog not found: {0}")]
CatalogNotFound(String),
/// Schema not found
#[error("Schema not found: {0}")]
SchemaNotFound(String),
/// Table not found
#[error("Table not found: {0}")]
TableNotFound(String),
/// Invalid snapshot
#[error("Invalid snapshot: {0}")]
InvalidSnapshot(String),
/// Invalid catalog configuration
#[error("Invalid configuration: {0}")]
InvalidConfig(String),
/// A write or promotion tried to change an existing column to a type that
/// DuckLake cannot adopt through metadata-only schema evolution.
#[error(
"Unsupported type change during {operation}: column '{column}' from '{from}' to '{to}'"
)]
UnsupportedTypeChange {
operation: TypeChangeOperation,
column: String,
from: String,
to: String,
},
/// Unsupported DuckLake type
#[error("Unsupported DuckLake type: {0}")]
UnsupportedType(String),
/// Unsupported feature
#[error("Unsupported feature: {0}")]
Unsupported(String),
/// ObjectStore error
#[error("ObjectStore error: {0}")]
ObjectStore(#[from] object_store::Error),
/// IO error
#[error("IO error: {0}")]
Io(#[from] std::io::Error),
/// Parquet error
#[error("Parquet error: {0}")]
Parquet(#[from] parquet::errors::ParquetError),
/// A concurrent write conflict detected at commit time: another writer
/// published a newer generation of the table since this write began. The
/// loser aborts (DuckLake-style optimistic concurrency) rather than silently
/// unioning or clobbering the concurrent commit. Callers may retry.
#[error("Write conflict: {0}")]
Conflict(String),
/// Generic error
#[error("Internal error: {0}")]
Internal(String),
}
impl From<DuckLakeError> for datafusion::error::DataFusionError {
fn from(err: DuckLakeError) -> Self {
match err {
// If it's already a DataFusion error, unwrap it
DuckLakeError::DataFusion(e) => e,
// For all other errors, wrap them as External
other => datafusion::error::DataFusionError::External(Box::new(other)),
}
}
}