| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.drill.exec.store.easy.json.loader; |
| |
| import static org.apache.drill.test.rowSet.RowSetUtilities.dec; |
| import static org.junit.Assert.assertNotNull; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.junit.Assert.fail; |
| |
| import java.nio.charset.StandardCharsets; |
| import java.time.Duration; |
| import java.time.LocalDateTime; |
| |
| import org.apache.drill.categories.JsonTest; |
| import org.apache.drill.common.exceptions.UserException; |
| import org.apache.drill.common.types.TypeProtos.MinorType; |
| import org.apache.drill.exec.physical.rowSet.RowSet; |
| import org.apache.drill.exec.record.metadata.SchemaBuilder; |
| import org.apache.drill.exec.record.metadata.TupleMetadata; |
| import org.apache.drill.test.rowSet.RowSetUtilities; |
| import org.junit.Test; |
| import org.junit.experimental.categories.Category; |
| |
| /** |
| * Tests JSON scalar handling. Without a schema, the first non-null value |
| * determines the type of the column. With a schema, then the schema determines |
| * the column type independent of the first data row. |
| * <p> |
| * In either case, all scalars perform conversion from all other scalars. If data |
| * is clean, then the conversion will never be used. If the data is messy, then |
| * the result, when combined with a schema, produces a repeatable (if perhaps still |
| * messy) result. The goal is that, with a schema, the query should not fail due |
| * to a few messy rows a billion rows in, or due to the order that the scanners |
| * see the data. |
| */ |
| @Category(JsonTest.class) |
| public class TestScalars extends BaseJsonLoaderTest { |
| |
| /** |
| * Test Boolean type using type inference to guess the type from the |
| * first row of data. All other types can be converted to Boolean. |
| */ |
| @Test |
| public void testBoolean() { |
| String json = |
| "{a: true} {a: false} {a: null} " + |
| "{a: 1} {a: 0} " + |
| "{a: 1.0} {a: 0.0} " + |
| "{a: \"true\"} {a: \"\"} {a: \"false\"} {a: \"other\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.BIT) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow(true) // true |
| .addRow(false) // false |
| .addRow((Boolean) null) // null |
| .addRow(true) // 1 |
| .addRow(false) // 0 |
| .addRow(true) // 1.0 |
| .addRow(false) // 0.0 |
| .addRow(true) // "true" |
| .addRow((Boolean) null) // "" |
| .addRow(false) // "false" |
| .addRow(false) // "other" |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testAllTextBoolean() { |
| String json = |
| "{a: true} {a: false} {a: null}"; |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allTextMode = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow("true") // true |
| .addRow("false") // false |
| .addRow((String) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| /** |
| * Test Boolean with a provided schema which states the column type |
| * independent of the first value. Test with leading values which are |
| * not Boolean. |
| */ |
| @Test |
| public void testBooleanWithSchema() { |
| String json = |
| "{a: 1} {a: 0} " + |
| "{a: 1.0} {a: 0.0} " + |
| "{a: \"true\"} {a: \"\"} {a: \"false\"} {a: \"other\"}" + |
| "{a: true} {a: false} {a: null}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("a", MinorType.BIT) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow(true) // 1 |
| .addRow(false) // 0 |
| .addRow(true) // 1.0 |
| .addRow(false) // 0.0 |
| .addRow(true) // "true" |
| .addRow((Boolean) null) // "" |
| .addRow(false) // "false" |
| .addRow(false) // "other" |
| .addRow(true) // true |
| .addRow(false) // false |
| .addRow((Boolean) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testInt() { |
| String json = |
| "{a: 1} {a: 0} {a: -300} {a: null} " + |
| "{a: true} {a: false} " + |
| "{a: 1.0} {a: 1.4} {a: 1.5} {a: 0.0} " + |
| "{a: \"\"} {a: \"3\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.BIGINT) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow(1) // 1 |
| .addRow(0) // 0 |
| .addRow(-300) // -300 |
| .addRow((Long) null) // null |
| .addRow(1) // true |
| .addRow(0) // false |
| .addRow(1) // 1.0 |
| .addRow(1) // 1.4 |
| .addRow(2) // 1.5 |
| .addRow(0) // 0.0 |
| .addRow((Long) null) // "" |
| .addRow(3) // "3" |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testIntAsDouble() { |
| String json = |
| "{a: 1} {a: 0} {a: -300} {a: null} " + |
| "{a: true} {a: false} " + |
| "{a: 1.0} {a: 1.5} {a: 0.0} " + |
| "{a: \"\"} {a: \"3\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.readNumbersAsDouble = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.FLOAT8) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow(1D) // 1 |
| .addRow(0D) // 0 |
| .addRow(-300D) // -300 |
| .addRow((Double) null) // null |
| .addRow(1D) // true |
| .addRow(0D) // false |
| .addRow(1D) // 1.0 |
| .addRow(1.5D) // 1.5 |
| .addRow(0D) // 0.0 |
| .addRow((Double) null) // "" |
| .addRow(3D) // "3" |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testAllTextInt() { |
| String json = |
| "{a: 1} {a: 0} {a: -300} {a: null}"; |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allTextMode = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow("1") // 1 |
| .addRow("0") // 0 |
| .addRow("-300") // -300 |
| .addRow((String) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testIntWithSchema() { |
| String json = |
| "{a: true} {a: false} " + |
| "{a: 1.0} {a: 1.4} {a: 1.5} {a: 0.0} " + |
| "{a: \"\"} {a: \"3\"} " + |
| "{a: 1} {a: 0} {a: -300} {a: null}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("a", MinorType.BIGINT) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow(1) // true |
| .addRow(0) // false |
| .addRow(1) // 1.0 |
| .addRow(1) // 1.4 |
| .addRow(2) // 1.5 |
| .addRow(0) // 0.0 |
| .addRow((Long) null) // "" |
| .addRow(3) // "3" |
| .addRow(1) // 1 |
| .addRow(0) // 0 |
| .addRow(-300) // -300 |
| .addRow((Long) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| /** |
| * There are limits on Drill's generosity. If no conversion exists |
| * to int, the query will fail with a descriptive error. |
| */ |
| @Test |
| public void testIntWithError() { |
| String json = |
| "{a: 1}\n{a: \"abc\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.open(json); |
| try { |
| loader.next(); |
| fail(); |
| } catch (UserException e) { |
| |
| // Robust check of error contents. Only need test once, error |
| // code is generic |
| String msg = e.getMessage(); |
| assertTrue(msg.contains("not compatible")); |
| assertTrue(msg.contains("Column: a")); |
| assertTrue(msg.contains("Column type: BIGINT")); |
| assertTrue(msg.contains("JSON token type: string")); |
| assertTrue(msg.contains("JSON token: abc")); |
| assertTrue(msg.contains("Line: 2")); |
| } finally { |
| loader.close(); |
| } |
| } |
| |
| @Test |
| public void testFloat() { |
| String json = |
| "{a: 0.0} {a: 1.0} {a: 1.25} {a: -123.125} {a: null} " + |
| "{a: -Infinity} {a: NaN} {a: Infinity} " + |
| "{a: 0} {a: 12} " + |
| "{a: true} {a: false} " + |
| "{a: \"\"} {a: \"3.75\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allowNanInf = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.FLOAT8) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow(0.0) // 0.0 |
| .addRow(1.0) // 1.0 |
| .addRow(1.25) // 1.25 |
| .addRow(-123.125) // -123.125 |
| .addRow((Double) null) // null |
| .addRow(Double.NEGATIVE_INFINITY) // -Inf |
| .addRow(Double.NaN) // Nan |
| .addRow(Double.POSITIVE_INFINITY) // Inf |
| .addRow(0.0) // 0 |
| .addRow(12.0) // 12 |
| .addRow(1.0) // true |
| .addRow(0.0) // false |
| .addRow((Double) null) // "" |
| .addRow(3.75) // "3.75" |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testAllTextFloat() { |
| String json = |
| "{a: 0.0} {a: 1.0} {a: 1.25} {a: -123.125} {a: null} " + |
| "{a: -Infinity} {a: NaN} {a: Infinity}"; |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allTextMode = true; |
| loader.jsonOptions.allowNanInf = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow("0.0") |
| .addRow("1.0") |
| .addRow("1.25") |
| .addRow("-123.125") |
| .addRow((String) null) |
| .addRow("-Infinity") |
| .addRow("NaN") |
| .addRow("Infinity") |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testFloatWithSchema() { |
| String json = |
| "{a: 0} {a: 12} " + |
| "{a: true} {a: false} " + |
| "{a: \"\"} {a: \"3.75\"} " + |
| "{a: 0.0} {a: 1.0} {a: 1.25} {a: -123.125} {a: null} " + |
| "{a: -Infinity} {a: NaN} {a: Infinity}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("a", MinorType.FLOAT8) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.jsonOptions.allowNanInf = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow(0.0) // 0 |
| .addRow(12.0) // 12 |
| .addRow(1.0) // true |
| .addRow(0.0) // false |
| .addRow((Double) null) // "" |
| .addRow(3.75) // "3.75" |
| .addRow(0.0) // 0.0 |
| .addRow(1.0) // 1.0 |
| .addRow(1.25) // 1.25 |
| .addRow(-123.125) // -123.125 |
| .addRow((Double) null) // null |
| .addRow(Double.NEGATIVE_INFINITY) // -Inf |
| .addRow(Double.NaN) // Nan |
| .addRow(Double.POSITIVE_INFINITY) // Inf |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testFloatWithError() { |
| String json = |
| "{a: 1.25}\n{a: \"abc\"}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.open(json); |
| try { |
| loader.next(); |
| fail(); |
| } catch (UserException e) { |
| String msg = e.getMessage(); |
| assertTrue(msg.contains("not compatible")); |
| assertTrue(msg.contains("Column type: DOUBLE")); |
| } finally { |
| loader.close(); |
| } |
| } |
| |
| @Test |
| public void testString() { |
| String json = |
| "{a: \"\"} {a: \"foo\"} {a: \" bar \"} {a: null} " + |
| "{a: 0} {a: 12} " + |
| "{a: true} {a: false} " + |
| "{a: 0.0} {a: 1.25}"; |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow("") // "" |
| .addRow("foo") // "foo" |
| .addRow(" bar ") // " bar " |
| .addRow((String) null) // null |
| .addRow("0") // 0 |
| .addRow("12") // 12 |
| .addRow("true") // true |
| .addRow("false") // false |
| .addRow("0.0") // 0.0 |
| .addRow("1.25") // 1.25 |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testAllTextString() { |
| String json = |
| "{a: \"\"} {a: \"foo\"} {a: \" bar \"} {a: null}"; |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allTextMode = true; |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| TupleMetadata expectedSchema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| RowSet expected = fixture.rowSetBuilder(expectedSchema) |
| .addRow("") // "" |
| .addRow("foo") // "foo" |
| .addRow(" bar ") // " bar " |
| .addRow((String) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testStringWithSchema() { |
| String json = |
| "{a: 0} {a: 12} " + |
| "{a: true} {a: false} " + |
| "{a: 0.0} {a: 1.25} " + |
| "{a: \"\"} {a: \"foo\"} {a: \" bar \"} {a: null}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("a", MinorType.VARCHAR) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow("0") // 0 |
| .addRow("12") // 12 |
| .addRow("true") // true |
| .addRow("false") // false |
| .addRow("0.0") // 0.0 |
| .addRow("1.25") // 1.25 |
| .addRow("") // "" |
| .addRow("foo") // "foo" |
| .addRow(" bar ") // " bar " |
| .addRow((String) null) // null |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testProvidedSchemaNumbers() { |
| String json = |
| |
| // null is ambiguous |
| "{s: null, i: null, bi: null, f4: null, f8: null, d: null}\n" + |
| // Strings are also |
| "{s: \"10\", i: \"10\", bi: \"10\", f4: \"10\", f8: \"10\", d: \"10\"}\n" + |
| "{ f4: \"10.5\", f8: \"10.5\", d: \"10.5\"}\n" + |
| "{ f4: \"-1e5\", f8: \"-1e5\", d: \"-1e5\"}\n" + |
| |
| // Float-only values |
| "{ f4: \"NaN\", f8: \"NaN\"}\n" + |
| "{ f4: \"Infinity\", f8: \"Infinity\"}\n" + |
| "{ f4: \"-Infinity\", f8: \"-Infinity\"}\n" + |
| |
| // Large decimal |
| "{d: \"123456789012345678901234.5678\" }\n" + |
| |
| // Ambiguous numbers |
| "{s: 10, i: 10, bi: 10, f4: 10, f8: 10, d: 10}\n" + |
| "{ f4: 10.5, f8: 10.5, d: 10.5}\n" + |
| "{ f4: -1e5, f8: -1e5, d: -1e5}\n" + |
| |
| // Float-only values |
| "{ f4: NaN, f8: NaN}\n" + |
| "{ f4: Infinity, f8: Infinity}\n" + |
| "{ f4: -Infinity, f8: -Infinity}\n" + |
| |
| // Large decimal |
| "{d: 123456789012345678901234.5678 }\n"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("s", MinorType.SMALLINT) |
| .addNullable("i", MinorType.INT) |
| .addNullable("bi", MinorType.BIGINT) |
| .addNullable("f4", MinorType.FLOAT4) |
| .addNullable("f8", MinorType.FLOAT8) |
| .addNullable("d", MinorType.VARDECIMAL, 38, 4) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.jsonOptions.allowNanInf = true; |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| // s i bi f4 f8 d |
| .addRow(null, null, null, null, null, null) |
| .addRow(10, 10, 10, 10, 10, dec("10")) |
| .addRow(null, null, null, 10.5, 10.5D, dec("10.5")) |
| .addRow(null, null, null, -1e5, -1e5D, dec("-1e5")) |
| .addRow(null, null, null, Float.NaN, Double.NaN, null) |
| .addRow(null, null, null, Float.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, null) |
| .addRow(null, null, null, Float.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, null) |
| .addRow(null, null, null, null, null, dec("123456789012345678901234.5678")) |
| .addRow(10, 10, 10, 10, 10, dec("10")) |
| .addRow(null, null, null, 10.5, 10.5D, dec("10.5")) |
| .addRow(null, null, null, -1e5, -1e5D, dec("-1e5")) |
| .addRow(null, null, null, Float.NaN, Double.NaN, null) |
| .addRow(null, null, null, Float.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, null) |
| .addRow(null, null, null, Float.NEGATIVE_INFINITY, Double.NEGATIVE_INFINITY, null) |
| .addRow(null, null, null, null, null, dec("123456789012345678901234.5678")) |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testProvidedSchemaWithDates() { |
| LocalDateTime local = LocalDateTime.of(2020, 4, 21, 11, 22, 33, 456_000_000); |
| LocalDateTime localEpoch = LocalDateTime.of(1970, 1, 1, 0, 0, 0); |
| long localTs = Duration.between(localEpoch, local).toMillis(); |
| LocalDateTime localDate = LocalDateTime.of(2020, 4, 21, 0, 0, 0); |
| long localDateTs = Duration.between(localEpoch, localDate).toMillis(); |
| int localTimeTs = (int) (localTs - localDateTs); |
| String json = |
| "{ts: null, d: null, t: null}\n" + |
| "{ts: \"2020-04-21T11:22:33.456\", d: \"2020-04-21\", t: \"11:22:33.456\"}\n" + |
| "{ts: " + localTs + ", d: " + localDateTs + ", t: " + localTimeTs + "}\n"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("ts", MinorType.TIMESTAMP) |
| .addNullable("d", MinorType.DATE) |
| .addNullable("t", MinorType.TIME) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow(null, null, null) |
| .addRow(localTs, localDateTs, localTimeTs) |
| .addRow(localTs, localDateTs, localTimeTs) |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testProvidedSchemaWithIntervals() { |
| String json = |
| "{i: null, iy: null, id: null}\n" + |
| "{i: \"P1Y2M3DT4H5M6S\", iy: \"P1Y2M\", id: \"P3DT4H5M6S\"}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("i", MinorType.INTERVAL) |
| .addNullable("iy", MinorType.INTERVALYEAR) |
| .addNullable("id", MinorType.INTERVALDAY) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| org.joda.time.Period full = org.joda.time.Period.years(1).withMonths(2) |
| .withDays(3).withHours(4).withMinutes(5).withSeconds(6); |
| org.joda.time.Period ym = org.joda.time.Period.years(1).withMonths(2); |
| org.joda.time.Period dhms = org.joda.time.Period.days(3).withHours(4) |
| .withMinutes(5).withSeconds(6); |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addRow(null, null, null) |
| .addRow(full, ym, dhms) |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| |
| @Test |
| public void testProvidedSchemaWithBinary() { |
| String json = |
| "{b: null}\n" + |
| "{b: \"ZHJpbGw=\"}"; |
| TupleMetadata schema = new SchemaBuilder() |
| .addNullable("b", MinorType.VARBINARY) |
| .build(); |
| |
| JsonLoaderFixture loader = new JsonLoaderFixture(); |
| loader.builder.providedSchema(schema); |
| loader.open(json); |
| RowSet results = loader.next(); |
| assertNotNull(results); |
| |
| byte[] bytes = "Drill".getBytes(StandardCharsets.UTF_8); |
| RowSet expected = fixture.rowSetBuilder(schema) |
| .addSingleCol(null) |
| .addSingleCol(bytes) |
| .build(); |
| RowSetUtilities.verify(expected, results); |
| assertNull(loader.next()); |
| loader.close(); |
| } |
| } |