Skip to content

Commit 17c500a

Browse files
lidavidmFokko
andauthored
Make field repr explicit about optionality, add exception type (#43)
- Fix some clang-tidy lints (`virtual ~Foo() = default` -> `~Foo() override = default`, missing `explicit`) - Add `iceberg::IcebergError` instead of `std::runtime_error` - Make field repr explicitly put `(optional)` - Note current time complexity of field accessors --------- Co-authored-by: Fokko Driesprong <[email protected]>
1 parent ebacc54 commit 17c500a

File tree

6 files changed

+91
-48
lines changed

6 files changed

+91
-48
lines changed

src/iceberg/exception.h

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one
3+
* or more contributor license agreements. See the NOTICE file
4+
* distributed with this work for additional information
5+
* regarding copyright ownership. The ASF licenses this file
6+
* to you under the Apache License, Version 2.0 (the
7+
* "License"); you may not use this file except in compliance
8+
* with the License. You may obtain a copy of the License at
9+
*
10+
* http://www.apache.org/licenses/LICENSE-2.0
11+
*
12+
* Unless required by applicable law or agreed to in writing,
13+
* software distributed under the License is distributed on an
14+
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
* KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations
17+
* under the License.
18+
*/
19+
20+
#pragma once
21+
22+
/// \file iceberg/exception.h
23+
/// Common exception types for Iceberg. Note that this library primarily uses
24+
/// return values for error handling, not exceptions. Some operations,
25+
/// however, will throw exceptions in contexts where no other option is
26+
/// available (e.g. a constructor). In those cases, an exception type from
27+
/// here will be used.
28+
29+
#include <stdexcept>
30+
31+
namespace iceberg {
32+
33+
/// \brief Base exception class for exceptions thrown by the Iceberg library.
34+
class ICEBERG_EXPORT IcebergError : public std::runtime_error {
35+
public:
36+
explicit IcebergError(const std::string& what) : std::runtime_error(what) {}
37+
};
38+
39+
} // namespace iceberg

src/iceberg/schema_field.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -52,8 +52,8 @@ const std::shared_ptr<Type>& SchemaField::type() const { return type_; }
5252
bool SchemaField::optional() const { return optional_; }
5353

5454
std::string SchemaField::ToString() const {
55-
return std::format("{} ({}): {}{}", name_, field_id_, *type_,
56-
optional_ ? "" : " (required)");
55+
return std::format("{} ({}): {} ({})", name_, field_id_, *type_,
56+
optional_ ? "optional" : "required");
5757
}
5858

5959
bool SchemaField::Equals(const SchemaField& other) const {

src/iceberg/type.cc

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <iterator>
2424
#include <stdexcept>
2525

26+
#include "iceberg/exception.h"
2627
#include "iceberg/util/formatter.h"
2728

2829
namespace iceberg {
@@ -32,7 +33,7 @@ StructType::StructType(std::vector<SchemaField> fields) : fields_(std::move(fiel
3233
for (const auto& field : fields_) {
3334
auto [it, inserted] = field_id_to_index_.try_emplace(field.field_id(), index);
3435
if (!inserted) {
35-
throw std::runtime_error(
36+
throw IcebergError(
3637
std::format("StructType: duplicate field ID {} (field indices {} and {})",
3738
field.field_id(), it->second, index));
3839
}
@@ -66,8 +67,8 @@ std::optional<std::reference_wrapper<const SchemaField>> StructType::GetFieldByI
6667
}
6768
std::optional<std::reference_wrapper<const SchemaField>> StructType::GetFieldByName(
6869
std::string_view name) const {
69-
// TODO: what is the right behavior if there are duplicate names? (Are
70-
// duplicate names permitted?)
70+
// N.B. duplicate names are not permitted (looking at the Java
71+
// implementation) so there is nothing in particular we need to do here
7172
for (const auto& field : fields_) {
7273
if (field.name() == name) {
7374
return field;
@@ -85,9 +86,8 @@ bool StructType::Equals(const Type& other) const {
8586

8687
ListType::ListType(SchemaField element) : element_(std::move(element)) {
8788
if (element_.name() != kElementName) {
88-
throw std::runtime_error(
89-
std::format("ListType: child field name should be '{}', was '{}'", kElementName,
90-
element_.name()));
89+
throw IcebergError(std::format("ListType: child field name should be '{}', was '{}'",
90+
kElementName, element_.name()));
9191
}
9292
}
9393

@@ -136,14 +136,12 @@ bool ListType::Equals(const Type& other) const {
136136
MapType::MapType(SchemaField key, SchemaField value)
137137
: fields_{std::move(key), std::move(value)} {
138138
if (this->key().name() != kKeyName) {
139-
throw std::runtime_error(
140-
std::format("MapType: key field name should be '{}', was '{}'", kKeyName,
141-
this->key().name()));
139+
throw IcebergError(std::format("MapType: key field name should be '{}', was '{}'",
140+
kKeyName, this->key().name()));
142141
}
143142
if (this->value().name() != kValueName) {
144-
throw std::runtime_error(
145-
std::format("MapType: value field name should be '{}', was '{}'", kValueName,
146-
this->value().name()));
143+
throw IcebergError(std::format("MapType: value field name should be '{}', was '{}'",
144+
kValueName, this->value().name()));
147145
}
148146
}
149147

@@ -226,7 +224,7 @@ bool DoubleType::Equals(const Type& other) const {
226224
DecimalType::DecimalType(int32_t precision, int32_t scale)
227225
: precision_(precision), scale_(scale) {
228226
if (precision < 0 || precision > kMaxPrecision) {
229-
throw std::runtime_error(
227+
throw IcebergError(
230228
std::format("DecimalType: precision must be in [0, 38], was {}", precision));
231229
}
232230
}
@@ -287,8 +285,7 @@ bool UuidType::Equals(const Type& other) const {
287285

288286
FixedType::FixedType(int32_t length) : length_(length) {
289287
if (length < 0) {
290-
throw std::runtime_error(
291-
std::format("FixedType: length must be >= 0, was {}", length));
288+
throw IcebergError(std::format("FixedType: length must be >= 0, was {}", length));
292289
}
293290
}
294291

src/iceberg/type.h

Lines changed: 25 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ namespace iceberg {
4141
/// \brief Interface for a data type for a field.
4242
class ICEBERG_EXPORT Type : public iceberg::util::Formattable {
4343
public:
44-
virtual ~Type() = default;
44+
~Type() override = default;
4545

4646
/// \brief Get the type ID.
4747
[[nodiscard]] virtual TypeId type_id() const = 0;
@@ -79,14 +79,20 @@ class ICEBERG_EXPORT NestedType : public Type {
7979
/// \brief Get a view of the child fields.
8080
[[nodiscard]] virtual std::span<const SchemaField> fields() const = 0;
8181
/// \brief Get a field by field ID.
82+
///
83+
/// \note This is O(1) complexity.
8284
[[nodiscard]] virtual std::optional<std::reference_wrapper<const SchemaField>>
8385
GetFieldById(int32_t field_id) const = 0;
8486
/// \brief Get a field by index.
87+
///
88+
/// \note This is O(1) complexity.
8589
[[nodiscard]] virtual std::optional<std::reference_wrapper<const SchemaField>>
8690
GetFieldByIndex(int32_t index) const = 0;
8791
/// \brief Get a field by name (case-sensitive). Behavior is undefined if
8892
/// the field name is not unique; prefer GetFieldById or GetFieldByIndex
8993
/// when possible.
94+
///
95+
/// \note This is currently O(n) complexity.
9096
[[nodiscard]] virtual std::optional<std::reference_wrapper<const SchemaField>>
9197
GetFieldByName(std::string_view name) const = 0;
9298
};
@@ -99,7 +105,7 @@ class ICEBERG_EXPORT NestedType : public Type {
99105
class ICEBERG_EXPORT StructType : public NestedType {
100106
public:
101107
explicit StructType(std::vector<SchemaField> fields);
102-
~StructType() = default;
108+
~StructType() override = default;
103109

104110
TypeId type_id() const override;
105111
std::string ToString() const override;
@@ -129,7 +135,7 @@ class ICEBERG_EXPORT ListType : public NestedType {
129135
explicit ListType(SchemaField element);
130136
/// \brief Construct a list of the given element type.
131137
ListType(int32_t field_id, std::shared_ptr<Type> type, bool optional);
132-
~ListType() = default;
138+
~ListType() override = default;
133139

134140
TypeId type_id() const override;
135141
std::string ToString() const override;
@@ -157,7 +163,7 @@ class ICEBERG_EXPORT MapType : public NestedType {
157163
/// \brief Construct a map of the given key/value fields. The field names
158164
/// should be "key" and "value", respectively.
159165
explicit MapType(SchemaField key, SchemaField value);
160-
~MapType() = default;
166+
~MapType() override = default;
161167

162168
const SchemaField& key() const;
163169
const SchemaField& value() const;
@@ -189,7 +195,7 @@ class ICEBERG_EXPORT MapType : public NestedType {
189195
class ICEBERG_EXPORT BooleanType : public PrimitiveType {
190196
public:
191197
BooleanType() = default;
192-
~BooleanType() = default;
198+
~BooleanType() override = default;
193199

194200
TypeId type_id() const override;
195201
std::string ToString() const override;
@@ -202,7 +208,7 @@ class ICEBERG_EXPORT BooleanType : public PrimitiveType {
202208
class ICEBERG_EXPORT IntType : public PrimitiveType {
203209
public:
204210
IntType() = default;
205-
~IntType() = default;
211+
~IntType() override = default;
206212

207213
TypeId type_id() const override;
208214
std::string ToString() const override;
@@ -215,7 +221,7 @@ class ICEBERG_EXPORT IntType : public PrimitiveType {
215221
class ICEBERG_EXPORT LongType : public PrimitiveType {
216222
public:
217223
LongType() = default;
218-
~LongType() = default;
224+
~LongType() override = default;
219225

220226
TypeId type_id() const override;
221227
std::string ToString() const override;
@@ -229,7 +235,7 @@ class ICEBERG_EXPORT LongType : public PrimitiveType {
229235
class ICEBERG_EXPORT FloatType : public PrimitiveType {
230236
public:
231237
FloatType() = default;
232-
~FloatType() = default;
238+
~FloatType() override = default;
233239

234240
TypeId type_id() const override;
235241
std::string ToString() const override;
@@ -243,7 +249,7 @@ class ICEBERG_EXPORT FloatType : public PrimitiveType {
243249
class ICEBERG_EXPORT DoubleType : public PrimitiveType {
244250
public:
245251
DoubleType() = default;
246-
~DoubleType() = default;
252+
~DoubleType() override = default;
247253

248254
TypeId type_id() const override;
249255
std::string ToString() const override;
@@ -259,7 +265,7 @@ class ICEBERG_EXPORT DecimalType : public PrimitiveType {
259265

260266
/// \brief Construct a decimal type with the given precision and scale.
261267
DecimalType(int32_t precision, int32_t scale);
262-
~DecimalType() = default;
268+
~DecimalType() override = default;
263269

264270
/// \brief Get the precision (the number of decimal digits).
265271
[[nodiscard]] int32_t precision() const;
@@ -283,7 +289,7 @@ class ICEBERG_EXPORT DecimalType : public PrimitiveType {
283289
class ICEBERG_EXPORT DateType : public PrimitiveType {
284290
public:
285291
DateType() = default;
286-
~DateType() = default;
292+
~DateType() override = default;
287293

288294
TypeId type_id() const override;
289295
std::string ToString() const override;
@@ -297,7 +303,7 @@ class ICEBERG_EXPORT DateType : public PrimitiveType {
297303
class ICEBERG_EXPORT TimeType : public PrimitiveType {
298304
public:
299305
TimeType() = default;
300-
~TimeType() = default;
306+
~TimeType() override = default;
301307

302308
TypeId type_id() const override;
303309
std::string ToString() const override;
@@ -321,7 +327,7 @@ class ICEBERG_EXPORT TimestampBase : public PrimitiveType {
321327
class ICEBERG_EXPORT TimestampType : public TimestampBase {
322328
public:
323329
TimestampType() = default;
324-
~TimestampType() = default;
330+
~TimestampType() override = default;
325331

326332
bool is_zoned() const override;
327333
TimeUnit time_unit() const override;
@@ -338,7 +344,7 @@ class ICEBERG_EXPORT TimestampType : public TimestampBase {
338344
class ICEBERG_EXPORT TimestampTzType : public TimestampBase {
339345
public:
340346
TimestampTzType() = default;
341-
~TimestampTzType() = default;
347+
~TimestampTzType() override = default;
342348

343349
bool is_zoned() const override;
344350
TimeUnit time_unit() const override;
@@ -354,7 +360,7 @@ class ICEBERG_EXPORT TimestampTzType : public TimestampBase {
354360
class ICEBERG_EXPORT BinaryType : public PrimitiveType {
355361
public:
356362
BinaryType() = default;
357-
~BinaryType() = default;
363+
~BinaryType() override = default;
358364

359365
TypeId type_id() const override;
360366
std::string ToString() const override;
@@ -368,7 +374,7 @@ class ICEBERG_EXPORT BinaryType : public PrimitiveType {
368374
class ICEBERG_EXPORT StringType : public PrimitiveType {
369375
public:
370376
StringType() = default;
371-
~StringType() = default;
377+
~StringType() override = default;
372378

373379
TypeId type_id() const override;
374380
std::string ToString() const override;
@@ -381,8 +387,8 @@ class ICEBERG_EXPORT StringType : public PrimitiveType {
381387
class ICEBERG_EXPORT FixedType : public PrimitiveType {
382388
public:
383389
/// \brief Construct a fixed type with the given length.
384-
FixedType(int32_t length);
385-
~FixedType() = default;
390+
explicit FixedType(int32_t length);
391+
~FixedType() override = default;
386392

387393
/// \brief The length (the number of bytes to store).
388394
[[nodiscard]] int32_t length() const;
@@ -402,7 +408,7 @@ class ICEBERG_EXPORT FixedType : public PrimitiveType {
402408
class ICEBERG_EXPORT UuidType : public PrimitiveType {
403409
public:
404410
UuidType() = default;
405-
~UuidType() = default;
411+
~UuidType() override = default;
406412

407413
TypeId type_id() const override;
408414
std::string ToString() const override;

test/core/schema_field_test.cc

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ TEST(SchemaFieldTest, Basics) {
4444
EXPECT_EQ("foo bar", field.name());
4545
EXPECT_EQ(iceberg::FixedType(10), *field.type());
4646
EXPECT_TRUE(field.optional());
47-
EXPECT_EQ("foo bar (2): fixed(10)", field.ToString());
48-
EXPECT_EQ("foo bar (2): fixed(10)", std::format("{}", field));
47+
EXPECT_EQ("foo bar (2): fixed(10) (optional)", field.ToString());
48+
EXPECT_EQ("foo bar (2): fixed(10) (optional)", std::format("{}", field));
4949
}
5050
{
5151
iceberg::SchemaField field = iceberg::SchemaField::MakeRequired(

0 commit comments

Comments
 (0)