Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON & XML Sources #507

Open
wants to merge 71 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
c11a877
Start implementation (not working yet)
Apr 24, 2024
5202005
Start implementation (not working yet)
Apr 24, 2024
1fa81f6
Fix gradle compile issue
Apr 24, 2024
f294e89
Add json to document conversion
Apr 24, 2024
c248533
Add JSON enumerator
Apr 25, 2024
54746cb
Add namespace and meta functions
Apr 25, 2024
001248f
Fix gradle
Apr 25, 2024
1a4f8af
Add example data
Apr 25, 2024
12aa4e8
Add json conversion
Apr 25, 2024
e56271d
Improve meta retrieval
Apr 30, 2024
f986ca6
Add delegate
May 2, 2024
d1292e8
Start document source refactoring
May 2, 2024
8196df9
Introduce data model handling
May 3, 2024
71977e1
Implement ScanRule, Scan and costs
May 6, 2024
1b88ae4
Fix query execution
Jun 19, 2024
1f9064c
Fix merge conflict
Jun 19, 2024
5ee9966
Fix json queries
Jun 19, 2024
78fdfde
Fix json file path
Jun 19, 2024
f8f01a8
Add directory import
Jun 19, 2024
4de81b6
Add error on modification of collections provided by a source
Jun 27, 2024
794e4d2
Add simple XML source
Jun 27, 2024
01a09c9
Add tests for value conversion
Jun 27, 2024
d0e7cae
Xml enumerator fix
Jul 5, 2024
e53caae
Add error on duplicate files
Jul 17, 2024
0180e22
Add upload and link modes to json source
Jul 17, 2024
a1e3191
Add upload and link modes to xml source
Jul 17, 2024
b96d772
Add useful error message on duplicate file names for JSON source
Jul 17, 2024
d996505
Fix error on dropping document source
Jul 17, 2024
e09e8b7
Fix broken linking on data sources
Jul 19, 2024
32c743e
Add url option to json source
Aug 5, 2024
749c06f
Add url import to xml source
Aug 5, 2024
464fa8b
Add xml file name from url to xml source
Aug 5, 2024
8ae810d
Set useful default value for url
Aug 5, 2024
4ad101b
Fix JSON bugs
Aug 5, 2024
d2c2f43
Fix heap issues on XML source creation
Aug 5, 2024
cb05187
Fix heap issues on XML source enumeration
Aug 5, 2024
4878f08
Merge with master
gartens Aug 6, 2024
445368d
Remove comment
gartens Aug 6, 2024
563356d
Reformat & optimize imports
gartens Aug 6, 2024
bcb7f44
Allow MIT
gartens Aug 6, 2024
25c55fb
Fix XmlToPolyConverterTest
gartens Aug 6, 2024
898960d
Use GenericRuntimeException instead of RuntimeException
gartens Aug 6, 2024
013a474
Add missing dependency on :dbms:shadowJar
gartens Aug 6, 2024
23f98e1
Allow MIT
gartens Aug 6, 2024
b84513c
Add missing dependency on :dbms:shadowJar
gartens Aug 6, 2024
9552b0b
Remove broken upload feature
Aug 7, 2024
84af7ed
Fix leftovers from rebase
gartens Aug 7, 2024
684d340
Fix unit tests
Aug 7, 2024
2606d1a
Fix parser and add more complex example
Aug 8, 2024
c6c1f8a
Various changes according to code review on PR
Aug 11, 2024
4f8d21f
Format
gartens Aug 12, 2024
9501699
Convert ExportedDocument to record class
gartens Aug 19, 2024
ce91406
Remove unused method
gartens Aug 19, 2024
d956739
Convert ExportedColumn into a record
gartens Aug 19, 2024
af8f049
Move implements a class up to AbstractJdbcSource
gartens Aug 19, 2024
a3398de
Use exhaustive switch expressions
gartens Oct 21, 2024
fb7ba52
Use GenericRuntimeException instead of RuntimeException
gartens Oct 21, 2024
80033f2
Use .snapshot instead of .getInstance().getSnapshot()
gartens Oct 21, 2024
d41e57b
Use exhaustive switch expression instead of if
gartens Oct 21, 2024
85f63cc
Fix typos in log messages
gartens Oct 21, 2024
8d833d5
Small improvements
gartens Oct 21, 2024
3ad762e
Remove unused class
gartens Oct 21, 2024
bdb4d90
Remove unused variable
gartens Oct 21, 2024
6ef698c
Annotate parameter as not null
gartens Oct 21, 2024
34eadc0
Replace builder with passing parameters directly
gartens Oct 21, 2024
19fc8f1
Inline simple conversions
gartens Oct 21, 2024
f1e2869
Remove superfluous overrides
gartens Oct 21, 2024
16dce89
Fix typo in JsonToPolyConversion
gartens Oct 21, 2024
e0af943
Annotate parameter as not null
gartens Oct 21, 2024
8d5bc93
Use stricter visibilities for classes & methods
gartens Oct 21, 2024
8a654b2
Remove unused field "fields" from {Json,Xml}Scan
gartens Oct 21, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@

public enum ConnectionMethod {
LINK,
UPLOAD;
UPLOAD,
URL;


public static ConnectionMethod from( String name ) {
return ConnectionMethod.valueOf( name.toUpperCase() );
Expand Down
85 changes: 38 additions & 47 deletions core/src/main/java/org/polypheny/db/adapter/DataSource.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,68 +18,26 @@

import com.google.gson.JsonObject;
import com.google.gson.JsonSerializer;
import java.util.List;
import java.util.Map;
import lombok.AllArgsConstructor;
import java.util.Set;
import lombok.Getter;
import org.pf4j.ExtensionPoint;
import org.polypheny.db.catalog.catalogs.AdapterCatalog;
import org.polypheny.db.catalog.entity.LogicalAdapter.AdapterType;
import org.polypheny.db.type.PolyType;
import org.polypheny.db.catalog.logistic.DataModel;

@Getter
public abstract class DataSource<S extends AdapterCatalog> extends Adapter<S> implements ExtensionPoint {

private final Set<DataModel> supportedDataModels;
private final boolean dataReadOnly;


protected DataSource( final long adapterId, final String uniqueName, final Map<String, String> settings, final DeployMode mode, boolean dataReadOnly, S catalog ) {
protected DataSource( final long adapterId, final String uniqueName, final Map<String, String> settings, final DeployMode mode, boolean dataReadOnly, S catalog, Set<DataModel> supportedModels ) {
super( adapterId, uniqueName, settings, mode, catalog );
this.dataReadOnly = dataReadOnly;

this.supportedDataModels = Set.copyOf( supportedModels );
informationPage.setLabel( "Sources" );
}


public abstract Map<String, List<ExportedColumn>> getExportedColumns();


@AllArgsConstructor
public static class ExportedColumn {

public final String name;
public final PolyType type;
public final PolyType collectionsType;
public final Integer length;
public final Integer scale;
public final Integer dimension;
public final Integer cardinality;
public final boolean nullable;
public final String physicalSchemaName;
public final String physicalTableName;
public final String physicalColumnName;
public final int physicalPosition;
public final boolean primary;


public String getDisplayType() {
String typeStr = type.getName();
if ( scale != null ) {
typeStr += "(" + length + "," + scale + ")";
} else if ( length != null ) {
typeStr += "(" + length + ")";
}

if ( collectionsType != null ) {
typeStr += " " + collectionsType.getName();
if ( cardinality != null ) {
typeStr += "(" + dimension + "," + cardinality + ")";
} else if ( dimension != null ) {
typeStr += "(" + dimension + ")";
}
}
return typeStr;
}

}

Expand All @@ -104,4 +62,37 @@ private AdapterType getAdapterType() {
return AdapterType.SOURCE;
}


public boolean supportsRelational() {
return supportedDataModels.contains( DataModel.RELATIONAL );
}


public boolean supportsDocument() {
return supportedDataModels.contains( DataModel.DOCUMENT );
}


public boolean supportsGraph() {
return supportedDataModels.contains( DataModel.GRAPH );
}


public RelationalDataSource asRelationalDataSource() {
// should be overridden by subclasses accordingly
throw new IllegalStateException( "This source does not support the relational data model." );
}


public DocumentDataSource asDocumentDataSource() {
// should be overridden by subclasses accordingly
throw new IllegalStateException( "This source does not support the document data model." );
}


public DocumentDataSource asGraphDataSource() {
// should be overridden by subclasses accordingly
throw new IllegalStateException( "This source does not support the graph data model." );
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
/*
* Copyright 2019-2024 The Polypheny Project
*
* Licensed 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.polypheny.db.adapter;

import java.util.List;
import lombok.Getter;
import org.polypheny.db.catalog.logistic.EntityType;

public interface DocumentDataSource {

List<ExportedDocument> getExportedCollection();

record ExportedDocument( String name, boolean isModifiable, EntityType type ) {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*
* Copyright 2019-2024 The Polypheny Project
*
* Licensed 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.polypheny.db.adapter;

import java.util.List;
import java.util.Map;
import org.polypheny.db.type.PolyType;

public interface RelationalDataSource {

Map<String, List<ExportedColumn>> getExportedColumns();

record ExportedColumn( String name, PolyType type, PolyType collectionsType, Integer length, Integer scale, Integer dimension, Integer cardinality, boolean nullable, String physicalSchemaName, String physicalTableName, String physicalColumnName, int physicalPosition, boolean primary ) {

public String getDisplayType() {
String typeStr = type.getName();
if ( scale != null ) {
typeStr += "(" + length + "," + scale + ")";
} else if ( length != null ) {
typeStr += "(" + length + ")";
}

if ( collectionsType != null ) {
typeStr += " " + collectionsType.getName();
if ( cardinality != null ) {
typeStr += "(" + dimension + "," + cardinality + ")";
} else if ( dimension != null ) {
typeStr += "(" + dimension + ")";
}
}
return typeStr;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,10 @@ public Expression asExpression() {
return Expressions.call( Catalog.CATALOG_EXPRESSION, "getCollection", Expressions.constant( id ) );
}


@Override
public String getNamespaceName() {
return Catalog.snapshot().getNamespace( namespaceId ).orElseThrow().name;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
import org.polypheny.db.catalog.catalogs.LogicalDocumentCatalog;
import org.polypheny.db.catalog.entity.logical.LogicalCollection;
import org.polypheny.db.catalog.entity.logical.LogicalNamespace;
import org.polypheny.db.catalog.exceptions.GenericRuntimeException;
import org.polypheny.db.catalog.logistic.Pattern;
import org.polypheny.db.catalog.snapshot.LogicalDocSnapshot;

Expand All @@ -45,7 +46,15 @@ public class LogicalDocSnapshotImpl implements LogicalDocSnapshot {
public LogicalDocSnapshotImpl( Map<Long, LogicalDocumentCatalog> catalogs ) {
this.namespaces = ImmutableMap.copyOf( catalogs.values().stream().collect( Collectors.toMap( n -> n.getLogicalNamespace().id, LogicalCatalog::getLogicalNamespace ) ) );
this.collections = ImmutableMap.copyOf( catalogs.values().stream().flatMap( c -> c.getCollections().values().stream() ).collect( Collectors.toMap( c -> c.id, c -> c ) ) );
this.collectionNames = ImmutableMap.copyOf( this.collections.values().stream().collect( Collectors.toMap( c -> c.name, c -> c ) ) );
this.collectionNames = ImmutableMap.copyOf( this.collections.values().stream().collect(
Collectors.toMap(
c -> c.name,
c -> c,
( existing, replacement ) -> {
throw new GenericRuntimeException( "A collection of documents called '" + existing.name + "' already exists." );
}
)
) );
this.namespaceCollections = ImmutableMap.copyOf( catalogs.values().stream().collect( Collectors.toMap( c -> c.getLogicalNamespace().id, c -> List.copyOf( c.getCollections().values() ) ) ) );
}

Expand Down
2 changes: 1 addition & 1 deletion core/src/main/java/org/polypheny/db/routing/DmlRouter.java
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public interface DmlRouter {
/**
* Routes DML queries and returns a RelNode.
*/
AlgNode routeDml( LogicalRelModify node, Statement statement );
AlgNode routeRelationalDml( LogicalRelModify node, Statement statement );

/**
* Routes conditional executes and directly returns a RelNode.
Expand Down
22 changes: 17 additions & 5 deletions core/src/main/java/org/polypheny/db/security/SecurityManager.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,8 +25,9 @@
import javax.annotation.Nullable;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;


@Slf4j
public class SecurityManager {

private static SecurityManager INSTANCE = null;
Expand Down Expand Up @@ -74,15 +75,26 @@ public boolean checkPathAccess( Path path ) {
dir = dir.getParent();
}
AuthStatus status = this.status.get( dir );

if ( dir.startsWith( "classpath:" ) ) {
status.setStep( AuthStep.SUCCESSFUL );
return true;
}
if ( status == null ) {
log.debug( "No auth status available for directory {}", dir );
return false;
}
if ( status.step == AuthStep.SUCCESSFUL ) {
return true;
}
if ( Arrays.stream( Objects.requireNonNull( status.path.toFile().listFiles() ) ).noneMatch( f -> f.getName().equals( "polypheny.access" ) && f.isFile() ) ) {
// TODO: if more fine-grained access control is required, add as content of file
try {
if ( Arrays.stream( Objects.requireNonNull( status.path.toFile().listFiles() ) ).noneMatch( f -> f.getName().equals( "polypheny.access" ) && f.isFile() ) ) {
// TODO: if more fine-grained access control is required, add as content of file
return false;
}
} catch ( Exception e ) {
log.debug( "Filed to check for polypheny.access as the specified path is not a directory {}", dir );
return false;
}

status.setStep( AuthStep.SUCCESSFUL );
return true;
}
Expand Down
2 changes: 2 additions & 0 deletions core/src/main/java/org/polypheny/db/util/Source.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,8 @@ public interface Source {

String path();

boolean isFile();

Reader reader() throws IOException;

InputStream openStream() throws IOException;
Expand Down
16 changes: 11 additions & 5 deletions core/src/main/java/org/polypheny/db/util/Sources.java
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ private static String trimOrNull( String s, String suffix ) {
}


private static boolean isFile( Source source ) {
private static boolean protocolIsFile( Source source ) {
return source.protocol().equals( "file" );
}

Expand Down Expand Up @@ -190,6 +190,12 @@ public String path() {
}


@Override
public boolean isFile() {
return file != null;
}


@Override
public Reader reader() throws IOException {
final InputStream is;
Expand Down Expand Up @@ -234,7 +240,7 @@ public Source trimOrNull( String suffix ) {

@Override
public Source append( Source child ) {
if ( isFile( child ) ) {
if ( protocolIsFile( child ) ) {
if ( child.file().isAbsolute() ) {
return child;
}
Expand All @@ -261,16 +267,16 @@ public Source append( Source child ) {

@Override
public Source relative( Source parent ) {
if ( isFile( parent ) ) {
if ( isFile( this ) && file.getPath().startsWith( parent.file().getPath() ) ) {
if ( protocolIsFile( parent ) ) {
if ( protocolIsFile( this ) && file.getPath().startsWith( parent.file().getPath() ) ) {
String rest = file.getPath().substring( parent.file().getPath().length() );
if ( rest.startsWith( File.separator ) ) {
return Sources.file( null, rest.substring( File.separator.length() ) );
}
}
return this;
} else {
if ( !isFile( this ) ) {
if ( !protocolIsFile( this ) ) {
String rest = Sources.trimOrNull( url.toExternalForm(), parent.url().toExternalForm() );
if ( rest != null && rest.startsWith( "/" ) ) {
return Sources.file( null, rest.substring( 1 ) );
Expand Down
Loading