Skip to content

Commit 2bfbfc8

Browse files
apacheGH-38532: [MATLAB] Add a validate method to all arrow.array.Array classes (apache#47059)
### Rationale for this change As a follow up to apache#38531 (see apache#38531 (comment)), we should consider adding a `validate` method to all `arrow.array.Array` classes, which would allow users to explicitly validate the contents of an `arrow.array.Array` after it is created. ### What changes are included in this PR? Added `validate()` as a method to `arrow.array.Array`. This method has one name-value pair which is called `ValidationMode`. `ValidationMode` can either be specified as `"minimal"` or `"full"`. By default, `ValidationMode="minimal"`. **Example Usage:** ```matlab >> offsets = arrow.array(int32([0 1 0])); >> values = arrow.array(1:3); >> array = arrow.array.ListArray.fromArrays(offsets, values); >> array.validate(ValidationMode="full") >> array.validate(ValidationMode="full") Error using . (line 63) Offset invariant failure: non-monotonic offset at slot 2: 0 < 1 Error in arrow.array.Array/validate (line 68) obj.Proxy.validate(struct(ValidationMode=uint8(opts.ValidationMode))); ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ``` ### Are these changes tested? Yes. Added a MATLAB test class called `tValidateArray.m`. ### Are there any user-facing changes? Yes. There is a new public method that is accessible via any subclass of `arrow.array.Array`. * GitHub Issue: apache#38532 Lead-authored-by: Sarah Gilmore <[email protected]> Co-authored-by: Sarah Gilmore <[email protected]> Co-authored-by: Kevin Gurney <[email protected]> Signed-off-by: Sarah Gilmore <[email protected]>
1 parent 104f04e commit 2bfbfc8

File tree

6 files changed

+152
-36
lines changed

6 files changed

+152
-36
lines changed

matlab/src/cpp/arrow/matlab/array/proxy/array.cc

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
#include "arrow/util/utf8.h"
2020

2121
#include "arrow/matlab/array/proxy/array.h"
22+
#include "arrow/matlab/array/validation_mode.h"
2223
#include "arrow/matlab/bit/unpack.h"
2324
#include "arrow/matlab/error/error.h"
2425
#include "arrow/matlab/index/validate.h"
@@ -41,6 +42,7 @@ Array::Array(std::shared_ptr<arrow::Array> array) : array{std::move(array)} {
4142
REGISTER_METHOD(Array, isEqual);
4243
REGISTER_METHOD(Array, slice);
4344
REGISTER_METHOD(Array, exportToC);
45+
REGISTER_METHOD(Array, validate);
4446
}
4547

4648
std::shared_ptr<arrow::Array> Array::unwrap() { return array; }
@@ -175,4 +177,37 @@ void Array::exportToC(libmexclass::proxy::method::Context& context) {
175177
error::C_EXPORT_FAILED);
176178
}
177179

180+
void Array::validate(libmexclass::proxy::method::Context& context) {
181+
namespace mda = ::matlab::data;
182+
mda::StructArray args = context.inputs[0];
183+
const mda::TypedArray<std::uint8_t> validation_mode_mda = args[0]["ValidationMode"];
184+
const auto validation_mode_integer = uint8_t(validation_mode_mda[0]);
185+
// Convert integer representation to ValidationMode enum.
186+
const auto validation_mode = static_cast<ValidationMode>(validation_mode_integer);
187+
switch (validation_mode) {
188+
case ValidationMode::None: {
189+
// Do nothing.
190+
break;
191+
}
192+
case ValidationMode::Minimal: {
193+
MATLAB_ERROR_IF_NOT_OK_WITH_CONTEXT(array->Validate(), context,
194+
error::ARRAY_VALIDATE_MINIMAL_FAILED);
195+
break;
196+
}
197+
case ValidationMode::Full: {
198+
MATLAB_ERROR_IF_NOT_OK_WITH_CONTEXT(array->ValidateFull(), context,
199+
error::ARRAY_VALIDATE_FULL_FAILED);
200+
break;
201+
}
202+
default: {
203+
// Throw an error if an unsupported enumeration value is provided.
204+
const auto msg = "Unsupported ValidationMode enumeration value: " +
205+
std::to_string(validation_mode_integer);
206+
context.error =
207+
libmexclass::error::Error{error::ARRAY_VALIDATE_UNSUPPORTED_ENUM, msg};
208+
return;
209+
}
210+
}
211+
}
212+
178213
} // namespace arrow::matlab::array::proxy

matlab/src/cpp/arrow/matlab/array/proxy/array.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,8 @@ class Array : public libmexclass::proxy::Proxy {
4747

4848
void exportToC(libmexclass::proxy::method::Context& context);
4949

50+
void validate(libmexclass::proxy::method::Context& context);
51+
5052
std::shared_ptr<arrow::Array> array;
5153
};
5254

matlab/src/cpp/arrow/matlab/array/proxy/list_array.cc

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,6 @@
1717

1818
#include "arrow/matlab/array/proxy/list_array.h"
1919
#include "arrow/matlab/array/proxy/numeric_array.h"
20-
#include "arrow/matlab/array/validation_mode.h"
2120
#include "arrow/matlab/error/error.h"
2221
#include "arrow/matlab/proxy/wrap.h"
2322

@@ -29,7 +28,6 @@ ListArray::ListArray(std::shared_ptr<arrow::ListArray> list_array)
2928
: proxy::Array{std::move(list_array)} {
3029
REGISTER_METHOD(ListArray, getValues);
3130
REGISTER_METHOD(ListArray, getOffsets);
32-
REGISTER_METHOD(ListArray, validate);
3331
}
3432

3533
libmexclass::proxy::MakeResult ListArray::make(
@@ -98,37 +96,4 @@ void ListArray::getOffsets(libmexclass::proxy::method::Context& context) {
9896
context.outputs[0] = factory.createScalar(offsets_int32_array_proxy_id);
9997
}
10098

101-
void ListArray::validate(libmexclass::proxy::method::Context& context) {
102-
namespace mda = ::matlab::data;
103-
mda::StructArray args = context.inputs[0];
104-
const mda::TypedArray<std::uint8_t> validation_mode_mda = args[0]["ValidationMode"];
105-
const auto validation_mode_integer = uint8_t(validation_mode_mda[0]);
106-
// Convert integer representation to ValidationMode enum.
107-
const auto validation_mode = static_cast<ValidationMode>(validation_mode_integer);
108-
switch (validation_mode) {
109-
case ValidationMode::None: {
110-
// Do nothing.
111-
break;
112-
}
113-
case ValidationMode::Minimal: {
114-
MATLAB_ERROR_IF_NOT_OK_WITH_CONTEXT(array->Validate(), context,
115-
error::ARRAY_VALIDATE_MINIMAL_FAILED);
116-
break;
117-
}
118-
case ValidationMode::Full: {
119-
MATLAB_ERROR_IF_NOT_OK_WITH_CONTEXT(array->ValidateFull(), context,
120-
error::ARRAY_VALIDATE_FULL_FAILED);
121-
break;
122-
}
123-
default: {
124-
// Throw an error if an unsupported enumeration value is provided.
125-
const auto msg = "Unsupported ValidationMode enumeration value: " +
126-
std::to_string(validation_mode_integer);
127-
context.error =
128-
libmexclass::error::Error{error::ARRAY_VALIDATE_UNSUPPORTED_ENUM, msg};
129-
return;
130-
}
131-
}
132-
}
133-
13499
} // namespace arrow::matlab::array::proxy

matlab/src/cpp/arrow/matlab/array/proxy/list_array.h

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ class ListArray : public arrow::matlab::array::proxy::Array {
3232
protected:
3333
void getValues(libmexclass::proxy::method::Context& context);
3434
void getOffsets(libmexclass::proxy::method::Context& context);
35-
void validate(libmexclass::proxy::method::Context& context);
3635
};
3736

3837
} // namespace arrow::matlab::array::proxy

matlab/src/matlab/+arrow/+array/Array.m

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,20 @@
5353
proxy = libmexclass.proxy.Proxy(Name=traits.TypeProxyClassName, ID=typeStruct.ProxyID);
5454
type = traits.TypeConstructor(proxy);
5555
end
56+
57+
function validate(obj, opts)
58+
arguments
59+
obj
60+
opts.Mode(1, 1) arrow.array.ValidationMode = arrow.array.ValidationMode.Minimal
61+
end
62+
63+
if opts.Mode == arrow.array.ValidationMode.None
64+
id = "arrow:array:InvalidValidationMode";
65+
msg = "Invalid Mode. Mode must be ""Minimal"" or ""Full"".";
66+
error(id, msg);
67+
end
68+
obj.Proxy.validate(struct(ValidationMode=uint8(opts.Mode)));
69+
end
5670
end
5771

5872
methods (Access = private)
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
%TVALIDATEARRAY Test class for the arrow.array.Array/validate() method.
2+
3+
% Licensed to the Apache Software Foundation (ASF) under one or more
4+
% contributor license agreements. See the NOTICE file distributed with
5+
% this work for additional information regarding copyright ownership.
6+
% The ASF licenses this file to you under the Apache License, Version
7+
% 2.0 (the "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, software
13+
% distributed under the License is distributed on an "AS IS" BASIS,
14+
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
15+
% implied. See the License for the specific language governing
16+
% permissions and limitations under the License.
17+
18+
classdef tValidateArray < matlab.unittest.TestCase
19+
20+
methods (Test)
21+
22+
function InvalidModeInput(test)
23+
% Verify arrow.array.Array/validate() throws an exception if
24+
% provided an invalid value for the Mode name-value
25+
% pair.
26+
array = arrow.array.Float64Array.fromMATLAB(1:5);
27+
28+
% Cannot convert "abc" to a ValidationMode value
29+
fcn = @() array.validate(Mode="abc");
30+
test.verifyError(fcn, "MATLAB:validation:UnableToConvert");
31+
32+
% Mode must be scalar
33+
modes = [arrow.array.ValidationMode.Full arrow.array.ValidationMode.Minimal];
34+
fcn = @() array.validate(Mode=modes);
35+
test.verifyError(fcn, "MATLAB:validation:IncompatibleSize");
36+
37+
% ValidationMode.None is not supported
38+
mode = arrow.array.ValidationMode.None;
39+
fcn = @() array.validate(Mode=mode);
40+
test.verifyError(fcn, "arrow:array:InvalidValidationMode");
41+
end
42+
43+
function ValidationModeMinimalFails(test)
44+
% Verify arrow.array.Array/validate() throws an exception
45+
% with the ID arrow:array:ValidateMinimalFailed if
46+
% Mode="Minimal" and the array fails the "Minimal"
47+
% validation checks.
48+
offsets = arrow.array(int32([0 1 3 4 5]));
49+
values = arrow.array([1 2 3]);
50+
array = arrow.array.ListArray.fromArrays(offsets, values, ValidationMode="None");
51+
fcn = @() array.validate(Mode="Minimal");
52+
test.verifyError(fcn, "arrow:array:ValidateMinimalFailed")
53+
end
54+
55+
function ValidationModeMinimalPasses(test)
56+
% Verify arrow.array.Array/validate() does not throw an
57+
% exception if Mode="Minimal" and the array passes the
58+
% "Minimal" validation checks.
59+
offsets = arrow.array(int32([0 1 0]));
60+
values = arrow.array([1 2 3]);
61+
% NOTE: the array is actually invalid, but it passes the
62+
% "Minimal" validation checks.
63+
array = arrow.array.ListArray.fromArrays(offsets, values);
64+
fcn = @() array.validate(Mode="Minimal");
65+
test.verifyWarningFree(fcn, "arrow:array:ValidateMinimalFailed")
66+
end
67+
68+
function ValidationModeFullFails(test)
69+
% Verify arrow.array.Array/validate() throws an exception
70+
% with the ID arrow:array:ValidateFullFailed if
71+
% Mode="Full" and the array fails the "Full"
72+
% validation checks.
73+
offsets = arrow.array(int32([0 1 0]));
74+
values = arrow.array([1 2 3]);
75+
array = arrow.array.ListArray.fromArrays(offsets, values);
76+
fcn = @() array.validate(Mode="Full");
77+
test.verifyError(fcn, "arrow:array:ValidateFullFailed")
78+
end
79+
80+
function ValidationModeFullPasses(test)
81+
% Verify arrow.array.Array/validate() does not throw an
82+
% exception if Mode="Full" and the array passes
83+
% the "full" validation checks.
84+
offsets = arrow.array(int32([0 1 3]));
85+
values = arrow.array([1 2 3]);
86+
array = arrow.array.ListArray.fromArrays(offsets, values);
87+
fcn = @() array.validate(Mode="Full");
88+
test.verifyWarningFree(fcn);
89+
end
90+
91+
function DefaultValidationModeIsMimimal(test)
92+
% Verify the default Mode value is "Minimal".
93+
offsets = arrow.array(int32([0 1 2 3]));
94+
values = arrow.array([1 2 3]);
95+
array = arrow.array.ListArray.fromArrays(offsets, values);
96+
fcn = @() array.validate();
97+
test.verifyWarningFree(fcn, "arrow:array:ValidateMinimalFailed")
98+
end
99+
end
100+
101+
end

0 commit comments

Comments
 (0)