Skip to main content

greptimedb_ingester/
table.rs

1// Copyright 2023 Greptime Team
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15//! Table schema and data structures for GreptimeDB bulk insert operations
16
17use derive_builder::Builder;
18
19use crate::api::v1::{ColumnDataType, SemanticType};
20
21/// Extended data type information for columns that need additional parameters
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum DataTypeExtension {
24    /// Decimal128 with specific precision and scale
25    Decimal128 { precision: u8, scale: i8 },
26}
27
28/// Represents a time-series data table with schema
29#[derive(Debug, Clone, Builder)]
30#[builder(setter(into))]
31pub struct TableSchema {
32    /// Table name
33    name: String,
34    /// Table columns
35    #[builder(default)]
36    columns: Vec<Column>,
37}
38
39impl TableSchema {
40    /// Create a new table schema builder
41    pub fn builder() -> TableSchemaBuilder {
42        TableSchemaBuilder::default()
43    }
44
45    /// Get the table name
46    pub fn name(&self) -> &str {
47        &self.name
48    }
49
50    /// Get the table columns
51    pub fn columns(&self) -> &[Column] {
52        &self.columns
53    }
54
55    /// Add a tag column (for indexing and grouping)
56    pub fn add_tag<T: Into<String>>(mut self, name: T, data_type: ColumnDataType) -> Self {
57        self.columns.push(Column {
58            name: name.into(),
59            data_type,
60            semantic_type: SemanticType::Tag,
61            data_type_extension: None,
62        });
63        self
64    }
65
66    /// Add a timestamp column (timeline for time series)
67    pub fn add_timestamp<T: Into<String>>(mut self, name: T, data_type: ColumnDataType) -> Self {
68        self.columns.push(Column {
69            name: name.into(),
70            data_type,
71            semantic_type: SemanticType::Timestamp,
72            data_type_extension: None,
73        });
74        self
75    }
76
77    /// Add a field column (measurement values)
78    pub fn add_field<T: Into<String>>(mut self, name: T, data_type: ColumnDataType) -> Self {
79        self.columns.push(Column {
80            name: name.into(),
81            data_type,
82            semantic_type: SemanticType::Field,
83            data_type_extension: None,
84        });
85        self
86    }
87
88    /// Add a decimal128 field column with specific precision and scale
89    pub fn add_decimal128_field<T: Into<String>>(
90        mut self,
91        name: T,
92        precision: u8,
93        scale: i8,
94    ) -> Self {
95        self.columns.push(Column {
96            name: name.into(),
97            data_type: ColumnDataType::Decimal128,
98            semantic_type: SemanticType::Field,
99            data_type_extension: Some(DataTypeExtension::Decimal128 { precision, scale }),
100        });
101        self
102    }
103}
104
105/// Table column definition
106#[derive(Debug, Clone)]
107pub struct Column {
108    pub name: String,
109    pub data_type: ColumnDataType,
110    pub semantic_type: SemanticType,
111    /// Extended type information for data types that need additional parameters
112    pub data_type_extension: Option<DataTypeExtension>,
113}
114
115/// Represents a data row with type-safe value access
116#[derive(Debug, Clone, Default)]
117pub struct Row {
118    values: Vec<Value>,
119}
120
121impl Row {
122    /// Create a new empty row
123    pub fn new() -> Self {
124        Self::default()
125    }
126
127    /// Create a new row with pre-allocated capacity
128    pub fn with_capacity(capacity: usize) -> Self {
129        Self {
130            values: Vec::with_capacity(capacity),
131        }
132    }
133
134    /// Get the number of values in the row
135    pub fn len(&self) -> usize {
136        self.values.len()
137    }
138
139    /// Check if the row is empty
140    pub fn is_empty(&self) -> bool {
141        self.values.is_empty()
142    }
143
144    /// Create a row directly from values (more efficient than chaining add_value calls)
145    pub fn from_values(values: Vec<Value>) -> Self {
146        Self { values }
147    }
148
149    /// Add a value to the row
150    pub fn add_value(mut self, value: Value) -> Self {
151        self.values.push(value);
152        self
153    }
154
155    /// Add multiple values to the row
156    pub fn add_values(mut self, values: Vec<Value>) -> Self {
157        self.values.extend(values);
158        self
159    }
160
161    /// Add multiple values from an iterator
162    pub fn add_values_iter(mut self, values: impl IntoIterator<Item = Value>) -> Self {
163        self.values.extend(values);
164        self
165    }
166
167    /// Get boolean value at index (safe version with bounds checking)
168    pub fn get_bool(&self, index: usize) -> Option<bool> {
169        match self.values.get(index)? {
170            Value::Boolean(v) => Some(*v),
171            Value::Null => None,
172            other => handle_type_mismatch(index, "boolean", other),
173        }
174    }
175
176    /// Get boolean value at index (unsafe version without bounds checking)
177    /// # Safety
178    /// The caller must ensure that `index < self.values.len()`
179    pub unsafe fn get_bool_unchecked(&self, index: usize) -> Option<bool> {
180        match self.values.get_unchecked(index) {
181            Value::Boolean(v) => Some(*v),
182            Value::Null => None,
183            other => handle_type_mismatch(index, "boolean", other),
184        }
185    }
186
187    /// Get i8 value at index (safe version with bounds checking)
188    pub fn get_i8(&self, index: usize) -> Option<i8> {
189        match self.values.get(index)? {
190            Value::Int8(v) => Some(*v),
191            Value::Null => None,
192            other => handle_type_mismatch(index, "i8", other),
193        }
194    }
195
196    /// Get i8 value at index (unsafe version without bounds checking)
197    /// # Safety
198    /// The caller must ensure that `index < self.values.len()`
199    pub unsafe fn get_i8_unchecked(&self, index: usize) -> Option<i8> {
200        match self.values.get_unchecked(index) {
201            Value::Int8(v) => Some(*v),
202            Value::Null => None,
203            other => handle_type_mismatch(index, "i8", other),
204        }
205    }
206
207    /// Get i16 value at index (safe version with bounds checking)
208    pub fn get_i16(&self, index: usize) -> Option<i16> {
209        match self.values.get(index)? {
210            Value::Int16(v) => Some(*v),
211            Value::Null => None,
212            other => handle_type_mismatch(index, "i16", other),
213        }
214    }
215
216    /// Get i16 value at index (unsafe version without bounds checking)
217    /// # Safety
218    /// The caller must ensure that `index < self.values.len()`
219    pub unsafe fn get_i16_unchecked(&self, index: usize) -> Option<i16> {
220        match self.values.get_unchecked(index) {
221            Value::Int16(v) => Some(*v),
222            Value::Null => None,
223            other => handle_type_mismatch(index, "i16", other),
224        }
225    }
226
227    /// Get i32 value at index (safe version with bounds checking)
228    pub fn get_i32(&self, index: usize) -> Option<i32> {
229        match self.values.get(index)? {
230            Value::Int32(v) => Some(*v),
231            Value::Null => None,
232            other => handle_type_mismatch(index, "i32", other),
233        }
234    }
235
236    /// Get i32 value at index (unsafe version without bounds checking)
237    /// # Safety
238    /// The caller must ensure that `index < self.values.len()`
239    pub unsafe fn get_i32_unchecked(&self, index: usize) -> Option<i32> {
240        match self.values.get_unchecked(index) {
241            Value::Int32(v) => Some(*v),
242            Value::Null => None,
243            other => handle_type_mismatch(index, "i32", other),
244        }
245    }
246
247    /// Get i64 value at index (safe version with bounds checking)
248    pub fn get_i64(&self, index: usize) -> Option<i64> {
249        match self.values.get(index)? {
250            Value::Int64(v) => Some(*v),
251            Value::Null => None,
252            other => handle_type_mismatch(index, "i64", other),
253        }
254    }
255
256    /// Get i64 value at index (unsafe version without bounds checking)
257    /// # Safety
258    /// The caller must ensure that `index < self.values.len()`
259    pub unsafe fn get_i64_unchecked(&self, index: usize) -> Option<i64> {
260        match self.values.get_unchecked(index) {
261            Value::Int64(v) => Some(*v),
262            Value::Null => None,
263            other => handle_type_mismatch(index, "i64", other),
264        }
265    }
266
267    /// Get u8 value at index (safe version with bounds checking)
268    pub fn get_u8(&self, index: usize) -> Option<u8> {
269        match self.values.get(index)? {
270            Value::Uint8(v) => Some(*v),
271            Value::Null => None,
272            other => handle_type_mismatch(index, "u8", other),
273        }
274    }
275
276    /// Get u8 value at index (unsafe version without bounds checking)
277    /// # Safety
278    /// The caller must ensure that `index < self.values.len()`
279    pub unsafe fn get_u8_unchecked(&self, index: usize) -> Option<u8> {
280        match self.values.get_unchecked(index) {
281            Value::Uint8(v) => Some(*v),
282            Value::Null => None,
283            other => handle_type_mismatch(index, "u8", other),
284        }
285    }
286
287    /// Get u16 value at index (safe version with bounds checking)
288    pub fn get_u16(&self, index: usize) -> Option<u16> {
289        match self.values.get(index)? {
290            Value::Uint16(v) => Some(*v),
291            Value::Null => None,
292            other => handle_type_mismatch(index, "u16", other),
293        }
294    }
295
296    /// Get u16 value at index (unsafe version without bounds checking)
297    /// # Safety
298    /// The caller must ensure that `index < self.values.len()`
299    pub unsafe fn get_u16_unchecked(&self, index: usize) -> Option<u16> {
300        match self.values.get_unchecked(index) {
301            Value::Uint16(v) => Some(*v),
302            Value::Null => None,
303            other => handle_type_mismatch(index, "u16", other),
304        }
305    }
306
307    /// Get u32 value at index (safe version with bounds checking)
308    pub fn get_u32(&self, index: usize) -> Option<u32> {
309        match self.values.get(index)? {
310            Value::Uint32(v) => Some(*v),
311            Value::Null => None,
312            other => handle_type_mismatch(index, "u32", other),
313        }
314    }
315
316    /// Get u32 value at index (unsafe version without bounds checking)
317    /// # Safety
318    /// The caller must ensure that `index < self.values.len()`
319    pub unsafe fn get_u32_unchecked(&self, index: usize) -> Option<u32> {
320        match self.values.get_unchecked(index) {
321            Value::Uint32(v) => Some(*v),
322            Value::Null => None,
323            other => handle_type_mismatch(index, "u32", other),
324        }
325    }
326
327    /// Get u64 value at index (safe version with bounds checking)
328    pub fn get_u64(&self, index: usize) -> Option<u64> {
329        match self.values.get(index)? {
330            Value::Uint64(v) => Some(*v),
331            Value::Null => None,
332            other => handle_type_mismatch(index, "u64", other),
333        }
334    }
335
336    /// Get u64 value at index (unsafe version without bounds checking)
337    /// # Safety
338    /// The caller must ensure that `index < self.values.len()`
339    pub unsafe fn get_u64_unchecked(&self, index: usize) -> Option<u64> {
340        match self.values.get_unchecked(index) {
341            Value::Uint64(v) => Some(*v),
342            Value::Null => None,
343            other => handle_type_mismatch(index, "u64", other),
344        }
345    }
346
347    /// Get f32 value at index (safe version with bounds checking)
348    pub fn get_f32(&self, index: usize) -> Option<f32> {
349        match self.values.get(index)? {
350            Value::Float32(v) => Some(*v),
351            Value::Null => None,
352            other => handle_type_mismatch(index, "f32", other),
353        }
354    }
355
356    /// Get f32 value at index (unsafe version without bounds checking)
357    /// # Safety
358    /// The caller must ensure that `index < self.values.len()`
359    pub unsafe fn get_f32_unchecked(&self, index: usize) -> Option<f32> {
360        match self.values.get_unchecked(index) {
361            Value::Float32(v) => Some(*v),
362            Value::Null => None,
363            other => handle_type_mismatch(index, "f32", other),
364        }
365    }
366
367    /// Get f64 value at index (safe version with bounds checking)
368    pub fn get_f64(&self, index: usize) -> Option<f64> {
369        match self.values.get(index)? {
370            Value::Float64(v) => Some(*v),
371            Value::Null => None,
372            other => handle_type_mismatch(index, "f64", other),
373        }
374    }
375
376    /// Get f64 value at index (unsafe version without bounds checking)
377    /// # Safety
378    /// The caller must ensure that `index < self.values.len()`
379    pub unsafe fn get_f64_unchecked(&self, index: usize) -> Option<f64> {
380        match self.values.get_unchecked(index) {
381            Value::Float64(v) => Some(*v),
382            Value::Null => None,
383            other => handle_type_mismatch(index, "f64", other),
384        }
385    }
386
387    /// Get binary value at index (safe version with bounds checking)
388    pub fn get_binary(&self, index: usize) -> Option<Vec<u8>> {
389        match self.values.get(index)? {
390            Value::Binary(v) => Some(v.clone()),
391            Value::String(v) => Some(v.as_bytes().to_vec()), // JSON type
392            Value::Null => None,
393            other => handle_type_mismatch(index, "binary", other),
394        }
395    }
396
397    /// Get binary value at index (unsafe version without bounds checking)
398    /// # Safety
399    /// The caller must ensure that `index < self.values.len()`
400    pub unsafe fn get_binary_unchecked(&self, index: usize) -> Option<Vec<u8>> {
401        match self.values.get_unchecked(index) {
402            Value::Binary(v) => Some(v.clone()),
403            Value::String(v) => Some(v.as_bytes().to_vec()), // JSON type
404            Value::Null => None,
405            other => handle_type_mismatch(index, "binary", other),
406        }
407    }
408
409    /// Take binary value at index (safe version with bounds checking)
410    pub fn take_binary(&mut self, index: usize) -> Option<Vec<u8>> {
411        if index >= self.values.len() {
412            return None;
413        }
414        unsafe { self.take_binary_unchecked(index) }
415    }
416
417    /// Take binary value at index (unsafe version without bounds checking)
418    /// # Safety
419    /// The caller must ensure that `index < self.values.len()`
420    pub unsafe fn take_binary_unchecked(&mut self, index: usize) -> Option<Vec<u8>> {
421        match std::mem::replace(self.values.get_unchecked_mut(index), Value::Null) {
422            Value::Binary(v) => Some(v),
423            Value::String(v) => Some(v.into_bytes()), // JSON type
424            Value::Null => None,
425            other => handle_type_mismatch(index, "binary", &other),
426        }
427    }
428
429    /// Get string value at index (safe version with bounds checking)
430    pub fn get_string(&self, index: usize) -> Option<String> {
431        match self.values.get(index)? {
432            Value::String(v) => Some(v.clone()),
433            Value::Null => None,
434            other => handle_type_mismatch(index, "string", other),
435        }
436    }
437
438    /// Get string value at index (unsafe version without bounds checking)
439    /// # Safety
440    /// The caller must ensure that `index < self.values.len()`
441    pub unsafe fn get_string_unchecked(&self, index: usize) -> Option<String> {
442        match self.values.get_unchecked(index) {
443            Value::String(v) => Some(v.clone()),
444            Value::Null => None,
445            other => handle_type_mismatch(index, "string", other),
446        }
447    }
448
449    /// Take string value at index (safe version with bounds checking)
450    pub fn take_string(&mut self, index: usize) -> Option<String> {
451        if index >= self.values.len() {
452            return None;
453        }
454        unsafe { self.take_string_unchecked(index) }
455    }
456
457    /// Take string value at index (unsafe version without bounds checking)
458    /// # Safety
459    /// The caller must ensure that `index < self.values.len()`
460    pub unsafe fn take_string_unchecked(&mut self, index: usize) -> Option<String> {
461        match std::mem::replace(self.values.get_unchecked_mut(index), Value::Null) {
462            Value::String(v) => Some(v),
463            Value::Null => None,
464            other => handle_type_mismatch(index, "string", &other),
465        }
466    }
467
468    /// Get date value at index (safe version with bounds checking)
469    pub fn get_date(&self, index: usize) -> Option<i32> {
470        match self.values.get(index)? {
471            Value::Date(v) => Some(*v),
472            Value::Null => None,
473            other => handle_type_mismatch(index, "date", other),
474        }
475    }
476
477    /// Get date value at index (unsafe version without bounds checking)
478    /// # Safety
479    /// The caller must ensure that `index < self.values.len()`
480    pub unsafe fn get_date_unchecked(&self, index: usize) -> Option<i32> {
481        match self.values.get_unchecked(index) {
482            Value::Date(v) => Some(*v),
483            Value::Null => None,
484            other => handle_type_mismatch(index, "date", other),
485        }
486    }
487
488    /// Get datetime value at index (safe version with bounds checking)
489    pub fn get_datetime(&self, index: usize) -> Option<i64> {
490        match self.values.get(index)? {
491            Value::Datetime(v) => Some(*v),
492            Value::Null => None,
493            other => handle_type_mismatch(index, "datetime", other),
494        }
495    }
496
497    /// Get datetime value at index (unsafe version without bounds checking)
498    /// # Safety
499    /// The caller must ensure that `index < self.values.len()`
500    pub unsafe fn get_datetime_unchecked(&self, index: usize) -> Option<i64> {
501        match self.values.get_unchecked(index) {
502            Value::Datetime(v) => Some(*v),
503            Value::Null => None,
504            other => handle_type_mismatch(index, "datetime", other),
505        }
506    }
507
508    /// Get timestamp value at index (generic, supports all timestamp types, safe version with bounds checking)
509    pub fn get_timestamp(&self, index: usize) -> Option<i64> {
510        match self.values.get(index)? {
511            Value::TimestampSecond(v) => Some(*v),
512            Value::TimestampMillisecond(v) => Some(*v),
513            Value::TimestampMicrosecond(v) => Some(*v),
514            Value::TimestampNanosecond(v) => Some(*v),
515            Value::Null => None,
516            other => handle_type_mismatch(index, "timestamp", other),
517        }
518    }
519
520    /// Get timestamp value at index (generic, supports all timestamp types, unsafe version without bounds checking)
521    /// # Safety
522    /// The caller must ensure that `index < self.values.len()`
523    pub unsafe fn get_timestamp_unchecked(&self, index: usize) -> Option<i64> {
524        match self.values.get_unchecked(index) {
525            Value::TimestampSecond(v) => Some(*v),
526            Value::TimestampMillisecond(v) => Some(*v),
527            Value::TimestampMicrosecond(v) => Some(*v),
528            Value::TimestampNanosecond(v) => Some(*v),
529            Value::Null => None,
530            other => handle_type_mismatch(index, "timestamp", other),
531        }
532    }
533
534    /// Get time32 value at index (safe version with bounds checking)
535    pub fn get_time32(&self, index: usize) -> Option<i32> {
536        match self.values.get(index)? {
537            Value::TimeSecond(v) => Some(*v),
538            Value::TimeMillisecond(v) => Some(*v),
539            Value::Null => None,
540            other => handle_type_mismatch(index, "time32", other),
541        }
542    }
543
544    /// Get time32 value at index (unsafe version without bounds checking)
545    /// # Safety
546    /// The caller must ensure that `index < self.values.len()`
547    pub unsafe fn get_time32_unchecked(&self, index: usize) -> Option<i32> {
548        match self.values.get_unchecked(index) {
549            Value::TimeSecond(v) => Some(*v),
550            Value::TimeMillisecond(v) => Some(*v),
551            Value::Null => None,
552            other => handle_type_mismatch(index, "time32", other),
553        }
554    }
555
556    /// Get time64 value at index (safe version with bounds checking)
557    pub fn get_time64(&self, index: usize) -> Option<i64> {
558        match self.values.get(index)? {
559            Value::TimeMicrosecond(v) => Some(*v),
560            Value::TimeNanosecond(v) => Some(*v),
561            Value::Null => None,
562            other => handle_type_mismatch(index, "time64", other),
563        }
564    }
565
566    /// Get time64 value at index (unsafe version without bounds checking)
567    /// # Safety
568    /// The caller must ensure that `index < self.values.len()`
569    pub unsafe fn get_time64_unchecked(&self, index: usize) -> Option<i64> {
570        match self.values.get_unchecked(index) {
571            Value::TimeMicrosecond(v) => Some(*v),
572            Value::TimeNanosecond(v) => Some(*v),
573            Value::Null => None,
574            other => handle_type_mismatch(index, "time64", other),
575        }
576    }
577
578    /// Get decimal128 value at index (safe version with bounds checking)
579    pub fn get_decimal128(&self, index: usize) -> Option<i128> {
580        match self.values.get(index)? {
581            Value::Decimal128(v) => Some(*v),
582            Value::Null => None,
583            other => handle_type_mismatch(index, "decimal128", other),
584        }
585    }
586
587    /// Get decimal128 value at index (unsafe version without bounds checking)
588    /// # Safety
589    /// The caller must ensure that `index < self.values.len()`
590    pub unsafe fn get_decimal128_unchecked(&self, index: usize) -> Option<i128> {
591        match self.values.get_unchecked(index) {
592            Value::Decimal128(v) => Some(*v),
593            Value::Null => None,
594            other => handle_type_mismatch(index, "decimal128", other),
595        }
596    }
597}
598
599/// Handle type mismatch with debug assertion
600#[inline]
601fn handle_type_mismatch<T>(index: usize, expected: &str, actual: &Value) -> Option<T> {
602    if cfg!(debug_assertions) {
603        panic!("Expected `{expected}` value at index {index}, got {actual:?}")
604    }
605    None
606}
607
608/// Type-safe value wrapper for all GreptimeDB data types
609#[derive(Debug, Clone)]
610pub enum Value {
611    // Boolean
612    Boolean(bool),
613
614    // Integer types
615    Int8(i8),
616    Int16(i16),
617    Int32(i32),
618    Int64(i64),
619    Uint8(u8),
620    Uint16(u16),
621    Uint32(u32),
622    Uint64(u64),
623
624    // Float types
625    Float32(f32),
626    Float64(f64),
627
628    // String and Binary types
629    Binary(Vec<u8>),
630    String(String),
631
632    // Date and Time types
633    Date(i32),     // Days since Unix epoch
634    Datetime(i64), // Milliseconds since Unix epoch
635
636    // Timestamp types
637    TimestampSecond(i64),
638    TimestampMillisecond(i64),
639    TimestampMicrosecond(i64),
640    TimestampNanosecond(i64),
641
642    // Time types (time of day without date)
643    TimeSecond(i32),
644    TimeMillisecond(i32),
645    TimeMicrosecond(i64),
646    TimeNanosecond(i64),
647
648    // Decimal type (`precision` and `scale` are placed in the column schema)
649    Decimal128(i128),
650
651    // JSON type (stored as string)
652    Json(String),
653
654    // Null value
655    Null,
656}
657
658#[cfg(test)]
659mod tests {
660    use super::*;
661
662    #[test]
663    fn test_get_bool_correct_types() {
664        let row = Row::from_values(vec![
665            Value::Boolean(true),
666            Value::Boolean(false),
667            Value::Null,
668        ]);
669
670        // Test correct type
671        assert_eq!(row.get_bool(0), Some(true));
672        assert_eq!(row.get_bool(1), Some(false));
673
674        // Test null value
675        assert_eq!(row.get_bool(2), None);
676    }
677
678    #[test]
679    fn test_get_bool_unchecked_correct_types() {
680        let row = Row::from_values(vec![
681            Value::Boolean(false),
682            Value::Boolean(true),
683            Value::Null,
684        ]);
685
686        unsafe {
687            // Test correct type
688            assert_eq!(row.get_bool_unchecked(0), Some(false));
689            assert_eq!(row.get_bool_unchecked(1), Some(true));
690
691            // Test null value
692            assert_eq!(row.get_bool_unchecked(2), None);
693        }
694    }
695
696    #[test]
697    #[should_panic(expected = "Expected `boolean` value at index 0, got Int32(42)")]
698    fn test_get_bool_type_mismatch_debug_assert() {
699        let row = Row::from_values(vec![Value::Int32(42)]);
700
701        // This should trigger debug_assert! in debug mode
702        let _ = row.get_bool(0);
703    }
704
705    #[test]
706    #[should_panic(expected = "Expected `boolean` value at index 0, got String(\"test\")")]
707    fn test_get_bool_unchecked_type_mismatch_debug_assert() {
708        let row = Row::from_values(vec![Value::String("test".to_string())]);
709
710        unsafe {
711            // This should trigger debug_assert! in debug mode
712            let _ = row.get_bool_unchecked(0);
713        }
714    }
715}