This is an automated email from the ASF dual-hosted git repository. bchapuis pushed a commit to branch calcite-schema-ddl in repository https://gitbox.apache.org/repos/asf/incubator-baremaps.git
commit e37d95e23cea5903d9bccc82d34aa8362f8ac568 Author: Bertil Chapuis <[email protected]> AuthorDate: Mon Apr 14 17:49:38 2025 +0200 Add RPSL schema and tests --- .../apache/baremaps/calcite/rpsl/RpslSchema.java | 155 +++++++++++++++++++++ .../baremaps/calcite/rpsl/RpslSchemaTest.java | 151 ++++++++++++++++++++ .../baremaps/calcite/rpsl/RpslTableTest.java | 2 +- .../org/apache/baremaps/iploc/IpLocObjectTest.java | 2 +- .../org/apache/baremaps/rpsl/RpslObjectTest.java | 2 +- .../org/apache/baremaps/rpsl/RpslParserTest.java | 4 +- baremaps-testing/data/{ripe => rpsl}/sample.txt | 0 .../org/apache/baremaps/testing/TestFiles.java | 4 +- 8 files changed, 313 insertions(+), 7 deletions(-) diff --git a/baremaps-calcite/src/main/java/org/apache/baremaps/calcite/rpsl/RpslSchema.java b/baremaps-calcite/src/main/java/org/apache/baremaps/calcite/rpsl/RpslSchema.java new file mode 100644 index 000000000..e5f98af3c --- /dev/null +++ b/baremaps-calcite/src/main/java/org/apache/baremaps/calcite/rpsl/RpslSchema.java @@ -0,0 +1,155 @@ +/* + * 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 + * 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.baremaps.calcite.rpsl; + +import java.io.File; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.Objects; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.schema.Table; +import org.apache.calcite.schema.impl.AbstractSchema; + +/** + * A Calcite schema implementation for RPSL data. This schema provides access to RPSL files + * through the Apache Calcite framework for SQL querying. + */ +public class RpslSchema extends AbstractSchema { + + private final File directory; + private final Map<String, Table> tableMap; + private final RelDataTypeFactory typeFactory; + + /** + * Constructs a RpslSchema with the specified directory. + * + * @param directory the directory containing RPSL files + * @param typeFactory the type factory to use for creating tables + * @throws IOException if an I/O error occurs + */ + public RpslSchema(File directory, RelDataTypeFactory typeFactory) throws IOException { + this.directory = Objects.requireNonNull(directory, "Directory cannot be null"); + this.typeFactory = Objects.requireNonNull(typeFactory, "Type factory cannot be null"); + this.tableMap = new HashMap<>(); + + // Process files in the directory + File[] files = directory.listFiles((dir, name) -> + name.toLowerCase().endsWith(".rpsl") || name.toLowerCase().endsWith(".txt")); + + if (files != null) { + for (File file : files) { + // Extract the base name without extension (e.g., "routing" from "routing.rpsl") + String fileName = file.getName(); + String tableName = fileName; + + // Remove all extensions (e.g., "routing.rpsl" -> "routing") + while (tableName.contains(".")) { + int lastDotIndex = tableName.lastIndexOf('.'); + if (lastDotIndex > 0) { + tableName = tableName.substring(0, lastDotIndex); + } else { + break; + } + } + + // Create the table with the file reference + tableMap.put(tableName, createTable(file)); + } + } + } + + /** + * Constructs a RpslSchema with a single file. + * + * @param file the RPSL file + * @param typeFactory the type factory to use for creating tables + * @throws IOException if an I/O error occurs + */ + public RpslSchema(File file, RelDataTypeFactory typeFactory, boolean isDirectory) throws IOException { + if (isDirectory) { + // If isDirectory is true, treat the file as a directory + this.directory = Objects.requireNonNull(file, "Directory cannot be null"); + this.typeFactory = Objects.requireNonNull(typeFactory, "Type factory cannot be null"); + this.tableMap = new HashMap<>(); + + // Process files in the directory + File[] files = file.listFiles((dir, name) -> + name.toLowerCase().endsWith(".rpsl") || name.toLowerCase().endsWith(".txt")); + + if (files != null) { + for (File rpslFile : files) { + // Extract the base name without extension (e.g., "routing" from "routing.rpsl") + String fileName = rpslFile.getName(); + String tableName = fileName; + + // Remove all extensions (e.g., "routing.rpsl" -> "routing") + while (tableName.contains(".")) { + int lastDotIndex = tableName.lastIndexOf('.'); + if (lastDotIndex > 0) { + tableName = tableName.substring(0, lastDotIndex); + } else { + break; + } + } + + // Create the table with the file reference + tableMap.put(tableName, createTable(rpslFile)); + } + } + } else { + // If isDirectory is false, treat the file as a single file + this.directory = Objects.requireNonNull(file, "File cannot be null"); + this.typeFactory = Objects.requireNonNull(typeFactory, "Type factory cannot be null"); + this.tableMap = new HashMap<>(); + + // Extract the base name without extension (e.g., "routing" from "routing.rpsl") + String fileName = file.getName(); + String tableName = fileName; + + // Remove all extensions (e.g., "routing.rpsl" -> "routing") + while (tableName.contains(".")) { + int lastDotIndex = tableName.lastIndexOf('.'); + if (lastDotIndex > 0) { + tableName = tableName.substring(0, lastDotIndex); + } else { + break; + } + } + + // Create the table with the file reference + tableMap.put(tableName, createTable(file)); + } + } + + /** + * Creates a table for the given file. + * + * @param file the RPSL file + * @return the created table + * @throws IOException if an I/O error occurs + */ + private Table createTable(File file) throws IOException { + return new RpslTable(file); + } + + @Override + protected Map<String, Table> getTableMap() { + return tableMap; + } +} \ No newline at end of file diff --git a/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslSchemaTest.java b/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslSchemaTest.java new file mode 100644 index 000000000..276880127 --- /dev/null +++ b/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslSchemaTest.java @@ -0,0 +1,151 @@ +/* + * 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 + * 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.baremaps.calcite.rpsl; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.StandardCopyOption; +import java.sql.Connection; +import java.sql.DriverManager; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Statement; +import org.apache.baremaps.testing.TestFiles; +import org.apache.calcite.config.CalciteConnectionConfig; +import org.apache.calcite.config.CalciteConnectionConfigImpl; +import org.apache.calcite.config.CalciteConnectionProperty; +import org.apache.calcite.jdbc.CalciteConnection; +import org.apache.calcite.rel.type.RelDataTypeFactory; +import org.apache.calcite.schema.SchemaPlus; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.io.TempDir; + +/** + * Test class for the RpslSchema implementation. + */ +public class RpslSchemaTest { + + @TempDir + Path tempDir; + + @BeforeEach + public void setup() throws SQLException, IOException { + // Create a temporary directory for test files + Path testDir = tempDir.resolve("rpsl-test"); + Files.createDirectories(testDir); + + // Copy the sample RPSL file from TestFiles + Path sourceFile = TestFiles.RPSL_TXT; + Path targetFile = testDir.resolve("sample.txt"); + Files.copy(sourceFile, targetFile, StandardCopyOption.REPLACE_EXISTING); + } + + @Test + public void testSchemaCreation() throws SQLException, IOException { + // Create a connection to Calcite + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class); + SchemaPlus rootSchema = calciteConnection.getRootSchema(); + + // Create and register the RPSL schema + RpslSchema schema = new RpslSchema(tempDir.resolve("rpsl-test").toFile(), calciteConnection.getTypeFactory()); + rootSchema.add("rpsl", schema); + + // Verify that the schema contains the expected table + assertTrue(rootSchema.getSubSchemaNames().contains("rpsl")); + assertTrue(rootSchema.getSubSchema("rpsl").getTableNames().contains("sample")); + + connection.close(); + } + + @Test + public void testSqlQuery() throws SQLException, IOException { + // Create a connection to Calcite + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class); + SchemaPlus rootSchema = calciteConnection.getRootSchema(); + + // Create and register the RPSL schema + RpslSchema schema = new RpslSchema(tempDir.resolve("rpsl-test").toFile(), calciteConnection.getTypeFactory()); + rootSchema.add("rpsl", schema); + + // Execute a simple SQL query - use lowercase for schema and table names + try (Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery( + "SELECT * FROM \"rpsl\".\"sample\""); + + // Verify that we get results + assertTrue(resultSet.next()); + + // Verify that the result set has the expected columns + assertNotNull(resultSet.getMetaData()); + assertTrue(resultSet.getMetaData().getColumnCount() > 0); + + // Verify that we can access the data + String inetnum = resultSet.getString("inetnum"); + assertNotNull(inetnum); + } + + connection.close(); + } + + @Test + public void testSingleFileSchema() throws SQLException, IOException { + // Create a connection to Calcite + Connection connection = DriverManager.getConnection("jdbc:calcite:"); + CalciteConnection calciteConnection = connection.unwrap(CalciteConnection.class); + SchemaPlus rootSchema = calciteConnection.getRootSchema(); + + // Get the sample RPSL file + File sampleFile = tempDir.resolve("rpsl-test").resolve("sample.txt").toFile(); + + // Create and register the RPSL schema with a single file + RpslSchema schema = new RpslSchema(sampleFile, calciteConnection.getTypeFactory(), false); + rootSchema.add("single", schema); + + // Verify that the schema contains the expected table + assertTrue(rootSchema.getSubSchemaNames().contains("single")); + assertTrue(rootSchema.getSubSchema("single").getTableNames().contains("sample")); + + // Execute a simple SQL query - use lowercase for schema and table names + try (Statement statement = connection.createStatement()) { + ResultSet resultSet = statement.executeQuery( + "SELECT * FROM \"single\".\"sample\""); + + // Verify that we get results + assertTrue(resultSet.next()); + + // Verify that the result set has the expected columns + assertNotNull(resultSet.getMetaData()); + assertTrue(resultSet.getMetaData().getColumnCount() > 0); + + // Verify that we can access the data + String inetnum = resultSet.getString("inetnum"); + assertNotNull(inetnum); + } + + connection.close(); + } +} \ No newline at end of file diff --git a/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslTableTest.java b/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslTableTest.java index 846ce1e08..be7558c34 100644 --- a/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslTableTest.java +++ b/baremaps-calcite/src/test/java/org/apache/baremaps/calcite/rpsl/RpslTableTest.java @@ -37,7 +37,7 @@ import org.junit.jupiter.api.Test; class RpslTableTest { private static final File SAMPLE_RPSL_FILE = - TestFiles.RIPE_TXT.toFile(); + TestFiles.RPSL_TXT.toFile(); @Test void testSchemaVerification() throws IOException { diff --git a/baremaps-core/src/test/java/org/apache/baremaps/iploc/IpLocObjectTest.java b/baremaps-core/src/test/java/org/apache/baremaps/iploc/IpLocObjectTest.java index 713b6a37f..5d2c58ff9 100644 --- a/baremaps-core/src/test/java/org/apache/baremaps/iploc/IpLocObjectTest.java +++ b/baremaps-core/src/test/java/org/apache/baremaps/iploc/IpLocObjectTest.java @@ -61,7 +61,7 @@ class IpLocObjectTest { @BeforeAll public static void beforeAll() throws Exception { // Load the NIC sample objects - var file = TestFiles.resolve("baremaps-testing/data/ripe/sample.txt"); + var file = TestFiles.RPSL_TXT; try (var input = Files.newInputStream(file)) { rpslObjects = new RpslReader().read(input).toList(); } diff --git a/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslObjectTest.java b/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslObjectTest.java index a3cfb75b5..80d0a3a84 100644 --- a/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslObjectTest.java +++ b/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslObjectTest.java @@ -32,7 +32,7 @@ class RpslObjectTest { @BeforeEach public void before() throws IOException { - var file = TestFiles.resolve("baremaps-testing/data/ripe/sample.txt"); + var file = TestFiles.RPSL_TXT; try (var input = Files.newInputStream(file)) { objects = new RpslReader().read(input).toList(); } diff --git a/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslParserTest.java b/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslParserTest.java index cd795c622..48e8feae9 100644 --- a/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslParserTest.java +++ b/baremaps-rpsl/src/test/java/org/apache/baremaps/rpsl/RpslParserTest.java @@ -30,7 +30,7 @@ class RpslParserTest { @Test void parseObjects() throws IOException { - var file = TestFiles.resolve("baremaps-testing/data/ripe/sample.txt"); + var file = TestFiles.RPSL_TXT; try (var input = Files.newInputStream(file)) { List<RpslObject> objects = new RpslReader().read(input).toList(); assertEquals(10, objects.size()); @@ -39,7 +39,7 @@ class RpslParserTest { @Test void parseAttributes() throws IOException { - var file = TestFiles.resolve("baremaps-testing/data/ripe/sample.txt"); + var file = TestFiles.RPSL_TXT; try (var input = Files.newInputStream(file)) { List<RpslObject> objects = new RpslReader().read(input).toList(); List<RpslAttribute> attributes = objects.stream() diff --git a/baremaps-testing/data/ripe/sample.txt b/baremaps-testing/data/rpsl/sample.txt similarity index 100% rename from baremaps-testing/data/ripe/sample.txt rename to baremaps-testing/data/rpsl/sample.txt diff --git a/baremaps-testing/src/main/java/org/apache/baremaps/testing/TestFiles.java b/baremaps-testing/src/main/java/org/apache/baremaps/testing/TestFiles.java index 921cd1319..372983fd4 100644 --- a/baremaps-testing/src/main/java/org/apache/baremaps/testing/TestFiles.java +++ b/baremaps-testing/src/main/java/org/apache/baremaps/testing/TestFiles.java @@ -101,8 +101,8 @@ public class TestFiles { public static final Path POINT_FLATGEOBUF = resolve("baremaps-testing/data/flatgeobuf/countries.fgb"); - public static final Path RIPE_TXT = - resolve("baremaps-testing/data/ripe/sample.txt"); + public static final Path RPSL_TXT = + resolve("baremaps-testing/data/rpsl/sample.txt"); /* The geometries of the osm-sample/sample.osm.xml file */
