1use std::any::type_name;
16use std::borrow::Borrow;
17use std::fmt::Formatter;
18use std::iter::Step;
19use std::num::TryFromIntError;
20use std::ops::{Add, AddAssign, Sub};
21use std::str::FromStr;
22
23use sea_orm::sea_query::{ArrayType, ValueTypeErr};
24use sea_orm::{ColIdx, ColumnType, DbErr, QueryResult, TryGetError};
25use serde::{Deserialize, Deserializer, Serialize, Serializer};
26use thiserror_ext::AsReport;
27use tracing::warn;
28
29use crate::catalog::source::OptionalAssociatedTableId;
30use crate::catalog::table::OptionalAssociatedSourceId;
31
32pub const OBJECT_ID_PLACEHOLDER: u32 = u32::MAX - 1;
33
34#[derive(Clone, Copy, Default, Hash, PartialOrd, PartialEq, Eq, Ord)]
35#[repr(transparent)]
36pub struct TypedId<const N: usize, P>(pub(crate) P);
37
38unsafe impl<const N: usize, P> ::prost::TransparentOver for TypedId<N, P> {
40 type Inner = P;
41}
42
43impl<const N: usize, P: std::fmt::Debug> std::fmt::Debug for TypedId<N, P> {
44 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
45 <P as std::fmt::Debug>::fmt(&self.0, f)
46 }
47}
48
49impl<const N: usize, P: std::fmt::Display> std::fmt::Display for TypedId<N, P> {
50 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
51 <P as std::fmt::Display>::fmt(&self.0, f)
52 }
53}
54
55impl<const N: usize, P: std::fmt::UpperHex> std::fmt::UpperHex for TypedId<N, P> {
56 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
57 <P as std::fmt::UpperHex>::fmt(&self.0, f)
58 }
59}
60
61impl<const N: usize, P: PartialEq> PartialEq<P> for TypedId<N, P> {
62 fn eq(&self, other: &P) -> bool {
63 self.0 == *other
64 }
65}
66
67impl<const N: usize, P: FromStr> FromStr for TypedId<N, P> {
68 type Err = <P as FromStr>::Err;
69
70 fn from_str(s: &str) -> Result<Self, Self::Err> {
71 Ok(Self(<P as FromStr>::from_str(s)?))
72 }
73}
74
75impl<const N: usize, P> Borrow<P> for TypedId<N, P> {
76 fn borrow(&self) -> &P {
77 unsafe { std::mem::transmute(self) }
79 }
80}
81
82impl<const N: usize, P> TypedId<N, P> {
83 pub const fn new(inner: P) -> Self {
84 TypedId(inner)
85 }
86}
87
88impl<const N: usize, P> TypedId<N, P>
89where
90 Self: UniqueTypedIdDeclaration,
91{
92 #[expect(clippy::wrong_self_convention)]
93 pub fn as_raw_id(self) -> P {
94 self.0
95 }
96}
97
98type TypedU32Id<const N: usize> = TypedId<N, u32>;
99
100impl<const N: usize> TypedU32Id<N> {
101 pub const fn placeholder() -> Self {
102 Self(OBJECT_ID_PLACEHOLDER)
103 }
104
105 pub fn is_placeholder(&self) -> bool {
106 self.0 == OBJECT_ID_PLACEHOLDER
107 }
108}
109
110impl<const N: usize, P> From<P> for TypedId<N, P> {
111 fn from(id: P) -> Self {
112 Self(id)
113 }
114}
115
116macro_rules! impl_typed_id_conversion {
120 ($unsigned:ty, $signed:ty) => {
121 paste::paste! {
122 impl<const N: usize> TypedId<N, $unsigned>
123 {
124 fn [< from_ $signed >](inner: $signed) -> Self {
125 Self(inner.try_into().unwrap_or_else(|e: TryFromIntError| {
126 if cfg!(debug_assertions) {
127 panic!(
128 "invalid {} id {} for {}: {:?}",
129 stringify!($signed),
130 inner,
131 type_name::<Self>(),
132 e.as_report()
133 );
134 } else {
135 warn!(
136 "invalid {} id {} for {}: {:?}",
137 stringify!($signed),
138 inner,
139 type_name::<Self>(),
140 e.as_report()
141 );
142 inner as _
143 }
144 }))
145 }
146
147 fn [< to_ $signed >](self) -> $signed {
148 self.0.try_into().unwrap_or_else(|e: TryFromIntError| {
149 if cfg!(debug_assertions) {
150 panic!(
151 "invalid {} id {} for {}: {:?}",
152 stringify!($unsigned),
153 self.0,
154 type_name::<Self>(),
155 e.as_report()
156 );
157 } else {
158 warn!(
159 "invalid {} id {} for {}: {:?}",
160 stringify!($unsigned),
161 self.0,
162 type_name::<Self>(),
163 e.as_report()
164 );
165 self.0 as _
166 }
167 })
168 }
169
170 pub fn [< as_ $signed _id >](self) -> $signed {
171 self.[< to_ $signed >]()
172 }
173 }
174 }
175 };
176}
177
178impl_typed_id_conversion!(u32, i32);
179impl_typed_id_conversion!(u64, i64);
180
181macro_rules! impl_sea_orm_for_typed_id {
185 ($unsigned:ty, $signed:ty) => {
186 paste::paste! {
187 impl<const N: usize> From<TypedId<N, $unsigned>> for sea_orm::Value
188 where
189 TypedId<N, $unsigned>: UniqueTypedIdDeclaration,
190 {
191 fn from(value: TypedId<N, $unsigned>) -> Self {
192 sea_orm::Value::from(value.[< to_ $signed >]())
193 }
194 }
195
196 impl<const N: usize> sea_orm::sea_query::ValueType for TypedId<N, $unsigned>
197 where
198 Self: UniqueTypedIdDeclaration,
199 {
200 fn try_from(v: sea_orm::Value) -> Result<Self, ValueTypeErr> {
201 let inner = <$signed as sea_orm::sea_query::ValueType>::try_from(v)?;
202 Ok(Self::[< from_ $signed >](inner))
203 }
204
205 fn type_name() -> String {
206 <$signed as sea_orm::sea_query::ValueType>::type_name()
207 }
208
209 fn array_type() -> ArrayType {
210 <$signed as sea_orm::sea_query::ValueType>::array_type()
211 }
212
213 fn column_type() -> ColumnType {
214 <$signed as sea_orm::sea_query::ValueType>::column_type()
215 }
216 }
217
218 impl<const N: usize> sea_orm::sea_query::Nullable for TypedId<N, $unsigned>
219 where
220 Self: UniqueTypedIdDeclaration,
221 {
222 fn null() -> sea_orm::Value {
223 <$signed as sea_orm::sea_query::Nullable>::null()
224 }
225 }
226
227 impl<const N: usize> sea_orm::TryGetable for TypedId<N, $unsigned>
228 where
229 Self: UniqueTypedIdDeclaration,
230 {
231 fn try_get_by<I: ColIdx>(res: &QueryResult, index: I) -> Result<Self, TryGetError> {
232 let inner = <$signed as sea_orm::TryGetable>::try_get_by(res, index)?;
233 Ok(Self::[< from_ $signed >](inner))
234 }
235 }
236
237 impl<const N: usize> sea_orm::TryFromU64 for TypedId<N, $unsigned>
238 where
239 Self: UniqueTypedIdDeclaration,
240 {
241 fn try_from_u64(n: u64) -> Result<Self, DbErr> {
242 Ok(Self::[< from_ $signed >](<$signed as sea_orm::TryFromU64>::try_from_u64(n)?))
243 }
244 }
245 }
246 };
247}
248
249impl_sea_orm_for_typed_id!(u32, i32);
250impl_sea_orm_for_typed_id!(u64, i64);
251
252impl<const N: usize, P: Serialize> Serialize for TypedId<N, P> {
253 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
254 where
255 S: Serializer,
256 {
257 <P as Serialize>::serialize(&self.0, serializer)
258 }
259}
260
261impl<'de, const N: usize, P: Deserialize<'de>> Deserialize<'de> for TypedId<N, P> {
262 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
263 where
264 D: Deserializer<'de>,
265 {
266 Ok(Self(<P as Deserialize>::deserialize(deserializer)?))
267 }
268}
269
270impl<const N: usize, P: Sub<Output = P>> Sub for TypedId<N, P> {
271 type Output = P;
272
273 fn sub(self, rhs: Self) -> Self::Output {
274 self.0 - rhs.0
275 }
276}
277
278impl<const N: usize, P: Step> Step for TypedId<N, P> {
279 fn steps_between(start: &Self, end: &Self) -> (usize, Option<usize>) {
280 P::steps_between(&start.0, &end.0)
281 }
282
283 fn forward_checked(start: Self, count: usize) -> Option<Self> {
284 P::forward_checked(start.0, count).map(Self)
285 }
286
287 fn backward_checked(start: Self, count: usize) -> Option<Self> {
288 P::backward_checked(start.0, count).map(Self)
289 }
290}
291
292macro_rules! impl_add {
293 ($type_name:ty) => {
294 impl<const N: usize> Add<$type_name> for TypedId<N, $type_name> {
295 type Output = Self;
296
297 fn add(self, rhs: $type_name) -> Self::Output {
298 Self(self.0.checked_add(rhs).unwrap())
299 }
300 }
301
302 impl<const N: usize> AddAssign<$type_name> for TypedId<N, $type_name> {
303 fn add_assign(&mut self, rhs: $type_name) {
304 self.0 = self.0.checked_add(rhs).unwrap()
305 }
306 }
307
308 impl<const N: usize> PartialEq<TypedId<N, $type_name>> for $type_name {
309 fn eq(&self, other: &TypedId<N, $type_name>) -> bool {
310 *self == other.0
311 }
312 }
313 };
314}
315
316impl_add!(u32);
317impl_add!(u64);
318
319#[expect(dead_code)]
320pub trait UniqueTypedIdDeclaration {}
321
322macro_rules! declare_id_type {
323 ($name:ident, $primitive:ty, $type_id:expr) => {
324 pub type $name = TypedId<{ $type_id }, $primitive>;
325 impl UniqueTypedIdDeclaration for $name {}
326 };
327}
328
329macro_rules! declare_id_types {
330 ($primitive:ty $(, $name:ident)+) => {
331 declare_id_types! {
332 $primitive, 0 $(, $name)+
333 }
334 };
335 ($primitive:ty, $next_type_id:expr) => {};
336 ($primitive:ty, $next_type_id:expr, $name:ident $(, $rest:ident)*) => {
337 declare_id_type! { $name, $primitive, $next_type_id }
338 declare_id_types! {
339 $primitive, $next_type_id + 1 $(, $rest)*
340 }
341 };
342 ($($invalid:tt)+) => {
343 compile_error!(stringify!($($invalid)+))
344 }
345}
346
347declare_id_types!(
348 u32,
349 TableId,
350 JobId,
351 DatabaseId,
352 SchemaId,
353 FragmentId,
354 ActorId,
355 WorkerId,
356 SinkId,
357 SourceId,
358 SubscriptionId,
359 IndexId,
360 ViewId,
361 FunctionId,
362 ConnectionId,
363 SecretId,
364 SubscriberId,
365 LocalOperatorId,
366 UserId,
367 RelationId
368);
369
370declare_id_type!(ObjectId, u32, 256);
371
372declare_id_types!(
373 u64,
374 GlobalOperatorId,
375 StreamNodeLocalOperatorId,
376 ExecutorId,
377 PartialGraphId,
378 HummockRawObjectId,
379 HummockSstableObjectId,
380 HummockSstableId,
381 HummockVectorFileId,
382 HummockHnswGraphFileId,
383 HummockVersionId,
384 CompactionGroupId,
385 IcebergCompactionTaskId
386);
387
388macro_rules! impl_as {
389 (@func $target_id_name:ident, $alias_name:ident) => {
390 paste::paste! {
391 pub fn [< as_ $alias_name >](self) -> $target_id_name {
392 $target_id_name::new(self.0)
393 }
394 }
395 };
396 (@func $target_id_name:ident) => {
397 paste::paste! {
398 impl_as! { @func $target_id_name, [< $target_id_name:snake >] }
399 }
400 };
401 ($src_id_name:ident $(,$target_id_name:ident)* $(,{$orig_target_id_name:ident , $alias_name:ident})*) => {
402 impl $src_id_name {
403 $(
404 impl_as! { @func $target_id_name }
405 )*
406 $(
407 impl_as! { @func $orig_target_id_name, $alias_name }
408 )*
409 }
410 }
411}
412
413impl JobId {
414 pub fn is_mv_table_id(self, table_id: TableId) -> bool {
415 self.0 == table_id.0
416 }
417}
418
419impl_as!(JobId, SinkId, IndexId, SubscriberId, {TableId, mv_table_id}, {SourceId, shared_source_id});
420impl_as!(TableId, JobId);
421
422impl From<StreamNodeLocalOperatorId> for LocalOperatorId {
423 fn from(value: StreamNodeLocalOperatorId) -> Self {
424 assert!(
425 value.0 <= u32::MAX as u64,
426 "oversized operator id {} in stream node",
427 value.0
428 );
429 Self(value.0 as u32)
430 }
431}
432
433impl From<LocalOperatorId> for StreamNodeLocalOperatorId {
434 fn from(value: LocalOperatorId) -> Self {
435 Self(value.0 as u64)
436 }
437}
438
439impl From<OptionalAssociatedTableId> for TableId {
440 fn from(value: OptionalAssociatedTableId) -> Self {
441 let OptionalAssociatedTableId::AssociatedTableId(table_id) = value;
442 table_id
443 }
444}
445
446impl From<TableId> for OptionalAssociatedTableId {
447 fn from(value: TableId) -> Self {
448 OptionalAssociatedTableId::AssociatedTableId(value)
449 }
450}
451
452impl_as!(SinkId, JobId);
453impl_as!(IndexId, JobId);
454impl_as!(SourceId, {JobId, share_source_job_id}, {TableId, cdc_table_id});
455impl_as!(SubscriptionId, SubscriberId);
456
457impl From<OptionalAssociatedSourceId> for SourceId {
458 fn from(value: OptionalAssociatedSourceId) -> Self {
459 let OptionalAssociatedSourceId::AssociatedSourceId(source_id) = value;
460 source_id
461 }
462}
463
464impl From<SourceId> for OptionalAssociatedSourceId {
465 fn from(value: SourceId) -> Self {
466 OptionalAssociatedSourceId::AssociatedSourceId(value)
467 }
468}
469
470macro_rules! impl_into_object {
471 ($mod_prefix:ty, $($type_name:ident),+) => {
472 $(
473 impl From<$type_name> for $mod_prefix {
474 fn from(value: $type_name) -> Self {
475 <$mod_prefix>::$type_name(value)
476 }
477 }
478 )+
479 };
480}
481
482impl_into_object!(
483 crate::user::grant_privilege::Object,
484 DatabaseId,
485 TableId,
486 SchemaId,
487 SinkId,
488 SourceId,
489 SubscriptionId,
490 ViewId,
491 FunctionId,
492 ConnectionId,
493 SecretId
494);
495
496impl_into_object!(
497 crate::ddl_service::alter_name_request::Object,
498 DatabaseId,
499 TableId,
500 SchemaId,
501 SinkId,
502 SourceId,
503 SubscriptionId,
504 IndexId,
505 ViewId
506);
507
508impl_into_object!(
509 crate::ddl_service::alter_owner_request::Object,
510 DatabaseId,
511 TableId,
512 SchemaId,
513 SinkId,
514 SourceId,
515 SubscriptionId,
516 ViewId,
517 ConnectionId,
518 FunctionId,
519 SecretId
520);
521
522impl_into_object!(
523 crate::ddl_service::alter_set_schema_request::Object,
524 TableId,
525 ViewId,
526 SourceId,
527 SinkId,
528 SubscriptionId,
529 FunctionId,
530 ConnectionId
531);
532
533macro_rules! impl_into_rename_object {
534 ($($type_name:ident),+) => {
535 paste::paste! {
536 $(
537 impl From<([<$type_name Id>], [<$type_name Id>])> for crate::ddl_service::alter_swap_rename_request::Object {
538 fn from((src_object_id, dst_object_id): ([<$type_name Id>], [<$type_name Id>])) -> Self {
539 crate::ddl_service::alter_swap_rename_request::Object::$type_name(crate::ddl_service::alter_swap_rename_request::ObjectNameSwapPair {
540 src_object_id: src_object_id.as_object_id(),
541 dst_object_id: dst_object_id.as_object_id(),
542 })
543 }
544 }
545 )+
546 }
547 };
548}
549
550impl_into_rename_object!(Table, View, Source, Sink, Subscription);
551
552macro_rules! impl_object_id_conversion {
553 ($($type_name:ident),+) => {
554 $(
555 impl From<$type_name> for ObjectId {
556 fn from(value: $type_name) -> Self {
557 Self::new(value.0)
558 }
559 }
560
561 impl $type_name {
562 pub fn as_object_id(self) -> ObjectId {
563 ObjectId::new(self.0)
564 }
565 }
566 )+
567
568 paste::paste! {
569 impl ObjectId {
570 $(
571 pub fn [< as_ $type_name:snake>](self) -> $type_name {
572 $type_name::new(self.0)
573 }
574 )+
575 }
576 }
577 };
578}
579
580impl_object_id_conversion!(
581 DatabaseId,
582 TableId,
583 SchemaId,
584 SinkId,
585 SourceId,
586 JobId,
587 SubscriptionId,
588 IndexId,
589 ViewId,
590 FunctionId,
591 ConnectionId,
592 SecretId
593);
594
595macro_rules! declare_relation {
596 ($($id_name:ident),+) => {
597 $(
598 impl $id_name {
599 pub fn as_relation_id(self) -> RelationId {
600 RelationId::new(self.0)
601 }
602 }
603 )+
604 };
605}
606
607declare_relation!(TableId, SourceId, SinkId, IndexId, ViewId, SubscriptionId);
608
609macro_rules! impl_hummock_object_id {
610 ($type_name:ty) => {
611 impl $type_name {
612 pub fn as_raw(&self) -> HummockRawObjectId {
613 HummockRawObjectId::new(self.0)
614 }
615 }
616
617 impl From<HummockRawObjectId> for $type_name {
618 fn from(id: HummockRawObjectId) -> Self {
619 Self(id.0)
620 }
621 }
622 };
623}
624
625impl_hummock_object_id!(HummockSstableObjectId);
626impl_hummock_object_id!(HummockVectorFileId);
627impl_hummock_object_id!(HummockHnswGraphFileId);